使用gdb调试多线程与多进程

时间:2023-01-20 16:41:09

GDB的基本介绍

GDB是GUN开源组织发布的一个强大的UNIX下的程序调试工具。或许,平时大家更加习惯图形界面的调试,比如VS上的IDE调试;但是如果我们在UNIX或Linux下做软件,我们呢就更加需要熟练GDB这个调试工具。

一般来说,GDB主要能够完成以下的几个功能:

①启动你的程序,可以按照你自己的要求随性所欲的运行程序;

②可以让被调试的程序在你自己所定的位置的断点处挺住;

③当程序被停住时,可以检查此时你的程序中所发生的事情;

④动态的改变你程序的执行环境。

GDB调试使用的是DEBUG版本的,所以我们在生产可执行文件时,我们需要在命令的尾部加上 -g,这样就可以生成的是DEBUG版本下的可执行文件。

调试代码的基本命令

命令 作用
list或l + 行号 显示从行号开始的源代码
list或l + 函数名 列出某个函数的源代码
run或r  运行程序
step或s 进入函数调用
breaktrace或bt 查看各级函数调用及参数
info或i locals查看当前栈帧局部变量的值
info break 查看断点信息
finish 执行到当前函数返回,然后停下来等待命令
print或p 打印表达式的值,通过表达式可以修改变量的值或者调用函数
break或b + 行号 在某一行设置断点
set var 修改变量的值
quit 退出调试
break + 函数名 在某个函数开头设置断点
continue或c 从当前位置开始连续而非单步执行调试程序
run或r 从开头连续执行程序而非单步执行
delete breakpoints 删除断点
delete breakpoints n 删除序号为n的断点
disable breakpoints 禁用断点
enable breakpoints 启用断点
info或i breakpoints 查看当前设置了哪些断点
display + 变量名 跟踪查看一个变量,每次停下来都显示这个值
undisplay 取消先前对那些变量设置的跟踪
until + x 跳至第x行
n或next 单步执行
p + 变量 打印变量值
list或l 列出源代码,接着上次的位置往下列,每次列10行
frame或f 帧编号,选择栈帧
start 开始执行程序,停在main函数第一行语句前面等待命令
call + 函数名 强制调用某个函数

GDB调试多进程

在默认情况下是调试多进程程序时GDB会默认调试主进程,但是GDB支持多进程的分别与同步调试。即GDB支持同时调试多个进程,只需要设置follow-fork-mode(默认为parent)和detach-on-fork(默认为on)即可。我们还可以使用catch fork指令,如果fork异常,会停止程序。

follow-fork-mode detach-on-fork 说明
parent on 只调试主进程(GDB默认)
child on 只调试子进程
parent off 同时调试两个进程,gdb跟主进程,子进程block(阻塞)在fork位置
child off 同时调试两个进程,gdb跟子进程,主进程block在fork位置

设置方法: set follow-fork-mode[parent|child] set detach-on-fork[on|off]

显示:show follow-fork-mode show detach-on-fork

使用gdb调试多线程与多进程

下面通过代码来演示

使用gdb调试多线程与多进程

调试步骤:

只调试父进程

使用gdb调试多线程与多进程

只调试子进程(与上面的对比起来看)

使用gdb调试多线程与多进程

下面呢我们开始直接调试两个进程(父进程运行调试,子进程阻塞等待)

使用gdb调试多线程与多进程

最后我们进行子进程运行调试,父进程阻塞等待

使用gdb调试多线程与多进程

GDB调试多线程

在多线程编程时,当我们需要调试时,有时需要控制某些线程停在断点处,有些线程继续执行;有时需要控制线程的运行程序;有时需要中断某个线程,切换到其他线程。这些呢都可以通过gdb来实现。GDB默认支持调试多线程,跟主线程,子线程block在create+thread。

gdb调试一般有两种模式:all-stop模式和no-stop模式(gdb7.0之前不支持no-stop模式)。

1.all-stop模式

在这种模式下,当你的程序在gdb由于任何原因而停止,所有的线程都会停止,而不仅仅是当前的线程。一般来说,gdb不能单步所有的线程。因为线程调度是gdb无法控制的。无论什么时候当gdb停止你的程序,它都会自动切换到触发断点的那个线程。

2.no-stop模式(网络编程常用)

顾名思义,启动不关模式。当程序在gdb中停止,只有当前的线程会被停止,而其他线程将会继续运行。这时候step,next这些命令就只对当前线程起作用。

如果需要打开no-stop模式,可以向~/.gdbinit添加配置文件:

使用gdb调试多线程与多进程

gdb支持的命里有两种类型:前台的(同步的)和后台(异步 )的。区别很简单,同步的在输出提示符之前会等待程序report一些线程已经终止的信息,异步则是直接返回。所以我们需要set target-async 1。set pagination off不要出现 Type <return> to continue 的提示信息 。最后一步是打开。

下面看一下gdb调试多线程常用命令:

命令 作用
info threads 显示所有可调试的线程
thread ID 切换到指定线程,gdb为每一个线程分配一个ID(与tid不同),编号一般从1开始
breakfilename:linenum thread all 在所有线程相应行设置断点,注意如果主线程不会执行到该行,并且启动all-stop模式,主线程执行n或s会切换过去
set scheduler-locking off|on\step 默认off,执行s或c其它线程也同步执行。on,只有当前相称执行。step,只有当前线程执行
show scheduler-locking 显示当前模式
thread apply all command 每个线程执行同意命令,如bt。或者thread apply 1 3 bt,即线程1,3执行bt。

通过代码来演示:

使用gdb调试多线程与多进程

调试步骤:

开始调试

使用gdb调试多线程与多进程

然后开始运行代码

单步调试

使用gdb调试多线程与多进程

切换到其他线程

使用gdb调试多线程与多进程

再切换到其他线程调试

使用gdb调试多线程与多进程

设置core文件

core的意思是核心,dumped的意思就是抛出,转储,core dumped就是核心转储的意思。当一个进程异常退出前,该进程会抛出当时该程序进程的内存详细情况存储在硬盘上,文件名通常是core,这就叫core dump。

进程异常终止通常是因为代码存在BUG,比如非法内存访问导致段错误,事后可以用调试器检查core文件以查清错误原因,这叫做事后调试。

uname -a 查看机器参数

ulimit -a 查看默认参数

ulimit -c 1024 设置core文件大小为1024

ulimit -c unlimit 设置core文件大小为无限

ulimit -c unlimited 生成core文件,也可以是指定大小,然后使用gdb ./main core启动,bt查看调用栈即可。

eg1(可以快速定位出问题的位置)

gdb a.out core.xxx

where

eg2 (在 gdb 中使用)

(gdb) core-file core.xxx