断点 breakpoint,即为了调试的需要,在程序中设置一些特殊标志,代码执行到这些具有特殊标志的位置时会暂停。一旦程序暂停,我们就可以查看或者修改程序运行的一些信息,比如内存信息、堆栈信息等,并且可以去检查程序运行的一些结果,去判断程序运行是否符合期望等。

总而言之,断点就是程序中断(暂停运行)的地方。gdb 提供了一些与断点有关的命令,比如设置断点、查看断点、条件断点等,尤其是设置断点的方法和技巧。

gdb 中的断点可以分为好个几种类,比如普通断点、条件断点、数据断点等,下面将详细介绍每一种断点的使用方式。

1. 在代码的某一行设置断点

先使用 gdb 启动 demo 程序,进入调试状态,在 Shell 中执行以下命令:

gdb demo

假设要在 main 函数中的某个位置设置一个断点,先来回顾源代码,查看 main 函数相关代码,如下所示,demo.cpp 内容如下:

#include <iostream>int main()
{int a=10, b=3;int c = a + b;std::cout << c << std::endl;return 0;
}

在源代码某一行设置断点的语法如下:

break 文件名:行号

我们希望在代码的第 6 行处设置断点,即希望程序运行到第 6 行时能够命中断点并暂停,则可以在 gdb 命令行窗口中输入以下命令:

break demo.cpp:6

这样就可以设置一个断点,break 也可以简写为 b

然后执行命令 r 启动程序。因为我们在第 6 行设置了第一个断点,所以代码运行到第 6 行时会暂停下来,这时就可以使用一些 gdb 命令来查看信息了。比如,输入 list (缩写为 l )来查看断点附近的代码,使用 print (缩写为 p )来查看变量的值。

完整过程如下:

$ gdb demo
...
Reading symbols from demo...done.
(gdb) break demo.cpp:6
Breakpoint 1 at 0x8a0: file demo.cpp, line 6.
(gdb) b demo.cpp:7
Breakpoint 2 at 0x8ab: file demo.cpp, line 7.
(gdb) r
Starting program: /home/wohu/cppProject/book_debug/chapter_3.1/demo Breakpoint 1, main () at demo.cpp:6
6       int c = a + b;
(gdb) print a
$1 = 10
(gdb) p b
$2 = 3
(gdb) p c
$3 = 0
(gdb) c
Continuing.Breakpoint 2, main () at demo.cpp:7
7       std::cout << c << std::endl;
(gdb) p c
$4 = 13

2. 为函数设置断点

2.1 单个唯一函数

为函数设置断点的语法如下:

break 函数名

为某个函数设置断点后,只要代码中调用该函数,就会命中并暂停。demo.cpp 代码如下:

#include <iostream>int add(int x, int y)
{return x + y;
}int main()
{int a = 10, b = 3;int c = add(a, b);std::cout << c << std::endl;return 0;
}

如果函数不存在,gdb 会给出提示。如果是一个合法的函数名,则会提示设置断点成功。输入 c 继续执行,所以会在 add 函数的第一行中断,

$ gdb demo
...
Reading symbols from demo...done.
(gdb) b add
Breakpoint 1 at 0x894: file demo.cpp, line 5.
(gdb) b add2
Function "add2" not defined.
Make breakpoint pending on future shared library load? (y or [n]) n
(gdb) r
Starting program: /home/wohu/cppProject/book_debug/chapter_3.1/demo Breakpoint 1, add (x=10, y=3) at demo.cpp:5
5       return x + y;
(gdb) p x
$1 = 10
(gdb) p y
$2 = 3
(gdb) c
Continuing.
13
[Inferior 1 (process 30073) exited normally]
(gdb)

2.2 多个同名函数

如果有多个函数名相同,只是参数不同,为同名函数设置断点会怎样呢?gdb 会为所有同名函数都设置断点,这一点其实很重要,尤其是在 C++ 的函数重载中,因为只看代码很难区分到底会调用哪一个函数。但是为函数设置断点后,就不用担心到底会执行哪一个函数。因为每个函数都会被设置断点,无论是哪一个函数被调用,都会命中。

demo.cpp 代码如下:

