消息显示

1. 启动时不显示提示信息

gdb 默认启动时会显示版本信息。使用 -q 选项可以把提示信息去掉:gdb -q

2. gdb退出时不显示提示信息

gdb 在退出时会提示:

1
2
3
A debugging session is active.
Inferior 1 [process 29686 ] will be killed.
Quit anyway? (y or n) n

如果不想显示这个信息,可以在 gdb 中设置:

查看更多

gdb 调试-观察点

1. 设置观察点

gdb 通过 watch 命令设置观察点,也就是当一个变量值发生变化时,程序就会停下来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// code
int a = 0;
void *thread1_func(void *p_arg) {
while (1) {
a++;
sleep(10);
}
}
int main(void) {
pthread_t t1;
pthread_create(&t1, NULL, thread1_func, (void*)"Thread 1");
sleep(1000);
return 0;
}

// gdb 调试
(gdb) watch a
Hardware watchpoint 1: a
(gdb) r
Starting program: /data/code/cpp/test/gdb_test/main
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff77c2700 (LWP 28025)]
[Switching to Thread 0x7ffff77c2700 (LWP 28025)]

Thread 2 "main" hit Hardware watchpoint 1: a

Old value = 0
New value = 1
thread1_func (p_arg=0x555555554834) at main_01.cpp:10
10 sleep(10);

如上代码和调试结果,当 a 的值发生变化时,程序就会停下来。也可以使用地址来监测:watch *(int*)0x555555755014

1
2
3
4
(gdb) p &a
$1 = (int *) 0x555555755014 <a>
(gdb) watch *(int*)0x555555755014
Hardware watchpoint 2: *(int*)0x555555755014

查看更多

gdb 调试断点

1. 在匿名空间设置断点

1
2
3
4
5
6
namespace Foo {
void foo() {}
}
namespace {
void bar() {}
}

在 gdb 中,如果要对 namespace Foo 中的 foo 函数设置断点,可以:(gdb) b Foo::foo

如果要对匿名空间中的 bar 函数设置断点,可以:(gdb) b (anonymous namespace)::bar

2. 在程序地址上打断点

调试汇编程序时,或者没有调试信息的程序时,可以在程序地址上打断点。方法

查看更多

gdb 调试 catchpoint

1. 让 catchpoint 只触发一次

使用 tcatch 命令设置 catchpoint 只触发一次。如下:tcatch fork

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// code
int main(void) {
pid_t pid;
int i = 0;
for (i = 0; i < 2; i++) {
pid = fork();
if (pid < 0) {
exit(1);
} else if (pid == 0) {
exit(0);
}
}
printf("hello world\n");
return 0;
}

// gdb 调试
(gdb) tcatch fork
Catchpoint 1 (fork)
(gdb) r
Starting program: /data/code/cpp/test/gdb_test/main

Temporary catchpoint 1 (forked process 30230), 0x00007ffff7ac67cc in __libc_fork () at ../sysdeps/nptl/fork.c:135
135 ../sysdeps/nptl/fork.c: No such file or directory.
(gdb) c
Continuing.
hello world
[Inferior 1 (process 30226) exited normally]

程序只在第一次调用 fork 时暂停

2. 设置 catchpoint

使用 catch 设置 catchpoint,比如:catch fork。每次调用 fork 都会暂停

查看更多

gdb 调试-打印

1. 打印大数组中的内容

在 gdb 中,如果要打印大数组的内容,缺省最多会显示 200 个元素。

可以使用:(gdb) set print elements number-of-elements 设置这个最大限制数

(gdb) set print elements unlimited 或者 (gdb) set print elements 0 设置为没有限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// code
int main() {
int array[300];
for (int i = 0; i < 300; i++) {
array[i] = i;
}
return 0;
}

// gdb 调试
(gdb) p array
$1 = {0, 1, 2, ... 198, 199...}
(gdb) set print elements 0
(gdb) p array
$2 = {0, 1, 2, ... 298, 299}

查看更多

gdb 调试函数

1. 列出函数名字

使用 info functions 命令可以列出可执行文件的所有函数名称。此命令也支持正则表达式,如:info functions regex 只会列出符合正则表达式的函数名称。

1
2
3
4
5
6
7
8
9
10
11
(gdb) info functions printf*
All functions matching regular expression "printf*":

File ../stdio-common/printf_fphex.c:
int __printf_fphex(_IO_FILE *, const struct printf_info *, const void * const *);

File argp-fmtstream.c:
ssize_t __argp_fmtstream_printf(struct argp_fmtstream *, const char *, ...);

File argp-help.c:
static void print_header(const char *, const struct argp *, struct pentry_state *);

2. 是否进入带调试信息的函数

使用 gdb 调试遇到函数时,使用 step(缩写为 s) 命令可以进入函数(函数必须有调试信息)。

可以使用 next(缩写为 n)不进入函数,gdb 会等函数执行完,再显示下一行要执行的程序代码

查看更多

利用 gdb 调试线程

使用 gdb 调试线程

本文通过实践来说明 gdb 如何调试多线程。

如下的程序我们在主程序中先创建两个线程,这两个线程分别打印自己的线程 ID。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void* pthread_run1(void*) {
while (true) {
printf("I am thread1,ID: %lu\n", pthread_self());
sleep(1);
}
}

void* pthread_run2(void*) {
while (true) {
printf("I am thread2,ID: %lu\n", pthread_self());
sleep(1);
}
}

int main() {
pthread_t tid1;
pthread_t tid2;
pthread_create(&tid1, NULL, pthread_run1, NULL);
pthread_create(&tid2, NULL, pthread_run2, NULL);
printf("I am main thread\n");
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}

我们可以通过 ps -eLf | grep xxx 来查看进程 ID 和 线程 ID。或者通过 pstree -p 主线程id 查看主线程和子线程之间的关系。

查看更多