前言

前面通过《启动调试》,《断点设置》,《变量查看》,我们已经了解了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),%eax
(gdb)

继续执行到下一个断点-continue

我们可能打了多处断点,或者断点打在循环内,这个时候,想跳过这个断点,甚至跳过多次断点继续执行该怎么做呢?可以使用continue命令(可简写为c)或者fg,它会继续执行程序,直到再次遇到断点处:

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
$ gdb gdbStep
(gdb)b 18 #在count函数循环内打断点
(gdb)run
Breakpoint 1, count (num=10) at gdbStep.c:18
18 i++;
(gdb) c #继续运行,直到下一次断住
Continuing.
1
Breakpoint 1, count (num=10) at gdbStep.c:18
18 i++;
(gdb) fg #继续运行,直到下一次断住
Continuing.
2
Breakpoint 1, count (num=10) at gdbStep.c:18
18 i++;
(gdb) c 3 #跳过三次
Will ignore next 2 crossings of breakpoint 1. Continuing.
3
4
5
Breakpoint 1, count (num=10) at gdbStep.c:18
18 i++;

继续运行到指定位置-until

假如我们在25行停住了,现在想要运行到29行停住,就可以使用until命令(可简写为u):

1
2
3
4
5
6
7
8
9
$ gdb gdbStep
(gdb)b 25
(gdb)run
(gdb) u 29
it will calc a + b
3 + 7 = 10
main () at gdbStep.c:29
29 count(c);
(gdb)

可以看到,在执行u 29之后,它在29行停住了。它利用的是临时断点。

跳过执行—skip

skip可以在step时跳过一些不想关注的函数或者某个文件的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ gdb gdbStep
(gdb) b 27
Breakpoint 1 at 0x4005e4: file gdbStep.c, line 27.
(gdb) skip function add #step时跳过add函数
Function add will be skipped when stepping.
(gdb) info skip #查看step情况
Num Type Enb What
1 function y add
(gdb) run
Starting program: /home/hyb/workspaces/gdb/gdbStep
it will calc a + b
Breakpoint 1, main () at gdbStep.c:27
27 int c = add(a,b);
(gdb) s
28 printf("%d + %d = %dn",a,b,c);
(gdb)

可以看到,再使用skip之后,使用step将不会进入add函数。
step也后面也可以跟文件:

1
(gdb)skip file gdbStep.c

这样gdbStep.c中的函数都不会进入。

其他相关命令:

  • skip delete [num] 删除skip
  • skip enable [num] 使能skip
  • skip disable [num] 去使能skip

其中num是前面通过info skip看到的num值,上面可以带或不带该值,如果不带num,则针对所有skip,如果带上了,则只针对某一个skip。

总结

本文主要介绍了一些简单情况的单步调试方法或常见命令使用,但这些已经够用了,毕竟大部分程序的执行或停止都在我们的掌控之中了。

GDB调试指南-单步调试的更多相关文章

  1. GDB调试指南-启动调试

    前言 GDB(GNU Debugger)是UNIX及UNIX-like下的强大调试工具,可以调试ada, c, c++, asm, minimal, d, fortran, objective-c, ...

  2. [原创]GDB调试指南-断点设置

    前言 上篇<GDB调试指南-启动调试>我们讲到了GDB启动调试的多种方式,分别应用于多种场景.今天我们来介绍一下断点设置的多种方式. 为何要设置断点 在介绍之前,我们首先需要了解,为什么需 ...

  3. 调试多线程 & 查死锁的bug & gcore命令 & gdb对多线程的调试 & gcore & pstack & 调试常用命令

    gdb thread apply all bt 如果你发现有那么几个栈停在 pthread_wait 或者类似调用上,大致就可以得出结论:就是它们几个儿女情长,耽误了整个进程. 注意gdb的版本要高于 ...

  4. Hawk 4.7 单步调试

    单步调试的意义 已经编写的工作流,可能会因为某些外界环境的变化而出错,此时需要排除错误,我们可以使用单步调试. 单步调试的本质,相当于只使用前n个模块,这样就能看到每个步骤下,流的改变. 例子 还是上 ...

  5. 自己动手实现java断点/单步调试(一)

    又是好长时间没有写博客了,今天我们就来谈一下java程序的断点调试.写这篇主题的主要原因是身边的公司或者个人都执着于做apaas平台,简单来说apaas平台就是一个零代码或者低代码的配置平台,通过配置 ...

  6. GDB 调试指南

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,欢迎大家关注,二维码文末可以扫. 00 介绍 ...

  7. GDB调试指南-变量查看

    前言 在启动调试以及设置断点之后,就到了我们非常关键的一步-查看变量.GDB调试最大的目的之一就是走查代码,查看运行结果是否符合预期.既然如此,我们就不得不了解一些查看各种类型变量的方法,以帮助我们进 ...

  8. GDB:从单线程调试到多线程调试(MFiX单步调试)

    GDB:从单线程调试到多线程调试 1. 裸跑GDB 1.1 安装GDB sudo apt-get install gdb 1.2 编译程序 由于需要调试,因此编译的时候需要添加-g编译参数: 1.3 ...

  9. GDB 单步调试汇编

    本文同时发表在 https://github.com/zhangyachen/zhangyachen.github.io/issues/134 之前在看汇编的时候一直是肉眼看GCC -S的结果,缺点是 ...

随机推荐

  1. keras中保存自定义层和loss

    在keras中保存模型有几种方式: (1):使用callbacks,可以保存训练中任意的模型,或选择最好的模型 logdir = './callbacks' if not os.path.exists ...

  2. jquery选择器之获取父级元素、同级元素、子元素

    一.获取父级元素 1. parent([expr]): 获取指定元素的所有父级元素 二.获取同级元素: 1.next([expr]): 获取指定元素的下一个同级元素 2.nextAll([expr]) ...

  3. 把cifar数据转换为图片

    参考 https://gist.github.com/fzliu/64821d31816bce595a4bbd98588b37f5 """ make_cifar10.py ...

  4. Linux实验总结(第二周)

    测试一--vi 每个.c一个文件,每个.h一个文件,文件名中最好有自己的学号 用Vi输入图中代码,并用gcc编译通过 在Vi中使用K查找printf的帮助文档 提交vi编辑过程截图,要全屏,包含自己的 ...

  5. Appium获取元素的方式

    1.apk包名和launcherActivity 1.1.获取包名 所有应用包名列表 adb shell pm list packages 第三方应用包名列表 adb shell pm list pa ...

  6. rsync配置文件模板

    用脚本实现服务端rsyncd的部署cat /server/scripts/rsync_install.sh #!/bin/bash #安装包 yum install -y rsync &> ...

  7. Nginx_安全1

    Nginx 安全 nginx隐藏版本号 # 在Nginx的配置文件中进行修改,增加下面这个. server_tokens on; nginx对IP和目录限速 # Nginx可以通过HTTPLimitZ ...

  8. 3)利用Build.php自动创建目录和文件

    (1)首先做法参照: thinkphp5的手册的  命令行--->自动生成目录结构 或者看云的资料:https://www.kancloud.cn/manual/thinkphp5/118021 ...

  9. D. Colored Boots(STL)

    There are nn left boots and nn right boots. Each boot has a color which is denoted as a lowercase La ...

  10. Git ubuntu 升级

    外文文档 This team will distribute the most current stable package of Git for Ubuntu. Stable releases: h ...