程序调试的利器GDB

Linux 包含了一个叫 gdb 的 GNU 调试程序. gdb 是一个用来调试 C 和 C++ 程序的强力调试器. 它使你能在程序运行时观察程序的内部结构和内存的使用情况. 以下是 gdb 所提供的一些功能:
它使你能监视你程序中变量的值.
它使你能设置断点以使程序在指定的代码行上停止执行.
它使你能一行行的执行你的代码.

    在命令行上键入 gdb 并按回车键就可以运行 gdb 了, 如果一切正常的话, gdb 将被启动并且你将在屏幕上看到类似的内容:
GDB is free software and you are welcome to distribute copies of it
under certain conditions; type "show copying" to see the conditions.
There is absolutely no warranty for GDB; type "show warranty" for details.
GDB 4.14 (i486-slakware-linux), Copyright 1995 Free Software Foundation, Inc.
(gdb)
    当你启动 gdb 后, 你能在命令行上指定很多的选项. 你也可以以下面的方式来运行 gdb :
gdb <fname>
     当你用这种方式运行 gdb , 你能直接指定想要调试的程序. 这将告诉gdb 装入名为 fname 的可执行文件. 你也可以用 gdb  去检查一个因程序异常终止而产生的 core 文件, 或者与一个正在运行的程序相连. 你可以参考 gdb 指南页或在命令行上键入 gdb -h  得到一个有关这些选项的说明的简单列表.

为调试编译代码(Compiling Code for Debugging)
    为了使 gdb 正常工作, 你必须使你的程序在编译时包含调试信息. 调试信息包含你程序里的每个变量的类型和在可执行文件里的地址映射以及源代码的行号.  gdb 利用这些信息使源代码和机器码相关联.
    在编译时用 -g 选项打开调试选项.

gdb 基本命令
     gdb 支持很多的命令使你能实现不同的功能. 这些命令从简单的文件装入到允许你检查所调用的堆栈内容的复杂命令, 表27.1列出了你在用 gdb 调试时会用到的一些命令. 想了解 gdb 的详细使用请参考 gdb 的指南页.
  表 27.1. 基本 gdb 命令.

命   令 描  述
file 装入想要调试的可执行文件.
kill 终止正在调试的程序.
list 列出产生执行文件的源代码的一部分.
next 执行一行源代码但不进入函数内部.
step 执行一行源代码而且进入函数内部.
run 执行当前被调试的程序
quit 终止 gdb
watch 使你能监视一个变量的值而不管它何时被改变.
break 在代码里设置断点, 这将使程序执行到这里时被挂起.
make 使你能不退出 gdb 就可以重新产生可执行文件.
shell 使你能不离开 gdb 就执行 UNIX shell 命令.

     gdb 支持很多与 UNIX shell 程序一样的命令编辑特征. 你能象在 bash 或 tcsh里那样按 Tab 键让 gdb 帮你补齐一个唯一的命令, 如果不唯一的话 gdb 会列出所有匹配的命令. 你也能用光标键上下翻动历史命令.

gdb 应用举例
    本节用一个实例教你一步步的用 gdb 调试程序. 被调试的程序相当的简单, 但它展示了 gdb 的典型应用.

    下面列出了将被调试的程序. 这个程序被称为 greeting , 它显示一个简单的问候, 再用反序将它列出.
#include  <stdio.h>
main ()
{
char my_string[] = "hello there";
  my_print (my_string);
my_print2 (my_string);
}

void my_print (char *string)
{
  printf ("The string is %s\n", string);
}

void my_print2 (char *string)
{
char *string2;
  int size, i;

size = strlen (string);
  string2 = (char *) malloc (size + 1);
  for (i = 0; i < size; i++)
    string2[size – i] = string;
  string2[size+1] = \0';
  printf ("The string printed backward is %s\n", string2);
}
    用下面的命令编译它:

gcc -o test test.c
    这个程序执行时显示如下结果:
