背景
以下内容是基于自己在公司培训的材料上补充而来。
信息显示
显示gdb版本信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| $ gdb
GNU gdb (GDB) 8.3
Copyright (C) 2019 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-apple-darwin16.7.0".
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".
|
启动时不显示提示信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| $ gdb -q
(gdb) show version
GNU gdb (GDB) 8.3
Copyright (C) 2019 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-apple-darwin16.7.0".
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".
|
退出时不显示提示信息
1
2
3
4
5
6
| (gdb) quit
A debugging session is active.
Inferior 1 [process 19119] will be killed.
Quit anyway? (y or n)
|
如果不想显示这个信息,则可以在gdb中使用如下命令把提示信息关掉:
建议把这个命令加到.gdbinit文件中。
gdbinit配置文件
当gdb启动时,它在当前用户的HOME
目录下寻找.gdbinit
文件;如果该文件存在,则gdb就执行该文件中的所有命令。通常,该文件用于简单的配置命令,如设置所需的缺省汇编程序格式(Intel® 或 Motorola)或用于显示输入和输出数据的缺省基数(十进制或十六进制)。它还可以读取宏编码语言,从而允许实现更强大的自定义。该语言遵循如下基本格式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| define <command>
<code>
end
document <command>
<help text>
end
# breakpoint aliases
define bpl
info breakpoints
end
document bpl
Syntax: bpl
| List all breakpoints.
end
|
这里是一些常用的设置,可以放到.gdbinit
的一些配置:
1
2
3
4
5
6
7
8
9
10
11
| # 保存历史命令(gdb默认不保存历史命令)
set history filename ~/.gdb_history
set history save on
# 退出时不显示提示信息
set confirm off
# 按照派生类型打印对象
set print object on
# 打印数组的索引下标
set print array-indexes on
# 每行打印一个结构体成员
set print pretty on
|
断点
保存已经设置的断点
在gdb中可以使用set breakpoints file_name_to_save
将已经设置的断点保存下来,下次调试时可以使用source file_name_to_safe
加载保存的断点。
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
| $ gdb -q float_range
Reading symbols from float_range...
(gdb) set listsize 0
(gdb) l
1 #include <stdio.h>
2 #include <float.h>
3 #include <math.h>
4
5 int main(void) {
6 printf("FLT_RADIX = %d\n", FLT_RADIX);
7 printf("FLT_MIN = %e\n", FLT_MIN);
8 printf("FLT_MIN = %f\n", FLT_MIN);
9 printf("FLT_MAX = %e\n", FLT_MAX);
10 printf("FLT_MAX = %f\n", FLT_MAX);
11 return 0;
12 }
(gdb) b float_range.c:6
Breakpoint 1 at 0x100000d33: file float_range.c, line 6.
(gdb) b float_range.c:8
Breakpoint 2 at 0x100000d69: file float_range.c, line 8.
(gdb) b float_range.c:10
Breakpoint 3 at 0x100000da9: file float_range.c, line 10.
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000100000d33 in main at float_range.c:6
2 breakpoint keep y 0x0000000100000d69 in main at float_range.c:8
3 breakpoint keep y 0x0000000100000da9 in main at float_range.c:10
(gdb) save breakpoints float_range_bp
Saved to file 'float_range_bp'.
(gdb) quit
$ gdb -q float_range
Reading symbols from float_range...
(gdb) source float_range_bp
Breakpoint 1 at 0x100000d33: file float_range.c, line 6.
Breakpoint 2 at 0x100000d69: file float_range.c, line 8.
Breakpoint 3 at 0x100000da9: file float_range.c, line 10.
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x0000000100000d33 in main at float_range.c:6
2 breakpoint keep y 0x0000000100000d69 in main at float_range.c:8
3 breakpoint keep y 0x0000000100000da9 in main at float_range.c:10
|
设置条件断点
我们可以在gdb
中设置条件断点,也就是只有在满足条件时,断点才会被出发,命令格式break ... if cond
。
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
32
33
34
35
36
37
38
39
40
| $ gdb -q gdb_cond
Reading symbols from gdb_cond...
(gdb) l
1 #include <stdio.h>
2
3 int main() {
4 int i = 0;
5 int sum = 0;
6
7 for (i = 1; i <= 200; ++i) {
8 sum += i;
9 }
10
11 printf("sum = %d\n", sum);
12 return 0;
13 }
(gdb) b 8 if i == 100
Breakpoint 1 at 0x100000f51: file gdb_cond.c, line 8.
(gdb) b 8 if i == 182
Note: breakpoint 1 also set at pc 0x100000f51.
Breakpoint 2 at 0x100000f51: file gdb_cond.c, line 8.
(gdb) r
Starting program: /Users/wg/develop/c/gdb_cond
[New Thread 0x1103 of process 55419]
[New Thread 0x1403 of process 55419]
Thread 2 hit Breakpoint 1, main () at gdb_cond.c:8
8 sum += i;
(gdb) p i
$1 = 100
(gdb) c
Continuing.
Thread 2 hit Breakpoint 2, main () at gdb_cond.c:8
8 sum += i;
(gdb) p i
$2 = 182
(gdb) q
|
函数
列出函数的名字
gdb
调试时,我们可以使用info functions
命令列出可执行文件的所有函数名称。
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
| $ gdb -q pthread_local_data
Reading symbols from pthread_local_data...
(gdb) l
1 #include <pthread.h>
2 #include <stdio.h>
3
4 static pthread_key_t log_key;
5
6 void write_to_thread_log(const char* message) {
7 FILE * log_file = (FILE*)pthread_getspecific(log_key);
8 fprintf(log_file, "%s\n", message);
9 }
10
11 void close_thread_log(void* thread_log) {
12 fclose((FILE*)thread_log);
13 }
14
15 void *thread_func(void *arg) {
16 char thread_log_filename[64];
17 FILE *thread_log;
18 snprintf(thread_log_filename, sizeof(thread_log_filename),
19 "thread%d.log", (int)pthread_self());
20 thread_log = fopen(thread_log_filename, "w");
21 pthread_setspecific(log_key, thread_log);
22 write_to_thread_log("Thread starting.");
23
24 return NULL;
25 }
26
27 int main() {
28 int i = 0;
29 const int thread_num = 8;
30 pthread_t tids[thread_num];
31 pthread_key_create(&log_key, close_thread_log);
32 for (; i < thread_num; ++i) {
33 pthread_create(&tids[i], NULL, thread_func, NULL);
34 }
35
36 for (i = 0; i < thread_num; ++i) {
37 pthread_join(tids[i], NULL);
38 }
39
40 return 0;
41 }
(gdb) info functions
All defined functions:
File pthread_local_data.c:
11: void close_thread_log(void *);
27: int main();
15: void *thread_func(void *);
6: void write_to_thread_log(const char *);
Non-debugging symbols:
0x0000000100000000 _mh_execute_header
|
退出正在调试的函数
当我们单步调试一个函数时,如果不想继续跟踪下去,可以使用finish
命令,这样函数会继续执行完,并且打印返回值,然后等待接下来的命令。
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
| $ gdb -q gdb_finish
Reading symbols from gdb_finish...
(gdb) l
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 int magic(int x, int y) {
5 int seed = 127;
6 int sum = 0;
7 sum = (x * y) + (x * y);
8 for (int i = 0; i < 128; ++i) {
9 sum += sum & seed + (x & i) + (y | i);
10 }
11 sum = sum << 4;
12 return sum;
13 }
14
15 void show_platform() {
16 fprintf(stdout, "sizeof(int) = %zu\n", sizeof(int));
17 fprintf(stdout, "sizeof(double) = %zu\n", sizeof(double));
18 return;
19 }
20
21 int main() {
22 int x = 2;
23 int y = 4;
24 int sum = magic(x, y);
25 fprintf(stdout, "magic_num = %d\n", sum);
26 show_platform();
27 return 0;
28 }
(gdb) b 7
Breakpoint 1 at 0x100000e38: file gdb_finish.c, line 7.
(gdb) r
Starting program: /Users/wg/develop/c/gdb_finish
[New Thread 0x1103 of process 14830]
[New Thread 0x1403 of process 14830]
Thread 2 hit Breakpoint 1, magic (x=2, y=4) at gdb_finish.c:7
7 sum = (x * y) + (x * y);
(gdb) info locals
seed = 127
sum = 0
(gdb) info args
x = 2
y = 4
(gdb) finish
Run till exit from #0 magic (x=2, y=4) at gdb_finish.c:7
0x0000000100000f28 in main () at gdb_finish.c:24
24 int sum = magic(x, y);
Value returned is $1 = 8192
(gdb) n
25 fprintf(stdout, "magic_num = %d\n", sum);
(gdb) n
magic_num = 8192
26 show_platform();
|
查看函数堆栈信息
我们可以使用backtrace
查看函数的调用堆栈信息,然后借助frame
命令可以查看具体的栈帧信息。
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
| $ gdb -q gdb_finish
Reading symbols from gdb_finish...
(gdb) l
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 void show_platform() {
5 fprintf(stdout, "sizeof(int) = %zu\n", sizeof(int));
6 fprintf(stdout, "sizeof(double) = %zu\n", sizeof(double));
7 return;
8 }
9
10 int magic(int x, int y) {
11 int seed = 127;
12 int sum = 0;
13 sum = (x * y) + (x * y);
14 for (int i = 0; i < 128; ++i) {
15 sum += sum & seed + (x & i) + (y | i);
16 }
17 sum = sum << 4;
18 show_platform();
19 return sum;
20 }
21
22 int main() {
23 int x = 2;
24 int y = 4;
25 int sum = magic(x, y);
26 fprintf(stdout, "magic_num = %d\n", sum);
27 show_platform();
28 return 0;
29 }
(gdb) b 5
Breakpoint 1 at 0x100000e2d: file gdb_finish.c, line 5.
(gdb) r
Starting program: /Users/wg/develop/c/gdb_finish
[New Thread 0x1103 of process 23988]
[New Thread 0x1403 of process 23988]
Thread 2 hit Breakpoint 1, show_platform () at gdb_finish.c:5
5 fprintf(stdout, "sizeof(int) = %zu\n", sizeof(int));
(gdb) backtrace
#0 show_platform () at gdb_finish.c:5
#1 0x0000000100000eed in magic (x=2, y=4) at gdb_finish.c:18
#2 0x0000000100000f28 in main () at gdb_finish.c:25
(gdb) bt
#0 show_platform () at gdb_finish.c:5
#1 0x0000000100000eed in magic (x=2, y=4) at gdb_finish.c:18
#2 0x0000000100000f28 in main () at gdb_finish.c:25
(gdb) frame 1
#1 0x0000000100000eed in magic (x=2, y=4) at gdb_finish.c:18
18 show_platform();
(gdb) info args
x = 2
y = 4
(gdb) frame 2
#2 0x0000000100000f28 in main () at gdb_finish.c:25
25 int sum = magic(x, y);
(gdb) info locals
x = 2
y = 4
sum = 1606416368
|
观察点
设置观察点
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
| $ gdb -q gdb_watch
Reading symbols from gdb_watch...
(gdb) l
1 #include <stdio.h>
2
3 int main() {
4 int sum = 0;
5 for (int i = 0; i < 128; ++i) {
6 sum += i;
7 }
8 fprintf(stdout, "sum = %d\n", sum);
9 return 0;
10 }
(gdb) start
Temporary breakpoint 1 at 0x100000f2f: file gdb_watch.c, line 4.
Starting program: /Users/wg/develop/c/gdb_watch
[New Thread 0x1103 of process 40461]
[New Thread 0x1403 of process 40461]
Thread 2 hit Temporary breakpoint 1, main () at gdb_watch.c:4
4 int sum = 0;
(gdb) watch sum
Hardware watchpoint 2: sum
(gdb) n
Thread 2 hit Hardware watchpoint 2: sum
Old value = 16438
New value = 0
main () at gdb_watch.c:5
5 for (int i = 0; i < 128; ++i) {
(gdb)
6 sum += i;
(gdb)
5 for (int i = 0; i < 128; ++i) {
(gdb)
6 sum += i;
(gdb)
Thread 2 hit Hardware watchpoint 2: sum
Old value = 0
New value = 1
main () at gdb_watch.c:5
5 for (int i = 0; i < 128; ++i) {
(gdb)
6 sum += i;
(gdb)
Thread 2 hit Hardware watchpoint 2: sum
Old value = 1
New value = 3
main () at gdb_watch.c:5
5 for (int i = 0; i < 128; ++i) {
(gdb) c
Continuing.
Thread 2 hit Hardware watchpoint 2: sum
Old value = 3
New value = 6
main () at gdb_watch.c:5
5 for (int i = 0; i < 128; ++i) {
(gdb) info watchpoints
Num Type Disp Enb Address What
2 hw watchpoint keep y sum
breakpoint already hit 8 times
|
设置读写观察点
我们使用awatch
命令设置读写观察点,每次读取或者改变观察点所观察的值时程序都会暂停下来。
PS:需要注意的是awatch
命令只对硬件观察点才生效。
* 断点分为两类:硬件断点和软件断点。硬件断点需要目标 CPU 的支持,其使用硬件提供的断点寄存器来与设置断点的地址进行匹配,断点寄存器的数量是有限的。
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
| $ gdb -q gdb_rwwatch
Reading symbols from gdb_rwwatch...
(gdb) l
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <pthread.h>
4 #include <unistd.h>
5
6 int a = 0;
7 int stop = 0;
8
9 void *thread_write_func(void *arg) {
10 arg;
11 while (!stop) {
12 ++a;
13 sleep(10);
14 }
15
16 return NULL;
17 }
18
19 void *thread_read_func(void *arg) {
20 arg;
21 while (!stop) {
22 fprintf(stdout, "a = %d\n", a);
23 sleep(10);
24 }
25 return NULL;
26 }
27
28 int main() {
29 pthread_t t1 = 0;
30 pthread_t t2 = 0;
31
32 pthread_create(&t1, NULL, thread_write_func, NULL);
33 pthread_create(&t2, NULL, thread_read_func, NULL);
34
35 sleep(1000);
36 stop = 1;
37 sleep(10);
38 return 0;
39 }
(gdb) awatch a
Hardware access (read/write) watchpoint 1: a
(gdb) info watchpoints
Num Type Disp Enb Address What
1 acc watchpoint keep y a
(gdb) r
Using host libthread_db library "/lib64/libthread_db.so.1".
[New Thread 0x7ffff77f0700 (LWP 8798)]
[Switching to Thread 0x7ffff77f0700 (LWP 8798)]
Hardware access (read/write) watchpoint 1: a
Value = 0
0x0000000000400641 in thread_write_func (arg=0x0) at gdb_rwwatch.c:12
12 ++a;
(gdb) c
Continuing.
Hardware access (read/write) watchpoint 1: a
Old value = 0
New value = 1
thread_write_func (arg=0x0) at gdb_rwwatch.c:13
13 sleep(10);
(gdb) c
Continuing.
[New Thread 0x7ffff6fef700 (LWP 8799)]
[Switching to Thread 0x7ffff6fef700 (LWP 8799)]
Hardware access (read/write) watchpoint 1: a
Value = 1
0x0000000000400679 in thread_read_func (arg=0x0) at gdb_rwwatch.c:22
22 fprintf(stdout, "a = %d\n", a);
(gdb) c
Continuing.
a = 1
[Switching to Thread 0x7ffff77f0700 (LWP 8798)]
Hardware access (read/write) watchpoint 1: a
Value = 1
0x0000000000400641 in thread_write_func (arg=0x0) at gdb_rwwatch.c:12
12 ++a;
|
多进程/多线程
调试子进程
在调试多进程程序时,gdb默认会追踪父进程,如下所示:
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
32
33
34
35
| $ gdb -q test_gdb_fork
Reading symbols from test_gdb_fork...
(gdb) l
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/types.h>
4 #include <unistd.h>
5
6 int main() {
7 pid_t pid = fork();
8 if (pid == -1) {
9 perror("call fork failed ");
10 exit(1);
11 } else if(pid > 0) { // parent process
12 exit(0);
13 }
14
15 fprintf(stderr, "Hello fork.\n");
16 return 0;
17 }
(gdb) start
Temporary breakpoint 1 at 0x100000edf: file test_gdb_fork.c, line 7.
[New Thread 0x1103 of process 3742]
[New Thread 0x1403 of process 3742]
Thread 2 hit Temporary breakpoint 1, main () at test_gdb_fork.c:7
7 pid_t pid = fork();
(gdb) n
8 if (pid == -1) {
(gdb)
11 } else if(pid > 0) { // parent process {
(gdb)
12 exit(0);
(gdb)
[Inferior 1 (process 3742) exited normally]
|
可以看到程序执行到第12行:父进程退出。
如果要调试子进程,要使用如下命令:set follow-fork-mode child
,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| $ gdb -q test_gdb_fork
Reading symbols from test_gdb_fork...done.
(gdb) set follow-fork-mode child
(gdb) start
Temporary breakpoint 1 at 0x400635: file test_gdb_fork.c, line 7.
Temporary breakpoint 1, main () at test_gdb_fork.c:7
7 pid_t pid = fork();
(gdb) n
[New process 19341]
[Switching to process 19341]
8 if (pid == -1) {
(gdb)
11 } else if(pid > 0) { // parent process
(gdb)
15 fprintf(stderr, "Hello fork.\n");
(gdb)
Hello fork.
16 return 0;
(gdb)
17 }
|
可以看到程序执行到第15行:子进程打印”hello fork”。
>请在程序调用fork之前设置set follow-fork-mode child
。
>这个命令目前只有Linux
支持,其它很多操作系统都不支持,使用时请注意。
同时调试子进程和父进程
在调试多进程程序时,gdb默认只会追踪父进程的运行,而子进程会独立运行,gdb不会控制。以下面程序为例,可以看到父进程执行到第8行的时候子进程已经运行并输出了信息:
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
32
33
34
35
36
37
38
39
| $ gdb -q test_gdb_fork
Reading symbols from test_gdb_fork...done.
(gdb) l
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <sys/types.h>
4 #include <unistd.h>
5
6 int main() {
7 pid_t pid = fork();
8 if (pid == -1) {
9 perror("call fork failed ");
10 exit(1);
11 } else if(pid > 0) { // parent process {
12 fprintf(stderr, "I am parent process\n");
13 exit(0);
14 }
15
16 fprintf(stderr, "I am child process\n");
17 return 0;
18 }
(gdb) start
Temporary breakpoint 1 at 0x400665: file test_gdb_fork.c, line 7.
Temporary breakpoint 1, main () at test_gdb_fork.c:7
7 pid_t pid = fork();
(gdb) n
Detaching after fork from child process 23495.
I am child process
8 if (pid == -1) {
(gdb)
11 } else if(pid > 0) { // parent process {
(gdb)
12 fprintf(stderr, "I am parent process\n");
(gdb)
I am parent process
13 exit(0);
(gdb)
[Inferior 1 (process 23477) exited normally]
|
如果要同时调试父进程和子进程(调试父进程的时候子进程处于停止状态),可以使用set detach-on-fork off
(默认 detach-on-fork
的值是 on
)命令,这样就能同时调试父子进程,并且在调试一个进程时,另外一个进程处于挂起状态。仍以上面程序为例:
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
32
33
34
| $ gdb -q test_gdb_fork
Reading symbols from test_gdb_fork...done.
(gdb) show detach-on-fork
Whether gdb will detach the child of a fork is on.
(gdb) set detach-on-fork off
(gdb) start
Temporary breakpoint 1 at 0x400665: file test_gdb_fork.c, line 7.
Temporary breakpoint 1, main () at test_gdb_fork.c:7
7 pid_t pid = fork();
(gdb) n
[New process 25321]
8 if (pid == -1) {
(gdb)
11 } else if(pid > 0) { // parent process {
(gdb)
12 fprintf(stderr, "I am parent process\n");
(gdb)
I am parent process
13 exit(0);
(gdb) info inferiors
Num Description Executable
2 process 25321 study/c/test_gdb_fork
* 1 process 25314 study/c/test_gdb_fork
(gdb) inferior 2
[Switching to inferior 2 [process 25321] (study/c/test_gdb_fork)]
[Switching to thread 2 (process 25321)]
#0 0x00007ffff72b3922 in __libc_fork ()
at ../nptl/sysdeps/unix/sysv/linux/fork.c:136
136 pid = ARCH_FORK ();
(gdb) info inferiors
Num Description Executable
* 2 process 25321 study/c/test_gdb_fork
1 process 25314 study/c/test_gdb_fork
|
在使用set detach-on-fork off
命令后,执行info inferiors
查看进程状态,可以看到父子进程都在被gdb
调试的状态,前面显示”*
“是正在调试的进程。当父进程退出后,用inferior infno
切换到子进程去调试。
>请注意这个命令目前只有Linux支持,其它很多操作系统都不支持。
查看线程信息
用gdb
调试多线程程序,可以用info threads
命令查看所有线程的信息,以下面的程序为例:
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
| $ gdb -q test_gdb_threads
Reading symbols from test_gdb_threads...
(gdb) l
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <pthread.h>
5
6 void *thread_func(void *arg) {
7 (void)arg;
8 while (1) {
9 fprintf(stderr, "[pid:%d][tid:%d][debug] In thread.\n",
10 getpid(), pthread_self());
11 sleep(30);
12 }
13 return NULL;
14 }
15
16 int main() {
17 pthread_t tids[2];
18 pthread_create(&tids[0], NULL, thread_func, NULL);
19 pthread_create(&tids[1], NULL, thread_func, NULL);
20
21 pthread_join(tids[0], NULL);
22 pthread_join(tids[1], NULL);
23 return 0;
24 }
(gdb) b 9
Breakpoint 1 at 0x100000e23: file test_gdb_threads.c, line 9.
(gdb) r
Starting program: /Users/wg/develop/c/test_gdb_threads
[New Thread 0x1103 of process 48700]
[New Thread 0x1403 of process 48700]
[New Thread 0x120f of process 48700]
[New Thread 0x1503 of process 48700]
[Switching to Thread 0x120f of process 48700]
Thread 3 hit Breakpoint 1, thread_func (arg=0x0) at test_gdb_threads.c:9
9 fprintf(stderr, "[pid:%d][tid:%d][debug] In thread.\n",
(gdb) info threads
Id Target Id Frame
2 Thread 0x1403 of process 48700 0x00007fff8f265f46 in ?? ()
from /usr/lib/system/libsystem_kernel.dylib
* 3 Thread 0x120f of process 48700 thread_func (arg=0x0) at test_gdb_threads.c:9
4 Thread 0x1503 of process 48700 thread_func (arg=0x0) at test_gdb_threads.c:9
(gdb) backtrace
#0 thread_func (arg=0x0) at test_gdb_threads.c:9
#1 0x00007fff8f35093b in _pthread_body () from /usr/lib/system/libsystem_pthread.dylib
#2 0x00007fff8f350887 in _pthread_start () from /usr/lib/system/libsystem_pthread.dylib
#3 0x00007fff8f35008d in thread_start () from /usr/lib/system/libsystem_pthread.dylib
#4 0x0000000000000000 in ?? ()
(gdb) thread 4
[Switching to thread 4 (Thread 0x1503 of process 48700)]
#0 thread_func (arg=0x0) at test_gdb_threads.c:9
9 fprintf(stderr, "[pid:%d][tid:%d][debug] In thread.\n",
(gdb) backtrace
#0 thread_func (arg=0x0) at test_gdb_threads.c:9
#1 0x00007fff8f35093b in _pthread_body () from /usr/lib/system/libsystem_pthread.dylib
#2 0x00007fff8f350887 in _pthread_start () from /usr/lib/system/libsystem_pthread.dylib
#3 0x00007fff8f35008d in thread_start () from /usr/lib/system/libsystem_pthread.dylib
#4 0x0000000000000000 in ?? ()
|
只允许一个线程运行
用gdb
调试多线程程序时,一旦程序断住,所有的线程都处于暂停状态。此时当调试其中一个线程时(比如执行step
,next
命令),所有的线程都会同时执行。以下面程序为例:
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
| $ gdb -q test_gdb_threads
Reading symbols from test_gdb_threads...
(gdb) l
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <pthread.h>
5
6 int a = 0;
7 int b = 0;
8
9 void *thread_func1(void *arg) {
10 (void)arg;
11 while (1) {
12 ++a;
13 fprintf(stderr, "[pid:%d][tid:%d][debug] In thread.\n",
14 getpid(), pthread_self());
15 sleep(1);
16 }
17 return NULL;
18 }
19
20 void *thread_func2(void *arg) {
21 (void)arg;
22 while (1) {
23 ++b;
24 fprintf(stderr, "[pid:%d][tid:%d][debug] In thread.\n",
25 getpid(), pthread_self());
26 sleep(1);
27 }
28 return NULL;
29 }
30
31 int main() {
32 pthread_t tids[2];
33 pthread_create(&tids[0], NULL, thread_func1, NULL);
34 pthread_create(&tids[1], NULL, thread_func2, NULL);
35
36 pthread_join(tids[0], NULL);
37 pthread_join(tids[1], NULL);
38 return 0;
39 }
(gdb) b 12
Breakpoint 1 at 0x100000da3: file test_gdb_threads.c, line 12.
(gdb) r
Starting program: /Users/wg/develop/c/test_gdb_threads
[New Thread 0x1103 of process 54356]
[New Thread 0x1403 of process 54356]
[pid:54356][tid:70713344][debug] In thread.
[New Thread 0x120f of process 54356]
[New Thread 0x1503 of process 54356]
[Switching to Thread 0x120f of process 54356]
Thread 3 hit Breakpoint 1, thread_func1 (arg=0x0) at test_gdb_threads.c:12
12 ++a;
(gdb) p a
$1 = 0
(gdb) p b
$2 = 1
(gdb) n
13 fprintf(stderr, "[pid:%d][tid:%d][debug] In thread.\n",
(gdb)
14 getpid(), pthread_self());
(gdb) c
Continuing.
[pid:54356][tid:70176768][debug] In thread.
[pid:54356][tid:70713344][debug] In thread.
[pid:54356][tid:70713344][debug] In thread.
Thread 3 hit Breakpoint 1, thread_func1 (arg=0x0) at test_gdb_threads.c:12
12 ++a;
(gdb) p b
$3 = 3
(gdb) p a
$4 = 1
|
如果想在调试一个线程时,让其它线程暂停执行,可以使用set scheduler-locking on
命令.
TODO
thread apply all
汇编
参考资料
https://legacy.gitbook.com/book/wizardforcel/100-gdb-tips/details