#include <iostream>
#include <string>int add(int x, int y)
{return x + y;
}std::string add(std::string x, std::string y)
{return x + y;
}int main()
{int a = 10, b = 3;int c = add(a, b);std::string s1 = "hello";std::string s2 = "world";std::string s = add(s1, s2);std::cout << c << std::endl;std::cout << s << std::endl;return 0;
}

使用 gdb 调试

$ gdb demo
...
Reading symbols from demo...done.
(gdb) b add
Breakpoint 1 at 0xdc4: add. (2 locations)
(gdb) r
Starting program: /home/wohu/cppProject/book_debug/chapter_3.1/demo Breakpoint 1, add (x=10, y=3) at demo.cpp:6
6       return x + y;
(gdb) c
Continuing.Breakpoint 1, add (x="hello", y="world") at demo.cpp:10
10  {(gdb) p x
$1 = "hello"
(gdb) list
5   {6       return x + y;
7   }
8
9   std::string add(std::string x, std::string y)
10  {11      return x + y;
12  }
13
14  int main()
(gdb)

会看到提示有两个地方设置了函数断点,然后在 gdb 中输入 r 开始运行程序。首先命中第一个 add(int, int) 函数。然后输入 c ,继续运行,马上在第二个 add(std::string, std::string) 函数中断。

如果多个类是继承关系,由于虚函数也是同名函数,所以当为函数设置断点时,无论是什么类型的函数,只要函数名满足条件,都会被设置断点。

如果只想为特定的函数设置断点,则需要添加限定符,以便区分到底是为哪个函数设置断点。如下 demo.cpp 代码

#include <iostream>
#include <string>class test_1
{public:test_1() {}virtual ~test_1() {}virtual void test_fun(){printf("test_1 test_fun\n");}
};
class test_2 : public test_1
{public:test_2() {}virtual ~test_2() {}virtual void test_fun(){printf("test_2 test_fun\n");}
};void test_fun(int i)
{printf("i is %d\n", i);
}
void test_fun(const char *str)
{printf("str is %s\n", str);
}int main(int argc, char *argv[])
{test_fun(10);test_fun("test");test_1 *test = new test_1();test->test_fun();test_1 *test2 = new test_2();test2->test_fun();
}

又新增了 test_1test_2 两个类,并且 test_2test_1 继承而来,代码中包含 4 个 test_fun 函数,如果在 gdb 中执行命令 b test_fun ,则 4 个函数都会被设置断点。假设我们只想对 test_1 中的test_funtest_fun(int) 设置断点,则分别执行命令:

b test_1::test_fun
b test_fun(int)

就会只对这两个函数设置断点,另外两个函数则不会被设置断点,

(gdb) b test_1::test_fun
Breakpoint 1 at 0xc02: file demo.cpp, line 11.
(gdb) b test_fun(int)
Breakpoint 2 at 0xa65: file demo.cpp, line 27.
(gdb) r
Starting program: /home/wohu/cppProject/book_debug/chapter_3.1/demo Breakpoint 2, test_fun (i=10) at demo.cpp:27
27      printf("i is %d\n", i);
(gdb) c
Continuing.
i is 10
str is testBreakpoint 1, test_1::test_fun (this=0x555555769280) at demo.cpp:11
11          printf("test_1 test_fun\n");
(gdb)

2.3 使用正则表达式设置函数断点

如果想为多个函数设置断点,但是这些函数名又各不相同,则不能使用前面提到的方法。但是如果这些函数名遵循一定的规则或者模式,则可以使用正则表达式来为这些函数设置断点,比如使用 * 等。

对代码稍作改动,添加一个函数,名称为 test_fun_x,这时代码包含多个以 test_fun 开头的函数名,就可以使用正则表达式来为满足规则的函数设置断点,语法如下:

rb 正则表达式   rbreak 正则表达式

为所有以 test_fun 开头的函数设置断点,在 gdb 中输入以下命令:

rb test_fun*

这样就为所有以test_fun开头的函数设置了断点,

(gdb) rb test_fun*
Breakpoint 1 at 0xc28: file demo.cpp, line 11.
void test_1::test_fun();
Breakpoint 2 at 0xcc4: file demo.cpp, line 21.
void test_2::test_fun();
Breakpoint 3 at 0xa8a: file demo.cpp, line 31.
void test_fun(char const*);
Breakpoint 4 at 0xa65: file demo.cpp, line 27.
void test_fun(int);
Breakpoint 5 at 0xab1: file demo.cpp, line 36.
void test_fun_A(char const*);
Breakpoint 6 at 0xbab: file demo.cpp, line 47.
static void _GLOBAL__sub_I__Z8test_funi();

2.4 通过偏移量设置断点

当前代码执行到某一行时,如果要为当前代码行的前面某一行或者后面某一行设置断点,就可以使用这个功能来达到快速设置断点的目的。

语法如下:

b +偏移量
b -偏移量

比如当前代码执行至第 73 行,如果要在第 78 行设置断点,可以执行以下命令:

b +5

如果要在第 68 行处设置断点,可以执行以下命令:

b -5

使用如下:

(gdb) c
Continuing.Breakpoint 4, test_fun (i=10) at demo.cpp:27
27      printf("i is %d\n", i);
(gdb) b +3
Note: breakpoint 3 also set at pc 0x555555554a8a.
Breakpoint 7 at 0x555555554a8a: file demo.cpp, line 30.
(gdb) b +2
Note: breakpoints 3 and 7 also set at pc 0x555555554a8a.
Breakpoint 8 at 0x555555554a8a: file demo.cpp, line 29.
(gdb)

2.5 设置条件断点

所谓条件断点,就是当满足一定条件时断点才会命中。普通的断点只要代码执行到断点处就会命中并暂停下来,而条件断点必须要满足设置的条件,才能够命中并暂停。条件断点的语法如下:

b 断点 条件

其中的 断点 可以是前面按照代码行的方式设置的断点,也可以是函数断点。条件 一般是一个 bool 表达式,比如 if i == 5 。条件断点在一些特殊的调试场合是非常有效的,比如在循环中,循环变量达到某个值时问题才会出现。如果循环变量很大,每次单步执行,是不太可能的。

比如有一个上千次的循环,但是当循环变量达到 900 时才会出问题,这时就可以设置一个条件断点,使得循环变量达到 900 时才会中断。先来查看测试代码,其中包括一个循环,如代码清单所示。

#include <iostream>
#include <string>void test_loop()
{for (int i = 0; i < 1000; i++){printf("i is %d\n", i);}printf("exit the loop\n");
}int main(int argc, char *argv[])
{test_loop();
}

我们可以在代码的第 8 行设置一个条件断点,当 i 等于 900 的时候命中。在 gdb 中输入以下命令:

(gdb) b demo.cpp:8 if i==900
Breakpoint 1 at 0x7e2: file demo.cpp, line 8.
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00000000000007e2 in test_loop() at demo.cpp:8stop only if i==900
(gdb)

然后输入 r 开始执行程序,并且会在 i 等于900的时候命中断点并暂停。此时输入 print i 查看变量 i 的当前值,发现确实是 900。

(gdb) r
Starting program: /home/wohu/cppProject/book_debug/chapter_3.1/demo
i is 0
i is 1
i is 2
i is 3
...
Breakpoint 1, test_loop () at demo.cpp:8
8           printf("i is %d\n", i);
(gdb) p i
$1 = 900
(gdb)

也可以为函数断点设置条件,比如函数 void cond_fun_test(int a,const char *str) ,如下代码清单所示,

#include <iostream>
#include <string>void fun_test(int a, const char *str)
{printf("a is %d, str is %s\n", a, str);
}int main(int argc, char *argv[])
{fun_test(10, "test");
}

假设我们希望在调用 fun_test 函数并且参数 a 等于 10 时,程序暂停,则可以使用以下命令:

b fun_test if a==10

如果希望 str 等于 test 时暂停,则可以使用下面的命令来设置条件断点:

b fun_test if str="test"

完整示例如下:

(gdb) b fun_test if a==10
Breakpoint 1 at 0x799: file demo.cpp, line 6.
(gdb) r
Starting program: /home/wohu/cppProject/book_debug/chapter_3.1/demo Breakpoint 1, fun_test (a=10, str=0x5555555548d9 "test") at demo.cpp:6
6       printf("a is %d, str is %s\n", a, str);
(gdb) p a
$1 = 10
(gdb) c
Continuing.
a is 10, str is test
[Inferior 1 (process 17825) exited normally]
(gdb) b fun_test if str=="test"
Note: breakpoint 1 also set at pc 0x555555554799.
Breakpoint 2 at 0x555555554799: file demo.cpp, line 6.
(gdb) r
Starting program: /home/wohu/cppProject/book_debug/chapter_3.1/demo Breakpoint 1, fun_test (a=10, str=0x5555555548d9 "test") at demo.cpp:6
6       printf("a is %d, str is %s\n", a, str);
(gdb) p str
$2 = 0x5555555548d9 "test"
(gdb) c
Continuing.
a is 10, str is test
[Inferior 1 (process 17835) exited normally]
(gdb)

2.6 在指令地址上设置断点

如果调试程序没有符号信息,而我们又想在某些地方设置断点时,则可以使用在指令地址上设置断点的功能。语法如下:

b *指令地址

先使用无调试符号的方式生成可执行文件。对 Makefile 稍做修改,去除 -g 选项,使得生成的可执行文件不包含调试符号信息。

启动 gdb 并调试 ,然后在测试函数 fun_test 上设置一个断点。因为没有调试符号信息,所以第一步先获得 fun_test 函数的地址,执行下述命令:

p fun_test

该命令会获得函数 fun_test 的函数地址,这里是 0x400a0b。然后为地址 0x400a0b 设置断点,命令如下:

b *0x400a0b

gdb 中输入 r ,运行程序,就会在函数 fun_test 中暂停

备注:自己测试结果如下,不清楚哪里出现问题。

(gdb) p fun_test
$1 = {<text variable, no debug info>} 0x78a <fun_test(int, char const*)>
(gdb) print fun_test
$2 = {<text variable, no debug info>} 0x78a <fun_test(int, char const*)>
(gdb) b *0x78a
Breakpoint 1 at 0x78a
(gdb) r
Starting program: /home/wohu/cppProject/book_debug/chapter_3.1/demo
Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x78a(gdb)

2.7 设置临时断点

临时断点是指这个断点是临时的,只命中一次,然后会被自动删除,后续即使代码被多次调用也不会再次命中。语法如下:

tbreak 断点
tb 断点

示例代码

#include <iostream>
#include <string>void fun_test(int a, const char *str)
{printf("a is %d, str is %s\n", a, str);
}int main(int argc, char *argv[])
{for (int i = 0; i < 10; i++){fun_test(10, "test");}
}

我们在一个循环中调用函数 fun_test ,但是只想在 fun_test 函数中命中一次,此时就可以设置一个临时断点。在 gdb 中执行下述命令:

tb fun_test

完整过程如下:

Reading symbols from demo...done.
(gdb) tb fun_test
Temporary breakpoint 1 at 0x799: file demo.cpp, line 6.
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     del  y   0x0000000000000799 in fun_test(int, char const*) at demo.cpp:6
(gdb) r
Starting program: /home/wohu/cppProject/book_debug/chapter_3.1/demo Temporary breakpoint 1, fun_test (a=10, str=0x5555555548e9 "test")at demo.cpp:6
6       printf("a is %d, str is %s\n", a, str);
(gdb) c
Continuing.
a is 10, str is test
a is 10, str is test
a is 10, str is test
a is 10, str is test
a is 10, str is test
a is 10, str is test
a is 10, str is test
a is 10, str is test
a is 10, str is test
a is 10, str is test
[Inferior 1 (process 14553) exited normally]
(gdb) q

gdb 笔记(03)— 某一行设置断点、为函数(单个唯一函数、多个同名函数、使用正则)设置断点、设置条件断点、设置临时断点相关推荐

  1. C++PrimerPlus 学习笔记 | 第八章 函数探幽 |3.默认参数 4.函数重载

    默认参数 默认参数是指当函数调用的时候省略了实参自动使用的一个值. // 函数原型 void wow(int n = 1); // 如果这样调用函数等价于 wow(1) wow() // == wow ...

  2. gdb 笔记(04)— 启用/禁用断点、启用断点一次、查看断点、删除断点、启用断点并命中N次、忽略断点前N次命中)

    1. 启用.禁用断点 如果一个断点被禁用,则该断点不会被命中,但是它仍然会在断点列表中显示.我们仍然可以通过 info b 来查看被禁用的断点,也可以通过启用断点命令来重新启用被禁用的断点. 禁用断点 ...

  3. gdb 笔记(02)— gdb 调试执行(启动调试、添加参数、附加到进程、调试 core 文件)

    在编译程序时,使用 gcc 或者 g++ 时一定要加上 -g 选项,如 gcc -g -o hello hello.c 以便调试程序含有调试符号信息,从而能够正常调试程序.否则则会出现如下提示,导致不 ...

  4. Python 基础学习笔记 03

    Python基础系列 Python 基础学习笔记 01 Python 基础学习笔记 02 Python 基础学习笔记 03 Python 基础学习笔记 04 Python 基础学习笔记 05 文章目录 ...

  5. 侯捷C++课程笔记03: STL标准库与泛型编程

    本笔记根据侯捷老师的课程整理而来:STL标准库与泛型编程 pdf版本笔记的下载地址: 笔记03_STL标准库与泛型编程,排版更美观一点(访问密码:3834) 侯捷C++课程笔记03: STL标准库与泛 ...

  6. JavaWeb黑马旅游网-学习笔记03【登陆和退出功能】

    Java后端 学习路线 笔记汇总表[黑马程序员] JavaWeb黑马旅游网-学习笔记01[准备工作] JavaWeb黑马旅游网-学习笔记02[注册功能] JavaWeb黑马旅游网-学习笔记03[登陆和 ...

  7. JQuery-学习笔记03【基础——DOM操作】

    Java后端 学习路线 笔记汇总表[黑马程序员] JQuery-学习笔记01[基础--JQuery基础]--[day01] JQuery-学习笔记02[基础--JQuery选择器] JQuery-学习 ...

  8. Filter和Listener-学习笔记03【Filter案例】

    Java后端 学习路线 笔记汇总表[黑马程序员] Filter和Listener-学习笔记01[Filter快速入门] Filter和Listener-学习笔记02[Filter细节] Filter和 ...

  9. JSP、EL和JSTL-学习笔记03【EL介绍和运算符、EL获取域中存储的数据】

    Java后端 学习路线 笔记汇总表[黑马程序员] JSP.EL和JSTL-学习笔记01[JSP基础语法] JSP.EL和JSTL-学习笔记02[MVC] JSP.EL和JSTL-学习笔记03[EL介绍 ...

最新文章

  1. 比较全的字符串验证类,有人顶的话以后继续发
  2. C#中Escape编码的加密、解密
  3. 【SSM框架系列】SpringMVC的文件上传、拦截器及异常处理
  4. Oracle中根据表明获取对应表触发器名称
  5. 如何在C/S下打印报表
  6. 字符串操作之字符串拷贝功能实现
  7. mysql 备份需要的权限_mysqldump 备份数据库用户所需要的权限
  8. vs运行时候冒了这个错:无法启动IIS Express Web 服务器~Win10
  9. java二叉树求权值_百度笔试题目:二叉树路径权值和【转】
  10. python——pandas库之Series数据结构基础
  11. C++RAII惯用法:C++资源管理的利器
  12. Paip.Php Java 异步编程。推模型与拉模型。响应式(Reactive)”编程FutureData总结... 1
  13. Cisco 路由器破解密码
  14. java 计算同比增长率
  15. 这些CAD制图初学入门问题你遇到过吗?CAD制图初学入门全攻略!
  16. 基于java嗖嗖移动业务大厅
  17. 计算机毕业设计Java-超市会员积分管理系统-(源码+系统+mysql数据库+lw文档)
  18. 强化学习——蒙特卡洛方法
  19. 学生家乡网页设计作品静态HTML网页—— HTML+CSS+JavaScript制作辽宁沈阳家乡主题网页源码(11页)
  20. 一个div分上下两部分,上部分高度不固定,下面部分自动填满剩余高度

热门文章

  1. Python按键精灵自动化_Python基础:了解Python与pycharm
  2. 引领网页设计潮流的优秀网页作品赏析《第四季》
  3. SpringCloud Alibaba Sentinel 熔断降级 - 程序配制方式实现
  4. 首贴—新人报到,请多关照
  5. 情景分析通用—海量数据中统计最热门查询TOP10
  6. try-catch-finally的用法之finally(总被执行)
  7. 【从零开始的MC服务器】第三步:了解开服文件
  8. 聚合和组合的区别(Java)
  9. 五年制转本学历很重要江苏专转本
  10. java.lang.ClassNotFoundException:javax.xml.bind.DatatypeConverter【解决办法】