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

时间:2022-08-01 16:43:50
  1. 默认设置下,在调试多进程程序时GDB只会调试主进程。但是GDB(>V7.0)支持多进程的分别以及同时调试,换句话说,GDB可以同时调试多个程序。只需要设置follow-fork-mode(默认值:parent)和detach-on-fork(默认值:on)即可。
       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]

  查询正在调试的进程:info inferiors
  切换调试的进程: inferior
  添加新的调试进程: add-inferior [-copies n] [-exec executable] ,可以用file executable来分配给inferior可执行文件。
  其他:remove-inferiors infno, detach inferior

2.GDB默认支持调试多线程,跟主线程,子线程block在create thread。
   查询线程:info threads
   切换调试线程:thread

测试代码:

/*************************************************************************
    > File Name: multithread.c
    > Author: sunxingying
    > Mail: 1159015605@qq.com 
    > Created Time: 20170604日 星期日 042241************************************************************************/

#include<stdio.h>
#include <pthread.h>

void processA();
void processB();
void * processAworker(void *arg);

int main(int argc, const char *argv[])
{
    int pid;
    pid = fork();

     if(pid != 0)
        processA();
     else
       processB();

     return 0;
}

void processA() 
{
    pid_t pid = getpid();
    char prefix[] = "ProcessA: ";
    char tprefix[] = "thread ";
    int tstatus;
    pthread_t pt;

    printf("%s%lu %s\n", prefix, pid, "step1");

    tstatus = pthread_create(&pt, NULL, (void*)processAworker, NULL);
    if( tstatus != 0 )
    {
        printf("ProcessA: Can not create new thread.");                         }

    processAworker(NULL);
    sleep(1);
}

void * processAworker(void *arg)
{
    pid_t pid = getpid();
    pthread_t tid = pthread_self();
    char prefix[] = "ProcessA: ";
    char tprefix[] = "thread ";

    printf("%s%lu %s%lu %s\n", prefix, pid, tprefix, tid, "step2");
    printf("%s%lu %s%lu %s\n", prefix, pid, tprefix, tid, "step3");

    return NULL;
}

void processB()
{
    pid_t pid = getpid();
    char prefix[] = "ProcessB: ";
    printf("%s%lu %s\n", prefix, pid, "step1");
    printf("%s%lu %s\n", prefix, pid, "step2");
    printf("%s%lu %s\n", prefix, pid, "step3");

}

调试:
1. 调试主进程,block子进程。

[dasheng@localhost wangluo]$ gdb multithread
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-90.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/dasheng/code/wangluo/multithread...done.
(gdb) set detach-on-fork off
(gdb) show detach-on-fork
Whether gdb will detach the child of a fork is off.
(gdb) catch fork
Catchpoint 1 (fork)
(gdb) r
Starting program: /home/dasheng/code/wangluo/multithread 
[Thread debugging using libthread_db enabled]

Catchpoint 1 (forked process 3185), 0x00129424 in __kernel_vsyscall ()
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.132.el6.i686
(gdb) break mu
multithread.c   munlockall      munmap@got.plt  munmap_chunk
munlock         munmap          munmap@plt      muntrace
(gdb) break multithread.c :20
Breakpoint 2 at 0x8048546: file multithread.c, line 20.
(gdb) cont
Continuing.
[New process 3185]
[Thread debugging using libthread_db enabled]

Breakpoint 2, main (argc=1, argv=0xbffff3c4) at multithread.c:20
20       if(pid != 0)
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.132.el6.i686
(gdb) info inferiors
  Num  Description       Executable        
  2    process 3185      /home/dasheng/code/wangluo/multithread 
* 1    process 3182      /home/dasheng/code/wangluo/multithread 

2.切换到子进程:

(gdb) inferior 2
[Switching to inferior 2 [process 3185] (/home/dasheng/code/wangluo/multithread)]
[Switching to thread 2 (Thread 0xb7ff16c0 (LWP 3185))] 
#0  0x00129424 in ?? ()
(gdb) info inferiors
  Num  Description       Executable        
* 2    process 3185      /home/dasheng/code/wangluo/multithread 
  1    process 3182      /home/dasheng/code/wangluo/multithread 
