从 0 开始学习 Linux 系列之「08.15 个 gdb 调试基础命令」

代码键盘

版权声明:本文为 cdeveloper 原创文章,可以随意转载,但必须在明确位置注明出处!

gdb 简介

gdbUNIXUNIX-like 下的调试工具,在 Linux 下一般都直接在命令行中用 gdb 来调试程序,相比 Windows 上的集成开发环境 IDE 提供的图形界面调试,一开始使用 gdb 调试可能会让你感到生无可恋,但是只要熟悉了 gdb 调试的常用命令,调试出程序会很有成就感,一方面因为这些命令就类似图形界面调试按钮背后的逻辑,另一方面用命令行来调试程序,逼格瞬间就上了一个档次,这次就跟大家分享 gdb 调试的基本技术和 15 个常用调试命令

在此之前,我们先来回顾下在 Windows 上使用 IDE 的图形界面调试过程。

IDE 的调试步骤

在 Windows 的 IDE 下调试程序,例如使用 VS,一般都有下面这几个操作:

  1. Debug 模式编译并启动程序
  2. 程序运行出错,打断点分析出错的地方
  3. 单步运行程序,包括:step over 单步执行;step into 跳入函数;step return 跳出函数
  4. 还有全速运行,打印或者监视变量,冻结或解冻线程等调试技术

在 IDE 中上面的这些步骤一般都有固定的按钮提供给我们使用,非常的简单方便,我们只要多练习练习,在图形界面调试程序不会很难,但是在 Linux 下用命令来调试程序就比图形界面要复杂很多了。

其实,你知道真正的调试高手是什么样的吗?就是 Ta 对计算机原理和程序本身的逻辑理解非常深刻在 Ta 的脑海中已经可以模拟程序的运行过程,并且知道可能出错的地方,这样连断点都不用打了,而且 Bug 的命中率也不低,或许这就是真正的大佬吧 :)

我们回到正题,来介绍在 Linux 使用命令行的调试过程。

gdb 的调试步骤

在 Linux 下既然是使用命令行来调试,顾名思义就是手敲命令来调试程序,大体分为下面几个步骤,后面会详细介绍:

  1. 编译可以调试的程序
  2. 运行程序,打断点
  3. 单步调试,监控变量
  4. 可视化调试
  5. 其他调试技术

可以看出,与 IDE 调试过程差不多,但是实际操作起来可是千差万别,可不是点按钮了,而是自己敲调试程序的命令,就相当于你正在学习那些调试按钮背后的原理,把这种方法学会,不用学就会使用 IDE 来调试程序,不管你信不信,我反正是信了 :)

下面开始正式的调试技术介绍。

15 个 gdb 调试基础命令

下面来正式介绍 gdb 常用的调试技术,都是调试命令,只看不做比较乏味,还是建议你跟我一起动手调试下面的程序,这样才能真正的学会,这是本次要调试的 hello.c 程序,非常简单:

#include <stdio.h>

int add(int x, int y) {
    return x + y;
}

int main() {
    int a = 1;
    int b = 2;
    printf("a = %d\n", a);
    printf("b = %d\n", b);

    int c = add(a, b);
    printf("%d + %d = %d\n", a, b, c);
    return 0;
}

1. 编译可以调试的程序

我们平常使用 gcc 编译的程序如果不加 [-g] 选项:

gcc hello.c -o hello

gdb 会提示该可执行文件没有调试符号,不能调试:

gdb hello
# gdb 提示信息
Reading symbols from a.out...(no debugging symbols found)...done.

如果需要让程序可以调试,就必须在编译的时候加上 [-g] 参数

gcc -g hello.c -o hello

2. 载入要调试的程序

我们在命令行下需要手动载入待调试的程序,有 2 种方法:

方法一 - gdb 可执行文件

使用如下的命令来载入可执行文件 hello 到 gdb 中:

gdb hello

载入成功,gdb 会打印一段提示信息,并且命令行前缀变为 (gdb),下面是我的 Ubuntu 打印的信息:

GNU gdb (Ubuntu 7.11.90.20161005-0ubuntu1) 7.11.90.20161005-git
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from hello...done.
(gdb) q

