一.多线程调试
代码
#include <thread>
#include <vector>
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
using namespace std;
int f1(int a, int b){
int i = 0;
while(true) {
cout << "f1 : " << i << endl << endl;
sleep(2);
i++;
}
return 0;
}
int f2(int a, int b){
int i = 0;
while(true) {
cout << "f2 : " << i << endl << endl;
sleep(2);
i++;
}
return 0;
}
void print() {
cout << "HI" << endl << endl;
}
int f3(int a, int b){
int i = 0;
while(true) {
cout << "f3 : " << i << endl << endl;
print();
sleep(2);
i++;
}
return 0;
}
int main() {
thread t1(f1, 1, 2);
thread t2(f2, 1, 2);
thread t3(f3, 1, 2);
while(1)
{
printf("开始休眠两秒钟...\n\n");
sleep(2);
}
return 0;
}
涉及到的gdb命令
set scheduler-locking [on|off|step]
当我们在一个线程中的代码打断点时,比如在第36行中打断点,程序运行到第36行时所有的线程都停住(断点会停住所有的线程)。继续运行时根据scheduler-locking的模式,运行命令会有不同的行为。
注意 : 和设置的断点一样step, next停住时,所有线程都停住
1.on模式
运行step, next和continue命令,只有t3线程运行,其他的线程都不运行。
2.off模式
运行step, next和continue命令,所有线程都运行。
3.step模式
运行step, next命令,只有t3线程运行;运行continue命令,所有线程都运行。
二.多进程调试
代码
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("本程序的进程编号是:%d\n",getpid());
int ipid=fork();
sleep(1);
printf("pid=%d\n",ipid);
while (true) {
if (ipid!=0) {
printf("父进程编号是:%d\n",getpid());
}
else {
printf("子进程编号是:%d\n",getpid());
}
sleep(1);
}
}
gdb调试一个进程,如果该进程fork/vfork了子进程,gdb会继续调试该进程,子进程并不受任何影响(比如在第17行打断点。父进程停住,但子进程会继续执行)。如果我们在子进程的代码处设置断点,子进程会收到SIGTRAP(5号信号,可以在linux系统中使用kill -l查询信号量)并终止,无法通过设置断点的方式调试子进程,那仫该如何调试子进程呢?在我的这篇文章中只介绍下面几种调试子进程的方法:follow-fork-mode,attach子进程方法。
1.follow-fork-mode
set follow-fork-mode [parent|child]
parent:gdb默认调试的是父进程,如果参数是parent则fork之后继续调试父进程,子进程不受影响,子进程会继续运行
child:如果想调试子进程,则修改参数为child,set follow-fork-mode child之后调试子进程,父进程不受影响。
show follow-fork-mode,查看当前调试的fork进程的模式
2.detach-on-fork
该参数表明gdb在fork之后是否断开(detach)某个进程的调试,或者交给gdb控制.
set detach-on-fork [on|off]
on:断开调试follow-fork-mode调试的指定进程
off:gdb将控制父进程和子进程。follow-fork-mode指定的进程将被调试,另一个进程置于暂停(suspended)状态。
show detach-on-fork,查看detach-on-fork模式
gdb将每一个被调试进程的执行状态记录在一个名为inferior的结构中。一般情况下一个inferior对应一个进程,每个不同的inferior有不同的地址空间。inferior有时候会在进程没有启动的时候就存在。
3.info inferiors
查询正在调试的进程,gdb会为他们分配唯一的Num号,其中前面带’*'号的就是正在调试的进程
4.inferior [inferior num]
切换调试的进程为inferior num的进程处
- add-inferior [-copies n] [-exec executable]
添加n个新的调试进程,可以用file executable来分配给inferior可执行文件。如果不指定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。删除前需要先kill或者detach这个inferior,因为当一个inferior正在运行时不能被删除.
10.set schedule-multiple [on|off]
off:只有当前inferior会执行。
on:全部是执行状态的inferior都会执行。
show schedule-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状态的提示信息。
show print inferior-events ,查看print inferior-events设置的状态。
13.maint info program-spaces,用来显示当前gdb管理的地址空间的数目。
除了follow-fork-mode方法还有没有其他的方法调试多进程呢?我们知道gdb有附着(attach)到正在运行的进程的功能,即attach 命令。因此我们可以利用该命令attach到子进程然后进行调试。attach命令能够应付各种各样复杂的进程系统,比如孙子/曾孙进程,比如守护进程(精灵进程).