gdb 调试工具学习

1.兹声明,此文章为本人原创,文责自负。
2.如需转载,麻烦您联系作者,并取得作者的明示同意。感谢。

工作中遇到一个c++ 进程crash的问题,在分析解决的过程的中用到了gdb,结合搜索的相关资料和例子,对gdb做下简单总结,完整的gdb 文档请参看 The GNU Project Debugger, 文中例子运行的机器为 Fedora 31.

1. 什么是 gdb ?

gdb 是一个程序调试工具!
(1). 一个可以让你清晰的知道程序在整个执行过程中每一步做了什么的工具!!!
(2). 多用于c/c++项目的调试,当然也支持一些别的语言。

2. gdb 能做什么?

(1) 在执行/运行一个可执行文件/程序后,正常情况下,我们能够看到的仅仅是标准输出/或者执行结果,对于程序的每一步执行情况,我们只能够通过源码去猜测实际中执行的样子,显然有的时候结果并不一定是理想结果,那我们此时一定好奇该程序这个时候做了什么呢?
(2) 我想单步调试自己的程序,就像在学c语言的时候用visual studio 6.0去单步调试和加断点。
(3) 自己的一个程序crash了,我想知道为啥crash?

当你在实际的工作中,你一定面对过这样的需求/疑问,好吧,gdb 就是用来解决以上痛点的!
简而言之,gdb 可以帮助你的事情有:
1). 加载程序后,以单步/断点/条件等各种方式运行,并可实时的查看运行过程中各种调用关系及各变量状态
2). 分析coredump,并打印crash时的堆栈及各状态,找到程序crash的原因

3. gdb 怎么用?

在bash中,执行 gdb, 如果安装了gdb tool,正常会进入到一个gdb的界面上。按一下help里面列出了很多命令可以使用。
还是从前面的两个用途处着手,看下常用的gdb 是怎样执行的。

1). 调试程序:

源文件例子:

#include <iostream>                                                              
#include <assert.h>                                                              
                                                                                 
                                                                                 
static int sum (int numberOne, int numberTwo);                                   
static int divide(int numberOne, int numberTwo);                                 
                                                                                 
int main() {                                                                     
//assert(false);                                                                 
  int a = 0;                                                                     
  int b = 5;                                                                     
  int c = sum(a, b);                                                             
  int d = divide(a, b);                                                          
  return 0;                                                                      
}                                                                                
                                                                                 
static int sum (int numberOne, int numberTwo) {                                  
  return numberOne + numberTwo;                                                  
}                                                                                
                                                                                 
static int divide(int numberOne, int numberTwo) {                                
  return numberOne / numberTwo;                                                  
}

(1). 在使用gdb 去调试程序的时候,首先你得先编译出这一个程序。在编译的时候,请一定注意要使用 -g 的编译参数(将程序指定于可供调试)。例如 g++ -g main.cpp -o test 去编译一个 test的程序。
(2). 先加载程序到gdb中,命令gdb <program> or 先 gdb 进入到gdb的控制台,然后file <program>。请将<program>改为你要调试的程序名。例: 运行 gdb test
(3). 这个时候程序加载到gdb中了,请运行下 run来让程序跑起来吧。
(4). 运行run后,程序就会从头开始执行,如果没有问题这个程序就会正常退出,exit 0.这个过程中,也不会有什么输出。那么如果我想要让程序不一下执行而是在某些地方停下呢。请使用断点命令 break <source file>:<line number>。例如 break main.cpp:3,不仅可以对具体的行加断点还可以在某个函数处加断点。例如break sum,这个时候重新run下,可以看到程序会先停在断点main.cpp的第三行, 如下

Starting program: /home/tli/docs/test 

Breakpoint 1, main () at main.cpp:10
Breakpoint 1, main () at main.cpp:10
10    int a = 0;
(gdb)

continue 可以直接跳到下一个断点,step 可以让你跳到下一行。另外直接按 enter, 什么都不输入,将重复上一个命令。按 continue 后,如下所示。

Breakpoint 3, sum (numberOne=0, numberTwo=5) at main.cpp:18
18    return numberOne + numberTwo;
(gdb) 

直接跳到了我们加的sum 断点处,其实我们可以看到入参是0和 5,继续按step, step, 跳两步就看到程序跑到了

(gdb) step
19  }
(gdb) step
main () at main.cpp:13
13    int d = divide(a, b);

你也可以使用 print 命令去打印相关变量的值,比如看下这个时候a, b的值。print a print b

(gdb) print a
$11 = 0
(gdb) print b
$12 = 5

这个print 还是蛮强大的,还可以打印对象。在工作中遇到的例子中可以使用 print *this 来打印当前的对象的信息,还可以print map对象里面的内容。其余的命令再慢慢探索吧。
到这里,对调试程序的过程应该有了初步的了解了。

2). 分析core dump