注:q 退出 gdb

方法二 - 使用 gdb 提供的 file 命令

第二种方法是在 gdb 环境中使用 file 命令,我们需要先进入 gdb 环境下:

gdb

使用 file hello 载入待调试程序:

...
(gdb) file hello
Reading symbols from hello...done.
(gdb) q

3. 查看调试程序

在 gdb 下查看调试程序使用命令 list 或简写 l,「回车」列出后面程序:

(gdb) list
1   #include <stdio.h>
2   
3   int add(int x, int y) {
4       return x + y;
5   }
6   
7   
8   int main() {
9       int a = 1;
10      int b = 2;
(gdb) 
11      printf("a = %d\n", a);
12      printf("b = %d\n", b);
13      
14      int c = add(a, b);
15      printf("%d + %d = %d\n", a, b, c);
16      return 0;
17  }

4. 添加断点

在 gdb 下添加断点使用命令 break 或简写 b,有下面几个常见用法(这里统一用 b):

  1. b function_name
  2. b row_num
  3. b file_name:row_num
  4. b row_num if condition

比如我们以第一个为例,在 main 函数上添加断点:

(gdb) b main
Breakpoint 1 at 0x6e8: file hello.c, line 4.

打印的信息告诉我们在 hello.c 文件的第 4 行,地址 0x6e8 处添加了一个断点,那如何查看断点呢?

5. 查看断点

在 gdb 下查看断点使用命令 info break 或简写 i b,比如查看刚才打的断点:

