linux下的c/c++调试器gdb

Reference:  http://www.cnblogs.com/xd502djj/archive/2012/08/30/2663960.html

 

linux下的c/c++调试器gdb

gdb
Linux 包含了一个叫 gdb 的 GNU 调试程序. gdb 是一个用来调试 C 和 C++ 程序的强力调试器. 它使你能在程序运行时观察程序的内部结构和内存的使用情况. 以下是 gdb 所提供的一些功能:

* 设置断点;
* 监视程序变量的值;
* 程序的单步执行;
* 修改变量的值。
       
gdb支持下列语言C, C++ ,FORTRAN, PACAL, Java, Chill, assembly, Modula-2. 一般来说,GDB会根据调试的程序来确定的相应的调试语言,比如说,扩展名为.c, GDB should it is a c programme,extern_name is .c, .cc, .cp, .cxx, .cpp, .c++, GDB should they are c++ programme
    在命令行上键入 gdb 并按回车键就可以运行 gdb 了, 如果一切正常的话, gdb 将被启动并且你将在屏幕上看到类似的内容:
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or 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.
This GDB was configured as "i386-redhat-linux-gnu".
(gdb)


在可以使用 gdb 调试程序之前,必须使用 -g 选项编译源文件。也就是说,如果你写了下面的一个程序,名字命名为test.c++, 编译时使用如下语句:
#g++ -o test -g test.c++
#./test
test.c++ 源程序如下:
#include <iostream.h>

static char buff [256];
static char* string;
int main ()
{

printf ("Please input a string: ");
gets (string);

printf (" Your string is: %s ", string);
}
运行后,程序出现了错误,这时你就可以使用gdb来查错了,方法如下:
#gdb test
(gdb)run 运行test(二进制)程序
如果你在编译时没有加上参数-g选项,你也可以通过如下语句来达到相同的效果
#gdb
(gdb)file test
如果你想查看源程序的部分代码,你可以用list命令来实现,如:
(gdb)list

可在 makefile 中如下定义 CFLAGS 变量:
CFLAGS = -g
运行 gdb 调试程序时通常使用如下的命令:
gdb progname

在 gdb 提示符下按回车健将重复上一个命令.

gdb命 令 描 述
file FILE 装入想要调试的可执行文件.
kill 终止正在调试的程序.
list 列出产生执行文件的源代码的一部分.
next 执行一行源代码但不进入函数内部.
step 执行一行源代码而且进入函数内部.
run 执行当前被调试的程序
q(quit) 终止 gdb
watch expr 使你能监视一个变量的值而不管它何时被改变.
rwatch expr 当expr被程序读出时,程序被暂停
awatch expr 当expr被程序读出时然后再被写入时,程序被暂停
info whatchpoints 显示所设置的观测点的列表 the same as info break
make 使你能不退出 gdb 就可以重新产生可执行文件.
shell 使你能不离开 gdb 就执行 UNIX shell 命令. such as:(gdb)shell gedit

set and unset 分别用来设置和取消参数和环境变量,such as: (gdb)set i=9 (gdb)unset i

whatis 告诉你变量的类型,如(gdb)whatis i

如果你想得到结构体的定义,可用ptype command,  such as: have a structive s, (gdb)ptype s

break NUM 在指定的行上设置断点。

b(abbreviate of break) +offset
b(abbreviate of break) -offset
以上两个命令在当前运行到的前几行或后几行设置断点

break filename:linenum
在文件名的的第linenum处设置断点
break filename:functionname
在函数前设置断点

info break 显示当前断点清单,包括到达断点处的次数等。

disable make the breakpoint don't execute
enable make the breakpoint can exec

example:

(gdb) l
39 pro1=pro2;
40 pro2.display();
41 pro1.display();
42 return 0;
43 }
(gdb) b 39
Breakpoint 3 at 0x8048862: file debug.c++, line 39.
(gdb) b -6
Breakpoint 4 at 0x804883f: file debug.c++, line 38.
(gdb) info bre
Num Type Disp Enb Address What
3 breakpoint keep y 0x08048862 in main at debug.c++:39
4 breakpoint keep y 0x0804883f in main at debug.c++:38
(gdb) disable 4
(gdb) info bre
Num Type Disp Enb Address What
3 breakpoint keep y 0x08048862 in main at debug.c++:39
4 breakpoint keep n 0x0804883f in main at debug.c++:38
(gdb) enable 4
(gdb) info bre
Num Type Disp Enb Address What
3 breakpoint keep y 0x08048862 in main at debug.c++:39
4 breakpoint keep y 0x0804883f in main at debug.c++:38
(gdb) disable 3 4
(gdb) info bre
Num Type Disp Enb Address What
3 breakpoint keep n 0x08048862 in main at debug.c++:39
4 breakpoint keep n 0x0804883f in main at debug.c++:38

tbreak 设置的断点,当使用一次后就不能再使用了


