GDB 是我们平时调试 c/c++程序的利器, 查起复杂的 bug 问题,比打印大法要好的多,但是也不得不说, gdb 在默认情况下用起来并不是很好用,最近学习到几个高级点的技巧,分享下:

一 美化打印

先上个例子:

#include <stdio.h>typedef struct {int i ;int j;char * str;int array[10];
} test_type_t;int main (int argc, char * argv[])
{test_type_t t = {1,2,"test a str" ,{1,2,3,4,5,6,7,8,9,10}};printf("i:%d,j:%d\n", t.i,t.j);return 0;
}

gdb 调试:

(gdb) n
14   printf("i:%d,j:%d\n", t.i,t.j);
(gdb) p t
$1 = {i = 1, j = 2, str = 0x400710 "test a str", array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}
(gdb) set print pretty
(gdb) p t
$2 = {i = 1,j = 2,str = 0x400710 "test a str",array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
}
(gdb)

set print pretty 通过这个命令可以美化 gdb 的打印,特别是复杂的数据结构,比较推荐.set print array-indexes on 打印数组的下标

这些命令如果记不住怎么办, 类似 linux 启动,会执行些初始化脚本,gdb 也一样,脚本名为:.gdbinit将刚才的两个命令加入进去,就不用每次执行这个命令调试.

二 动态打印

一般情况下,我们用 gdb 调试的时候,先在产生问题的代码的调用逻辑链上设置端点,调试,有时候为了更方便调试,我们会在这些关键地方加上打印语句. 有时候加的打印代码不够全,来回要反复几次. 每次加好打印代码后,需要编译程序,来回折腾几次,确实比较麻烦.

GDB 提供动态打印功能,可以在不修改代码情况下,动态插入打印语句,说的比较抽象,举例说明:

  2 #include <stdio.h>34 typedef struct {5  int i ;6  int j;7  char * str;8  int array[10];9 } test_type_t;1011 int main (int argc, char * argv[])12 {13   test_type_t t = {1,2,"test a str" ,{1,2,3,4,5,6,7,8,9,10}};14   printf("i:%d,j:%d\n", t.i,t.j);1516   for (int k = 0; k <= 10; k++ ) {17       t.array[k] += 10;18   }19   printf("index 0 :%d" ,t.array[0]);20   return 0;21 }

上面位置k <= 10; 这一行越界,调试下:

(gdb) dprintf 17,"k:%d,array[k]:%d\n",k,t.array[k]
Dprintf 2 at 0x40067f: file 1.c, line 17.
(gdb) r
Starting program: /test/a.outBreakpoint 1, main (argc=1, argv=0x7fffffffe648) at 1.c:13
13   test_type_t t = {1,2,"test a str" ,{1,2,3,4,5,6,7,8,9,10}};
Missing separate debuginfos, use: debuginfo-install glibc-2.17-196.el7_4.2.x86_64 libgcc-4.8.5-16.el7_4.2.x86_64 libstdc++-4.8.5-16.el7_4.2.x86_64
(gdb) c
Continuing.
i:1,j:2
k:0,array[k]:1
k:1,array[k]:2
k:2,array[k]:3
k:3,array[k]:4
k:4,array[k]:5
k:5,array[k]:6
k:6,array[k]:7
k:7,array[k]:8
k:8,array[k]:9
k:9,array[k]:10
k:10,array[k]:0
index 0 :11[Inferior 1 (process 150109) exited normally]
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00000000004005fc in main(int, char**) at 1.c:13breakpoint already hit 1 time
2       dprintf        keep y   0x000000000040067f in main(int, char**) at 1.c:17breakpoint already hit 11 timesprintf "k:%d,array[k]:%d\n",k,t.array[k]

我们通过dprintf 17,"k:%d,array[k]:%d\n",k,t.array[k] 在 17 行动态插入打印语句,然后执行的时候会直接打印出来,17 为行号,后面的语法类似 printf.

动态的语句实际是一个断点(通过 info b 可以看到),只不过这个断点会自动的恢复,这种动态语句是否可以继续在上面设置端点的,答案是不行的,通过 next 执行的时候也执行不到这个语句,只是运行到这里面的时候,可以自动打印变量.

三 断点信息的保存和加载

调试大型程序的时候,往往会设置很多断点,如果需要多次调试,来回敲这些断点信息,也是很烦的,可以通过save breakpoints filename的方法,将断点设置信息保存到filename文件中,GDB 启动后,在通过source filename方式加载:

(gdb) save breakpoints break.bp
Saved to file 'break.bp'.
(gdb) quit
[root@localhost test]# ls
1.c  a.out  break.bp
[root@localhost test]# cat break.bp
break main
dprintf  /test/1.c:17,"k:%d,array[k]:%d\n",k,t.array[k]

加载:

[root@localhost test]# gdb ./a.out
Reading symbols from /test/a.out...done.
(gdb) source break.bp
Breakpoint 1 at 0x4005fc: file 1.c, line 13.
Dprintf 2 at 0x40067f: file 1.c, line 17.
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00000000004005fc in main(int, char**) at 1.c:13
2       dprintf        keep y   0x000000000040067f in main(int, char**) at 1.c:17printf "k:%d,array[k]:%d\n",k,t.array[k]

