使用gdb来调试多线程、多进程

时间:2021-07-11 08:12:07

1、GDB基础调试命令介绍

GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。或许,各位比较喜欢那种图形界面方式的,像VC、BCB等IDE的调试,但如果你是在UNIX平台下做软件,你会发现GDB这个调试工具有比VC、BCB的图形化调试器更强大的功能。所谓“寸有所长,尺有所短”就是这个道理。
gdb调试 使用的是 debug 版本的 ,所以 我们在生成可执行文件的时候 ,我们需要在命令尾部 加上 - g ,加上可以生成的是 debug版本下的可执行文件 
 gcc <filename> -g 
 调试代码的基本命令:
                     使用gdb来调试多线程、多进程

2、使用GDB调试多线程运行

GDB默认是支持多线程调试的,但是要怎么来实现多线程的调试呢? 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添加配置文件:
  1. #Enable the async interface  
  2. set target-async 1  
  3. #If using the CLI, pagination breaks non-stop  
  4. set pagination off  
  5. #Finall, turn it on  
  6. set non-stop on  
        gdb支持的命里有两种类型:前台的(同步的)和后台(异步 )的。区别很简单,同步的在输出提示符之前会等待程序report一些线程已经终止的信息,异步则是直接返回。所以我们需要set target-async 1。set pagination off不要出现 Type <return> to continue 的提示信息 。最后一步是打开。        下面是常用命令:
  • info threads                                         显示所有线程
  • thread id                                             切换到指定线程
  • break filename:linenum thread all      在所有线程相应行设置断点,注意如果主线程不会执行到该行,并且启动all-stop模式,主    线程执行n或s会切换过去
  • set scheduler-locking off\on\step       默认off,执行s或c其它线程也同步执行。                                                                                  on,只有当前线程执行。                                                                                                                                                                  step,除了next过一个函数的情况(熟悉情况的人可能知道,这其实是一个设置断点然后     continue的行为)以外,只有当前线程会执行
  • show scheduler-locking                      显示当前模式
  • thread apply all command                  每个线程执行同意命令,如bt。或者thread apply 1 3 bt,即线程1,3执行bt。
        主要是我们要用能用的上的,比如no-stop模式,一般多线程调试就很有用的。
下面使用代码来演示一下多线程的调试: 这是一份多线程的代码:
#include<stdio.h>
#include<pthread.h>

//实现线程的那部分代码
void * thread1(void * argc)
{
printf("pid : %d ,tid: %u\n",getpid(),pthread_self());
//在此处调用pthread_exit函数
pthread_exit(10);
}
void * thread2(void * argc)
{
printf("pid : %d ,tid: %u\n",getpid(),pthread_self());
return (void*)20;
}
int main()
{
pthread_t pthread1,pthread2;
pthread_create(&pthread1,NULL,&thread1,NULL);
pthread_create(&pthread2,NULL,&thread2,NULL);
//调用线程取消函数
pthread_cancel(pthread2);
//在此处调用线程等待函数
void * retval1= NULL;
void * retval2= NULL;
pthread_join(pthread1,&retval1);
pthread_join(pthread2,&retval2);
printf("thread1 ret is %d \n",retval1);
printf("thread2 ret is %d \n",retval2);

return 0;
}
开始调试:
使用gdb来调试多线程、多进程
单步运行: 使用gdb来调试多线程、多进程
切换线程2调试 使用gdb来调试多线程、多进程
切换到线程3调试

使用gdb来调试多线程、多进程

3、使用GDB实现多进程之间的调试

(1).单独调试子进程

    我们可以先运行程序,然后再另一终端使用ps -ef | grep "main"(main此处是可执行文件名)搜索到子进程pid,然后启动gdb,在将其附加(attach)到gdb调试器上。
  • attach child-pid        使用该命令后,直接run即可,和调试普通程序就没区别了
  • dettach                     脱离进程

(2).使用调试器选项follow-fork-mode

    我们知道如果不设置任何选项,gdb默认调试父进程。调试器选项用法如下:
  • set follow-fork-mode mode     其中mode的可选值是parent和child,分别表示调试父进程和子进程。
  • info inferiors                             查询正在调试的进程
  • inferior processnum                 切换进程
    默认设置下,在调试多进程程序时GDB只会调试主进程。但是GDB(>V7.0)支持多进程的分别以及同时调试,换句话说,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跟主进程,子进程blockfork位置

child

off

同时调试两个进程,gdb跟子进程,主进程blockfork位置

设置方法:set follow-fork-mode [parent|child]   set detach-on-fork [on|off]
下面是使用代码来实现多进程调试:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>


//这是一个多进程的代码

int main()
{
printf("start new process\n");
pid_t pid = fork();//使用fork函数生成子进程
if(pid == 0 )
{
//子进程执行的代码
int count = 0;
printf("child ! pid = %d,ppid = %d \n",getpid(),getppid());
sleep(1);

}
else
{
//父进程执行代码
printf("parent ! pid = %d,ppid = %d \n",getpid(),getppid());
sleep(1);
}
return 0 ;
}
只调试父进程 使用gdb来调试多线程、多进程
只调试子进程(与上面的对比来看看) 使用gdb来调试多线程、多进程
同时调试两个进程(父进程调试 运行 ,子进程block(以阻塞方式等待)) 使用gdb来调试多线程、多进程

同时调试两个进程(子进程调试 运行 ,父进程block(以阻塞方式等待))(对比上面的看) 使用gdb来调试多线程、多进程