bt 显示所有的调用栈帧。该命令可用来显示函数的调用顺序。
clear 删除设置在特定源文件、特定行上的断点。其用法为:clear FILENAME:NUM。
delete 删除所有设置的断点
continue 继续执行正在调试的程序。该命令用在程序由于处理信号或断点而
导致停止运行时。
display EXPR 每次程序停止后显示表达式的值。表达式由程序定义的变量组成。
help NAME 显示指定命令的帮助信息。

info files 显示被调试文件的详细信息。
info func 显示所有的函数名称。
info local 显示当函数中的局部变量信息。
info prog 显示被调试程序的执行状态。
info var 显示所有的全局和静态变量名称。
make 在不退出 gdb 的情况下运行 make 工具。
print EXPR 显示表达式 EXPR 的值。

使用where命令查看程序出错的地方

显示当前路径变量的设置情况
(gdb)show path
显示当前环境变量的设置情况
(gdb)show envir(abbreviate of environment)

也可以用cd and pwd commands

terminate a child thread use kill command

如果想查看命令的列表或选项,可以在命令行下输入:
(gdb)M-?(就是ESC+?)
不过这种方式在远程调试下不起作用。

break [file:]function(types) (函数重载时,就需加上类型)

用于函数的命令

call, finish, return

call name(flags)  调用并执行名为name,参数为args的函数

finish  如果可以则中止当前函数并打印它的返回值

return value  停止执行当前函数,并将value返回给调用者

such as:(gdb)call max(max1,mini1)

使用return函数退出函数,你也可以使用return命令返回一个随意的值来测试边界条件


要指定一个或多个其他的目录,可以用一个或多个-d <路径名>选项启动GDB,如

$gdb -d /source/project test


要定位一个特定字符串在当前文件中的下一次出现可以使用search<字符串> 命令。使用反向查找命令reverse-search<字符串>来查找字符串上一次出现的地方


附加一个正在运行的进程将会自动将其停止以便你能使用常规的GDB命令来检查它的状态,such as:

(gdb)attach 1158(PID)

使用detach命令以允许该进程继续执行,或者使用quit命令分离并退出GDB

GDB有能力在调试程序时处理任何一种信号,可以告诉GDB需要处理哪一种信号。可以要求GDB收到所指定的信号后立刻停止正大运行的程序,以便进行调试,你可以用GDB的handle命令来完成这一功能,如下如示:
(gdb)handle <signal><keywords....>

gdb下的多线程调试(摘录)

gdb对于多线程程序的调试有如下的支持:

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

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

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

(gdb) info threads
  4 Thread 1099119552 (LWP 12940) 0xffffe002 in ?? ()
  3 Thread 1090731072 (LWP 12939) 0xffffe002 in ?? ()
  2 Thread 1082342592 (LWP 12938) 0xffffe002 in ?? ()
* 1 Thread 1073951360 (LWP 12931) main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb)

注意,行首的蓝色文字为gdb分配的线程号,对线程进行切换时,使用该该号码,而不是上文标出的绿色数字。

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

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

(gdb) info threads
  4 Thread 1099119552 (LWP 12940) 0xffffe002 in ?? ()
  3 Thread 1090731072 (LWP 12939) 0xffffe002 in ?? ()
  2 Thread 1082342592 (LWP 12938) 0xffffe002 in ?? ()
* 1 Thread 1073951360 (LWP 12931) main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb) thread 4
[Switching to thread 4 (Thread 1099119552 (LWP 12940))]#0 0xffffe002 in ?? ()
(gdb) info threads
* 4 Thread 1099119552 (LWP 12940) 0xffffe002 in ?? ()
  3 Thread 1090731072 (LWP 12939) 0xffffe002 in ?? ()
  2 Thread 1082342592 (LWP 12938) 0xffffe002 in ?? ()
  1 Thread 1073951360 (LWP 12931) main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb)

     以上即为使用gdb提供的对多线程进行调试的一些基本命令。另外,gdb也提供对线程的断点设置以及对指定或所有线程发布命令的命令。

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



以下这部分是转载的
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[i];
  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[i];

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[i]

    你能通过设置一个观察 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[i];

  string2[size] = `\0';

  printf ("The string printed backward is %s\n", string2);

}


 



以下是英文解释,用man gdb就可以了
You can run gdb with no arguments or options; but the most usual way to
       start GDB is with one argument or two, specifying an executable program
       as the argument:
                                                                                
       gdb program
                                                                                
       You can also start with both an executable program and a core file
       specified:
                                                                                
       gdb program core
                                                                                
       You can, instead, specify a process ID as a second argument, if you
       want to debug a running process:
                                                                                
       gdb program 1234
                                                                                
       would attach GDB to process 1234 (unless you also have a file named
       `1234'; GDB does check for a core file first).
                                                                                
       Here are some of the most frequently needed GDB commands:
                                                                                
       break [file:]function(types) (函数重载时,就需加上类型)