对面前的程序简单做一个修改,来对gdb 如何分析core dump进行说明。
程序例子:

  1 #include <iostream>                                                              
  2 #include <assert.h>                                                              
  3                                                                                  
  4                                                                                  
  5 static int sum (int numberOne, int numberTwo);                                   
  6 static int divide(int numberOne, int numberTwo);                                 
  7                                                                                  
  8 int main() {                                                                     
  9 //assert(false);                                                                 
 10   int a = 0;                                                                     
 11   int b = 5;                                                                     
 12   int c = sum(a, b);                                                             
 13   int d = divide(b, a);                                                          
 14   return 0;                                                                      
 15 }                                                                                
 16                                                                                  
 17 static int sum (int numberOne, int numberTwo) {                                  
 18   return numberOne + numberTwo;                                                  
 19 }                                                                                
 20                                                                                  
 21 static int divide(int numberOne, int numberTwo) {                                
 22   return numberOne / numberTwo;                                                  
 23 }

这个时候 g++ -g main.cpp -o test1,编译出可执行文件test1。我们在 shell 中运行一下我们的程序 ./test1

[tli@localhost docs]$ ./test1 
Floating point exception (core dumped)

这里告诉了我们是一个Floating point exception。我们暂且当作没看到它,仍然用解析core dump的方式去分析。因为很多情况下,我们只拿到的是一个dump文件/程序没有预兆的crash了。那么此例子中./test1程序产生的core dump文件去哪里了呢? linux下有一个文件/proc/sys/kernel/core_pattern来说明了产生的core dump文件的名字格式。执行下cat /proc/sys/kernel/core_pattern,可以看到是使用ststemd-coredump去指定的。如下示

[tli@localhost docs]$ cat /proc/sys/kernel/core_pattern
|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h

其实我们可以覆盖此文件,来指定输出的coredump文件目录和格式。那么使用systemd-coredump产生的目录是在 /var/lib/systemd/coredump中。如下我们看到我们的文件

[liyang@localhost coredump]$ ls -al
total 80
drwxr-xr-x. 2 root root  4096 Jan 15 15:19 .
drwxr-xr-x. 6 root root  4096 Nov 13 19:59 ..
-rw-r-----+ 1 root root 68558 Jan 15 15:07 core.test1.1000.e283c0ef6c8a489f82332660910c539c.44694.1579072044000000000000.lz4
[tli@localhost coredump]$ pwd
/var/lib/systemd/coredump

这个core.test1.* 就是我们要找的test1 产生的core dump文件了,这个格式是lz4压缩格式的,直接将它作为core dump文件传入 gdb, 会有一个 is not a core dump: File format not recognized 的报错。gdb 是不认这个格式的,解压缩它。

sudo lz4 core.test1.1000.e283c0ef6c8a489f82332660910c539c.44694.1579072044000000000000.lz4

这个解压缩的文件 core.test1.1000.e283c0ef6c8a489f82332660910c539c.44694.1579072044000000000000,就是我们要的core dump文件了。
有了coredump文件后,开始解析它。

  1. gdb <program> <core dump>
gdb test1 core.test1.1000.e283c0ef6c8a489f82332660910c539c.44694.1579072044000000000000
  1. 然后我们就可以看到程序直接是停在了的如下图所示的divide函数中。
Core was generated by `./test1'.
Program terminated with signal SIGFPE, Arithmetic exception.
#0  0x00000000004011a9 in divide (numberOne=5, numberTwo=0) at main.cpp:22
22    return numberOne / numberTwo;
(gdb) 

显然分析,我们就知道了numberTwo不应该为0。

  1. 可以查看完整的栈关系 bt or bt full, 如下图所示,整个调用的过程更加清晰了。
Core was generated by `./test1'.
Program terminated with signal SIGFPE, Arithmetic exception.
#0  0x00000000004011a9 in divide (numberOne=5, numberTwo=0) at main.cpp:22
22    return numberOne / numberTwo;
(gdb) bt full
#0  0x00000000004011a9 in divide (numberOne=5, numberTwo=0) at main.cpp:22
No locals.
#1  0x000000000040117d in main () at main.cpp:13
        a = 0
        b = 5
        c = 5
        d = 1255048768
(gdb)

在此过程中,请结合print使用更佳。
借用gdb 来分析 coredump的过程结束。

本文参考资料链接:

  1. https://www.cs.umd.edu/~srhuang/teaching/cmsc212/gdb-tutorial-handout.pdf
  2. https://sourceware.org/gdb/download/onlinedocs/
  3. https://stackoverflow.com/questions/2065912/core-dumped-but-core-file-is-not-in-the-current-directory
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容

  • 什么是coredump Coredump叫做核心转储,它是进程运行时在突然崩溃的那一刻的一个内存快照。操作系统在程...
    java菜阅读 4,204评论 0 4
  • 程序调试的基本思想是“分析现象->假设错误原因->产生新的现象去验证假设”这样一个循环过程,根据现象如何假设错误原...
    Manfred_Zone阅读 16,523评论 0 26
  • 调试前准备 获取进程的内核转储(core dump) why:最大好处是,其保存了问题发生时的状态。记录进程当前状...
    Gitlusen阅读 922评论 0 2
  • 刚回家又大吃一顿... 吃的啥呢?剩饭。 本来说好的晚上减肥,一口都不吃的。结果坚持了没几天,现在不光是回家吃水果...
    霁粹阅读 179评论 0 0
  • 我一直是个怂货,秒5拖了这么多年不敢看,就是因为不想接受两人不能在一起的爱情童话。 可《你的名字》太出众,我也总觉...
    蓝水蓝阅读 236评论 0 0