GDB 的 7 个单步调试命令
创始人
2024-03-02 13:41:10
0

即使是复杂的函数,也有几种方法可以单步调试,所以下次在排除代码故障时,可以尝试一下这些 GDB 技术。

调试器 是一个可以运行你的代码并检查问题的软件。GNU Debugger(GBD)是最流行的调试器之一,在这篇文章中,我研究了 GDB 的 step 命令和其他几种常见情况的相关命令。step 是一个被广泛使用的命令,但它有一些人们不太了解的地方,可能会使得他们十分困惑。此外,还有一些方法可以在不使用 step 命令的情况下进入一个函数,比如使用不太知名的 advance 命令。

1、无调试符号

考虑以下这个简单的示例程序:

#include 


int num() {
    return 2;
}

void bar(int i) {
    printf("i = %d\n", i);
}


int main() {
    bar(num());
    return 0;
}

如果你在没有 调试符号 debugging sysbols 的情况下进行编译(LCTT 译注:即在使用 gcc 编译程序时没有写 -g 选项),然后在 bar 上设置一个断点,然后尝试在这个函数内使用 step 来单步执行语句。GDB 会给出一个 没有行号信息 no line number information 的错误信息。

gcc exmp.c -o exmp
gdb ./exmp
(gdb) b bar
Breakpoint 1 at 0x401135
(gdb) r
Starting program: /home/ahajkova/exmp
Breakpoint 1, 0x0000000000401135 in bar ()
(gdb) step
Single stepping until exit from function bar,
which has no line number information.
i = 2
0x0000000000401168 in main ()

2、stepi 命令

但是你仍然可以在没有行号信息的函数内部单步执行语句,但要使用 stepi 命令来代替 stepstepi 一次只执行一条指令。当使用 GDB 的 stepi 命令时,先做 display/i $pc 通常很有用,这会在每一步之后显示 程序计数器 program counter 的值和相应的 机器指令 machine instruction :

(gdb) b bar
Breakpoint 1 at 0x401135
(gdb) r
Starting program: /home/ahajkova/exmp
Breakpoint 1, 0x0000000000401135 in bar ()
(gdb) display/i $pc
1: x/i $pc
=> 0x401135 : sub $0x10,%rsp

在上述的 display 命令中,i 代表机器指令,$pc 表示程序计数器寄存器(即 PC 寄存器)。

使用 info registers 命令,来打印寄存器的内容,也是十分有用的。

(gdb) info registers
rax 0x2 2
rbx 0x7fffffffdbc8 140737488346056
rcx 0x403e18 4210200
(gdb) print $rax
$1 = 2
(gdb) stepi
0x0000000000401139 in bar ()
1: x/i $pc
=> 0x401139 : mov %edi,-0x4(%rbp)

3、复杂的函数调用

在带调试符号的 -g 选项,重新编译示例程序后,你可以使用行号在 mainbar 调用上设置断点,然后再单步执行 bar 函数的语句:

gcc -g exmp.c -o exmp
gdb ./exmp
(gdb) b exmp.c:14
Breakpoint 1 at 0x401157: file exmp.c, line 14.
(gdb) r
Starting program: /home/ahajkova/exmp
Breakpoint 1, main () at exmp.c:14
14 bar(num());

接下来,用 step,来单步执行 bar() 函数的语句:

(gdb) step
num () at exmp.c:4
4 return 2;

函数调用的参数需要在实际的函数调用之前进行处理,bar() 函数的参数是 num() 函数,所以 num() 会在 bar() 被调用之前执行。但是,通过 GDB 调试,你怎么才能如愿以偿地进入 bar() 函数呢?你可以使用 finish 命令,并再次使用 step 命令。

(gdb) finish
Run till exit from #0 num () at exmp.c:4
0x0000000000401161 in main () at exmp.c:14
14 bar(num());
Value returned is $1 = 2
(gdb) step
bar (i=2) at exmp.c:9
9 printf("i = %d\n", i);

4、tbreak 命令

tbreak 命令会设置一个临时断点。如果你不想设置永久断点,那么这个命令是很有用的。举个例子?,你想进入一个复杂的函数调用,例如 f(g(h()), i(j()), ...),在这种情况下,你需要一个很长的 step/finish/step 序列,才能到达 f 函数。如果你设置一个临时断点,然后再使用 continue 命令,这样就不需要以上的序列了。为了证明这一点,你需要像以前一样将断点设置在 mainbar 调用上。然后在 bar 上设置临时断点。当到达该临时断点后,临时断点会被自动删除。

(gdb) r
Starting program: /home/ahajkova/exmp
Breakpoint 1, main () at exmp.c:14
14 bar(num());
(gdb) tbreak bar
Temporary breakpoint 2 at 0x40113c: file exmp.c, line 9.

在调用 bar 的时候遇到断点,并在 bar 上设置临时断点后,你只需要使用 continue 继续运行直到 bar 结束。

(gdb) continue
Continuing.
Temporary breakpoint 2, bar (i=2) at exmp.c:9
9 printf("i = %d\n", i);

5、disable 命令

类似地,你也可以在 bar 上设置一个正常的断点,然后执行 continue,然后在不再需要第二个断点时,使用 disable 命令禁用这个断点,这样也能达到与 tbreak 相同的效果。

(gdb) b exmp.c:14
Breakpoint 1 at 0x401157: file exmp.c, line 14.
(gdb) r
Starting program: /home/ahajkova/exmp
Breakpoint 1, main () at exmp.c:14
14 bar(num());
(gdb) b bar
Breakpoint 2 at 0x40113c: file exmp.c, line 9.
(gdb) c
Continuing.
Breakpoint 2, bar (i=2) at exmp.c:9
9 printf("i = %d\n", i);
(gdb) disable 2

