首页线程间通信 › GDB调试信号、多线程、多进程

GDB调试信号、多线程、多进程

GDB的功能很强大,本文主要介绍用GDB来调试信号、多进程、多线程,具体如下:

(一)信号

GDB有能力在你调试程序的时候处理任何一种信号,你可以告诉GDB需要处理哪一种信号。你可以要求GDB收到你所指定的信号时,马上停住正在运行的程序,以供你进行调试。你可以用GDB的handle命令来完成这一功能。

handle <signal> <keywords…>
在GDB中定义一个信号处理。信号<signal>可以以SIG开头或不以SIG开头,可以用定义一个要处理信号的范围(如:SIGIO- SIGKILL,表示处理从SIGIO信号到SIGKILL的信号,其中包括SIGIO, SIGIOT,SIGKILL三个信号),也可以使用关键字 all来标明要处理所有的信号。一旦被调试的程序接收到信号,运行程序马上会被GDB停住,以供调试。其<keywords>可以是以下几种关键字的一个或多个。

nostop
当被调试的程序收到信号时,GDB不会停住程序的运行,但会打出消息告诉你收到这种信号。
stop
当被调试的程序收到信号时,GDB会停住你的程序。
print
当被调试的程序收到信号时,GDB会显示出一条信息。
noprint
当被调试的程序收到信号时,GDB不会告诉你收到信号的信息。
pass
noignore
当被调试的程序收到信号时,GDB不处理信号。这表示,GDB会把这个信号交给被调试程序会处理。
nopass
ignore
当被调试的程序收到信号时,GDB不会让被调试程序来处理这个信号。

info signals
info handle
查看有哪些信号在被GDB检测中。

(二)线程

如果你程序是多线程的话,你可以定义你的断点是否在所有的线程上,或是在某个特定的线程。GDB很容易帮你完成这一工作。

break <linespec> thread <threadno>
break <linespec> thread <threadno> if …
linespec指定了断点设置在的源程序的行号。threadno指定了线程的ID,注意,这个ID是GDB分配的,你可以通过“info threads”命令来查看正在运行程序中的线程信息。如果你不指定
thread <threadno>则表示你的断点设在所有线程上面。你还可以为某线程指定断点条件。如:

(gdb) break frik.c:13 thread 28 if bartab > lim

当你的程序被GDB停住时,所有的运行线程都会被停住。这方便你你查看运行程序的总体情况。而在你恢复程序运行时,所有的线程也会被恢复运行。那怕是主进程在被单步调试时。

线程有自己的寄存器,运行时堆栈或许还会有私有内存。
gdb提供了以下供调试多线程的进程的功能:
*   自动通告新线程。
*   \ “thread   THREADNO\ “,一个用来在线程之间切换的命令。
*   \ “info   threads\ “,一个用来查询现存线程的命令。
*   \ “thread   apply   [THREADNO]   [ALL]   ARGS\ “,一个用来向线程提供命令的命令。
*   线程有关的断点设置。
注意:这些特性不是在所有gdb版本都能使用,归根结底要看操作系统是否支持。
如果你的gdb不支持这些命令,会显示出错信息:
(gdb)   info   threads
(gdb)   thread   1
Thread   ID   1   not   known.   Use   the   \ “info   threads\ ”   command   to
see   the   IDs   of   currently   known   threads.
gdb的线程级调试功能允许你观察你程序运行中所有的线程,但无论什么时候
gdb控制,总有一个“当前”线程。调试命令对“当前”进程起作用。
一旦gdb发现了你程序中的一个新的线程,它会自动显示有关此线程的系统信
息。比如:
[New   process   35   thread   27]
不过格式和操作系统有关。
为了调试的目的,gdb自己设置线程号。
info   threads\ "
显示进程中所有的线程的概要信息。gdb按顺序显示:
1.线程号(gdb设置)
2.目标系统的线程标识。
3.此线程的当前堆栈。
一前面打\ "*\ "的线程表示是当前线程。
例如:
(gdb)   info   threads
3   process   35   thread   27   0x34e5   in   sigpause   ()
2   process   35   thread   23   0x34e5   in   sigpause   ()
*   1   process   35   thread   13   main   (argc=1,   argv=0x7ffffff8)
at   threadtest.c:68
thread   THREADNO\ ”
把线程号为THREADNO的线程设为当前线程。命令行参数THREADNO是gdb内定的
线程号。你可以用\ “info   threads\ “命令来查看gdb内设置的线程号。gdb显示该线程
的系统定义的标识号和线程对应的堆栈。比如:

(gdb)   thread   2
[Switching   to   process   35   thread   23]
0x34e5   in   sigpause   ()
\ “Switching后的内容取决于你的操作系统对线程标识的定义。

`thread   apply   [THREADNO]   [ALL]   ARGS\ ”
此命令让你对一个以上的线程发出相同的命令\ “ARGS\ “,[THREADNO]的含义同上。
如果你要向你进程中的所有的线程发出命令使用[ALL]选项。
无论gdb何时中断了你的程序(因为一个断点或是一个信号),它自动选择信号或
断点发生的线程为当前线程。gdb将用一个格式为\ “[Switching   to   SYSTAG]\ “的消息
来向你报告。

 

(三)进程

1. 用的是attach子进程的方法
attach到正在运行的进程的功能,即attach <pid>命令。因此我们可以利用该命令attach到子进程然后进行调试。

一般的步骤是  1.首先要在要调试的子进程初始代码中,加入一段特殊代码,使子进程睡眠等待,然后运行待调试的程序
2.用ps -ef |grep 查看产生的子进程pid
3. 然后gdb启动,attach <pid>到进程后在该代码段后设上断点,就可以调试了

eg.

pid = fork();
if (pid <0) {
printf(“fork err\n”);
exit(-1);
} else if (pid == 0) {
/* in child */
sleep(60); —————— (!)

int     value   = 10;

**********
} /**in parent****/

执行程序(比如说是 examp)
examp &   — 让examp后台运行吧。(不后台运行也可以);

查找进程id:
ps -ef

运行gdb:
gdb
(gdb) attach xxxxx  — xxxxx为利用ps命令获得的子进程 id
(gdb) stop — 这点很重要,你需要先暂停那个子进程,然后设置一些断点和一些Watch
(gdb) break xx
Breakpoint 1 at 0x10808: file eg1.c, line 37.
(gdb) c
Continuing.

2. 使用follow-fork-mode 的方法调试多进程
最好使用GDB 6.6或以上版本

使用follow-fork-mode 的方法调试多进程时的一般步骤是:
1.启动gdb
2设置set follow-fork-mode [child/parent] 想调试child就使用child参数,如调试parent则使用parent
3启动程序文件 file ./hello ———-当然路径名自己要搞对哦
4.break num ———–如调试子进程则num是子进程代码的行数,
还有个小技巧就是break fork也就是在fork函数处设置断点,

发表评论