千家信息网

Linux多进程和多线程的一次gdb调试

发表于:2024-09-22 作者:千家信息网编辑
千家信息网最后更新 2024年09月22日,本篇内容介绍了"Linux多进程和多线程的一次gdb调试"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成
千家信息网最后更新 2024年09月22日Linux多进程和多线程的一次gdb调试

本篇内容介绍了"Linux多进程和多线程的一次gdb调试"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

1 原文整理

默认设置下,在调试多进程程序时gdb只会调试主进程。gdb7以上的版本(gdb --version)支持多进程调试,只需要设置好follow-fork-mode(fork追踪模式)以及detach-on-fork(指示GDB在fork之后是否断开某个进程的调试)即可。

这两个参数的设置命令分别是:set follow-fork-mode [parent|child],set detach-on-fork [on|off]。两者结合起来构成了GDB的调试模式:

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

查看gdb默认的参数设置:

(gdb) show follow-fork-modeDebugger response to a program call of fork or vfork is "parent".(gdb) show detach-on-forkWhether gdb will detach the child of a fork is on.(gdb)
2 演示代码

下面这段代码的主要流程就是在main函数中fork创建一个子进程,然后在父进程中又创建一个线程,接着就使用gdb进行调试(block子进程)。注意,在调试设置断点的时候,由于之前调试的时候代码最前面没有加上这7行说明文字,所以设置断点的行号要加上7。

1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
/**  * @FileName    gdb_pthread.c * @Describe    A simple example for the debug of multiprocess and multithreading using gdb in linux system. * @Author      vfhky 2016-02-25 22:48 https://typecodes.com/cseries/multilprocessthreadgdb.html * @Compile     gcc gdb_pthread.c -g -o gdb_pthread * @Reference   http://blog.csdn.net/pbymw8iwm/article/details/7876797 */#include #include #include #include //Parent process handle.void Parent();//Child process handle.void Child();//Parent process handle after generate a thread.void * ParentDo( char *argv );int main( int argc, const char **argv ){    int pid;    pid = fork();    if(pid != 0)        //add the first breakpoint.        Parent();    else        Child();    return 0;}//Parent process handle.void Parent(){    pid_t pid = getpid();    char cParent[] = "Parent";    char cThread[] = "Thread";    pthread_t pt;    printf( "[%s]: [%d] [%s]\n", cParent, pid, "step1" );    if( pthread_create( &pt, NULL, (void *)*ParentDo, cThread ) )    {        printf( "[%s]: Can not create a thread.\n", cParent );    }    ParentDo( cParent );    sleep(1);}void * ParentDo( char *argv ){    pid_t pid = getpid();    pthread_t tid = pthread_self();     //Get the thread-id selfly.    char tprefix[] = "thread";    printf( "[%s]: [%d] [%s] [%lu] [%s]\n", argv, pid, tprefix, tid, "step2" );         //add the second breakpoint.    printf( "[%s]: [%d] [%s] [%lu] [%s]\n", argv, pid, tprefix, tid, "step3" );    return NULL;}void Child(){    pid_t pid = getpid();    char prefix[] = "Child";    printf( "[%s]: [%d] [%s]\n", prefix, pid, "step1" );    return;}

已知如果直接运行程序,那么输出的内容如下:

[vfhky@typecodes pthread_key]$ gdb_pthread[Parent]: [22648] [step1][Parent]: [22648] [thread] [139722467432256] [step2][Parent]: [22648] [thread] [139722467432256] [step3][Thread]: [22648] [thread] [139722450630400] [step2][Thread]: [22648] [thread] [139722450630400] [step3][Child]: [22649] [step1][vfhky@typecodes pthread_key]$
3 gdb调试

3.1 设置调试模式和Catchpoint

设置调试父子进程,gdb跟主进程,子进程block在fork位置。

[vfhky@typecodes pthread_key]$ gdb gdb_pthreadGNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-80.el7Copyright (C) 2013 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 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 "x86_64-redhat-linux-gnu".For bug reporting instructions, please see:...Reading symbols from /home/vfhky/bin/gdb_pthread...done.(gdb) set detach-on-fork off#####catch让程序在发生某种事件(fork、异常throw、异常catch、动态库加载等)的时候停止运行(gdb) catch fork Catchpoint 1 (fork)(gdb) info bNum     Type           Disp Enb Address            What1       catchpoint     keep y                      fork(gdb)

