前言
前面通过《启动调试》,《断点设置》,《变量查看》,我们已经了解了GDB基本的启动,设置断点,查看变量等,如果这些内容你还不知道,建议先回顾一下前面的内容。在启动调试设置断点观察之后,没有我们想要的信息怎么办呢?这个时候,就需要单步执行或者跳过当前断点继续执行等等。而本文所说的单步调试并非仅仅指单步执行,而是指在你的控制之下,按要求执行语句。
准备
老规矩,先准备一个示例程序如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
/*计算简单乘法,这里没有考虑溢出*/ int (int a, int b) { int c = a b; return c; } /*打印从0到num-1的数*/ int count(int num) { int i = 0; if( 0 > num) return 0; while(i < num) { printf( "%dn",i); i ; } return i; } int main(void) { int a = 3; int b = 7; printf( "it will calc a bn"); int c = add(a,b); printf( "%d %d = %dn",a,b,c); count(c); return 0; } |
编译:
1 |
gcc -g -o gdbStep gdbStep.c |
程序的功能比较简单,这里不多做解释。
特别简单说明一条命令,list(可简写为l),它可以将源码列出来,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
(gdb) list 1 2 3 /*计算简单乘法,这里没有考虑溢出*/ 4 int (int a, int b) 5 { 6 int c = a * b; 7 return c; 8 } 9 int main(void) 10 { (gdb) l 11 int a = 13; 12 int b = 57; 13 printf( "it will calc a * bn"); 14 int c = add(a,b); 15 printf( "%d*%d = %dn",a,b,c); 16 return 0; 17 } (gdb) |
单步执行-next
next命令(可简写为n)用于在程序断住后,继续执行下一条语句,假设已经启动调试,并在第12行停住,如果要继续执行,则使用n执行下一条语句,如果后面跟上数字num,则表示执行该命令num次,就达到继续执行n行的效果了:
1 2 3 4 5 6 7 8 9 10 11 |
$ gdb gdbStep #启动调试 (gdb)b 25 #将断点设置在12行 (gdb)run #运行程序 Breakpoint 1, main () at gdbStep.c:25 25 int b = 7; (gdb) n #单步执行 26 printf("it will calc a bn"); (gdb) n 2 #执行两次 it will calc a b 28 printf("%d %d = %dn",a,b,c); (gdb) |
从上面的执行结果可以看到,我们在25行处断住,执行n之后,运行到26行,运行n 2之后,运行到28行,但是有没有发现一个问题,为什么不会进入到add函数内部呢?那就需要用到另外一个命令啦。
单步进入-step
对于上面的情况,如果我们想跟踪add函数内部的情况,可以使用step命令(可简写为s),它可以单步跟踪到函数内部,但前提是该函数有调试信息并且有源码信息。
1 2 3 4 5 6 大专栏 GDB调试指南-单步调试 class="line">7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$ gdb gdbStep #启动调试 (gdb) b 25 #在12行设置断点 Breakpoint 1 at 0x4005d3: file gdbStep.c, line 25. (gdb) run #运行程序 Breakpoint 1, main () at gdbStep.c:25 25 int b = 7; (gdb) s 26 printf("it will calc a bn"); (gdb) s #单步进入,但是并没有该函数的源文件信息 _IO_puts (str=0x4006b8 "it will calc a b") at ioputs.c:33 33 ioputs.c: No such file or directory. (gdb) finish #继续完成该函数调用 Run till exit from #0 _IO_puts (str=0x4006b8 "it will calc a b") at ioputs.c:33 it will calc a b main () at gdbStep.c:27 27 int c = add(a,b); Value returned is $1 = 19 (gdb) s #单步进入,现在已经进入到了add函数内部 add (a=13, b=57) at gdbStep.c:6 6 int c = a b; |
从上面的过程可以看到,s命令会尝试进入函数,但是如果没有该函数源码,需要跳过该函数执行,可使用finish命令,继续后面的执行。如果没有函数调用,s的作用与n的作用并无差别,仅仅是继续执行下一行。它后面也可以跟数字,表明要执行的次数。
当然它还有一个选项,用来设置当遇到没有调试信息的函数,s命令是否跳过该函数,而执行后面的。默认情况下,它是会跳过的,即step-mode值是off:
1 2 3 4 |
(gdb) show step-mode Mode of the step operation is off. (gdb) set step-mode on (gdb) set step-mode off |
还有一个与step相关的命令是stepi(可简写为si),它与step不同的是,每次执行一条机器指令:
1 2 3 4 5 6 |
(gdb) si 0x0000000000400573 6 int c = a b; (gdb) display/i $pc 1: x/i $pc => 0x400573 <add 13>: mov -0x18(%rbp), |