The string is hello there

The string printed backward is
    输出的第一行是正确的, 但第二行打印出的东西并不是我们所期望的. 我们所设想的输出应该是:
The string printed backward is ereht olleh
    由于某些原因, my_print2 函数没有正常工作. 让我们用  gdb 看看问题究竟出在哪儿, 先键入如下命令:

gdb greeting

--------------------------------------------------------------------------------
注意: 记得在编译 greeting 程序时把调试选项打开.
--------------------------------------------------------------------------------

    如果你在输入命令时忘了把要调试的程序作为参数传给 gdb , 你可以在 gdb 提示符下用 file 命令来载入它:

(gdb) file greeting
    这个命令将载入 greeting 可执行文件就象你在 gdb 命令行里装入它一样.
    这时你能用 gdb 的 run 命令来运行 greeting 了. 当它在 gdb 里被运行后结果大约会象这样:

(gdb) run
Starting program: /root/greeting
The string is hello there
The string printed backward is
Program exited with code 041
    这个输出和在 gdb 外面运行的结果一样. 问题是, 为什么反序打印没有工作? 为了找出症结所在, 我们可以在 my_print2 函数的 for 语句后设一个断点, 具体的做法是在 gdb 提示符下键入 list 命令三次, 列出源代码:
(gdb) list

(gdb) list

(gdb) list
--------------------------------------------------------------------------------
技巧:  在 gdb 提示符下按回车健将重复上一个命令.
--------------------------------------------------------------------------------

    第一次键入 list 命令的输出如下:
1       #include  <stdio.h>
2
3       main ()
4       {
5         char my_string[] = "hello there";
6
7         my_print (my_string);
8         my_print2 (my_string);
9       }
10
    如果按下回车, gdb 将再执行一次 list 命令, 给出下列输出:
11      my_print (char *string)
12      {
13        printf ("The string is %s\n", string);
14      }
15
16      my_print2 (char *string)
17      {
18        char *string2;
19        int size, i;
20
    再按一次回车将列出 greeting 程序的剩余部分:
21        size = strlen (string);
22        string2 = (char *) malloc (size + 1);
23        for (i = 0; i < size; i++)
24          string2[size - i] = string;
25        string2[size+1] =
\0′;

26        printf ("The string printed backward is %s\n", string2);
27      }
    根据列出的源程序, 你能看到要设断点的地方在第24行, 在 gdb 命令行提示符下键入如下命令设置断点:
(gdb) break 24
    gdb 将作出如下的响应:
Breakpoint 1 at 0x139: file greeting.c, line 24
(gdb)
    现在再键入 run 命令, 将产生如下的输出:
Starting program: /root/greeting
The string is hello there
Breakpoint 1, my_print2 (string = 0xbfffdc4 "hello there") at greeting.c :24
24  string2[size-i]=string
    你能通过设置一个观察 string2[size – i] 变量的值的观察点来看出错误是怎样产生的, 做法是键入:
(gdb) watch string2[size – i]
    gdb 将作出如下回应:
Watchpoint 2: string2[size – i]
    现在可以用 next 命令来一步步的执行 for 循环了:
(gdb) next
    经过第一次循环后,  gdb 告诉我们 string2[size – i] 的值是 h. gdb 用如下的显示来告诉你这个信息:
Watchpoint 2, string2[size – i]
Old value = 0 \000'
New value = 104
h’
my_print2(string = 0xbfffdc4 "hello there") at greeting.c:23
23 for (i=0; i<size; i++)
    这个值正是期望的. 后来的数次循环的结果都是正确的. 当 i=10 时, 表达式 string2[size – i] 的值等于 e,  size – i 的值等于 1, 最后一个字符已经拷到新串里了.
     如果你再把循环执行下去, 你会看到已经没有值分配给 string2[0] 了,  而它是新串的第一个字符, 因为 malloc  函数在分配内存时把它们初始化为空(null)字符. 所以 string2 的第一个字符是空字符. 这解释了为什么在打印 string2  时没有任何输出了.

     现在找出了问题出在哪里, 修正这个错误是很容易的. 你得把代码里写入 string2 的第一个字符的的偏移量改为 size – 1  而不是 size. 这是因为 string2 的大小为 12, 但起始偏移量是 0, 串内的字符从偏移量 0 到 偏移量 10, 偏移量 11  为空字符保留.

    为了使代码正常工作有很多种修改办法. 一种是另设一个比串的实际大小小 1 的变量. 这是这种解决办法的代码:
