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

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

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

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

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

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

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

gdb demo

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

#include

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

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

#include

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

#include

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_1 和 test_2 两个类,并且 test_2 从 test_1 继承而来,代码中包含 4 个 test_fun 函数,如果在 gdb 中执行命令 b test_fun ,则 4 个函数都会被设置断点。假设我们只想对 test_1 中的test_fun 和 test_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 test

Breakpoint 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

#include

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:8

stop 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

#include

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 = {} 0x78a

(gdb) print fun_test

$2 = {} 0x78a

(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

#include

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

相关尊享内容

淘宝怎么改评价?修改差评的步骤有哪些?
365bet游戏下载

淘宝怎么改评价?修改差评的步骤有哪些?

📅 09-01 👑 389
Touch Surgery融资2000万美元,为手术提供HoloLens AR医疗方案
365app下载安装官方免费下载

Touch Surgery融资2000万美元,为手术提供HoloLens AR医疗方案

📅 07-08 👑 449
芯片,太热了
365bet游戏下载

芯片,太热了

📅 08-02 👑 352
感觉全身都在轻微的抖
365bet游戏下载

感觉全身都在轻微的抖

📅 08-31 👑 293