GDB 基本命令
如果想要使用gdb调试程序,再编译时必须加-g
参数。我们可以使用如下两种方式进行调试:
-
如果是未启动的程序名,使用
gdb 程序名
开始调试当前程序,或者先启动gdb
,再用file 可执行文件名
来调试文件。 -
如果程序已启动,需要调试程序的进程,
使用gdb -p 进程ID
,来调试进程。或者先启动gdb
,再attach 进程ID
来调试进程。
主要的命令和用法如下:
命令 | 作用 |
---|---|
directory DIR | 增加目录 DIR 到搜寻程序代码的目录列表 (如果你的程序代码和可执行档放在同一个目录下,就不须指定程序代码所在目录) |
l (list) | 从第当前行开始例出源代码 |
Enter (回车) | 重复上一次输入的命令 |
b (break) | 设置断点,如break test.cpp:100 断行数,break UserManager::GetUser 断函数 |
clear | 清除断点,如clear XXX ,XXX格式和上面b的格式相同 |
info break | 查看断点信息 |
delete | 清除断点,如delete NUM ,NUM是在info break显示的断点编号,不加NUM表示删除所有断点 |
disable | 禁止断点,如disable NUM ,NUM是在info break显示的断点编号,不加NUM表示禁止所有断点 |
enable | 启用断点,用法和disable类似 |
r (run) | 运行程序 |
bt (backtrace) | 显示程序堆栈信息 |
p (print) | 打印表达式的值 |
display | 在断点的停止的地方,显示指定的变量的值。 |
set | 设置变量的值。如set val=54 ,将54设置到val变量中 |
n (next) | 在触发断点之后,单步执行,不进函数调用 |
s (step) | 单步执行,会进入函数调用 |
finish | 继续执行,直到当前函数返回,并在调用函数的下一行停止 |
u (until) | 执行一行程序,若此时程序是在 for/while/do loop 循环的最后一行,则一直执行到循环结束后的第一行程序后停止 |
return | 强制从当前函数返回 |
CTRL + C | 在当前位置停止执行正在执行的程序,断点在当前行 |
quit | 退出gdb |
gdb 调试多线程
假设现在有一个主线程创建了一个子线程。
gdb调试时,设置断点,单步调试到pthread_create处的时候,这时候会创建子线程,会出现如下信息
[New Thread 0x7ffff6fd1700 (LWP 6376)]
默认情况下,gdb只跟踪主线程,新创建的线程都被阻塞在pthread_create函数处。
info threads
可以调试的所有线程,gdb会为每个线程分配一个ID,这个ID和线程ID不同,ID号一般从1开始。
如下,表示当前有两个线程1和2,*表示跟踪主线程1
(gdb) info threads
Id Target Id Frame
2 Thread 0x7ffff6fd1700 (LWP 6376) "test" 0x00007ffff70d0851 in clone ()
from /lib64/libc.so.6
* 1 Thread 0x7ffff7fee740 (LWP 6375) "test" main (argc=1, argv=0x7fffffffe2d8) at test.cpp:31
thread ID
切换当前调试的线程为指定ID号,ID是gdb分配的序号,不是线程TID。
set scheduler-locking off|on
on锁定其他线程,只有当前选择调试的线程执行,off表示不锁定任何线程,当运行到断点处,将所有的线程都暂停下来,直到指定某个线程继续执行,如果在当前线程下使用continue的话会启动所有线程(GDB默认)。
多线程调试控制指令
thread apply ID1 ID2 ...IDn gdb_command
指定多个线程执行gdb中的command指令
thread apply all command
指定所有线程执行gdb中的command指令
non-stop模式
上面说过一个线程中断在一个断点上,其他所有的线程都会被freeze。新版本的GDB中,引入了non-stop模式,在这个模式下:
-
当某个或多个线程在一个断点上,其他线程仍会并行运行
-
你可以选择某个被中断的线程,只让他运行。
-
non-stop模式表示不停止模式,除了断点有关的进程会被停下来,其他线程会继续执行。
设置non-stop模式,打开gdb后,在开始r之前,首先连续输入下面的指令
set target-async 1
set pagination off
set non-stop on
总结调试多线程的命令
info threads
显示当前可调试的所有线程,每个线程会有一个GDB为其分配的ID,后面操作线程的时候会用到这个ID。 前面有*的是当前调试的线程
thread ID(1,2,3…)
切换当前调试的线程为指定ID的线程
break thread_test.c:123 thread all
(例:在相应函数的位置设置断点break pthread_run1) 在所有线程中相应的行上设置断点
thread apply ID1 ID2 command
让一个或者多个线程执行GDB命令command
thread apply all command
让所有被调试线程执行GDB命令command
set scheduler-locking
选项 command 设置线程是以什么方式来执行命令
set scheduler-locking off
不锁定任何线程,也就是所有线程都执行,这是默认值
set scheduler-locking on
只有当前被调试程序会执行
set scheduler-locking on step
在单步的时候,除了next过一个函数的情况(熟悉情况的人可能知道,这其实是一个设置断点然后continue的行为)以外,只有当前线程会执行
线程池调试小技巧
调试进程池和线程池中的程序一个不错的方法,是将池中的个数减少至1,观察是否正确,然后逐步增加线程数量,调试线程的同步是否正确
gdb 调试core文件
什么是core文件
如果程序有问题,会产生“段错误(核心已转储)”时会生成具有堆栈信息和调试信息的文件,在编译时需要加-g
选项使程序生成调试信息,比如gcc -g test.c -o test
。
怎么配置生成core文件
- core文件开关
使用ulimit -c
查看core开关,如果为0表示关闭,不会生成core文件
使用ulimit -c [filesize]
设置core文件大小,当最小设置为4之后才会生成core文件
使用ulimit -c unlimited设置core文件大小为不限制,这是比较推荐的常用做法
- core文件命名和保存路径
core有默认的名称和路径,但是开发中通常会自己命名和指定保存路径。可以通过/proc/sys/kernel/core_pattern
设置core文件名和保存路径,方法如下:
echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
其中有一些参数含义如下:
%p
:insert pid into filename 添加pid
%u
:insert current uid into filename 添加当前uid
%g
:insert current gid into filename 添加当前gid
%s
:insert signal that caused the coredump into the filename 添加导致产生core的信号
%t
:insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
%h
:insert hostname where the coredump happened into filename 添加主机名
%e
:insert coredumping executable name into filename 添加命令名
关于core文件的调试
比如现在有一个服务器程序编译后的二进制文件名是GameServer,宕机后生成的core文件是core.30132。调试core有以下两种方法:
方法1:
gdb GameServer core.30132
方法2:
gdb -c core.30132 GameServer
在服务器开发中,如果服务器宕机,一般会生成一个core文件,我们可以用gdb来调试此core文件,并使用bt命令查看函数调用堆栈,就可以快速定位到程序的函数。
GDB 分屏操作
在GDB中使用分屏,我们可以很方便的一边查看代码,一边输入命令,主要有以下操作:
layout
:用于分割窗口,可以一边查看代码,一边测试。主要有以下几种用法:
layout src
:显示源代码窗口
layout asm
:显示汇编窗口
layout regs
:显示源代码/汇编和寄存器窗口
layout split
:显示源代码和汇编窗口
layout next
:显示下一个layout
layout prev
:显示上一个layout
Ctrl + L
:刷新窗口
Ctrl + x,再按1
:单窗口模式,显示一个窗口
Ctrl + x,再按2
:双窗口模式,显示两个窗口
Ctrl + x,再按a
:回到传统模式,即退出layout,回到执行layout之前的调试窗口