#include  <stdio.h>
main ()
{
char my_string[] = "hello there";
  my_print (my_string);
my_print2 (my_string);
}

my_print (char *string)
{
  printf ("The string is %s\n", string);
}

my_print2 (char *string)
{
  char *string2;
  int size, size2, i;

  size = strlen (string);
  size2 = size -1;
  string2 = (char *) malloc (size + 1);
for (i = 0; i < size; i++)
    string2[size2 – i] = string;
  string2[size] = `\0′;
  printf ("The string printed backward is %s\n", string2);
}

在Linux下调试程序一般用GDB来执行。
这里简要介绍一下是否gdb调试程序的方法:
(1)进入gdb调试:
gdb + 已经编译通过的可执行程序 -》 就进入调试模式。例如:gdb MiddlePublisher
(2)r + 运行时的参数 -》 开始运行可执行程序。例如 r -lxml2 -f refile
(3)b + 断点 -》设置调试的断点。两种:一种是:b CMSTask.cpp:200  表示在CMSTask.cpp文件的第200行设置断点。另一种:b TaskManager::buildPubWinTask  表示在执行buildPubWinTask这个函数的时候停止。
(4)取消断点:
dis 1 表示取消第一个断点
dis 2 表示取消第二个断点
(5)查看设置断点信息: info b
(6)在断点停止处查看所在代码的详细信息:l
(7)可以在gdb中直接编译,然后再重新运行时,gdb会直接执行新编译好的可执行程序。例如:直接在gdb下执行make后再重庆运行。
(8)跟进一个函数:s
如果设置的断点是在一个函数入口。到达该断点时,键入s就可以进入该函数内部进行调试。如果有多个函数就多次键入S来进入内部的函数。
PS:
1、在SecureCRT远程登录界面上开启多个窗口。在窗口之间切换时用:Alt+1,Alt+2…..表示切换到第1个,第2个窗口。
2、同样在在SecureCRT远程登录界面上要粘贴复制好的内容用:Shift+Insert。检查一切    
memcpy,   strcpy,   strcat   sprintf   动态数组下标。    
这种问题多半世内存访问错误或者缓冲区溢出覆盖堆栈造成的。    
调试方法:    
gdb   调试程序或者gdb调试core文件

编译时加入-g调试选项,去掉-Ox选项    

使用gdb运行,如果中断退出,使用bt命令查看调用堆栈,如果不是可以    
通过thr   n   (n表示线程号,用   info   thr查看)切换,然后bt看堆栈    
以上方法在kernel   2.6+gdb   6中有问题    

一:列文件清单    
1.   List    
(gdb)   list   line1,line2    

二:执行程序    
要想运行准备调试的程序,可使用run命令,在它后面可以跟随发给该程序的任何参数,包括标准输入和标准输出说明符(<和>)和外壳通配符(*、?、[、])在内。    
如果你使用不带参数的run命令,gdb就再次使用你给予前一条run命令的参数,这是很有用的。    
利用set   args   命令就可以修改发送给程序的参数,而使用show   args   命令就可以查看其缺省参数的列表。    
(gdb)set   args   –b   –x    
(gdb)   show   args    
backtrace命令为堆栈提供向后跟踪功能。    
Backtrace   命令产生一张列表,包含着从最近的过程开始的所以有效过程和调用这些过程的参数。    

三:显示数据    
利用print   命令可以检查各个变量的值。    
(gdb)   print   p   (p为变量名)    
whatis   命令可以显示某个变量的类型    
(gdb)   whatis   p    
type   =   int   *    

print   是gdb的一个功能很强的命令,利用它可以显示被调试的语言中任何有效的表达式。表达式除了包含你程序中的变量外,还可以包含以下内容:    
l   对程序中函数的调用    
(gdb)   print   find_entry(1,0)    
l   数据结构和其他复杂对象    
(gdb)   print   *table_start    
$8={e=reference=’\000’,location=0x0,next=0x0}    
l   值的历史成分    
(gdb)print   $1   ($1为历史记录变量,在以后可以直接引用   $1   的值)    
l   人为数组    
人为数组提供了一种去显示存储器块(数组节或动态分配的存储区)内容的方法。早期的调试程序没有很好的方法将任意的指针换成一个数组。就像对待参数一样,让我们查看内存中在变量h后面的10个整数,一个动态数组的语法如下所示:    
base@length    
因此,要想显示在h后面的10个元素,可以使用h@10:    
(gdb)print   h@10    
$13=(-1,345,23,-234,0,0,0,98,345,10)    

四:断点(breakpoint)    
break命令(可以简写为b)可以用来在调试的程序中设置断点,该命令有如下四种形式:    
l   break   line-number   使程序恰好在执行给定行之前停止。    
l   break   function-name   使程序恰好在进入指定的函数之前停止。    
l   break   line-or-function   if   condition   如果condition(条件)是真,程序到达指定行或函数时停止。    
l   break   routine-name   在指定例程的入口处设置断点    

如果该程序是由很多原文件构成的,你可以在各个原文件中设置断点,而不是在当前的原文件中设置断点,其方法如下:    
(gdb)   break   filename:line-number    
(gdb)   break   filename:function-name    

要想设置一个条件断点,可以利用break   if命令,如下所示:    
(gdb)   break   line-or-function   if   expr    
例:    
(gdb)   break   46   if   testsize==100    

从断点继续运行:countinue   命令    
五.断点的管理    

1.   显示当前gdb的断点信息:    
(gdb)   info   break    
他会以如下的形式显示所有的断点信息:    
Num   Type   Disp   Enb   Address   What    
1   breakpoint   keep   y   0x000028bc   in   init_random   at   qsort2.c:155    
2   breakpoint   keep   y   0x0000291c   in   init_organ   at   qsort2.c:168    
(gdb)    
2.删除指定的某个断点:    
(gdb)   delete   breakpoint   1    
该命令将会删除编号为1的断点,如果不带编号参数,将删除所有的断点    
(gdb)   delete   breakpoint    
3.禁止使用某个断点    
(gdb)   disable   breakpoint   1    
该命令将禁止断点   1,同时断点信息的   (Enb)域将变为   n    
4.允许使用某个断点    
(gdb)   enable   breakpoint   1    
该命令将允许断点   1,同时断点信息的   (Enb)域将变为   y    
5.清除原文件中某一代码行上的所有断点    
(gdb)clean   number    
注:number   为原文件的某个代码行的行号    
六.变量的检查和赋值    
l   whatis:识别数组或变量的类型    
l   ptype:比whatis的功能更强,他可以提供一个结构的定义    
l   set   variable:将值赋予变量    
l   print   除了显示一个变量的值外,还可以用来赋值    
七.单步执行    
l   next    
不进入的单步执行    
l   step    
进入的单步执行    
如果已经进入了某函数,而想退出该函数返回到它的调用函数中,可使用命令finish    
八.函数的调用    
l   call   name   调用和执行一个函数    
(gdb)   call   gen_and_sork(   1234,1,0   )    
(gdb)   call   printf(“abcd”)    
$1=4    
l   finish   结束执行当前函数,显示其返回值(如果有的话)    

九.机器语言工具    
有一组专用的gdb变量可以用来检查和修改计算机的通用寄存器,gdb提供了目前每一台计算机中实际使用的4个寄存器的标准名字:    
l   $pc   :   程序计数器    
l   $fp   :   帧指针(当前堆栈帧)    
l   $sp   :   栈指针    
l   $ps   :   处理器状态    

十.信号    
gdb通常可以捕捉到发送给它的大多数信号,通过捕捉信号,它就可决定对于正在运行的进程要做些什么工作。例如,按CTRL-C将中断信号发送给gdb, 通常就会终止gdb。但是你或许不想中断gdb,真正的目的是要中断gdb正在运行的程序,因此,gdb要抓住该信号并停止它正在运行的程序,这样就可以 执行某些调试操作。    

Handle命令可控制信号的处理,他有两个参数,一个是信号名,另一个是接受到信号时该作什么。几种可能的参数是:    
l   nostop   接收到信号时,不要将它发送给程序,也不要停止程序。    
l   stop   接受到信号时停止程序的执行,从而允许程序调试;显示一条表示已接受到信号的消息(禁止使用消息除外)    
l   print   接受到信号时显示一条消息    
l   noprint   接受到信号时不要显示消息(而且隐含着不停止程序运行)    
l   pass   将信号发送给程序,从而允许你的程序去处理它、停止运行或采取别的动作。    
l   nopass   停止程序运行,但不要将信号发送给程序。    
例如,假定你截获SIGPIPE信号,以防止正在调试的程序接受到该信号,而且只要该信号一到达,就要求该程序停止,并通知你。要完成这一任务,可利用如下命令:    
(gdb)   handle   SIGPIPE   stop   print    
请注意,UNIX的信号名总是采用大写字母!你可以用信号编号替代信号名    
如果你的程序要执行任何信号处理操作,就需要能够测试其信号处理程序,为此,就需要一种能将信号发送给程序的简便方法,这就是signal命令的任务。 该    命令的参数是一个数字或者一个名字,如SIGINT。假定你的程序已将一个专用的SIGINT(键盘输入,或CTRL-C;信号2)信号处理程序设置成 采   取某个清理动作,要想测试该信号处理程序,你可以设置一个断点并使用如下命令:    
(gdb)   signal   2    
continuing   with   signal   SIGINT(2)    
该程序继续执行,但是立即传输该信号,而且处理程序开始运行.    

十一.   原文件的搜索    
search   text:该命令可显示在当前文件中包含text串的下一行。    
Reverse-search   text:该命令可以显示包含text   的前一行。    

十二.UNIX接口    
shell   命令可启动UNIX外壳,CTRL-D退出外壳,返回到   gdb.    

十三.命令的历史    
为了允许使用历史命令,可使用   set   history   expansion   on   命令    
(gdb)   set   history   expansion   on    

小结:常用的gdb命令    
backtrace   显示程序中的当前位置和表示如何到达当前位置的栈跟踪(同义词:where)    
breakpoint   在程序中设置一个断点    
cd   改变当前工作目录    
clear   删除刚才停止处的断点    
commands   命中断点时,列出将要执行的命令    
continue   从断点开始继续执行    
delete   删除一个断点或监测点;也可与其他命令一起使用    
display   程序停止时显示变量和表达时    
down   下移栈帧,使得另一个函数成为当前函数    
frame   选择下一条continue命令的帧    
info   显示与该程序有关的各种信息    
jump   在源程序中的另一点开始运行    
kill   异常终止在gdb   控制下运行的程序    
list   列出相应于正在执行的程序的原文件内容    
next   执行下一个源程序行,从而执行其整体中的一个函数    
print   显示变量或表达式的值    
pwd   显示当前工作目录    
pype   显示一个数据结构(如一个结构或C++类)的内容    
quit   退出gdb    
reverse-search   在源文件中反向搜索正规表达式    
run   执行该程序    
search   在源文件中搜索正规表达式    
set   variable   给变量赋值    
signal   将一个信号发送到正在运行的进程    
step   执行下一个源程序行,必要时进入下一个函数    
undisplay   display命令的反命令,不要显示表达式    
until   结束当前循环    
up   上移栈帧,使另一函数成为当前函数    
watch   在程序中设置一个监测点(即数据断点)    
whatis   显示变量或函数类型    
****************************************************    
GNU的调试器称为gdb,该程序是一个交互式工具,工作在字符模式。在   X   Window   系统中,有一个gdb的前端图形工具,称为xxgdb。gdb   是功能强大的调试程序,可完成如下的调试任务:    
*   设置断点;    
*   监视程序变量的值;    
*   程序的单步执行;    
*   修改变量的值。    
在可以使用   gdb   调试程序之前,必须使用   -g   选项编译源文件。可在   makefile   中如下定义   CFLAGS   变量:    
CFLAGS   =   -g    
运行   gdb   调试程序时通常使用如下的命令:    
gdb   progname    

在   gdb   提示符处键入help,将列出命令的分类,主要的分类有:    
*   aliases:命令别名    
*   breakpoints:断点定义;    
*   data:数据查看;    
*   files:指定并查看文件;    
*   internals:维护命令;    
*   running:程序执行;    
*   stack:调用栈查看;    
*   statu:状态查看;    
*   tracepoints:跟踪程序执行。    
键入   help   后跟命令的分类名,可获得该类命令的详细清单。    

gdb   的常用命令    
命令   解释    
break   NUM   在指定的行上设置断点。    
bt   显示所有的调用栈帧。该命令可用来显示函数的调用顺序。    
clear   删除设置在特定源文件、特定行上的断点。其用法为clear   FILENAME:NUM    
continue   继续执行正在调试的程序。该命令用在程序由于处理信号或断点而   导致停止运行时。    
display   EXPR   每次程序停止后显示表达式的值。表达式由程序定义的变量组成。    
file   FILE   装载指定的可执行文件进行调试。    
help   NAME   显示指定命令的帮助信息。    
info   break   显示当前断点清单,包括到达断点处的次数等。    
info   files   显示被调试文件的详细信息。    
info   func   显示所有的函数名称。    
info   local   显示当函数中的局部变量信息。    
info   prog   显示被调试程序的执行状态。    
info   var   显示所有的全局和静态变量名称。    
kill   终止正被调试的程序。    
list   显示源代码段。    
make   在不退出   gdb   的情况下运行   make   工具。    
next   在不单步执行进入其他函数的情况下,向前执行一行源代码。    
print   EXPR   显示表达式   EXPR   的值。    

******gdb   使用范例************************    
—————–    
清单   一个有错误的   C   源程序   bugging.c    
代码:    

—————–    
1 #i   nclude    
2    
3 static   char   buff   [256];    
4 static   char*   string;    
5 int   main   ()    
6 {    
7   printf   ("Please   input   a   string:   ");    
8   gets   (string);      
9     printf   ("\nYour   string   is:   %s\n",   string);    
10   }    

—————–    
上面这个程序非常简单,其目的是接受用户的输入,然后将用户的输入打印出来。该程序使用了一个未经过初始化的字符串地址   string,因此,编译并运行之后,将出现   Segment   Fault   错误:    
$   gcc   -o   bugging   -g   bugging.c    
$   ./bugging    
Please   input   a   string:   asfd    
Segmentation   fault   (core   dumped)    
为了查找该程序中出现的问题,我们利用   gdb,并按如下的步骤进行:    
1.运行   gdb   bugging   命令,装入   bugging   可执行文件;    
2.执行装入的   bugging   命令   run;    
3.使用   where   命令查看程序出错的地方;    
4.利用   list   命令查看调用   gets   函数附近的代码;    
5.唯一能够导致   gets   函数出错的因素就是变量   string。用print命令查看   string   的值;    
6.在   gdb   中,我们可以直接修改变量的值,只要将   string   取一个合法的指针值就可以了,为此,我们在第8行处设置断点   break   8;    
7.程序重新运行到第   8行处停止,这时,我们可以用   set   variable   命令修改   string   的取值;    
8.然后继续运行,将看到正确的程序运行结果    


gdb调试程序已经run了

动态的增加断点
ctrl-z挂起 设置短点 fg唤醒

d   空格加断点num,是去断点的,c是跳到下一个断点

跳出函数,相当于step over   :finish.    
gdb对于多线程程序的调试有如下的支持:

  • 线程产生通知:在产生新的线程时, gdb会给出提示信息

(gdb) r
Starting program: /root/thread
[New Thread (LWP 12900)]
[New Thread (LWP 12907)]—以下三个为新产生的线程
[New Thread (LWP 12908)]
[New Thread (LWP 12909)]

  • 查看线程:使用info threads可以查看运行的线程。

(gdb) info threads
4 Thread (LWP 12940) 0xffffe002 in ?? ()
3 Thread (LWP 12939) 0xffffe002 in ?? ()
2 Thread (LWP 12938) 0xffffe002 in ?? ()
* 1 Thread (LWP 12931) main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb)
注意,行首的蓝色文字为gdb分配的线程号,对线程进行切换时,使用该该号码,而不是上文标出的绿色数字。

另外,行首的红色星号标识了当前活动的线程

  • 切换线程:使用 thread THREADNUMBER 进行切换,THREADNUMBER 为上文提到的线程号。下例显示将活动线程从 1 切换至 4。

(gdb) info threads
4 Thread (LWP 12940) 0xffffe002 in ?? ()
3 Thread (LWP 12939) 0xffffe002 in ?? ()
2 Thread (LWP 12938) 0xffffe002 in ?? ()
* 1 Thread (LWP 12931) main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb) thread 4
[Switching to thread 4 (Thread (LWP 12940))]#0 0xffffe002 in ?? ()
(gdb) info threads
* 4 Thread (LWP 12940) 0xffffe002 in ?? ()
3 Thread (LWP 12939) 0xffffe002 in ?? ()
2 Thread (LWP 12938) 0xffffe002 in ?? ()
1 Thread (LWP 12931) main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb)
以上即为使用gdb提供的对多线程进行调试的一些基本命令。另外,gdb也提供对线程的断点设置以及对指定或所有线程发布命令的命令。

初次接触gdb下多线程的调试,往往会忽视gdb中活动线程的概念。一般来讲,在使用gdb调试的时候,只有一个线程为活动线程,如果希望得到其他的线程的输出结果,必须使用thread命令切换至指定的线程,才能对该线程进行调试或观察输出结果。

在linux环境下调试多线程,总觉得不像.NET那么方便。这几天就为找一个死锁的bug折腾好久,介绍一下用过的方法吧。

多线程如果dump,多为段错误,一般都涉及内存非法读写。可以这样处理,使用下面的命令打开系统开关,让其可以在死掉的时候生成core文件。  
ulimit -c unlimited
这样的话死掉的时候就可以在当前目录看到core.pid(pid为进程号)的文件。接着使用gdb:
gdb ./bin ./core.pid
进去后,使用bt查看死掉时栈的情况,在使用frame命令。

还有就是里面某个线程停住,也没死,这种情况一般就是死锁或者涉及消息接受的超时问题(听人说的,没有遇到过)。遇到这种情况,可以使用:
gcore pid (调试进程的pid号)
手动生成core文件,在使用pstack(linux下好像不好使)查看堆栈的情况。如果都看不出来,就仔细查看代码,看看是不是在if,return,break,continue这种语句操作是忘记解锁,还有嵌套锁的问题,都需要分析清楚了。

最后,说一句,静心看代码,捶胸顿足是没有用的。


转自: http://www.cppblog.com/niewenlong/archive/2008/07/27/57270.html

留下评论

邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据