是不是很方便那.

四 宏打印

如下示例代码:

 1 #include <stdio.h>23 #define MAX(x,y) (x)>=(y)?(x):(y)45 int main(int argc , char * argv[])6 {7     char * p = (char*)0x12345;8     printf("max(3,5):%d\n",MAX(3,5));9     printf("print a test str\n");10     *p = 1;11     printf("end\n");12 }

如果通过-g选项编译是无法查看宏定义的,可以通过-g3 编译,就可以轻松查看宏定义和宏展开了,如下:

[root@localhost test]# gdb ./2
Reading symbols from /test/2...done.
(gdb) b main
Breakpoint 1 at 0x40064c: file 2.c, line 7.
(gdb) r
Starting program: /test/./2Breakpoint 1, main (argc=1, argv=0x7fffffffe648) at 2.c:7
7     char * p = (char*)0x12345;
Missing separate debuginfos, use: debuginfo-install glibc-2.17-196.el7_4.2.x86_64 libgcc-4.8.5-16.el7_4.2.x86_64 libstdc++-4.8.5-16.el7_4.2.x86_64
(gdb) n
8     printf("max(3,5):%d\n",MAX(3,5));
(gdb) p MAX(3,5)
$1 = 5
(gdb) macro expand MAX(3,5)
expands to: (3)>=(5)?(3):(5)

可以直接通过 p 命令打印宏,也可以进行macro expand MAX(3,5)宏展开,调试宏的时候比较有用.

五 gdb 调试界面

gdb 的调试一般是通过 n 执行下一条命令,然后通过 l 查看当前的代码,不够直观,gdb 其实也可以类似 IDE 那样,显示一行行代码执行,简单的直接输入layout 切换布局:再输入 n 的时候,箭头自动下移一行.

更棒的是可以同时显示源码和汇编,输入layout split

六 Debug 汇编

有时候,比较麻烦的 bug,可以需要反编译为汇编(disassemble /m main),然后调试, 还是如上的例子程序,调试如下,汇编模式常需要看寄存器的值,输入layout regs可以同时展示汇编和寄存器的值:这个模式下好处,可以通过 n 命令执行一条条看汇编指令.

我还是更喜欢以下的方式调试:

gdb) disassemble /m main
Dump of assembler code for function main(int, char**):
6 {0x000000000040063d <+0>: push   %rbp0x000000000040063e <+1>: mov    %rsp,%rbp0x0000000000400641 <+4>: sub    $0x20,%rsp0x0000000000400645 <+8>: mov    %edi,-0x14(%rbp)0x0000000000400648 <+11>: mov    %rsi,-0x20(%rbp)7     char * p = (char*)0x12345;
=> 0x000000000040064c <+15>: movq   $0x12345,-0x8(%rbp)8     printf("max(3,5):%d\n",MAX(3,5));0x0000000000400654 <+23>: mov    $0x5,%esi0x0000000000400659 <+28>: mov    $0x400720,%edi0x000000000040065e <+33>: mov    $0x0,%eax0x0000000000400663 <+38>: callq  0x400510 <printf@plt>9     printf("print a test str\n");0x0000000000400668 <+43>: mov    $0x40072d,%edi0x000000000040066d <+48>: callq  0x400530 <puts@plt>10     *p = 1;0x0000000000400672 <+53>: mov    -0x8(%rbp),%rax0x0000000000400676 <+57>: movb   $0x1,(%rax)11     printf("end\n");0x0000000000400679 <+60>: mov    $0x40073e,%edi0x000000000040067e <+65>: callq  0x400530 <puts@plt>12 }0x0000000000400683 <+70>: mov    $0x0,%eax0x0000000000400688 <+75>: leaveq0x0000000000400689 <+76>: retqEnd of assembler dump.
(gdb) ni
8     printf("max(3,5):%d\n",MAX(3,5));
(gdb) ni
0x0000000000400659 8     printf("max(3,5):%d\n",MAX(3,5));
(gdb) ni
0x000000000040065e 8     printf("max(3,5):%d\n",MAX(3,5));
(gdb) ni
0x0000000000400663 8     printf("max(3,5):%d\n",MAX(3,5));
(gdb) ni
max(3,5):5
9     printf("print a test str\n");
(gdb) ni
0x000000000040066d 9     printf("print a test str\n");
(gdb) ni
print a test str
10     *p = 1;
(gdb) info register

通过disassemble /m main 来将源码和汇编一起显示,ni 执行下一条汇编指令.info register来显示汇编信息,core 的位置的指令为:0x0000000000400676 <+57>: movb $0x1,(%rax) 将 1 赋值给 rax 寄存器保存的地址, 我们可以看下 rax 寄存器,p $rax 直接打印,然后通过x /x 0x12345 来测试下这个地址是否合法,这个地址不是栈的地址,也不是我们申请的堆地址,直接写,就引起的段错误.

