Linux-GDB调试

GDB 命令行调试之路(全 0-9)

0:命令概述 1: 基本命令. 2: 断点管理/多文件调试. 3: 函数基本调试. 4: 中级命令. 5: 监视内存. 6: 高级命令. 7: 类的调试. 8: 线程调试 9: 总结.


0:gdb常用命令概述

一、GDB常用命令简介

 

r run 运行.程序还没有运行前使用
c             cuntinue   运行到下一断点处
q quit                        退出
tab tab 命令补全功能
h    help 帮助
s step 跟入函数
n next 执行下一行的代码。如果是函数调用,也当作一行代码,执行到此函数返回
b    breakpoint

设置断点,用法:(1)b 函数名 (2)b 文件名:行号

info b info breakpoints 查看断点数
del delete 删除断点:delete 断点号
list 列出代码行。一次列10 行。 也可在 list 后面跟上 文件名:行号
watch watch 观察一个变量的值。每次中断时都会显示这个变量的值
what what 显示变量的类型
p print 打印一个变量的值。print 也可改变一个值,通过指令 p b = 100,变量b 的值就变成100了
start start 从主函数 main 开始运行调试
fin finish 运行直至当前函数返回

 

二、GDB命令详解

和所有常用的调试工具一样,gdb提供了以下功能:
 # 监视程序中变量的值
 # 在程序中设置断点
 # 程序的单步执行

在使用gdb前,必须先载入可执行文件,因为要进行调试,文件中就必须包含调试信息,所以在用gcc或cc编译时就需要用-g参数来打开程序的调试选项。

调试开始时,必须先载入要进行调试的程序,可以用以下两种方式:
 * 在启动gdb后执行以下命令:
   file 可执行文件路径
 * 在gdb启动时就载入程序:
   gdb 可执行文件路径

载入程序后,接下来就是要进行断点的设置,要监视的变量的添加等工作,下面对在这个过程中常会用到的命令逐一进行介绍:
 * list :显示程序中的代码,常用使用格式有:
    list
      输出从上次调用list命令开始往后的10行程序代码。
    list -
      输出从上次调用list命令开始往前的10行程序代码。
    list n
      输出第n行附近的10行程序代码。
    list function
      输出函数function前后的10行程序代码。
 * forward/search :从当前行向后查找匹配某个字符串的程序行。使用格式:
    forward/search 字符串
  查找到的行号将保存在$_变量中,可以用print $_命令来查看。
 * reverse-search :和forward/search相反,向前查找字符串。使用格式同上。
 * break :在程序中设置断点,当程序运行到指定行上时,会暂停执行。使用格式:
    break 要设置断点的行号
 * tbreak :设置临时断点,在设置之后只起作用一次。使用格式:
    tbreak 要设置临时断点的行号
 * clear :和break相反,clear用于清除断点。使用格式:
    clear 要清除的断点所在的行号
 * run :启动程序,在run后面带上参数可以传递给正在调试的程序。
 * awatch :用来增加一个观察点(add watch),使用格式:
    awatch 变量或表达式
  当表达式的值发生改变或表达式的值被读取时,程序就会停止运行。
 * watch :与awatch类似用来设置观察点,但程序只有当表达式的值发生改变时才会停止运行。使用格 式:
    watch 变量或表达式
  需要注意的是,awatch和watch都必须在程序运行的过程中设置观察点,即可运行run之后才能设置。
 * commands :设置在遇到断点后执行特定的指令。使用格式有:
    commands
      设置遇到最后一个遇到的断点时要执行的命令
    commands n
      设置遇到断点号n时要执行的命令
  注意,commands后面跟的是断点号,而不是断点所在的行号。
  在输入命令后,就可以输入遇到断点后要执行的命令,每行一条命令,在输入最后一条命令后输入end就可以结束输入。
 * delete :清除断点或自动显示的表达式。使用格式:
    delete 断点号
 * disable :让指定断点失效。使用格式:
    disable 断点号列表
  断点号之间用空格间隔开。
 * enable :和disable相反,恢复失效的断点。使用格式:
    enable 断点编号列表
 * ignore :忽略断点。使用格式:
    ignore 断点号 忽略次数
 * condition :设置断点在一定条件下才能生效。使用格式:
    condition 断点号 条件表达式
 * cont/continue :使程序在暂停在断点之后继续运行。使用格式:
    cont
      跳过当前断点继续运行。
    cont n
      跳过n次断点,继续运行。
  当n为1时,cont 1即为cont。
 * jump :让程序跳到指定行开始调试。使用格式:
    jump 行号
 * next :继续执行语句,但是跳过子程序的调用。使用格式:
    next
      执行一条语句
    next n
      执行n条语句
 * nexti :单步执行语句,但和next不同的是,它会跟踪到子程序的内部,但不打印出子程序内部的语句。使用格式同上。
 * step :与next类似,但是它会跟踪到子程序的内部,而且会显示子程序内部的执行情况。使用格式同上。
 * stepi :与step类似,但是比step更详细,是nexti和step的结合。使用格式同上。
 * whatis :显示某个变量或表达式的数据类型。使用格式:
    whatis 变量或表达式
 * ptype :和whatis类似,用于显示数据类型,但是它还可以显示typedef定义的类型等。使用格式:
    ptype 变量或表达式
 * set :设置程序中变量的值。使用格式:
    set 变量=表达式
    set 变量:=表达式
 * display :增加要显示值的表达式。使用格式:
    display 表达式
 * info display :显示当前所有的要显示值的表达式。
 * delete display/undisplay :删除要显示值的表达式。使用格式:
    delete display/undisplay 表达式编号
 * disable display :暂时不显示一个要表达式的值。使用格式:
    disable display 表达式编号
 * enable display :与disable display相反,使用表达式恢复显示。使用格式:
    enable display 表达式编号
 * print :打印变量或表达式的值。使用格式:
    print 变量或表达式
  表达式中有两个符号有特殊含义:$和$$。
  $表示给定序号的前一个序号,$$表示给定序号的前两个序号。
  如果$和$$后面不带数字,则给定序号为当前序号。
 * backtrace :打印指定个数的栈帧(stack frame)。使用格式:
    backtrace 栈帧个数
 * frame :打印栈帧。使用格式:
    frame 栈帧号
 * info frame :显示当前栈帧的详细信息。
 * select-frame :选择栈帧,选择后可以用info frame来显示栈帧信息。使用格式:
    select-frame 栈帧号
 * kill :结束当前程序的调试。
 * quit :退出gdb。

