Chinaunix首页 | 论坛 | 博客
  • 博客访问: 265350
  • 博文数量: 74
  • 博客积分: 1470
  • 博客等级: 上尉
  • 技术积分: 793
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-25 21:01
文章分类

全部博文(74)

文章存档

2011年(1)

2010年(32)

2009年(32)

2008年(9)

我的朋友

分类: C/C++

2010-04-14 10:50:19

GDB 是 linux 系统上常用的调试工具,本文介绍了使用 GDB 调试多进程程序的几种方法:
分别是 修改fork返回值方法,follow-fork-mode 方法和和 attach 子进程方法,其中 attach 子进程方法又有几种实现方式.
1. 修改fork返回值方法
在fork语句上设置断点,修改fork返回的值。这种做法并没有真正去触动已经存在的子进程,而是强迫夫进程去走子进程的流程。对付简单情况都非常有效。
例如:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
  int ret;
  ret = fork();
  
//修改ret的值,使其=0
  ret = 0;
  if(ret<0)
  {
    printf("fork error\n");
    exit(0);
  }
  else if(ret==0)
//child
  {
    printf("In child process\n");
    exit(0);
  }
  printf("In father process\n");
  return 0;
}


这样,在gdb中直接break到子进程中就ok了.

2. follow-fork-mode 方法
在2.5.60版Linux内核及以后,GDB对使用fork/vfork创建子进程的程序提供了follow-fork-mode选项来支持多进程调试。

follow-fork-mode的用法为:
set follow-fork-mode [parent|child]
    * parent: fork之后继续调试父进程,子进程不受影响。
    * child: fork之后调试子进程,父进程不受影响。
因此如果需要调试子进程,在启动gdb后:
(gdb) set follow-fork-mode child
并在子进程代码设置断点。
此外还有detach-on-fork参数,指示GDB在fork之后是否断开(detach)某个进程的调试,或者都交由GDB控制:
set detach-on-fork [on|off]
    * on: 断开调试follow-fork-mode指定的进程。
    * off: gdb将控制父进程和子进程。follow-fork-mode指定的进程将被调试,另一个进程置于暂停(suspended)状态。
注意,最好使用GDB 6.6或以上版本,如果你使用的是GDB6.4,就只有follow-fork-mode模式。
follow-fork-mode/detach-on-fork的使用还是比较简单的,但由于其系统内核和gdb版本限制,我们只能在符合要求的系统上才能使用。而且,由于follow-fork-mode的调试必然是从父进程开始的,对于fork多次,以至于出现孙进程或曾孙进程的系统,调试起来并不方便。
3.attach 子进程的方法
众所周知,GDB有附着(attach)到正在运行的进程的功能,即attach 命令。因此我们可以利用该命令attach到子进程然后进行调试。
但是有一个新的问题是,子进程一直在运行,attach上去后都不知道运行到哪里了。有没有办法解决呢?
一个办法是,在要调试的子进程初始代码中,比如main函数开始处,加入一段特殊代码,使子进程在某个条件成立时便循环睡眠等待,attach到进程后在该代码段后设上断点,再把成立的条件取消,使代码可以继续执行下去。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
  int ret;
  int debug_lock=1;
  ret = fork();
  if(ret<0)
  {
    printf("fork error\n");
    exit(0);
  }
  else if(ret==0)
//child
  {
    
//使子进程循环等待
    while(debug_lock) sleep(1);
    printf("In child process\n");
    exit(0);
  }
  
//使父进程循环等待
  while(debug_lock) sleep(1);
  printf("In father process\n");
  return 0;
}


首先编译fork.c
:~$ gcc -g -o fork fork.c
在另外一个shell中执行fork
:~$ ./fork
再回到刚才的shell里
:~$ ps -ef | grep fork
xulei     7338     1  0 08:25 ?        00:00:00 dbus-daemon --fork --print-address 20 --print-pid 22 --session
xulei     8441  8173  0 08:40 pts/0    00:00:01 vim fork.txt
xulei     8494  8451  0 08:40 pts/1    00:00:00 vim fork.c
xulei    12199 12169  0 10:15 pts/3    00:00:00 ./fork
xulei    12200 12199  0 10:15 pts/3    00:00:00 ./fork
xulei    12207  8503  0 10:15 pts/2    00:00:00 grep fork
gdb
GNU gdb 6.8-debian
Copyright (C) 2008 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 "i486-linux-gnu".
(gdb) attach 12200
Attaching to process 12200
Reading symbols from /home/xulei/fork...done.
Reading symbols from /lib/tls/i686/cmov/libc.so.6...done.
Loaded symbols for /lib/tls/i686/cmov/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
0xb7fab410 in __kernel_vsyscall ()
(gdb) bt
#0  0xb7fab410 in __kernel_vsyscall ()
#1  0xb7ed7c90 in nanosleep () from /lib/tls/i686/cmov/libc.so.6
#2  0xb7ed7ac7 in sleep () from /lib/tls/i686/cmov/libc.so.6
#3  0x08048456 in main () at fork.c:18
(gdb) up 3
#3  0x08048456 in main () at fork.c:18
18   while(debug_lock) sleep(1);
(gdb) l
13     printf("fork error\n");
14     exit(0);
15   }
16   else if(ret==0)//child
17   {
18   while(debug_lock) sleep(1);
19     printf("In child process\n");
20     exit(0);
21   }
22   while(debug_lock) sleep(1);
(gdb) set debug_lock=0
(gdb) b 19
Breakpoint 1 at 0x804845c: file fork.c, line 19.
(gdb) c
Continuing.
Breakpoint 1, main () at fork.c:19
19     printf("In child process\n");
(gdb)
这样gdb就走到子进程里了.
 
还有一种办法,就是在子进程(或父进程)开始处sleep()一段时间,这样attach到子(父)进程后在该代码段后设上断点,等到时间到了,就可以使代码可以继续执行下去。
还是刚才那段代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
  int ret;
  int status;
  ret = fork();
  if(ret<0)
  {
    printf("fork error\n");
    exit(0);
  }
  else if(ret==0)
//child
  {
    sleep(30);
    printf("In child process\n");
    exit(0);
  }
  printf("In father process\n");
  wait(&status);
  return 0;
}


编译
:~$ gcc -g -o fork fork.c
运行
:~$ ./fork
In father process
:~$ gdb
GNU gdb 6.8-debian
Copyright (C) 2008 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 "i486-linux-gnu".
(gdb) attach 12590
Attaching to process 12590
Reading symbols from /home/xulei/fork...done.
Reading symbols from /lib/tls/i686/cmov/libc.so.6...done.
Loaded symbols for /lib/tls/i686/cmov/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
0xb7f3d410 in __kernel_vsyscall ()
(gdb) b 20
Breakpoint 1 at 0x8048494: file fork.c, line 20.
(gdb) c
Continuing.
Breakpoint 1, main () at fork.c:20
20     printf("In child process\n");
(gdb)
因为sleep了30秒,而且在gdb中break到了子进程中,所以程序运行30秒后就可以调试子进程了.
还有一种GDB wrapper方法,读者可以参考后面的reference.
 
reference:
使用 GDB 调试多进程程序
 



阅读(2468) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~