七 诗词欣赏

滚滚长江东逝水,浪花淘尽英雄。出自明代[杨慎]滚滚长江东逝水,浪花淘尽英雄。
是非成败转头空。青山依旧在,几度夕阳红。
白发渔樵江渚上,惯看秋月春风。
一壶浊酒喜相逢。古今多少事,都付笑谈中。

你可能不知道的GDB命令相关推荐

  1. 又一波你可能不知道的 Linux 命令行网络监控工具

    又一波你可能不知道的 Linux 命令行网络监控工具 对任何规模的业务来说,网络监控工具都是一个重要的功能.网络监控的目标可能千差万别.比如,监控活动的目标可以是保证长期的网络服务.安全保护.对性能进 ...

  2. 你可能不知道的 docker 命令的奇淫怪巧

    你可能不知道的 docker 命令的奇淫怪巧 Intro 分享一些可能会用到的一些简单实用却可能是你不知道的 docker 命令 dangling images build 自己的 docker 镜像 ...

  3. 【Fluent TUI】如何开启TUI命令的智能提示,如何搜索不知道的TUI命令;代码智能提示、code hinting、command prompt

    一.问题背景 胡老师在他的公众号里发的关于TUI的文章,提到TUI命令有智能提示,但是我没有在preference对话框中找到相关选项. 后来发到群聊里,一个很友好的网友回答了这个问题--consol ...

  4. linux挂起 终端连不上吗,Linux系统11个不能不知道的终端命令

    Linux系统下有很丰富的终端命令,实际上这些命令可以看做是快捷键.如果知道这些命令,使用Linux系统无疑会变得更加方便.本文就来介绍一下Linux系统11个你不能不知道的终端命令. 1. 命令行日 ...

  5. 你所不知道的Shutdown命令

    你所不知道的Shutdown命令 当你按下win + R 在输入框中输入"shutdown -s -t 6"(拼手速的时候来了) 当你看到图一时,上面会写Windows将在6秒钟开 ...

  6. MySQL还能这样玩---第一篇之你所不知道的命令

    MySQL还能这样玩---第一篇之你所不知道的命令 命令篇 DDL---表定义相关命令操作 show create table 表名 ---查看创建表的SQL语句 alter table 表名 mod ...

  7. linux监控命令执行,你可能不知道的 即时监控 Linux 使用者执行指令的三种方法...

    原标题:你可能不知道的 即时监控 Linux 使用者执行指令的三种方法 这里介绍如何在 Linux 系统上以管理者权限即时监控一般使用者所执行的任何指令. Linux 的 root 管理者可对系统进行 ...

  8. 你应该知道的 Linux 命令行技巧

    2019独角兽企业重金招聘Python工程师标准>>> 这篇文章来源于Quroa的一个问答<What are some time-saving tips that every ...

  9. 你可能不知道的Shell

    Shell也叫做命令行界面,它是*nix操作系统下用户和计算机的交互界面.Shell这个词是指操作系统中提供访问内核服务的程序. 这篇文章向大家介绍Shell一些非广为人知.但却实用有趣的知识,权当品 ...

最新文章

  1. 1月23日服务器例行维护更新公告,1月24日服务器例行维护公告(已完成)
  2. 数据挖掘之人工神经网络BP算法
  3. distance from ifm to Sidney Sussex College: acceptable
  4. 用数组模拟队列的实现
  5. java基础知识 多线程
  6. c语言是否继续运行,C语言是否有运行时?
  7. 打造专业人才发展链条:基于任职资格的专业人才发展与认证体系
  8. 1512. 好数对的数目
  9. linux下tomcat8安装详解详解
  10. [Flex]实现Application未初始化前加载自定义配置内容
  11. python怎么群发邮件_小工具:使用python群发邮件
  12. 提交代码遇到的问题remote: Support for password authentication was removed on August 13, 2021.
  13. 为什么今年的苹果手机不支持5G?原因有四点,网友:错怪库克了
  14. 想方便快捷的分享/收藏图片?试试免费好用的微博/b站图床
  15. MacBook解决控制台图标问题
  16. 数据处理的基本方法--简要介绍
  17. yuv数据(nv12和nv21)和RGB数据之间转换的c++代码
  18. [OS-Linux] CentOS 7.x 系统安装以及常用配置
  19. android windows 无线视频传输,基于Android端到端实时无线视频传输系统
  20. 实验三:跟踪分析Linux内核的启动过程 ----- 20135108 李泽源

热门文章

  1. GLView基本分析
  2. POI 实现Word替换文本2种情况(正常文本、表格文本)
  3. OS X EI Captan 中Rootless
  4. matlab 函数,matlab 语法1
  5. Filter过滤器的理解(一)
  6. 【基础数学】克罗内克内积 Kronecker product
  7. SQL执行顺序与书写顺序
  8. Java小游戏——贪吃蛇
  9. 《第五周RFID作业》物联112118 林家辉
  10. upc 6617: Finite Encyclopedia of Integer Sequences(树的先序遍历第n/2个结点)