You can, instead, specify a process ID as a second argument, if you
       want to debug a running process:
                                                                                
       gdb program 1234
                                                                                
       would attach GDB to process 1234 (unless you also have a file named
       `1234'; GDB does check for a core file first).
                                                                                
       Here are some of the most frequently needed GDB commands:
                                                                                
       break [file:]function

You can, instead, specify a process ID as a second argument, if you
       want to debug a running process:
                                                                                
       gdb program 1234
                                                                                
       would attach GDB to process 1234 (unless you also have a file named
       `1234'; GDB does check for a core file first).
                                                                                
       Here are some of the most frequently needed GDB commands:
                                                                                
       break [file:]function

You can, instead, specify a process ID as a  second  argument,  if  you
       want to debug a running process:
                                                                                
       gdb program 1234
                                                                                
       would  attach  GDB  to  process 1234 (unless you also have a file named
       `1234'; GDB does check for a core file first).
                                                                                
       Here are some of the most frequently needed GDB commands:
                                                                                
       break [file:]function
               Set a breakpoint at function (in file).
 
       run [arglist]
              Start your program (with arglist, if specified).
 
       bt     Backtrace: display the program stack.
 
       print expr
               Display the value of an expression.
 
       c      Continue running your program (after stopping, e.g. at a  break-
              point).
              run [arglist]
              Start your program (with arglist, if specified).
 
       bt     Backtrace: display the program stack.
 
       print expr
               Display the value of an expression.
 
       c      Continue running your program (after stopping, e.g. at a  break-
              point).
                                                                                
       next   Execute  next program line (after stopping); step over any func-
              tion calls in the line.
 
       edit [file:]function
              look at the program line where it is presently stopped.
 
       list [file:]function
              type the text of the program in the  vicinity  of  where  it  is
              presently stopped.
 
       step   Execute  next program line (after stopping); step into any func-
       next   Execute  next program line (after stopping); step over any func-
              tion calls in the line.
 
       edit [file:]function
              look at the program line where it is presently stopped.
 
       list [file:]function
              type the text of the program in the  vicinity  of  where  it  is
              presently stopped.
 
       step   Execute  next program line (after stopping); step into any func-
              tion calls in the line.
 
       help [name]
              Show information about GDB command name, or general  information
              about using GDB.
 
       quit   Exit from GDB.
 
       For full details on GDB, see Using GDB: A Guide to the GNU Source-Level
       Debugger, by Richard M. Stallman and Roland H. Pesch.  The same text is
       available online as the gdb entry in the info program.
 
摘自 http://blog.chinaunix.net/space.php?uid=20535175&do=blog&id=1651304

 

调试Linux程序的时候,出现Segmentation Fault是最郁闷的事情了,程序代码量很大的时候,可能花很多时间都找不到出错原因。

       这里介绍一种对你调试Segmentation Fault很有帮助的方法,可能能迅速帮助你找到出错的代码行。

 

       这种方法需要用到Linux提供的core dump机制:当程序中出现内存操作错误时,会发生崩溃并产生核心文件(core文件)。使用GDB可以对产生的核心文件进行分析,找出程序是在什么时候崩溃的和在崩溃之前程序都做了些什么。

 

 

 

       首先,你的Segmentation Fault错误必须要能重现(废话…)。

 

       然后,依参照下面的步骤来操作:

 

       (1)无论你是用Makefile来编译,还是直接在命令行手工输入命令来编译,都应该加上 -g 选项。

 

       (2)一般来说,在默认情况下,在程序崩溃时,core文件是不生成的(很多Linux发行版在默认时禁止生成核心文件)。所以,你必须修改这个默认选项,在命令行执行:

 

       ulimit -c unlimited

 

       表示不限制生成的core文件的大小。

 

       (3)运行你的程序,不管用什么方法,使之重现Segmentation Fault错误。

 

       (4)这时,你会发现在你程序同一目录下,生成了一个文件名为 core.*** 的文件,即核心文件。例如,“core.15667”这样的文件。

 

       (5)用GDB调试它。假设你的可执行程序名为test,则在命令行执行:

 

       gdb test core.15667

 

       然后可能会显示出一堆信息:

 

 

 

GNU gdb Fedora (6.8-27.el5)

 

Copyright (C) 2008 Free Software Foundation, Inc.

 

License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

 

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 "i386-redhat-linux-gnu"...

 

 

 

warning: Can't read pathname for load map: Input/output error.

 

…………………(中间还有很多内容,此处省略)……………………………

 

Loaded symbols for /usr/lib/libgpg-error.so.0

 

Core was generated by `./test'.

 

Program terminated with signal 11, Segmentation fault.

 

[New process 15668]

 

#0  0x0804c760 in thread _handler () at test.cpp:707

 

707                             CDev* cur_dev = *it_d;

 

 

 

然后我们输入并执行命令bt

 

(gdb) bt

 

 

 

就会得到类似于下面的信息:

 

 

 

#0  0x0804c760 in thread _handler () at test.cpp:707

 

#1  0x006b149b in start_thread () from /lib/libpthread.so.0

 

#2  0x0060842e in clone () from /lib/libc.so.6

 

 

 

于是,我们一眼就看出来了:程序是在第707行使用指针时出的问题。

 

怎么样,方便吧?

http://blog.csdn.net/learnhard/article/details/4879834

 

posted on 2018-07-14 02:32  alex.shu  阅读(1483)  评论(0编辑  收藏  举报

导航