GDB调试
运行中程序GDB调试
测试程序
//test2.c
//功能:从0开始每秒打印
#include <stdio.h>
#include <unistd.h>
int aaa();
int bbb(int n);
int main()
{aaa();
}int aaa()
{bbb(0);
}int bbb(int n)
{for(int i = n; i < n+10000; i++){printf("i:%d\n", i);n++;sleep(1);}
}
gcc -g -o test2 test2.c
./test2
//结果
:0
i:1
i:2
i:3
i:4
i:5
i:6
i:7
......
操作步骤
1.运行程序
在终端窗口运行编译好的程序
./test2
2.进程ID
在另一个终端窗口,使用ps
命令找到正在运行程序的进程ID,其中进程ID是第二个
ps aux | grep test2
//结果如下,其中正在运行程序的进程ID是15554
username 15554 0.0 0.0 2776 1408 pts/1 S+ 22:38 0:00 ./test2
username 15557 0.0 0.0 12192 2432 pts/2 S+ 22:39 0:00 grep --color=auto test
3.附加GDB
使用GDB附加到正在运行的程序上
sudo gdb test2 -p 15554
这里的sudo加不加看环境,有些不用加
4.GDB调试
在GDB中,你可以使用常用的调试命令如bt
(查看调用堆栈),print
(打印变量值),continue
(继续执行程序),等等
这里因为sleep延时,直接continue后,test2继续运行,gdb这里卡住了,可以用CTRL-C重新中断
5.结束调试
命令 | 解析 |
---|---|
detach | 直接使用detach命令,可以从进程中分离GDB并让程序继续运行 |
attach PID | 重新将GDB附加到某个进程上 |
GDB调试多进程
测试程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main()
{printf("begin\n");if ( fork() != 0 ){printf("我是父进程:进程pid=%d,父进程ppid=%d\n",getpid(),getppid());int ii;for(ii=0; ii<200; ii++){printf("父进程ii=%d\n",ii);sleep(1);}exit(0);}else{printf("我是子进程:进程pid=%d,父进程ppid=%d\n",getpid(),getppid());int jj;for(jj=0; jj<200; jj++){printf("子进程jj=%d\n",jj);sleep(1);}exit(0);}
}
gcc -g -o test3 test3.c
./test3
//运行结果
我是父进程:进程pid=5003,父进程ppid=4675
父进程ii=0
我是子进程:进程pid=5004,父进程ppid=5003
子进程jj=0
父进程ii=1
子进程jj=1
子进程jj=2
父进程ii=2
子进程jj=3
父进程ii=3
子进程jj=4
父进程ii=4
父进程ii=5
子进程jj=5
子进程jj=6
父进程ii=6
子进程jj=7
父进程ii=7
父进程ii=8
子进程jj=8
......//开始调试
gdb test3
调试命令
命令 | 解析 |
---|---|
set follow-fork-mode child | 设置追踪子进程。gdb 默认调试的是父进程,如果想调试子进程,那么在用gdb调试的时候要增加。该命令要在子进程运行前设置 |
set detach-on-fork on/off | 继续其他进程/停止其他进程。当我们调试某个进程的时候,设置别的进程是否运行。默认是on,表示调试当前进程的时候,其他的进程继续运行。off,表示调试当前进程,其他的进程被 gdb 挂起。 |
info inferior | 通过查看可以调试的进程,方便后面切换进程 |
inferior processNum | 可以通过 info inferior 来查看可以调试的进程,当需要切换调试的进程时,根据processNum进行切换。这里的processNum是gdb自己排的进程编号,不是进程ID |
以下程序未设置命令,因此默认调试父进程,并且调试的时候子进程是运行的
eading symbols from test3...
(gdb) b 7
Breakpoint 1 at 0x1216: file test3.c, line 7.
(gdb) run
Starting program: /home/lisentao/Desktop/fatenone/project/gcc/gdb2/test3
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".Breakpoint 1, main () at test3.c:7
7 printf("begin\n");
(gdb) n
begin
9 if ( fork() != 0 )
(gdb) n
[Detaching after fork from child process 5242]
我是子进程:进程pid=5242,父进程ppid=5239
子进程jj=0
11 printf("我是父进程:进程pid=%d,父进程ppid=%d\n",getpid(),getppid());
(gdb) 子进程jj=1
子进程jj=2
子进程jj=3
子进程jj=4
子进程jj=5
子进程jj=6
使用set follow-fork-mode child命令调试子进程,并使用set detach-on-fork off命令挂起父进程,随后再切换调试父进程
Reading symbols from test3...
(gdb) b 7
Breakpoint 1 at 0x1216: file test3.c, line 7.
(gdb) set follow-fork-mode child
(gdb) set detach-on-fork off
(gdb) r
Starting program: /home/lisentao/Desktop/fatenone/project/gcc/gdb2/test3
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".Breakpoint 1, main () at test3.c:7
7 printf("begin\n");
(gdb) n
begin
9 if ( fork() != 0 )
(gdb) n
[Attaching after Thread 0x7ffff7fa9740 (LWP 5333) fork to child process 5336]
[New inferior 2 (process 5336)]
Reading symbols from /usr/lib/debug/.build-id/49/0fef8403240c91833978d494d39e537409b92e.debug...
Reading symbols from /usr/lib/debug/.build-id/41/86944c50f8a32b47d74931e3f512b811813b64.debug...
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[Switching to Thread 0x7ffff7fa9740 (LWP 5336)]
main () at test3.c:9
9 if ( fork() != 0 )
(gdb) n
24 printf("我是子进程:进程pid=%d,父进程ppid=%d\n",getpid(),getppid());
(gdb) n
我是子进程:进程pid=5336,父进程ppid=5333
26 for(jj=0; jj<200; jj++)
(gdb) n
28 printf("子进程jj=%d\n",jj);
(gdb) n
子进程jj=0
29 sleep(1);
(gdb) n
26 for(jj=0; jj<200; jj++)
(gdb) info inferiorNum Description Connection Executable 1 process 5333 1 (native) /home/lisentao/Desktop/fatenone/project/gcc/gdb2/test3
* 2 process 5336 1 (native) /home/lisentao/Desktop/fatenone/project/gcc/gdb2/test3
(gdb) inferior 1
[Switching to inferior 1 [process 5333] (/home/lisentao/Desktop/fatenone/project/gcc/gdb2/test3)]
[Switching to thread 1.1 (Thread 0x7ffff7fa9740 (LWP 5333))]
#0 arch_fork (ctid=0x7ffff7fa9a10) at ../sysdeps/unix/sysv/linux/arch-fork.h:52
52 ../sysdeps/unix/sysv/linux/arch-fork.h: 没有那个文件或目录.
(gdb) info inferiorNum Description Connection Executable
* 1 process 5333 1 (native) /home/lisentao/Desktop/fatenone/project/gcc/gdb2/test3 2 process 5336 1 (native) /home/lisentao/Desktop/fatenone/project/gcc/gdb2/test3
(gdb) n
56 in ../sysdeps/unix/sysv/linux/arch-fork.h
(gdb) n
__GI__Fork () at ../sysdeps/nptl/_Fork.c:29
29 ../sysdeps/nptl/_Fork.c: 没有那个文件或目录.
(gdb) n
50 in ../sysdeps/nptl/_Fork.c
(gdb) n
__libc_fork () at ./posix/fork.c:75
75 ./posix/fork.c: 没有那个文件或目录.
(gdb) n
113 in ./posix/fork.c
(gdb) n
126 in ./posix/fork.c
(gdb) n
128 in ./posix/fork.c
(gdb) n
132 in ./posix/fork.c
(gdb) n
main () at test3.c:9
9 if ( fork() != 0 )
(gdb) n
11 printf("我是父进程:进程pid=%d,父进程ppid=%d\n",getpid(),getppid());
(gdb) n
我是父进程:进程pid=5333,父进程ppid=5328
14 for(ii=0; ii<200; ii++)
(gdb) n
16 printf("父进程ii=%d\n",ii);
(gdb) n
父进程ii=0
17 sleep(1);
(gdb) n
n14 for(ii=0; ii<200; ii++)
(gdb) n
16 printf("父进程ii=%d\n",ii);
(gdb)
GDB调试多线程
测试程序
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>#define MAX 50
//全局变量
int number;
//创建一把互斥锁
pthread_mutex_t mutex;void* funcA_num(void* arg)
{for(int i=0; i<MAX; ++i){pthread_mutex_lock(&mutex);number++;printf("Thread A, id = %lu, number = %d\n", pthread_self(), number);pthread_mutex_unlock(&mutex);sleep(1);}return NULL;
}void* funcB_num(void* arg)
{for(int i=0; i<MAX; ++i){pthread_mutex_lock(&mutex);number++;printf("Thread B, id = %lu, number = %d\n", pthread_self(), number);pthread_mutex_unlock(&mutex);sleep(1);}return NULL;
}int main(int argc, const char* argv[])
{pthread_t p1, p2;// 初始化互斥锁pthread_mutex_init(&mutex, NULL);// 创建两个子线程pthread_create(&p1, NULL, funcA_num, NULL);pthread_create(&p2, NULL, funcB_num, NULL);for(int k = 0; k < MAX; k++){pthread_mutex_lock(&mutex);number++;printf("Thread Main, id = %lu, number = %d\n", pthread_self(), number);pthread_mutex_unlock(&mutex);sleep(1);}// 阻塞,资源回收pthread_join(p1, NULL);pthread_join(p2, NULL);// 销毁互斥锁// 线程销毁之后, 再去释放互斥锁pthread_mutex_destroy(&mutex);return 0;
}
gcc -g -o test4 test4.c -lphtread
./test4
//运行结果
Thread Main, id = 139380790110016, number = 1
Thread A, id = 139380784105024, number = 2
Thread B, id = 139380773619264, number = 3
Thread A, id = 139380784105024, number = 4
Thread B, id = 139380773619264, number = 5
Thread Main, id = 139380790110016, number = 6
......
线程查看命令
命令 | 解析 |
---|---|
ps -aL | grep xxx | 查看轻量级进程。轻量级的进程就是线程:p——process:进程; s——state:状态;a——all:全部;L——light:轻 |
pstree -p 主线程id | 主线程和子线程的关系用树形展开 |
XX@XX-virtual-machine:~/Desktop/fatenone/project/gcc/gdb2$ ps -aL|grep test44523 4523 pts/0 00:00:00 test44523 4524 pts/0 00:00:00 test44523 4525 pts/0 00:00:00 test4
XX@XX-virtual-machine:~/Desktop/fatenone/project/gcc/gdb2$ pstree -p 4523
test4(4523)─┬─{test4}(4524)└─{test4}(4525)
调试命令
命令 | 解析 |
---|---|
info threads | 查看线程 |
thread 线程号 | 切换到某个线程,这个线程号可以查看info threads |
set scheduler-locking on | 可以用来锁定当前线程,只观察这个线程的运行情况。当锁定这个线程时,其他线程就处于了暂停状态,也就是说你在当前线程执行 next、step、until、finish、return 命令时,其他线程是不会运行的。需要注意的是,你在使用 set scheduler-locking on/step 选项时要确认下当前线程是否是你期望锁定的线程,如果不是,可以使用 thread <线程编号> 切换到你需要的线程,然后再进行锁定。 |
set scheduler-locking step | 也是用来锁定当前线程,当且仅当使用 next 或 step 命令做单步调试时会锁定当前线程,如果使用 until、finish、return 等线程内的非单步调试命令,其他线程还是有机会运行的。相比较 on 选项值,step 选项值给为单步调试提供了更加精细化的控制,因为通常我们只希望在单步调试时,不希望其他线程对当前调试的各个变量值造成影响。 |
set scheduler-locking off | 关闭锁定当前线程,所有的线程都会执行。 |
opyright (C) 2022 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:
<https://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 test4...
(gdb) b 54
Breakpoint 1 at 0x1448: file test4.c, line 54.
(gdb) r
Starting program: /home/lisentao/Desktop/fatenone/project/gcc/gdb2/test4
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[New Thread 0x7ffff7a00640 (LWP 4930)]
Thread A, id = 140737347847744, number = 1
[New Thread 0x7ffff7000640 (LWP 4931)]
Thread B, id = 140737337361984, number = 2
Thread Main, id = 140737353783104, number = 3Thread 1 "test4" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdee8) at test4.c:54
54 sleep(1);
(gdb) info threadsId Target Id Frame
* 1 Thread 0x7ffff7fa9740 (LWP 4927) "test4" main (argc=1, argv=0x7fffffffdee8) at test4.c:542 Thread 0x7ffff7a00640 (LWP 4930) "test4" 0x00007ffff7ce57f8 in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=req@entry=0x7ffff79ffde0, rem=rem@entry=0x7ffff79ffde0)at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:783 Thread 0x7ffff7000640 (LWP 4931) "test4" 0x00007ffff7ce57f8 in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=req@entry=0x7ffff6fffde0, rem=rem@entry=0x7ffff6fffde0)at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
(gdb) thread 2
[Switching to thread 2 (Thread 0x7ffff7a00640 (LWP 4930))]
#0 0x00007ffff7ce57f8 in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=req@entry=0x7ffff79ffde0, rem=rem@entry=0x7ffff79ffde0)at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
78 ../sysdeps/unix/sysv/linux/clock_nanosleep.c: 没有那个文件或目录.
(gdb) info threadsId Target Id Frame 1 Thread 0x7ffff7fa9740 (LWP 4927) "test4" main (argc=1, argv=0x7fffffffdee8) at test4.c:54
* 2 Thread 0x7ffff7a00640 (LWP 4930) "test4" 0x00007ffff7ce57f8 in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=req@entry=0x7ffff79ffde0, rem=rem@entry=0x7ffff79ffde0)at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:783 Thread 0x7ffff7000640 (LWP 4931) "test4" 0x00007ffff7ce57f8 in __GI___clock_nanosleep (clock_id=clock_id@entry=0, flags=flags@entry=0, req=req@entry=0x7ffff6fffde0, rem=rem@entry=0x7ffff6fffde0)at ../sysdeps/unix/sysv/linux/clock_nanosleep.c:78
(gdb) c
Continuing.
Thread A, id = 140737347847744, number = 4
Thread B, id = 140737337361984, number = 5
Thread A, id = 140737347847744, number = 6
Thread B, id = 140737337361984, number = 7
Thread Main, id = 140737353783104, number = 8
[Switching to Thread 0x7ffff7fa9740 (LWP 4927)]Thread 1 "test4" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdee8) at test4.c:54
54 sleep(1);
(gdb) set scheduler-locking on
(gdb) c
Continuing.
Thread Main, id = 140737353783104, number = 9//可以看到此时除了main线程,线程A和线程B都已经锁住了Thread 1 "test4" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdee8) at test4.c:54
54 sleep(1);
(gdb) set scheduler-locking off
(gdb) c
Continuing.
Thread A, id = 140737347847744, number = 10
Thread B, id = 140737337361984, number = 11
Thread B, id = 140737337361984, number = 12
Thread A, id = 140737347847744, number = 13
Thread Main, id = 140737353783104, number = 14//使用set scheduler-lockingoff命令,A和B线程不会被锁住Thread 1 "test4" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdee8) at test4.c:54
54 sleep(1);
(gdb) n
Thread B, id = 140737337361984, number = 15
Thread A, id = 140737347847744, number = 16
48 for(int k = 0; k < MAX; k++)
(gdb) n
Thread A, id = 140737347847744, number = 17
Thread B, id = 140737337361984, number = 18
50 pthread_mutex_lock(&mutex);
(gdb) n
51 number++;
(gdb) n
52 printf("Thread Main, id = %lu, number = %d\n", pthread_self(), number);
(gdb) n
Thread Main, id = 140737353783104, number = 19
53 pthread_mutex_unlock(&mutex);
(gdb) set scheduler-locking step
(gdb) c
Continuing.
Thread A, id = 140737347847744, number = 20
Thread B, id = 140737337361984, number = 21//使用set scheduler-lockingstep命令,continue的时候A和B线程没有被锁住Thread 1 "test4" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdee8) at test4.c:54
54 sleep(1);
(gdb) n
48 for(int k = 0; k < MAX; k++)
(gdb) n
50 pthread_mutex_lock(&mutex);
(gdb) n
51 number++;
(gdb) n
52 printf("Thread Main, id = %lu, number = %d\n", pthread_self(), number);
(gdb) n
Thread Main, id = 140737353783104, number = 22//使用set scheduler-lockingstep命令,next的时候A和B线程被锁住
53 pthread_mutex_unlock(&mutex);
(gdb) nThread 1 "test4" hit Breakpoint 1, main (argc=1, argv=0x7fffffffdee8) at test4.c:54
54 sleep(1);
(gdb)
Core Dump基础
有时候写程序会出现 coredump 的错误,也就是内存溢出,程序挂掉。要去查看是程序中的那个地方导致了内存溢出,可以在GDB调试的时候加上 core 文件,core 文件里面记录了程序挂掉的一些重要信息。
调试命令
命令 | 解析 |
---|---|
ulimt -a | 看系统参数,core文件的默认大小是 0 |
ulimit -c unlimited | 将core文件的大小修改为无限制 |
引用
gdb调试正在运行中的程序_gdb调试正在运行的程序-CSDN博客
深入理解GDB调试:多进程、多线程及核心文件剖析-CSDN博客
GDB-2——GDB调试多线程 - Hello-World3 - 博客园
Linux多线程调试没那么难,别就会一个printf! - 知乎