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

来源:互联网 发布:西门子淘宝旗舰店 编辑:程序博客网 时间:2024/05/24 03:20

今天我们来谈谈如何使用gdb调试多进程和多线程程序???       

       其实gdb本身没有对多进程程序调试提供直接支持。例如,使用gdb调试某个进程,如果该进程fork了子进程,gdb会继续调试该进程,子进程会不受干扰地继续运行下去。如果我们事先在子进程代码里设定了断点,子进程会收到SIGTRAP信号并终止。所以我们可以通过去子进程设置断点,再通过step与continue交替运行,找到最终程序错误的“罪魁祸首”。

一、使用gdb调试多进程程序

      我们先来看一段测试代码,在该代码中创建了一个子进程,子进程中有一个除0错误,我们需要用gdb调试来找出这个错误。



调试原理:

       首先要强调的一点是,既然要调试,在Makefile文件里就要加上-g选项,才能支持调试。大家主要看我在创建的子进程的代码中加入了 sleep(60),这就是关键所在。为什么先要让子进程休眠,是因为在子进程休眠过程中,我们需要通过ps aux | grep '文件名'来找到当前运行进程的pid,用于attach pid调试进程使用。

调试过程:

      先来看看我使用gdb调试的整个过程,以及程序的运行结果:





      大家注意看,在我调试的最后一行print b,我打印b的值发现是0,这就找出了问题的关键!我的程序中出现了除0错误。

      我大概把调试过程梳理一遍,我们获取到当前进程的pid后,启动gdb进行调试,使用attach命令关联到当前进程,然后在子进程的代码中设置一个断点,用来调试子进程找出程序错误。然后使用step 与 continue交替使用进行单步执行,在执行其中我们还需要追踪变量的值使用print来判断错误出现在哪个变量中。

二、使用gdb调试多线程程序

大家先来看我写的测试代码,在代码中我总共只创建了两个线程。顺便再来熟悉一下pthread_create创建线程这个函数。






首先要获取到进程的pid后,启动gdb来调试,再使用attach进程号来关联到当前运行的进程。因为进程中创建了两个线程,再加上主线程总共是三个。

info pthreads用来查看当前所有在运行的线程,最前面的编号是gdb分配的线程号;

thread ID 切换当前调试的线程为指定ID的线程,在此程序中就会有三个线程1,2,3可供切换;

break thread_test.c:123 thread all 在所有线程中相应的行上设置断点;

set scheduler-locking off|on|step 我们会发现,在使用step或者continue命令调试当前被调试线程的时候,其他线程也是同时执行的,怎么只让被调试程序执行呢?通过这个命令就可以实现这个需求。off 不锁定任何线程,也就是所有线程都执行,这是默认值。 on 只有当前被调试程序会执行。 step 在单步的时候,除了next过一个函数的情况以外,只有当前线程会执行。

调试过程仅供参考:



在我的调试过程中,我没有使用set scheduler-locking off|on|step 这条指令,所以当我选择的三号线程执行结束时,其他线程也都执行结束后退出了。这是调试程序结果:


三、gdb调试程序指令

gdb program

program 也就是你的执行文件,一般在当前目录下。

gdb program core

用gdb同时调试一个运行程序和core文件,core是程序非法执行后core dump后产生的文件。

gdb program 1234

如果你的程序是一个服务程序,那么你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去,并调试他。program应该在PATH环境变量中搜索得到。

set args 可指定运行时参数。(如:set args 10 20 30 40 50 ) 
show args 命令可以查看设置好的运行参数。 
run (r)     启动程序 
不指定运行参数 r 
指定运行参数    r 10 20 30 40 50

info terminal显示你程序用到的终端的模式。 

使用重定向控制程序输出。如:run > outfile 

tty命令可以设置输入输出使用的终端设备。如:tty /dev/tty1

break      设置断点,可以简写为b 
b 10  设置断点,在源程序第10行 
b func 设置断点,在func函数入口处

info b  查询所有断点

watch                  为表达式(变量)expr设置一个观察点。当表达式值有变化时,马上停住程序。 
rwatch      表达式(变量)expr被读时,停住程序。 
awatch                 表达式(变量)的值被读或被写时,停住程序。 
info watchpoints列出当前所设置了的所有观察点。