正如你所看到的,info breakpoints 命令在 Enb 列下显示为 n,这意味着这个断点已被禁用。但你也能在再次需要这个断点时,再启用它。

(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000000401157 in main at exmp.c:14
breakpoint already hit 1 time
2 breakpoint keep n 0x000000000040113c in bar at exmp.c:9
breakpoint already hit 1 time
(gdb) enable 2
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000040116a in main at exmp.c:19
breakpoint already hit 1 time
2 breakpoint keep y 0x0000000000401158 in bar at exmp.c:14
breakpoint already hit 1 time

6、advance 命令运行程序到指定的位置

另一个进入函数内部的方法是 advance 命令。你可以简单地用 advance bar,来代替 tbreak bar ; continue。这一命令会将程序继续运行到指定的位置。

advance 命令的一个很棒的地方在于:如果程序并没有到达你试图进入的位置,那么 GDB 将在当前函数运行完成后停止。因此,程序的执行会受到限制:

Breakpoint 1 at 0x401157: file exmp.c, line 14.
(gdb) r
Starting program: /home/ahajkova/exmp
Breakpoint 1, main () at exmp.c:14
14 bar(num());
(gdb) advance bar
bar (i=2) at exmp.c:9
9 printf("i = %d\n", i);

7、skip 命令

进入 bar 函数的另一种方式是使用 skip num 命令:

(gdb) b exmp.c:14
Breakpoint 1 at 0x401157: file exmp.c, line 14.
(gdb) skip num
Function num will be skipped when stepping.
(gdb) r
Starting program: /home/ahajkova/exmp
Breakpoint 1, main () at exmp.c:14
14 bar(num());
(gdb) step
bar (i=2) at exmp.c:9
9 printf("i = %d\n", i);

请使用 info skip 命令,来了解 GDB 跳过了哪些函数。num() 函数被标记为 y,表示跳过了 num() 函数:

(gdb) info skip
Num Enb Glob File RE Function
1 y n  n num

如果不再需要 skip,可以禁用(并稍后重新启用)或完全删除它。你可以添加另一个 skip,并禁用第一个 skip,然后全部删除。要禁用某个 skip,必须指定其编号(例如,skip disable 1),如果没有指定,则会禁用所有的 skip。启用或删除 skip 的工作原理相同:

(gdb) skip bar
(gdb) skip disable 1
(gdb) info skip
Num Enb Glob File RE Function
1 n n  n num
2 y n  n bar
(gdb) skip delete
(gdb) info skip
Not skipping any files or functions.

GDB 的 step 命令

使用 GDB 的 step 命令是调试程序的一个有用工具。即使是复杂的函数,也有几种方法可以单步调试这些函数,所以下次你在排除代码问题的时候,可以尝试一下这些 GDB 技术。


via: https://opensource.com/article/22/12/gdb-step-command

作者:Alexandra 选题:lkxed 译者:chai001125 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

相关内容

今天国际取得AI生成网页前...
金融界2025年6月18日消息,国家知识产权局信息显示,深圳市今天...
2025-06-18 10:43:16
python 基础系列篇:...
python 基础系列篇:三、认识函数、方法...
2025-06-01 09:29:50
从应用层到MCU,看Win...
文本编辑器/文本编辑框是应用层常见的键盘处理程序。微软泄露的Win...
2025-06-01 00:14:54
【UNIX 环境编程】GC...
💭 写在前面:本文将介绍如何使用 G...
2025-05-31 02:18:08
QT桌面客户端在Linux...
QT桌面客户端在Linux下的开发流程可以概括为以下几个主要步骤。...
2025-05-30 11:41:09
Jetson Orin平台...
前言 有个问题关于pcie vnet nvdia/drivers/...
2025-05-29 22:50:24

热门资讯

Helix:高级 Linux ... 说到 基于终端的文本编辑器,通常 Vim、Emacs 和 Nano 受到了关注。这并不意味着没有其他...
使用 KRAWL 扫描 Kub... 用 KRAWL 脚本来识别 Kubernetes Pod 和容器中的错误。当你使用 Kubernet...
JStock:Linux 上不... 如果你在股票市场做投资,那么你可能非常清楚投资组合管理计划有多重要。管理投资组合的目标是依据你能承受...
通过 SaltStack 管理... 我在搜索Puppet的替代品时,偶然间碰到了Salt。我喜欢puppet,但是我又爱上Salt了:)...
Epic 游戏商店现在可在 S... 现在可以在 Steam Deck 上运行 Epic 游戏商店了,几乎无懈可击! 但是,它是非官方的。...
《Apex 英雄》正式可在 S... 《Apex 英雄》现已通过 Steam Deck 验证,这使其成为支持 Linux 的顶级多人游戏之...
如何在 Github 上创建一... 学习如何复刻一个仓库,进行更改,并要求维护人员审查并合并它。你知道如何使用 git 了,你有一个 G...
2024 开年,LLUG 和你... Hi,Linuxer,2024 新年伊始,不知道你是否已经准备好迎接新的一年~ 2024 年,Lin...
什么是 KDE Connect... 什么是 KDE Connect?它的主要特性是什么?它应该如何安装?本文提供了基本的使用指南。科技日...
Opera 浏览器内置的 VP... 昨天我们报道过 Opera 浏览器内置了 VPN 服务,用户打开它可以防止他们的在线活动被窥视。不过...