如要查看所有的gdb命令,可以在gdb下键入两次Tab(制表符),运行“help command”可以查看命令command的详细使用格式。




1

先要说点关于ios的事.

大家开发ios时用的最多的软件是xcode.在苹果步步推广llvm的同时。也在步步推广lldb. (llvm对应gcc),(lldb对应gdb)。 好在lldb里面对应的调试命令跟gdb里没太大区别。所以学好gdb,到时转lldb。可以写一篇10分钟精通lldb..:) 学习起来可以参考的东西也比较多。推荐看一本书《the art of debugging with gdb ,ddd and eclipse 》

gdb--命令行调试器。虽说xcode比较强大,但是命令行调试可以让你更精确的调试。而且这在很多操作系统里都是可以通用的命令。所以学好gdb这个调试器祖先。应付xcode的调试就可以轻车熟路。

先入个门.然后像跑hello,world一样跑一个hello,gdb,所以,主要说以下两点

 


1.配置gdb调试环境 
2.hello gdb!
3.FAQ 

 

 

 

配置gdb调试环境

 

如果你环境已经正确.请跳过这节.验证方式 在命令行里敲gdb,会出现gdb的版权信息.

 

1: bogon:enviro zk$ gdb 2: GNU gdb 6.3.50-20050815 (Apple version gdb-1820) (Sat Jun 16 02:40:11 UTC 2012) 3: Copyright 2004 Free Software Foundation, Inc. 4: GDB is free software, covered by the GNU General Public License, and you are 5: welcome to change it and/or distribute copies of it under certain conditions. 6: Type "show copying" to see the conditions. 7: There is absolutely no warranty forGDB. Type "show warranty" for details. 8: This GDB was configured as "x86_64-apple-darwin". 9: (gdb)

 

自xcode4.3后。在xcode以单独的app发布。相当于绿色软件。所以,如果你之前用以下命令行卸载过xcode

 


sudo /Developer/Library/uninstall-devtools --mode=all

 

