1、GDB基础调试命令介绍
GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。或许,各位比较喜欢那种图形界面方式的,像VC、BCB等IDE的调试,但如果你是在UNIX平台下做软件,你会发现GDB这个调试工具有比VC、BCB的图形化调试器更强大的功能。所谓“寸有所长,尺有所短”就是这个道理。
gdb调试 使用的是 debug 版本的 ,所以 我们在生成可执行文件的时候 ,我们需要在命令尾部 加上 - g ,加上可以生成的是 debug版本下的可执行文件
gcc <filename> -g
调试代码的基本命令:
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添加配置文件:
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;
}
开始调试:
单步运行:
切换线程2调试
切换到线程3调试
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异常,会停止程序。
设置方法:set follow-fork-mode [parent|child] set detach-on-fork [on|off]
follow-fork-mode | detach-on-fork | 说明 |
parent | on | 只调试主进程(GDB默认) |
child | on | 只调试子进程 |
parent | off | 同时调试两个进程,gdb跟主进程,子进程block在fork位置 |
child | off | 同时调试两个进程,gdb跟子进程,主进程block在fork位置 |
下面是使用代码来实现多进程调试:
#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 ;
}
只调试父进程
只调试子进程(与上面的对比来看看)
同时调试两个进程(父进程调试 运行 ,子进程block(以阻塞方式等待))
同时调试两个进程(子进程调试 运行 ,父进程block(以阻塞方式等待))(对比上面的看)