参考和学习视频:
B站up主【拥有6块腹肌的程序猿的个人空间-哔哩哔哩】 https://b23.tv/rPiVDqX
视频地址:https://www.bilibili.com/video/BV1mp4y1A7Ss?vd_source=f8a6ae1f67ceef69d4edbdd3820288ec
GDB的一些基本命令
run:运行程序。可以带参数,例如 run arg1 arg2。
break:设置断点。可以在指定文件和行号上设置断点,或者在指定函数名处设置断点。
continue:继续执行程序,直到下一个断点或程序结束。
next:单步执行,逐过程地执行代码行(不进入函数内部)。
step:单步执行,逐语句地执行代码行(进入函数内部)。
print 或 p:打印变量的值。例如 print variable_name 或 p variable_name。
backtrace 或 bt:显示当前调用堆栈的跟踪信息,即显示函数调用关系。
info locals:显示当前函数的局部变量及其值。
info breakpoints 或 info b:列出当前已设置的所有断点。
delete breakpoints <编号>:删除指定编号的断点。可以使用 "info breakpoints" 命令查看已设置断点的编号。
GDB调试coredump文件
- coredump文件成为核心转储文件,存储进程运行时,突然崩溃的一瞬间的进程在内存中的一个快照(文件),包括此刻的内存、寄存器状态、运行堆栈等信息。
- 测试代码:
//test.c #include <stdio.h> int main() { int *ptr = NULL; *ptr = 100; //崩溃在此处 return 0; }编译:
gcc -g -o test test.c - 生成core文件:
运行程序,出现如下输出:
- 没有core文件怎么办?
注:博主使用Ubuntu20.04,仅供参考!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
参考步骤:- 查看ulimit -c输出是否为unlimited,配置
ulimit -c unlimited; - 步骤一完成后仍没有core文件,继续下面一系列操作:
1. 查看文件:cat /proc/sys/kernel/core_pattern 若输出为:|/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E 继续下面操作,否则跳转到步骤2 关闭apport服务:编辑文件/etc/default/apport,将enabled修改为1 2. 编辑/etc/sysctl.conf文件,在文件末尾添加以下两行来设置core_uses_pid和core_pattern的值: kernel.core_uses_pid = 1 # 用来控制core文件的生成 kernel.core_pattern = /path/to/core/files/core.%p # 控制core文件在目录/path/to/core/files/下面。 更新sysctl设置以使更改生效: sudo sysctl -p - 永久修改
ulimit -c为ulimited:打开终端并使用root权限登录或使用sudo命令。 编辑/etc/security/limits.conf文件: sudo nano /etc/security/limits.conf 在文件末尾添加以下行来设置core文件大小为unlimited: * hard core unlimited * soft core unlimited 保存并关闭文件(在nano编辑器中按下Ctrl+X,然后按Y确认保存)。 编辑/etc/pam.d/common-session文件: sudo nano /etc/pam.d/common-session 在打开的文件中找到以下行并注释掉(在行前面添加#符号): #session required pam_limits.so 保存并关闭文件。 重新启动系统以使更改生效。 现在,ulimit -c将被永久设置为unlimited,允许生成任意大小的core转储文件。
- 查看ulimit -c输出是否为unlimited,配置
- 调试core文件:
- 代码
//测试程序 //文件:test.c #include <stdio.h> #include <string.h> void func(char *ptr) { strcpy(ptr, "This is test code ..."); //此处崩溃 } int main() { char *ptr = NULL; func(ptr); return 0; } - 编译:
gcc -g -o test test.c - 运行:
./test,输出:Segmentation fault (core dumped),生成core文件:core.6981(6981为pid); - 调试命令:格式"
gdb [程序] [core文件]",具体命令:gdb test core.6981
输出:
图中红框位置已经给出崩溃原因。
输入:bt命令查看堆栈信息:
可以看到当前函数栈有两个函数,分别为main函数和func函数。
输入:f [函数栈编号]切换到对应函数栈,f 0:
看到崩溃位置,可以看到func函数入参ptr为0x0。GDB调试多线程
- 代码
-
调试多线程程序:
-
代码:
//文件名:gdb_thread.c #include <stdio.h> #include <unistd.h> #include <pthread.h> void *thread_entry_funcA(void *arg) { int i = 0; for(i = 0; i < 10; i++) { printf("[thread_entry_funcA]: %d\n", i); sleep(1); } return NULL; } void *thread_entry_funcB(void *arg) { int i = 0; for(i = 0; i < 10; i++) { printf("[thread_entry_funcB]: %d\n", i); sleep(1); } return NULL; } int main() { pthread_t tidA, tidB; int ret = pthread_create(&tidA, NULL, thread_entry_funcA, NULL); if(ret < 0) { perror("pthread_ create"); return 0; } ret = pthread_create(&tidB, NULL, thread_entry_funcB, NULL); if(ret < 0) { perror("pthread_ create"); return 0; } pthread_join(tidA, NULL); pthread_join(tidB, NULL); return 0; } - 编译和运行输出:
编译:gcc -g -o gdb_thread gdb_thread.c -lpthread
运行输出:
-
调试和查看线程信息:
l即list命令,用于查看代码。list命令会显示当前正在运行的代码周围的源代码行。默认情况下,list命令将会输出当前执行到的代码行以及前后的代码。
l 10:显示第10行前后。
list 1,10: 显示第1至第10行的代码。
list main: 显示main函数中的代码。
list foo: 显示foo函数中的代码。
b即breakpoint,用于设置断点,即在程序的特定位置停止执行。在指定文件和行号上设置断点: break <文件名>:<行号> 例如:break main.c:10 在main.c文件的第10行设置断点。 在指定函数上设置断点: break <函数名> 例如:break myFunction 在myFunction函数的入口处设置断点。 在指定地址上设置断点: break *<地址> 例如:break *0x12345678 在地址0x12345678处设置断点。 条件断点: break <位置> if <条件> 例如:break main.c:20 if i == 5 当变量i等于5时,在main.c文件的第20行设置断点。 info breakpoint,简写:i b 查看断点信息。图中两个断点,分别设置在第10行和第20行,断点信息为:
(gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0x0000000000001209 in thread_entry_funcA at gdb_thread.c:10 2 breakpoint keep y 0x000000000000125d in thread_entry_funcB at gdb_thread.c:21运行程序:
run 或 r
停在了第10行,也就输第一个断点处。
通过info threads命令查看全部线程信息
*共有三个线程,前面有“”号的为当前正在执行的线程thread_entry_funcA,线程thread_entry_funcB初始化完成了
输入t [线程id]切换至对应线程--t 3
直接给出了断点信息。
切换到对应线程后,通过bt命令查看当前线程的堆栈信息
通过单步进行调试
f 0切换到对应堆栈,n命令单步执行,打印出了[thread_entry_funcA]: 0,然后线程id为3的线程运行到断点2,打印一下变量i的值:p i,单步执行后打印了[thread_entry_funcB]: 0,再执行后又切换到线程id为2后运行到断点1,打印i值。** - gdb中调度器锁
调试时除了当前线程在运行,想要规定其他线程的运行情况,可以使用set scheduler-locking [mode]命令来进行设置。
mode主要有三个选择:set scheduler-locking off:不锁定任何线程,所有线程都可以继续执行(遇见断点会停,也会出现上面调试某一线程时切换线程的情况。)set scheduler-locking on:只有当前线程可继续执行,其他线程暂停运行。set scheduler-locking step:- 当单步执行某一线程时,保证在调试过程中当前线程不会发生改变。其他线程也会随着被调试线程的单步执行而执行;
- 但如果该模式下执行continue、until、finish命令,则其他线程也会执行,并且如果某一线程执行过程中遇到断点,则GDB会将该线程作为当前线程(切换至对应线程)。
查看调度器锁模式:show scheduler-locking.
-