请在命令行下执行,不然你会在命令行中找不到gdb :(

 


xcode-select -swith 当前xcode.app/Contents/Developer 所在路径 

 

比如说,我现在的的xcode.app 放在

 


/Volums/Workspace/Xcode.app

 

那么就执行

 

xcode-select -switch /Volums/Workspace/Xcode.app/Contents/Developer

 

 

 

hello gdb!

 

首先准备好我们的调试文件test.cpp  如下

#include <iostream>
int main(0
{
int i=0;
i=10;
std::cout<<"hello,gdb"<<i<<std::endl;
return 0;


 

编译之。。

 


gcc -lStdC++ -g -Wall -o test test.cpp 

 

或者用  

 


g++ -g -o test test.cpp 

 

为简单起见 以后通用g++  ,这是GNU默认的c++编译器。 
   参数解释: 
      -g 产生相应的调试信息, 
      -o 编译产生后的文件名 在这里也就test准备开始调试 执行gdb载入要调试的程序(注意,这里并没开始执行test,只是载入)

 


gdb test

 

这个时候。你会看到命令行如下输出

Codebogon:pg_019 cubase01$ gdb test 
GNU gdb 6.3.50-20050815 (Apple version gdb-1820) (Sat Jun 16 02:40:11 UTC 2012) 
Copyright 2004 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 "x86_64-apple-darwin"...Reading symbols for shared libraries ... done 
(gdb)

 

此时,让我们开始在调试环境下执行test,输入

 

run

 

 

你将看到输出

 


(gdb) run 
Starting program: /Users/Desktop/Debugging/test 
Reading symbols for shared libraries ++......................... done 
hello,gdb10 
Program exited normally. 

 

好了,以上的步骤,就相当于你在xcode里用debug模式运行程序。

让程序在运行中停下来 
在gdb中,有3种方式让程序停下来。

1.breakpoint  这种形式是我们最常用的,反映在xcode里就是在行前双击。产生一个蓝色的标签。 
2.watchpoint  这种用的也多,如果你要监测一段内存是否发生变化。或者说一个变量是否发生变化,可以用它。 
3.catchpoint  这种形式是当特点形式的事件发生时,会停止程序运行。比如说,系统抛异常了。这个在xcode里也比较常用 ,捕获异常时非常方便,能让开发人员很快的定位到代码中抛异常的位置。

这3种point 在GNU中统称breakpoint--断点GNU 参考 。比较囧 ,当然。重点说1.breakpoint 的应用。

有以下用法

break 3       //在当前调试的文件中的第3行停止程序 
break main   //在当前调试的文件中的symbol table中找main这样的名字。如果找到。则停止程序。 
break test.cpp:3    // 在test.cpp文件中的第3行停止程序 
break test.cpp:main  //在test.cpp文件的symbol table中找main这样的名字。如果找到。则停止程序。

你可以在命令行里执行以上所有

 


(gdb) b 3 
Breakpoint 1 at 0x100000d34: file test.cpp, line 3. 
(gdb) b main 
Note: breakpoint 1 also set at pc 0x100000d34. 
Breakpoint 2 at 0x100000d34: file test.cpp, line 4. 
(gdb) b test.cpp:3 
Note: breakpoints 1 and 2 also set at pc 0x100000d34. 
Breakpoint 3 at 0x100000d34: file test.cpp, line 4. 
(gdb) b test.cpp:main 
Note: breakpoints 1, 2 and 3 also set at pc 0x100000d34. 
Breakpoint 4 at 0x100000d34: file test.cpp, line 4. 

 

查看一下断点的情况

执行

 


info break 

 

这命令有点长,你可以用缩写 i b 
关于缩写,gdb遵循一个这样的原则。只要缩写不会产生歧义。任何缩写都可以 
比如说 info break 
我打以下命令都可以 

 


i b 
i bre 
break 
i bre 

 

执行i b 后,显示如下

 

gdb) i b 
Num Type Disp Enb Address What 
1 breakpoint keep y 0x0000000100000d34 in main at test.cpp:3 
2 breakpoint keep y 0x0000000100000d34 in main at test.cpp:4 
3 breakpoint keep y 0x0000000100000d34 in main at test.cpp:4 
4 breakpoint keep y 0x0000000100000d34 in main at test.


 

以上各行含义解释如下 
Num 
这是一个断点的标示id,以后有需要索引这个断点的地方全靠它。 
Type

如我们前面所说。这是3个断点中的一种 
Disp

缩写(Dispensable),代表的意思就说能不能被自动删掉。有一处断点叫临时断点。就是中断一次后。断点就被删除了,下次再经过时,不会中断 tbreak  3 (在第3行设置临时断点) 这里显示的是keep ,代表永久断点。 
Enb

缩写(Enable) 在xcode中。你可以暂时性的不激活断点,但是断点还存在,y代表激活。n代表不激活。 
Address            
这个代表的是断点在内存中设置的具体位置。一般汇编人员用。。普通青年用不上。 
What

这个是对断点的一些信息描述。

如上所示。我们在第3行打一个断点。以某种方式在第4行打了3个断点。重复的断点只会停一次。

现在,让我再次以调试模式运行test 。输入

 

run

 

 

显示

 


Starting program: 
/Users/cubase01/Desktop/Debugging/Chapter_01/insert_sort/pg_019/test 
Breakpoint 1, main () at test.cpp:4 
int i=0; 

 

程序停下来了。。此时程序并没有执行第4行,是将要执行这一行,切记。

我想。你有以下这几个疑问,FAQ时间到。

 

FAQ:

1.不是说在第3行有断点吗?怎么先到了第4行?

在xcode里调试。开发人员会有一种错觉。就是认为源文件与调试的行是一一对应的。实际上不是。g++ 在编译时通过 -g命令生成一个叫symbol table的东东,翻成中文叫符号表。作用就是将二进制代码与源文件对应起来。  将断点断在main上。gdb认为这对调试没有任何作用。所以会自动优化到int i=0 。这种优化在-g命令时还不是很明显。当你开启g++ -O9时,g++会强力优化代码 。比如说以下代码

 


int i=0; 
i=1; 
i=2; 
i=3; 

 

用g++ -O9编译后。会被优化成int i=3;如果你用优化后的代码去调试。。要得到准确的行号是很难的。这可以算是一个比较麻烦的地方。因为优化很可能优化出bug。导致无法正常调试。

2.怎么查看i的值?

在命令行里输入

 


print i (缩写 p i)

 

输出

 


(gdb) p i 
$1 = 0 
(gdb) 

 

3 怎么单步调试?

在命令行里输入

 

next (缩写 n)

 

输出

 


(gdb) next 
5 i=10; 

 

 4.我这样一步一步点好累啊!!能一次走两步么 ?

 

n 3

 

一次走3步。

5.我想重新调试程序,怎么办啊! 再次运行

 

run

 

6 我想直接执行到下一个断点,怎么办? 

 


continue

 

如果后面没有断点了.那就直接结束了.

 

 

 

2


1.多文件调试断点标记方法。
2.断点基本管理。
3.函数基本调试.

 

1.多文件调试断点标记方法

首先引入两个文件。

main.c

 


#include <stdio.h>
void swap(int *a, int 
*b);
int main( void )
{
int i = 3;
int j 
= 5;
printf("i: %d, j: %d\n", i, 
j);
swap(&i, 
&j);
printf("i: %d, j: %d\n", i, 
j);
return 0;
}

 

 

swapper.c

 


void swap(int *a, int *b)
{
int c = 
*a;
*a = *b;
*b = c;
}

 

 

 

 

编译之


gcc -g3 -Wall main.c swapper.c
gcc -o swap main.o swapper.o

 

载入swap 程序


gdb swap

1.那我想。怎么让我调试到swap函数呢?

   有几种方法:

1.直接用在gdb中强行指定


b swapper.c:swap

//或者以下


b swapper.c:1

2.利用step命令,相当于xcode  里的step into 命令。首先在main 函数执行到swap时打上断点也就是


b main.c:10

当执行到将要调用swap函数时,执行next 命令(缩写n)

n

完整的命令行输出如下:



bogon:swap cubase01$ gdb swap
GNU gdb 6.3.50-20050815 (Apple version 
gdb-1820) (Sat Jun 16 02:40:11 UTC 2012)
Copyright 2004 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 "x86_64-apple-darwin"...Reading symbols 
for shared libraries .. done 
(gdb) b main.c:10
Breakpoint 1 at 0x100000e57: file main.c, line 
10.
(gdb) r 
Starting program: 
/Users/cubase01/Desktop/Debugging/Chapter_02/swap/swap 
Reading symbols for 
shared libraries +........................ done
i: 3, j: 5 
Breakpoint 1, main () at 
main.c:10
10 swap(&i, 
&j);
(gdb) s
swap (a=0x7fff5fbffac4, b=0x7fff5fbffac0) at 
swapper.c:3
int c = *a;
(gdb) 

 

 

2.断点基本管理

说到管理。我想做过数据库的都会想到 增删改查四大名捕。。。没错!那我们从4位入手。。


b 1
b main.c:4
b main.c:main


delete 1 //删除Num为1的断点。哪来的Num?? 输入 i b 在gdb中的断点列表的第一列!
delete 1 2 5 //可以同时删除多个断点
delete //如果没有参数 则删除所有断点

clear //消除gdb当前行的断点. 
clear main //注意没有delete main这样的命令 clear main的意思是,清除函数main内所有的断点。
clear main.c:main //同上 
clear 2
clear main.c:2


有人会问为什么一定要指定文件名?

答:因为你不一定是在这个文件里调试.

又问:哪我怎么知道我现在是在哪个文件里调试?

答:用list命令,同时你也可以用list命令切换你当前的命令作用对象.

 

禁用/启用


enable 1 //这里1代表的是i b 列表里的Num号
disable 1 5
enable once 1 5 // 只启用断点一起.中断后将置为disable状态

改的话主要用到地方是将普通断点改为条件断点,这在第3章会有具体介绍.

1:  2: cond 3 i >3 //将Num为3的断点加上i>3的条件,满足此条件时.停在此断点

1:  2: info break

 

3.函数基本调试

函数重要的就是进入函数与跳出函数,对应的gdb命令就是


step 
finish

看一下以下具体应用



(gdb) list main 
1 #include <stdio.h> 
2 void swap(int *a, int *b); 

int main( void ) 
5 { 
int i = 3; 
int j = 5; 

9 printf("i: %d, j: %d\n", i, j); 
10 swap(&i, &j); 
(gdb) b 10 
Breakpoint 11 at 0x100000e57: file main.c, line 10. 
(gdb) run 
Starting program: /Users/cubase01/Desktop/Debugging/Chapter_02/swap/swap 
i: 3, j: 5 
Breakpoint 11, main () at main.c:10 
10 swap(&i, &j); 
(gdb) step 
swap (a=0x7fff5fbffac4, b=0x7fff5fbffac0) at swapper.c:3 
int c = *a; 
(gdb) finish 
Run till exit from #0 swap (a=0x7fff5fbffac4, b=0x7fff5fbffac0) at swapper.c:3 
main () at main.c:11 
11 printf("i: %d, j: %d\n", i, j);

 

 

 

 

 

3

前面两往篇把基本的调试命令介绍了一篇.下面.来点稍微高级的.这章将要介绍的是

1: 断点的逻辑判断

1. 断点的逻辑判断

首先准备好要调试的文件.简单简单...

1: #include <stdio.h> 2: int main( void ) 3: { 4: int i; 5: for( i = 0; i < 10; ++i 6: ) 7: printf("hello world!"); 8: return 0; 9: }

看着这段代码,有几个调试需求如下:

需求1.请在i=5的时候停下来.

需求2.请在i=5后,停下来之后.直接跳到return 0;

 

用前面说的基本方法,满足这两个需求是没问题的.但是比较麻烦.

需求1. 见以下命令行输出

1: (gdb) list main 2: 1 #include <stdio.h> 3: 2 4: 3 int main( void ) 5: 4 { 6: 5 int i; 7: 6 8: 7 for( i = 0; i < 10; ++i ) 9: 8 printf("hello world!"); 10: 9 11: 10 return 0; 12: (gdb) b 8 13: Breakpoint 1 at 0x100000eed: file until-anomaly.c, line 8. 14: (gdb) run15: Starting program: /Users/cubase01/Desktop/Debugging/Chapter_02/until-anomaly/anomaly 16: Reading symbols for shared libraries +........................ done 17:  18: Breakpoint 1, main () at until-anomaly.c:8 19: 8 printf("hello world!"); 20: (gdb) c 5 21: Will ignore next 4 crossings of breakpoint 1. Continuing. 22:  23: Breakpoint 1, main () at until-anomaly.c:8 24: 8 printf("hello world!");25: (gdb) p i 26: $1 = 5 27: (gdb)

需求2.

则是在需求1的基础上在return 0 上打断点.

并删除打在printf("hello,world!")上的断点.

并执行continue

1: (gdb) b 10 2: Breakpoint 2 at 0x100000f03: file until-anomaly.c, line 10. 3: (gdb) i b 4: Num Type Disp Enb Address What 5: 1 breakpoint keep y 0x0000000100000eed in main at until-anomaly.c:8 6: breakpoint already hit 6 times 7: 2 breakpoint keep y 0x0000000100000f03 in main at until-anomaly.c:10 8: (gdb) d 1 9: (gdb) c 10: Continuing. 11:  12: Breakpoint 2, main () at until-anomaly.c:10 13: 10 return 0; 14: (gdb)

以上是用基本的命令完成的.可以看出比较麻烦.

下面介绍简单的方法.

需求1,2同时解决

1: (gdb) list main //这里例出当前正在调试谁的的具体信息 2: 1 #include <stdio.h> 3: 2 4: 3 int main( void ) 5: 4 { 6: 5 int i; 7: 6 8: 7 for( i = 0; i < 10; ++i ) 9: 8 printf("hello world!"); 10: 9 11: 10 return 0; 12: (gdb) b 8 if i==5 //这里就是所谓的条件断点.当i==5时,在第8行中断 13:Breakpoint 1 at 0x100000eed: file until-anomaly.c, line 8. 14: (gdb) r //运行调试程序 15: Starting program: /Users/cubase01/Desktop/Debugging/Chapter_02/until-anomaly/anomaly 16: Reading symbols for shared libraries +........................ done 17:  18: Breakpoint 1, main () at until-anomaly.c:8 19: 8 printf("hello world!"); 20: (gdb) p i //程序已中断,打印i值 i=5 21: $1 = 5 22:(gdb) u 10 //u 是until的缩写,它的意思是 执行到某一行.contine 类似 break 某行->continue的缩写 23: main () at until-anomaly.c:10 24: 10 return 0;25: (gdb)

 

条件断点中的包括所有c++中的逻辑判断符(>=,<=,etc).运算符(+ , - ,* / , etc).包括位运算(| ,~ , etc).

更重要的是: 你还可以在条件断点中用function,只要这个函数链接到了这个函数所在的调试库.如果不是调试库.而发行库.那这个函数的返回值只会int类型.对于double float 或者其他类型.会解释不正确. 比如说

1:  2: (gdb) p cos(0.0) 3: $1=-13232

想强转? (gdb) p (double)(0.0)  .没用.在以后介绍gdb变量的时候.会有一个折衷的办法.再说.

 

但如果只是设置了

1:  2: b 2

想临时加 if i>3怎么办?

这样,先查找到第line 3这个断点的Num.假设为9

输入

1:  2: cond 9 i > 3

取消条件断点? 简单

1:  2: cond 9

 

4

 

 

接下来的开始命令的具体事例讲解

1: commands 2: define

引入以下文件:

1:  2: #include <stdio.h> 3: int fibonacci(int n); 4: int main( void ) 5: { 6: printf("Fibonacci(3) is %d.\n"7: fibonacci(3)); 8:return 0; 9: } 10: int fibonacci( int n ) 11: { 12: if ( n <= 0 || n == 1 13: ) 14: return 1; 15: 16: else 17: return fibonacci(n-1) +18: fibonacci(n-2); 19: }

 

以下命令中的括号代表的意义为[]可选  {}必须

1.commands

1:  2: (gdb){commands 1} //在断点Num 1上开始commands 3: >[slient] //不显示断点时的当前行信息 4: >print "hello ,gdb \n" //打印当前n值 5: >[continue//继续执行当前断点 6: >{end} //必须以此结束commands

command命令里可以绑定很多函数.只要能够链接的到.库函数.你自己定义的函数都可以.甚至还可以使用函数的返回值.只要函数的返回值是int类型.

 

2.define

 

 

1:  2: {define print_and_go} 3: >printf arg0,arg1 4: >continue 5: >end 6:  7: commands 1 8: print_and_go "hello %d\n" n //注意这里没有逗号

 

命令行输出如下

 

1: (gdb) define print_and_go 2: Type commands for definition of "print_and_go". 3: End with a line saying just "end". 4: >printf arg0,arg15: >end 6: (gdb) break fibonacci 7: Breakpoint 1 at 0x100000e9b: file fibonacci.c, line 13. 8: (gdb) i b 9: Num Type Disp Enb Address What 10: 1 breakpoint keep y 0x0000000100000e9b in fibonacci at fibonacci.c:13 11: (gdb) commands 1 12: Type commands for when breakpoint 1 is hit, one per line. 13: End with a line saying just "end". 14: >print_and_go "the value is %d\n" n 15: >end 16: (gdb) run 17:Starting program: /Users/cubase01/Desktop/Debugging/Chapter_02/fibonacci/fibonacci 18: Reading symbols for shared libraries +........................ done 19:  20: Breakpoint 1, fibonacci (n=3) at fibonacci.c:13 21: 13 if ( n <= 0 || n == 1 ) 22: the value is 323: (gdb) c 24: Continuing. 25:  26: Breakpoint 1, fibonacci (n=2) at fibonacci.c:13 27: 13 if ( n <= 0 || n == 1 ) 28: the value is 229: (gdb) c 30: Continuing. 31:  32: Breakpoint 1, fibonacci (n=1) at fibonacci.c:13 33: 13 if ( n <= 0 || n == 1 ) 34: the value is 135: (gdb) c 36: Continuing. 37:  38: Breakpoint 1, fibonacci (n=0) at fibonacci.c:13 39: 13 if ( n <= 0 || n == 1 ) 40: the value is 041: (gdb) c 42: Continuing. 43:  44: Breakpoint 1, fibonacci (n=1) at fibonacci.c:13 45: 13 if ( n <= 0 || n == 1 ) 46: the value is 147: (gdb) c 48: Continuing. 49: Fibonacci(3) is 3. 50:  51: Program exited normally. 52: (gdb)

 

 

 

 

5

watchpoint,谁动了我的内存

 

1.watchpoint,谁动了我的内存

watchpoint 顾名思义,监视.在很多ide里都有这个功能.用起来跟breakpoint稍微有点不一样.我们知道breakpoint是可以在调试之前事先打好断点.但watchpoint不同.watchpoint必须要在当前scope里的设置.举例来说

拿以下fibonacci.c举例

1: #include <stdio.h> 2: int fibonacci(int n); 3: int global=0; 4: int 5: main( void ) 6: { 7: printf("Fibonacci(3) is %d.\n"8:fibonacci(3)); 9: global=2; 10: return 11: 0; 12: } 13: int fibonacci( int n ) 14: { 15: if ( n <= 0 || n == 1 16: ) 17: return 1; 18:19: else 20: return fibonacci(n-1) + 21: fibonacci(n-2); 22: }

 

命令行,监视全局变量


bogon:fibonacci cubase01$ gcc -g3 -o fibonacci fibonacci.c //compile 

bogon:fibonacci cubase01$ gdb fibonacci //加载gdb fibonacci
GNU gdb 6.3.50-20050815 (Apple 
version gdb-1820) (Sat Jun 16 02:40:11 UTC 2012)
Copyright 2004 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 "x86_64-apple-darwin"...Reading symbols 
for shared libraries .. done 
(gdb) wa global //设置watchpoint 注意这是一个global变量.所以它的scope也是global的
Hardware watchpoint 1: global
(gdb) r
Starting 
program: /Users/cubase01/Desktop/Debugging/Chapter_02/fibonacci/fibonacci 

Reading symbols for shared libraries +........................ 
done
Fibonacci(3) is 3.
Hardware watchpoint 1: global 
Old value = 0
New value = 2 //发现值改变.停下来.
main () at 
fibonacci.c:8
return 0;
(gdb) 

 

 

以上有几点要注意的地方 .

  1. watchpoint只能在当前scope里设置.

  2. watchpoint在脱离scope后.所有失去scope的变量会被自动删除.

  3. watchpoint监视的是内存值的变化.如果内存值被赋予了一个一样的值.那也不会起作用.


 

watchpoint也可以像条件断点一样加上条件.它们的区别是watchpoint会在作用域之后自动删除.没前作用域之前不会存在.

条件监视断点里可以包含的表达式有:

  1. 类似$arg0这些已经在gdb中定义的变量.

  2. 任何在当前 scpoe里的变量

  3. 定义了的字符,数字常量

  4. 预处理了的宏

  5. 语言里的条件判断

修改如下:


bogon:fibonacci cubase01gccg3ofibonaccifibonacci.cbogon:fibonaccicubase01 gdb
GNU gdb 6.3.50-20050815 (Apple version 
gdb-1820) (Sat Jun 16 02:40:11 UTC 2012)
Copyright 2004 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 "x86_64-apple-darwin".
(gdb) file 
fibonacci
Reading symbols for shared libraries .. done
Reading symbols 
from /Users/cubase01/Desktop/Debugging/Chapter_02/fibonacci/fibonacci...Reading 
symbols from 
/Users/cubase01/Desktop/Debugging/Chapter_02/fibonacci/fibonacci.dSYM/Contents/Resources/DWARF/fibonacci...done.
done.
(gdb) 
b main
Breakpoint 1 at 0x100000e34: file fibonacci.c, line 6.
(gdb) 
run
Starting program: 
/Users/cubase01/Desktop/Debugging/Chapter_02/fibonacci/fibonacci 
Reading 
symbols for shared libraries +........................ done 
Breakpoint 1, main () at 
fibonacci.c:6
int a=1;
(gdb) 
wa a
Hardware watchpoint 2: a
(gdb) r 
The program being debugged 
has been started already.
Start it from the beginning? (y or n) n
Program 
not restarted.
(gdb) c
Continuing.
Hardware watchpoint 2: a 
Old value = 0
New value = 1
main () at 
fibonacci.c:7

printf("Fibonacci(3) is %d.\n", fibonacci(3));
(gdb) 
c
Continuing.
Fibonacci(3) is 3.
Hardware watchpoint 2: a 
Old value = 1
New value = 2
main () at 
fibonacci.c:10
10 return 
0;
(gdb)

 

 

Tips:


(gdb) i b
Num Type Disp Enb Address What
hw watchpoint keep y global breakpoint already hit 1 time

有没有发现上面的watchpoint type 是hw,这是因为watchpoint可以由硬件实现.速度很快.如果硬件不能实现gdb则会利用VM技术实现.也比较快.如果前两种都不行.GDB会自己通过软件实现..很慢.

 

 

 

6

前面已经把95%的调试技巧全讲完了.接下来一起学习一下方便调试的东西.这些命令或者技巧是能让你更方便的调试.

接下来将介绍以下高级命令

display //在每次断点停下来时,打印当前scope中的变量

call     //调用当前scope中某个函数

 

怎么打印数组?
怎么查看当前stack中的变量?
怎么打印其他格式的值?
怎么管理display例表?
怎么临时改变变量的值?
怎么设置启动时的命令行参数?
为什么每次print时,都有1,2之类的东东在前面?
怎么重复上一命令?
怎么在gdb中设置一个变量?
Tips

怎么打印数组?

准备以下文件

int *x; 
main()
{
x = (int *) malloc( 25*sizeof(int) );
x[3] = 12;
}


(gdb) b 6
Breakpoint 1 at 0x100000f24: file solutions_in_gdb.c, line 6.
(gdb) 
r
Starting program: 
/Users/cubase01/Desktop/Debugging/Chapter_03/solutions_in_gdb/solutions_in_gdb 

Reading symbols for shared libraries +........................ done 
Breakpoint 1, main () at 
solutions_in_gdb.c:6
6 x[3] = 
12;
(gdb) p *x@25
$1 = {0 <repeats 25 times>}
(gdb) 
n
7 }
(gdb) 
*x@25 //这个是关键的东西 p指针@元素个数 
$2 = {0, 0, 0, 12, 0 <repeats 
21 times>}
(gdb) 

怎么查看当前stack中的变量?


info local

怎么打印其他格式的值?


p/x i //打印hex
p/c i //打印字符
p/s i //打印字符串
p/f i //打印float

怎么管理display例表?

(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
1: y i

Num

这个跟断点Num的意义一样.是display某值的一个标识.

Enb

缩写(enable),是否激活 

要禁用的话.启用用enable.删除display命令有点不合群.undisplay 1

 


disable display 1
enable display 1
undisplay 1

 

Expression

display 的变量值

 

 

怎么临时改变变量的值?


set i=3

 

怎么设置启动时的命令行参数?


set args 1 3 5

当然.你设置完后.要调用run启动这些命令.跟直接执行以下是一样的


run 1 3 5

打印命令行参数

 

info args

 

为什么每次print时,都有1,2之类的东东在前面?

这个东西叫 value history. 恶心翻译一下就是值的历史吧. 比如说你打印


int* a;
*a =1;
(gdb)p a
$1 =(int*) 0x32fa231 //打印出值的地址
(gdb)p 
$1 
//打印出$1更新后的值
$2 =(int*) 0x32fa231
(gdb)p 
*$2 
//打印出$2指向的值
3=1(gdb)p 
//打印出上一个value history的值 
4 = 1 (gdb) p  & 
//打印出上一个value history的地址
$5 =(int*) 0x32fa231

怎么重复上一命令??

直接按回车.

 

怎么在gdb中设置一个变量?


set a=isetb=2
$b++

Tips

大家觉得下面两个函数有什么区别? 麻烦用call命令在gdb中分别调用它们(跟gdb没关系.只是顺便举例).

1:  2: void p() 3: { 4: printf("hello"); 5: }

1:  2: void p() 3: { 4: printf("hello\n"); 5: }

发现区别没有.printf不一定要会即时调用.我建议大家看一下这篇文章.一个fork的面试题 .

 

 

 

7

这一篇主要说一下类的一些相关调试.

 

类相关gdb调试

准备以下类

// bintree.cc: routines to do insert and sorted print of a binary tree in C++

#include <cstdlib>
#include <iostream>
using namespace std;

class node {
public:
static class node *root; // root of the entire tree
int val; // stored value
class node *left; // ptr to smaller child
class node *right; // ptr to larger child
node(int x); // constructor, setting val = x
static void insert(int x); // insert x into the tree
static void printtree(class node *nptr); // print subtree rooted at *nptr
};

class node *node::root = 0;


node::node(int x)

val = x; 
left = right = 0;
}


void node::insert(int x)

if (node::root == 0) {
node::root = new node(x);
return;
}
class node *tmp=root;
while (1)
{
if (x < tmp->val)
{

if (tmp->left != 0) {
tmp = tmp->left;
else {
tmp->left = new node(x);
break;
}

else {

if (tmp->right != 0) {
tmp = tmp->right;
else {
tmp->right = new node(x);
break;
}

}
}
}


void node::printtree(class node *np)
{
if (np == 0) return;
node::printtree(np->left);
cout << np->val << endl;
node::printtree(np->right);
}


int main(int argc, char *argv[])
{
for (int i = 1; i < argc; i++)
node::insert(atoi(argv[i]));
node::printtree(node::root);
}

怎么打印类中的静态变量?

打印方法


p *node ::root

 

怎么查看类的结构?

 

(gdb) b main
Breakpoint 1 at 0x100000c9f: file bintree.cc, line 72.
(gdb) r
Starting program: /Users/cubase01/Desktop/Debugging/Chapter_03/bintree++/bintree 
Reading symbols for shared libraries ++......................... done

Breakpoint 1, main (argc=1, argv=0x7fff5fbffac0) at bintree.cc:72
72 for (int i = 1; i < argc; i++)
(gdb) ptype node
type = class node {
public:
int val;
node *left;
node *right;
static node *root;

node(int);
node(int);
node(int);
static void insert(int);
static void printtree(node*);
}
(gdb) 

 


ptype node

 

8

调试中最烦的就是线程. 线程太多队伍不好带...

在mac下.线程用的最多的还是pthread. 先准备好要调试的线程源码:

以下这个文件是从<<The art of debugging with gdb >>中借来用的.

sieve.c

// finds the primes between 2 and n; uses the Sieve of Eratosthenes,
// deleting all multiples of 2, all multiples of 3, all multiples of 5,
// etc.; not efficient, e.g. each thread should do deleting for a whole
// block of values of base before going to nextbase for more 

// usage: sieve nthreads n
// where nthreads is the number of worker threads

#include <stdio.h>
#include <math.h>
#include <pthread.h> 

#define MAX_N 100000000
#define MAX_THREADS 100

// shared variables
int nthreads, // number of threads (not counting main())
n, // upper bound of range in which to find primes
prime[MAX_N+1], // in the end, prime[i] = 1 if i prime, else 0
nextbase; // next sieve multiplier to be used

int work[MAX_THREADS]; // to measure how much work each thread does,
// in terms of number of sieve multipliers checked

// lock index for the shared variable nextbase
pthread_mutex_t nextbaselock = PTHREAD_MUTEX_INITIALIZER;

// ID structs for the threads
pthread_t id[MAX_THREADS];

// "crosses out" all multiples of k, from k*k on
void crossout(int k)
int i;

for (i = k; i*k <= n; i++) {
prime[i*k] = 0;
}
}

// worker thread routine
void *worker(int tn) // tn is the thread number (0,1,...)
int lim,base;

// no need to check multipliers bigger than sqrt(n)
lim = sqrt(n);

do {
// get next sieve multiplier, avoiding duplication across threads
pthread_mutex_lock(&nextbaselock);
base = nextbase += 2;
pthread_mutex_unlock(&nextbaselock);
if (base <= lim) {
work[tn]++; // log work done by this thread
// don‘t bother with crossing out if base is known to be
// composite
if (prime[base]) 
crossout(base);
}
else return
while (1);
}

main(int argc, char **argv)
int nprimes, // number of primes found 
totwork, // number of base values checked
i;
void *p;

n = atoi(argv[1]);
nthreads = atoi(argv[2]);
for (i = 2; i <= n; i++) 
prime[i] = 1;
crossout(2);
nextbase = 1;
// get threads started
for (i = 0; i < nthreads; i++) {
pthread_create(&id[i],NULL,(void *) worker,(void *) i);
}

// wait for all done
totwork = 0;
for (i = 0; i < nthreads; i++) {
pthread_join(id[i],&p);
printf("%d values of base done\n",work[i]);
totwork += work[i];
}
printf("%d total values of base done\n",totwork);

// report results
nprimes = 0;
for (i = 2; i <= n; i++) 
if (prime[i]) nprimes++;
printf("the number of primes found was %d\n",nprimes);

}

 

怎么列举当前的线程?

info threads

在断点停下后.怎么切换到其他线程去

thread n

怎么指定在某个断点停下,当在某个线程时.

break 30 thread 1

 

9

为什么你的程序会崩溃?

在写c/c++程序时.最常见的崩溃就是

invalid access of memory 读取了没有权限的内存导致崩溃.出现这种情况时. 硬件会执行一个jump命令到操作系统.由操作系统处理.

调试时,内存是在Virtual Memory(VM)里模拟.这也是为了方便内存调试.

反应在xcode里就是segementation fault/seg fault

在visual studio里是general protection fault 反正不管怎么取名.它的意思就是说程序读了它不该读的内存.

 

正是因为在调试时内存是在虚拟内存里分配的.所以有一些segmentation fault不是很明显.原因是:

在VM里的内存分配有一个最小分配值.取名叫page. 在奔腾系统处理器里.这个值默认为4096 bytes.所以当你有一个程序占用了10000 bytes时.它总共会占用3个page.而不是2.5个.page已经是最小单位.不能再分割.

这就会出现一个问题.如果程序在多出来的page里出了错.很可能在调试时并不会报错.


本文出自 “Mr_Computer” 博客,请务必保留此出处http://caochun.blog.51cto.com/4497308/1441109

Linux-GDB调试

上一篇:CentOS 6.5源码编译安装MySQL 5.6


下一篇:数据库-存储过程(T-SQL)