如下图所示:

3.2 开始gdb调试

(gdb) r                         ####运行到断点/捕捉点(第17行处的fork函数,23873是子进程PID)Starting program: /home/vfhky/bin/gdb_pthread [Thread debugging using libthread_db enabled]Using host libthread_db library "/lib64/libthread_db.so.1".Catchpoint 1 (forked process 23873), 0x00007ffff709b50c in __libc_fork () at ../nptl/sysdeps/unix/sysv/linux/fork.c:130130       pid = ARCH_FORK ();(gdb) bt                        #####查看堆栈情况#0  0x00007ffff709b50c in __libc_fork () at ../nptl/sysdeps/unix/sysv/linux/fork.c:130#1  0x00000000004007b4 in main (argc=1, argv=0x7fffffffe4c8) at gdb_pthread.c:17(gdb) info threads              #######显示运行的线程信息(23869是父进程的PID)  Id   Target Id         Frame * 1    Thread 0x7ffff7fe1740 (LWP 23869) "gdb_pthread">这时使用如下命令查看当前CentOS系统所有进程的状态:发现父进程PID为23869,通过fork产生的子进程为23873:[vfhky@typecodes ~]$ pstree -pul 同时,使用命令cat /proc/23869/status查看当前进程的详细信息:进程PID为23869,它的父进程(即GDB进程)为23859,同时这也是追踪进程ID,线程数Threads为1(共享使用该信号描述符的线程数,在POSIX多线程序应用程序中,线程组中的所有线程使用同一个信号描述符)。3.3 设置第一个断点在程序的第18行设置断点:(gdb) b gdb_pthread.c:18Breakpoint 2 at 0x4007b7: file gdb_pthread.c, line 18.(gdb) info b                        ######列出所有断点和捕捉点Num     Type           Disp Enb Address            What1       catchpoint     keep y                      fork, process 23873                 ########子进程23873        catchpoint already hit 1 time2       breakpoint     keep y   0x00000000004007b7 in main at gdb_pthread.c:18(gdb) 3.4 执行到第一个断点(gdb) c                #####执行到第18行处的断点Continuing.[New process 23873]                     #####父进程23869执行完第1个捕捉点的程序,产生子进程23873[Thread debugging using libthread_db enabled]Using host libthread_db library "/lib64/libthread_db.so.1".Breakpoint 2, main (argc=1, argv=0x7fffffffe4c8) at gdb_pthread.c:18            ##########父进程执行到第18行处的断点18              if(pid != 0)(gdb) info threads                      ####查看所有运行的线程,有父进程23869和子进程23873  Id   Target Id         Frame   2    Thread 0x7ffff7fe1740 (LWP 23873) "gdb_pthread" 0x00007ffff709b50c in __libc_fork () at ../nptl/sysdeps/unix/sysv/linux/fork.c:130* 1    Thread 0x7ffff7fe1740 (LWP 23869) "gdb_pthread" main (argc=1, argv=0x7fffffffe4c8) at gdb_pthread.c:18(gdb) info inferiors                    #####显示正在调试的进程  Num  Description       Executable          2    process 23873     /home/vfhky/bin/gdb_pthread                ########子进程* 1    process 23869     /home/vfhky/bin/gdb_pthread                ########父进程(gdb) info b                    #######查看当前所有的断点Num     Type           Disp Enb Address            What1       catchpoint     keep y                      fork, process 23873         catchpoint already hit 1 time2       breakpoint     keep y                    breakpoint already hit 1 time2.1                         y     0x00000000004007b7 in main at gdb_pthread.c:18 inf 22.2                         y     0x00000000004007b7 in main at gdb_pthread.c:18 inf 1(gdb) 截图如下:这时使用命令查看当前系统进程的状态:发现此时仍然只有父进程23869和子进程23873。[vfhky@typecodes ~]$ pstree -pul 3.5 执行到第一个断点此时如果切换到子进程23873(gdb) inferior 2[Switching to inferior 2 [process 23873] (/home/vfhky/bin/gdb_pthread)][Switching to thread 2 (Thread 0x7ffff7fe1740 (LWP 23873))] #0  0x00007ffff709b50c in __libc_fork () at ../nptl/sysdeps/unix/sysv/linux/fork.c:130130       pid = ARCH_FORK ();(gdb) info inferiors                 #####显示正在调试的进程   Num  Description       Executable        * 2    process 23873     /home/vfhky/bin/gdb_pthread                #####子进程  1    process 23869     /home/vfhky/bin/gdb_pthread                #####父进程(gdb) 3.6 重新切换到父进程23869(gdb) inferior 1[Switching to inferior 1 [process 23869] (/home/vfhky/bin/gdb_pthread)][Switching to thread 1 (Thread 0x7ffff7fe1740 (LWP 23869))] #0  main (argc=1, argv=0x7fffffffe4c8) at gdb_pthread.c:1818              if(pid != 0)(gdb) info inferiors                 #####显示正在调试的进程  Num  Description       Executable          2    process 23873     /home/vfhky/bin/gdb_pthread * 1    process 23869     /home/vfhky/bin/gdb_pthread (gdb) 3.7 设置第二个断点并调试在第50行设置断点继续调试主进程(使父进程产生线程),其中父进程和线程到底是谁先执行是由内核调度控制的。(gdb) b gdb_pthread.c:50Breakpoint 3 at 0x4008a7: gdb_pthread.c:50. (2 locations)(gdb) c                    ######继续执行代码到第50行处的断点Continuing.[Parent]: [23869] [step1]                              ######第33行父进程打印Parent()函数中的数据[New Thread 0x7ffff6fdd700 (LWP 24024)]                ######第35行父进程创建了一个线程24024(LWP表示轻量级进程)[Switching to Thread 0x7ffff6fdd700 (LWP 24024)]            #####已经自动切换到线程24024(LWP表示轻量。进程),也就是GDB继续调试线程而不是父进程了。Breakpoint 3, ParentDo (argv=0x7fffffffe390 "Thread") at gdb_pthread.c:50            ######线程24024阻塞在程序的第50行50              printf( "[%s]: [%d] [%s] [%lu] [%s]\n", argv, pid, tprefix, tid, "step2" );(gdb) 这时使用命令查看当前系统进程的状态:存在父进程23869和子进程23873以及父进程创建的一个线程24024(线程用大括号{}表示)。[vfhky@typecodes ~]$ pstree -pul 同时,使用命令cat /proc/23869/status查看当前进程的详细信息:进程PID为23869,它的父进程(即GDB进程)为23859,同时这也是追踪进程ID,线程数Threads为2(当前父进程23869+线程24024)。3.8 查看第二个断点处的调试信息(gdb) info inferiors                 #####显示正在调试的进程   Num  Description       Executable          2    process 23873     /home/vfhky/bin/gdb_pthread                     ###子进程* 1    process 23869     /home/vfhky/bin/gdb_pthread                     ###父进程 (gdb) info threads         ####查看所有运行的线程,父进程23869、子进程23873、线程24024,由星号可以发现目前调试已经切换到了线程24024了。   Id   Target Id         Frame * 3    Thread 0x7ffff6fdd700 (LWP 24024) "gdb_pthread" ParentDo (argv=0x7fffffffe390 "Thread") at gdb_pthread.c:50  2    Thread 0x7ffff7fe1740 (LWP 23873) "gdb_pthread" 0x00007ffff709b50c in __libc_fork () at ../nptl/sysdeps/unix/sysv/linux/fork.c:130  1    Thread 0x7ffff7fe1740 (LWP 23869) "gdb_pthread" ParentDo (argv=0x7fffffffe3a0 "Parent") at gdb_pthread.c:50(gdb) info b                #####查看设置的所有的断点breakpoint和捕捉点catchpoint(共3个):Num     Type           Disp Enb Address            What1       catchpoint     keep y                      fork, process 23873         catchpoint already hit 1 time2       breakpoint     keep y                    breakpoint already hit 1 time2.1                         y     0x00000000004007b7 in main at gdb_pthread.c:18 inf 22.2                         y     0x00000000004007b7 in main at gdb_pthread.c:18 inf 13       breakpoint     keep y                    breakpoint already hit 1 time3.1                         y     0x00000000004008a7 in ParentDo at gdb_pthread.c:50 inf 23.2                         y     0x00000000004008a7 in ParentDo at gdb_pthread.c:50 inf 1(gdb) 3.9 如果手动切换到线程24024(gdb) thread 3[Switching to thread 3 (Thread 0x7ffff6fdd700 (LWP 24024))]#0  ParentDo (argv=0x7fffffffe390 "Thread") at gdb_pthread.c:5050              printf( "[%s]: [%d] [%s] [%lu] [%s]\n", argv, pid, tprefix, tid, "step2" );(gdb) info threads                   #####查看所有运行的线程  Id   Target Id         Frame * 3    Thread 0x7ffff6fdd700 (LWP 24024) "gdb_pthread" ParentDo (argv=0x7fffffffe390 "Thread") at gdb_pthread.c:50  2    Thread 0x7ffff7fe1740 (LWP 23873) "gdb_pthread" 0x00007ffff709b50c in __libc_fork () at ../nptl/sysdeps/unix/sysv/linux/fork.c:130  1    Thread 0x7ffff7fe1740 (LWP 23869) "gdb_pthread" ParentDo (argv=0x7fffffffe3a0 "Parent") at gdb_pthread.c:50(gdb) info inferiors                 #####显示正在调试的进程    Num  Description       Executable          2    process 23873     /home/vfhky/bin/gdb_pthread * 1    process 23869     /home/vfhky/bin/gdb_pthread (gdb) 3.10 开始执行第二个断点处的代码(gdb) cContinuing.[Thread]: [23869] [thread] [140737337218816] [step2]            #####线程24024执行第50行处,打印数据[Thread]: [23869] [thread] [140737337218816] [step3]            #####线程24024执行第51行处,打印数据[Thread 0x7ffff6fdd700 (LWP 24024) exited]                      #####线程24024退出[Switching to Thread 0x7ffff7fe1740 (LWP 23869)]                #####切换到父进程中去Breakpoint 3, ParentDo (argv=0x7fffffffe3a0 "Parent") at gdb_pthread.c:50                #####父进程继续停在第50行处的断点50              printf( "[%s]: [%d] [%s] [%lu] [%s]\n", argv, pid, tprefix, tid, "step2" );(gdb) info inferiors                    ######列出正在调试进程(父进程23869和子进程23873),1前面的星号表示当前调试的进程(父进程23869)。   Num  Description       Executable          2    process 23873     /home/vfhky/bin/gdb_pthread * 1    process 23869     /home/vfhky/bin/gdb_pthread (gdb) info threads                     ######查看所有运行的线程  Id   Target Id         Frame   2    Thread 0x7ffff7fe1740 (LWP 23873) "gdb_pthread" 0x00007ffff709b50c in __libc_fork () at ../nptl/sysdeps/unix/sysv/linux/fork.c:130            #####子进程23873* 1    Thread 0x7ffff7fe1740 (LWP 23869) "gdb_pthread" ParentDo (argv=0x7fffffffe3a0 "Parent") at gdb_pthread.c:50          #####父进程23869(gdb) 这时使用命令查看当前系统进程的状态:存在父进程23869和子进程23873,其中线程24024已经结束了。[vfhky@typecodes ~]$ pstree -pul 3.11 继续调试父进程此时,由于线程的退出,父进程作为自动选择的要调试的线程。(gdb) cContinuing.[Parent]: [23869] [thread] [140737354012480] [step2]        #####父进程23869执行第50行[Parent]: [23869] [thread] [140737354012480] [step3]        #####父进程23869执行第51行[Inferior 1 (process 23869) exited normally]                #####正在调试的父进程23869退出(gdb) info inferiors             ######显示正在调试的进程  Num  Description       Executable          2    process 23873     /home/vfhky/bin/gdb_pthread        #####fork创建的子进程23873* 1                /home/vfhky/bin/gdb_pthread        #####fork创建的父进程23869已经退出 (gdb) info threads              ####显示正在运行的线程:只存在子进程23873,父进程23869已经退出   Id   Target Id         Frame   2    Thread 0x7ffff7fe1740 (LWP 23873) "gdb_pthread">这时使用命令查看当前系统进程的状态:只有子进程23873(由内核init进程接管这个孤儿进程),父进程23869也已经结束了。[vfhky@typecodes ~]$ pstree -pul 再用ps ux命令查看子进程23873: 4 附录在gdb中,经常用到的恢复程序运行和单步调试的命令有:continue        继续运行程序直到下一个断点(类似于VS里的F5)next            逐过程步进,不会进入子函数(类似VS里的F10)setp            逐语句步进,会进入子函数(类似VS里的F11)until           运行至当前语句块结束finish          运行至函数结束并跳出,并打印函数的返回值(类似VS的Shift+F11)

"Linux多进程和多线程的一次gdb调试"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

0