(gdb) inferior 1
[Switching to inferior 1 [process 3182] (/home/dasheng/code/wangluo/multithread)]
[Switching to thread 1 (Thread 0xb7ff16c0 (LWP 3182))] 
#0  main (argc=1, argv=0xbffff3c4) at multithread.c:20
20       if(pid != 0)
(gdb) info inferiors
  Num  Description       Executable        
  2    process 3185      /home/dasheng/code/wangluo/multithread 
* 1    process 3182      /home/dasheng/code/wangluo/multithread 

3.设断点继续调试主进程,主进程产生两个子线程:

(gdb) break multithread.c :55
Breakpoint 3 at 0x804867d: file multithread.c, line 55. (2 locations)
(gdb) cont
Continuing.
ProcessA: 3182 step1
[New Thread 0xb7ff0b70 (LWP 3462)]
ProcessA: 3182 thread 3086948032 step2

Breakpoint 3, processAworker (arg=0x0) at multithread.c:55
55      printf("%s%lu %s%lu %s\n", prefix, pid, tprefix, tid, "step3");
(gdb) info inferiors
  Num  Description       Executable        
  2    process 3185      /home/dasheng/code/wangluo/multithread 
* 1    process 3182      /home/dasheng/code/wangluo/multithread 
(gdb) info threads
  3 Thread 0xb7ff0b70 (LWP 3462)  0x00211d58 in clone ()
   from /lib/libc.so.6
  2 Thread 0xb7ff16c0 (LWP 3185)  0x00129424 in ?? ()
* 1 Thread 0xb7ff16c0 (LWP 3182)  processAworker (arg=0x0)
    at multithread.c:55

4.切换到主进程中的子线程,注意:线程2为前面产生的子进程

(gdb) thread 3
[Switching to thread 3 (Thread 0xb7ff0b70 (LWP 3462))]#0 0x00211d58 in clone () from /lib/libc.so.6
(gdb) cont
Continuing.
ProcessA: 3182 thread 3086945136 step2
ProcessA: 3182 thread 3086948032 step3
[Switching to Thread 0xb7ff0b70 (LWP 3462)]

Breakpoint 3, processAworker (arg=0x0) at multithread.c:55
55      printf("%s%lu %s%lu %s\n", prefix, pid, tprefix, tid, "step3");
(gdb) info threads
* 3 Thread 0xb7ff0b70 (LWP 3462)  processAworker (arg=0x0)
    at multithread.c:55
  2 Thread 0xb7ff16c0 (LWP 3185)  0x00129424 in ?? ()
  1 Thread 0xb7ff16c0 (LWP 3182)  0x00129424 in __kernel_vsyscall ()
(gdb) thread 1
[Switching to thread 1 (Thread 0xb7ff16c0 (LWP 3182))]#0 0x00129424 in __kernel_vsyscall ()

gdb调试常用命令:

$ gcc -g example.c -o example.x

通过上述命令对example.c编译之后,使用下列命令进入到gdb调试:

$ gdb example.x

在gdb调试中,常用的命令有以下几个:

$ list 缩略为 l

列出程序源码,每次列出10行,按回车重复运行上一命令;

$ run 缩略为 r

程序开始运行,在r后可以加入程序启动参数,程序运行到断点处暂停;

$ continue 缩略为 c

程序继续运行,到下一断点处暂停;

单步调试

$ step 缩略为s

$ next 缩略为 n

程序继续运行到下一断点;

$ break 缩略为 b

在程序某一位置设置断点;

$ info break 缩略为 i b

查看断点信息;

设置/查看运行参数

$ set args ---/show args

加载运行中的进程进行调试(Attach to the running process to be debugged.):

$ gdb attatch pid

Specifying source directories

$ dir dirname …

以十六进制输出内存块数据

$ x/28hx ---

段错误调试,core文件样例

  通过ulimit命令查看一下系统是否配置支持了dump core的功能。通过ulimit -c或ulimit -a,可以查看core file大小的配置情况,如果为0,则表示系统关闭了dump core;可以通过ulimit -c unlimited来打开。若发生了段错误,但没有core dump,是由于系统禁止core文件的生成。

$ gdb [exec file] [core file] | gdb -c corefile execfile

查看堆栈信息:

$ bt