维护停止点:

clear清除所有的已定义的停止点;     clear function清除所有设置在函数上的停止点;     clear linenum清除所有设置在指定行上的停止点;     clear filename:linenum清除所有设置在指定文件:指定行上的停止点;     delete [breakpoints] [range...]删除指定的断点,breakpoints为断点号。如果不指定断点号,则表示删除所有的断点;     range表示断点号的范围(如:3-7)。其简写命令为d;     比删除更好的一种方法是disable停止点,disable了的停止点,GDB不会删除,当你还需要时,enable即可,就好像回收站一样;     disable [breakpoints] [range...] disable所指定的停止点,breakpoints为停止点号。如果什么都不指定,表示disable所有的停止点。简写命令是dis.;     enable [breakpoints] [range...]enable所指定的停止点,breakpoints为停止点号;     enable [breakpoints] once range…enable所指定的停止点一次,当程序停止后,该停止点马上被GDB自动disable;     enable [breakpoints] delete range…enable所指定的停止点一次,当程序停止后,该停止点马上被GDB自动删除

调试代码:

run 运行程序,可简写为r        next 单步跟踪,函数调用当作一条简单语句执行,可简写为n       step 单步跟踪,函数调进入被调用函数体内,可简写为s     finish 退出函数       until 在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体,可简写为u       continue 继续运行程序,可简写为c     stepisi,nextini 单步跟踪一条机器指令,一条程序代码有可能由数条机器指令完成,stepi和nexti可以单步执行机器指令       info program 来查看程序的是否在运行,进程号,被暂停的原因。

查看运行数据:

print 打印变量、字符串、表达式等的值,可简写为p         p count 打印count的值        p cou1+cou2+cou3 打印表达式值        print接受一个表达式,GDB会根据当前的程序运行的数据来计算这个表达式,表达式可以是当前程序运行中的const常量、变量、函数等内容。但是GDB不能使用程序中定义的宏。

堆栈相关命令:

backtrace/bt  bt  用来打印栈帧指针,也可以在该命令后加上要打印的栈帧指针的个数,查看程序执行到此时,是经过哪些函数呼叫的程序,程序“调用堆栈”是当前函数之前的所有已调用函数的列表(包括当前函数)。每个函数及其变量都被分配了一个“帧”,最近调用的函数在 0 号帧中(“底部”帧);

frame  frame 1  用于打印指定栈帧;       info reg  info reg  查看寄存器使用情况 ;       info stack  info stack  查看堆栈使用情况 ;      up/down  up/down  跳到上一层/下一层函数


信号命令:

signal   signal SIGXXX   产生XXX信号,如SIGINT。一种速查Linux查询信号的方法:# kill -l

handle   在GDB中定义一个信号处理。信号可以以SIG开头或不以SIG开头,可以用定义一个要处理信号的范围(如:SIGIO-SIGKILL,表示处理从SIGIO信号到SIGKILL的信号,其中包括SIGIO,SIGIOT,SIGKILL三个信号),也可以使用关键字all来标明要处理所有的信号。一旦被调试的程序接收到信号,运行程序马上会被GDB停住,以供调试。其可以是以下几种关键字的一个或多个:
nostop/stop当被调试的程序收到信号时,GDB不会停住程序的运行,但会打出消息告诉你收到这种信号/GDB会停住你的程序;  
print/noprint当被调试的程序收到信号时,GDB会显示出一条信息/GDB不会告诉你收到信号的信息; 
pass noignore 当被调试的程序收到信号时,GDB不处理信号。这表示,GDB会把这个信号交给被调试程序会处理;
nopass ignore 当被调试的程序收到信号时,GDB不会让被调试程序来处理这个信号;
info signals info handle 可以查看哪些信号被GDB处理,并且可以看到缺省的处理方式;

single命令和shell的kill命令不同,系统的kill命令发信号给被调试程序时,是由GDB截获的,而single命令所发出一信号则是直接发给被调试程序的。

跳转执行:

jump  指定下一条语句的运行点。可以是文件的行号,可以是file:line格式,可以是+num这种偏移量格式。表式着下一条运行语句从哪里开始。相当于改变了PC寄存器内容,堆栈内容并没有改变,跨函数跳转容易发生错误。








原创粉丝点击