- A+
文章目录[隐藏]
- 1、无调试符号
- 2、stepi 命令
- 3、复杂的函数调用
- 4、tbreak 命令
- 5、disable 命令
- 6、advance 命令运行程序到指定的位置
- 7、skip 命令
- GDB 的 step 命令
作者:
Alexandra
译者:
LCTT chai001125
| 2022-12-19 09:38
即使是复杂的函数,也有几种方法可以单步调试,所以下次在排除代码故障时,可以尝试一下这些 GDB 技术。
调试器 是一个可以运行你的代码并检查问题的软件。GNU Debugger(GBD)是最流行的调试器之一,在这篇文章中,我研究了 GDB 的 step
命令和其他几种常见情况的相关命令。step
是一个被广泛使用的命令,但它有一些人们不太了解的地方,可能会使得他们十分困惑。此外,还有一些方法可以在不使用 step
命令的情况下进入一个函数,比如使用不太知名的 advance
命令。
1、无调试符号
考虑以下这个简单的示例程序:
#include <stdio.h> int num() { return 2; } void bar(int i) { printf("i = %d/n", i); } int main() { bar(num()); return 0; }
如果你在没有 调试符号 的情况下进行编译(LCTT 译注:即在使用 gcc
编译程序时没有写 -g
选项),然后在 bar
上设置一个断点,然后尝试在这个函数内使用 step
来单步执行语句。GDB 会给出一个 没有行号信息 的错误信息。
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
命令来代替 step
。stepi
一次只执行一条指令。当使用 GDB 的 stepi
命令时,先做 display/i $pc
通常很有用,这会在每一步之后显示 程序计数器 的值和相应的 机器指令:
(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 <bar+4>: 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 <bar+8>: mov %edi,-0x4(%rbp)
3、复杂的函数调用
在带调试符号的 -g
选项,重新编译示例程序后,你可以使用行号在 main
中 bar
调用上设置断点,然后再单步执行 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
命令,这样就不需要以上的序列了。为了证明这一点,你需要像以前一样将断点设置在 main
的 bar
调用上。然后在 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 <none> 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 <none> n num 2 y n <none> 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中国 荣誉推出
原创文章,作者:ItWorker,如若转载,请注明出处:https://www.ytso.com/294934.html
- 我的微信公众号
- 扫一扫关注
- 我的新浪微博号
- 扫一扫关注