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

来源:互联网 发布:itunes无法连接到网络 编辑:程序博客网 时间:2024/05/16 12:19

多进程调试:

首先来了解下会可能会用到的调试命令:

1.默认设置下,在调试多进程程序时GDB只会调试主进程。
但是如果设置follow-fork-mode的话,就可调试多个进程。
set follow-fork-mode parent|child:
进入gdb后默认调试的是parent,若是想要调试child的话,需要设置set follow-fork-mode child,然后进入调试。但是这种方式在同一时间只能调试单个进程。

set follow-fork-mode:查看当前调试的fork的模式。

2.detach-on-fork on|off:
设置为on的话,只调试父进程或子进程其中一个,需要根据follow-fork-mode决定,这是默认模式。
设置成off的话,父子进程都在gdb的控制之下,其中一个进程正常调试,需要根据follow-fork-mode决定,另一个进程会被设置为暂停状态。
show detach-on-fork:查看detach-on-fork的模式。

扩展:GDB将每一个被调试程序的执行状态记录在一个名为inferior的结构中。一般情况下一个inferior对应一个进程,每个不同的inferior有不同的地址空间。inferior有时候会在进程没有启动的时候就存在。

3.info inferiors:
显示GDB调试的所有inferior,GDB会为它们分配ID。其中带有*的进程是当前正在调试的进程。

4.inferior num:切换到编号为num的进程进行调试。

5,。add-inferior[-copies n][infno]:
增加n个inferior并执行程序为executable。如果不指定n则默认是增加一个inferior。
如果不指定executable,则执行程序留空,增加后可使用file命令重新指定执行程序。这时候创建的inferior其关联的进程并没启动。

6.clone-inferior [-copies n][infno]:
复制n个编号是infno的inferior。如果不指定n的话,就只复制一个inferior。
如果不指定infno,则就复制正在调试的inferior。

7.detach inferior infno:
detach编号是infno的inferior。注意这个inferior还存在,可以再次用run命令执行它。

8.kill inferior infno:
kill infno号的inferior。注意这个inferior仍然存在,可以再次使用run等命令来执行它。

9.remove-inferior infno:
删除一个infno号的inferior。如果inferior正在运行,则不能删除,所以删除前需要kill或者detach过。

10.set schedule-multiple on|off:
设置为off:表示当前inferior会执行。
设为on:全部是执行状态的inferior都会执行。
类似于scheduler-locking。

show shedule-multiple:
查看schedule-multiple的状态。

11.set follow-exec-mode new|same:
设置same:当发生exec的时候,在执行exec的inferior上控制子进程。
设置为new:新建一个inferior所执行起来的子进程。而父进程的inferior仍然保留,当前保留的inferior的程序状态是没有执行,

show follow-exec-mode:查看follow-exec-mode设置的模式。

12.set print inferior-events on|off:用来打开和关闭inferior状态的提示信息。

13.maint info program-spaces:
用来显示当前GDB一共管理了多少地址空间。

实例调试:

#include<stdio.h>#include<stdlib.h>#include<sys/types.h>int main(){    pid_t id;    id = fork();    if(id == 0)    {        //child        printf("child: %d,ppid: %d\n",getpid(),getppid());        exit(1);    }    else    {        sleep(1);        //father        printf("father: %d\n",getpid());        waitpid(id);    }    return 0;}

1.设置调试的模式:

这里写图片描述

2.切换调试的进程:

这里写图片描述

调试多线程:

首先来了解下使用gdb来调试多线程程序时会用到的命令参数:

1、info threads 显示当前可调试的所有线程,每个线程会有一个GDB为其分配的ID,后面操作线程的时候会用到这个ID。

2、显示的线程信息中:前面有*的是当前调试的线程。

3、thread ID 切换当前调试的线程为指定ID的线程。

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

5、thread apply ID1 ID2 command 让一个或者多个线程执行GDB命令command。

6、thread apply all command 让所有被调试线程执行GDB命令command。

7、set scheduler-locking [on/off/step] //设置scheduler-locking
默认情况下,所有的线程都会同时执行,而我们有时候需要这样的场景,只需要让一个线程单独执行,而让其他的线程都在等待,这时就可以使用这个命令来达到你的需求。
off 不锁定任何线程,也就是所有线程都执行,这是默认值。
on 只有当前被调试程序会执行。
step:阻止其他线程在当前线程单步调试的时候抢占当前线程。只有当next、continue、util以及finish的时候,其他线程才会获得重新运行的

8、show scheduler-locking //显示当前scheduler-locking

注意:set scheduler-locking要处于线程运行环境下才能生效,也就是程序已经运行并且暂停在某个断点处,否则会出现“Target ‘exec’ cannot support this command.”这样的错误;而且经过测试,设置后的scheduler-locking值在整个进程内有效,不属于某个线程。

实例调试:

#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<pthread.h>void* thread_run1(void* arg){     printf("thread1: %d\n",pthread_self());    pthread_exit((int*)1);    return NULL;}void* thread_run2(void* arg){    printf("thread2 : %d\n",pthread_self());       pthread_exit((int*)1);    return NULL;}int main(){    pthread_t tid1,tid2;    pthread_create(&tid1,NULL,thread_run1,NULL);    pthread_create(&tid2,NULL,thread_run2,NULL);    pthread_join(tid1,NULL);    pthread_join(tid2,NULL);    return 0;}

1.锁定线程及查看锁定的模式:

这里写图片描述

2.查看当前所有被调试的线程:

这里写图片描述

3.让所有的线程都打印堆栈信息:

这里写图片描述

4.切换调试线程:

这里写图片描述


如何使用gdb快速定位由段错误引发的程序崩溃

在Linux下编写代码,我们可能总是会遇到这样的错误:

这里写图片描述

这是一个指针越界的问题,那么我们如何可以不看代码而直接定位这个错误是出现在哪里呢?
想想以前自己是怎么处理这个问题的,直接gdb调试,然后在程序中打断点,然后开始慢慢调,常常需要花费大量的时间。真是太浪费时间了,唉、说多了都是泪。。。

下面让我们来了解下如何定位这样的错误呢?

首先,我们大家可能都会注意到,段错误的后面括号中(core dumped—》核心转储),
操作系统会将由于异常而挂掉的进程的错误信息储存到硬盘里。
会生成一个core.pid(pid是该进程的进程ID)。
但是默认是不会生成的,因为默认情况下core file的大小是0,所以不会生成该文件。

使用下面两个指令来查看系统中为core文件定义的默认大小以及如何来修改core文件的大小:

ulimit -a ——->查看系统中的资源上限

ulimit -c 1024 —–>将core file文件的大小修改为1024.

这里写图片描述

先将core file文件的大小进行修改(一个适宜的大小即可)。
使得可以保存进程异常退出时的错误信息

这里写图片描述

生成core文件:

这里写图片描述

最后进行gdb调试获取错误信息:

这里写图片描述

哈哈,又get到了一个新技能。。。