(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00000000000006e8 in main at hello.c:4

可以看到打印出刚才添加的 main 函数的断点信息:编号,类型,显示状态,是否启用,地址,其他信息,那又如何删除这个断点呢?

6. 禁用断点

在 gdb 下禁用断点使用命令 disable Num,比如禁用刚才打的断点:

(gdb) disable 1
(gdb) i b
Num     Type           Disp Enb Address            What
1       breakpoint     keep n   0x00000000000006e8 in main at hello.c:4

可以看到字段「Enb」已经变为 n,表示这个断点已经被禁用了。

7. 删除断点

在 gdb 下删除断点使用命令 delete 断点 Num 或简写 d Num,比如删除刚才的 Num = 1 的断点:

(gdb) d 1
(gdb) i b
No breakpoints or watchpoints.

删除后再次查看断点,提示当前没有断点,说明删除成功啦,下面来运行程序试试。

8. 运行程序

在 gdb 下使用命令 run 或简写 r 来运行当前载入的程序:

(gdb) r
Starting program: /home/orange/Desktop/gdb/hello 
a = 1
b = 2
1 + 2 = 3
[Inferior 1 (process 10415) exited normally]

我这次没有添加断点,程序全速运行,然后正常退出了。

9. 单步执行下一步

在 gdb 下使用命令 next 或简写 n 来单步执行下一步,假设我们在 main 打了断点:

(gdb) b main
Breakpoint 1 at 0x6e8: file hello.c, line 4.
(gdb) r
Starting program: /home/orange/Desktop/gdb/hello 

Breakpoint 1, main () at hello.c:4
4       int a = 1;
(gdb) n
5       printf("a = %d\n", a);

可以看到当前停在 int a = 1; 这一行,按 n 执行了下一句代码 printf("a = %d\n", a);

10. 跳入,跳出函数

在 gdb 下使用命令 step 或简写 s 来跳入一个函数,使用 finish 来跳出一个函数,我们在第 14 行 int c = add(a, b); 添加一个断点:

(gdb) b 14
Breakpoint 1 at 0x6f6: file hello.c, line 14.
(gdb) r
Starting program: /home/orange/Desktop/gdb/hello 
a = 1
b = 2

Breakpoint 1, main () at hello.c:14
14      int c = add(a, b);
(gdb) s
add (x=1, y=2) at hello.c:4
4       return x + y;
(gdb) finish
Run till exit from #0  add (x=1, y=2) at hello.c:4
0x0000555555554705 in main () at hello.c:14
14      int c = add(a, b);
Value returned is $1 = 3
(gdb) n
15      printf("%d + %d = %d\n", a, b, c);

这个过程是这样的:

  1. 在 14 行 int c = add(a, b); 添加断点
  2. 程序运行并停到 int c = add(a, b); 这一行
  3. s 跳入 add 函数
  4. finish 跳出 add 函数,并输出一些函数返回的信息

11. 打印变量

在 gdb 中使用命令 print var 或简写 p var 来打印一个变量或者函数的返回值,我们在第 10 行 int b = 2; 添加一个断点:

(gdb) b 10
Breakpoint 1 at 0x6c3: file hello.c, line 10.
(gdb) r
Starting program: /home/orange/Desktop/gdb/hello 

Breakpoint 1, main () at hello.c:10
10      int b = 2;
(gdb) p a
$1 = 1

我们打印出变量 a 的值为 1,在调试中比较频繁的操作是「监视变量」,在 gdb 中如何做呢?

12. 监控变量

在 gdb 中使用命令 watch var 来监控一个变量,使用 info watch 来查看监控的变量,我们这里来监控变量 c

(gdb) b 14
Breakpoint 1 at 0x6f6: file hello.c, line 14.
(gdb) r
Starting program: /home/orange/Desktop/gdb/hello 
a = 1
b = 2

Breakpoint 1, main () at hello.c:14
14      int c = add(a, b);
(gdb) watch c
Hardware watchpoint 2: c
(gdb) info watch
Num     Type           Disp Enb Address            What
2       hw watchpoint  keep y                      c

注意:程序必须要先运行才能监控

13. 查看变量类型

在 gdb 下使用命令 whatis 查看一个变量的类型:

(gdb) b 10
Breakpoint 1 at 0x6c3: file hello.c, line 10.
(gdb) r
Starting program: /home/orange/Desktop/gdb/hello 

Breakpoint 1, main () at hello.c:10
10      int b = 2;
(gdb) whatis b
type = int

这里变量 bint 类型。

14. 在 gdb 中进入 shell

在 gdb 下使用命令 shell 启动 shell :

(gdb) shell
orange@ubuntu:~/Desktop/gdb$ exit
exit
(gdb) 

使用 exit 会再次退回到 gdb 中。

15. 在 gdb 中实现可视化调试

谁说 gdb 只能在命令行调试呢?gdb 也支持「图形界面」,不过这里的图形界面都是用字符显示的,当然不如 VS 那种好看,不过使用可视化相比直接看命令行更加直观了。

在 gdb 下使用 wi 启动可视化调试:

(gdb) wi

效果如下图所示,上面是代码效果,下面是命令界面:

gdb 图形界面

有了图形界面,就再对照着图形界面将前面的命令再练习练习,看看自己手敲的命令背后到底做了些什么,加深下影响。

结语

这篇博客主要介绍了 gdb 基本的调试技术,一篇文章不可能面面俱到,还有很多命令没有介绍,如果你有兴趣的话,这里还有一份比较好的 gdb 快速学习指南 送给爱学习的你,不用客气,叫我雷锋就好。

最后,感谢你的阅读,我们下次再见 :)

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

推荐阅读更多精彩内容

  • 程序调试的基本思想是“分析现象->假设错误原因->产生新的现象去验证假设”这样一个循环过程,根据现象如何假设错误原...
    Manfred_Zone阅读 16,531评论 0 26
  • 通常软件开发中,很难有工程师能够一次性写出正确无误的程序代码。而程序调试以及测试步骤将会在整个软件开发过程中占据相...
    团团BB阅读 1,135评论 0 5
  • 概述 GDB是一个由GNU开源组织发布的、UNIX/Linux操作系统下的、基于命令行的、功能强大的程序调试工具。...
    咕咕鷄阅读 20,778评论 0 8
  • 作者: liigo原文链接: http://blog.csdn.net/liigo/archive/2006/01...
    wuqingyi阅读 1,816评论 0 4
  • 选择回家实习是我对人生的妥协,也是一个让我既高兴又有点忧伤的决定。 和大学里的好朋友们分开,不能几...
    小辣狗阅读 214评论 0 0