gdb调试技术3

本文首先以一个二叉树插入算法的实现作为例子说明GDB查看程序数据的相关方法,代码如下:

  1: //  bintree.c:  routines to do insert and sorted print of a binary tree

  2: 
  3: #include 
  4: #include 
  5: 
  6: struct
 node {
  7:    int
 val;             // stored value

  8:    struct
 node *left;   // ptr to smaller child

  9:    struct
 node *right;  // ptr to larger child

 10: };
 11: 
 12: typedef
 struct
 node *nsp;
 13: 
 14: nsp root;
 15: 
 16: nsp makenode(int
 x)
 17: {
 18:    nsp tmp;  
 19: 
 20:    tmp = (nsp) malloc(sizeof
(struct
 node));  
 21:    tmp->val = x;  
 22:    tmp->left = tmp->right = 0;
 23:    return
 tmp;
 24: }
 25: 
 26: void
 insert(nsp *btp, int
 x)
 27: {
 28:    nsp tmp = *btp;
 29: 
 30:    if
 (*btp == 0) {
 31:       *btp = makenode(x);
 32:       return
;
 33:    }
 34: 
 35:    while
 (1)
 36:    {
 37:       if
 (x < tmp->val) {
 38: 
 39:          if
 (tmp->left != 0) {
 40:             tmp = tmp->left;
 41:          } else
 {
 42:             tmp->left = makenode(x);
 43:             break
;
 44:          }
 45: 
 46:       } else
 {
 47: 
 48:          if
 (tmp->right != 0) {
 49:             tmp = tmp->right;
 50:          } else
 {
 51:             tmp->right = makenode(x);
 52:             break
;
 53:          }
 54: 
 55:       }
 56:    }
 57: }
 58: 
 59: 
 60: void
 printtree(nsp bt)
 61: {
 62:    if
 (bt == 0) return
;
 63:    printtree(bt->left);
 64:    printf("%d/n
",bt->val);
 65:    printtree(bt->right);
 66: }
 67: 
 68: 
 69: int
 main(int
 argc, char
 *argv[])
 70: {
 71:    root = 0;
 72:    for
 (int
 i = 1; i < argc; i++)
 73:       insert(&root, atoi(argv[i]));
 74:    printtree(root);
 75: }

在调试这个二叉树插入程序的时候,我们会非常关心insert方法的执行情况,在进入那个while(1)循环后,我们可能会做以下的操作:

(gdb) p tmp->val
$1=12
(gdb) p tmp->left
$2 = (struct node *) 0x8049698
(gdb) p tmp->right
$3 = (struct node *) 0x0

这个操作显得累赘又麻烦,我们可以有以下的改进措施:

1.直接打印结构体tmp

(gdb) p *tmp
$4 = {val = 12, left = 0x8049698, right = 0x0}

2.使用display命令 :我们在#37设置断点,然后运行程序,待程序运行至该断点停下后使用 display = disp 命令对某一个变量进行监视(之所以这样做是因为这个变量必须存在在该栈帧上,也就是说调试的时候这个变量的确被创建并且没有被销毁),程序以后只要一停止 就打印这个变量的值在屏幕上:

(gdb) disp *tmp
1: *tmp = {val = 12, left = 0x8049698, right = 0x0}
(gdb) c
Continuing.
Breakpoint 1, insert (btp=0x804967c, x=5) at bintree.c:37
37 if (x < tmp->val) {
1: *tmp = {val = 8, left = 0x0, right = 0x0}

也可以使用dis disp 1使这个监视动作失效(enable disp 1则恢复),undisp 1为删除。info display为查看当前所有自动打印点相关的信息

3.使用命令列表 :在上篇 中已经叙述,在此不再赘述。

4.使用call命令: 我们在代码中已经有了一个打印整个树的函数printtree,使用call命令我们可以直接利用代码中的方法进行变量监视,在每次insert完成的时候调用printtree对二叉树进行打印:

(gdb) commands 2
Type commands for when breakpoint 2 is hit, one per line.
End with a line saying just "end".
>printf "*********** current tree ***********"
>call printtree(root)
>end

5.使用DDD的Data Window图形化表示 :单击右键在root这个变量上然后选择display *root,每次在#37行停下时,在Data Window内对整个树的都有图形化表示,在左右子树上,你可以使用右键单击然后选择Display *()来显示。(Tips:你可以以--separate参数启动DDD,这样每个Window都是独立的,你可以获得更大的视野)。

补充:

1.打印数组: p *pointer@number_of_elements ,其中number_of_elements表示显示pointer这个变量中的几个成员。另外一种方式是类型转换,例如下列程序:

  1: int
 *x;
  2: main()
  3: {
  4:    x = (int
 *) malloc(25*sizeof
(int
));
  5:    x[3] = 12;
  6: }

除了可以使用:

(gdb) p *x@25
$1 = {0, 0, 0, 12, 0 }

我们还可以使用:

(gdb) p (int [25]) *x
$2 = {0, 0, 0, 12, 0 }

2.打印本地变量 :info locals,会打印当前栈帧的本地变量。

3.以不同形式打印变量 :p/paramenters variable  parameters 可以是 x 表示打印变量以十六进制表示,f为浮点,c为character,s为string。

4.打印历史查看过的变量:使用$number,而只使用$表示上一个变量。

(gdb) p tmp->left
$1 = (struct node *) 0x80496a8
(gdb) p *(tmp->left)
$2 = {val = 5, left = 0x0, right = 0x0}
(gdb) p *$1
$3 = {val = 5, left = 0x0, right = 0x0}

(gdb) p tmp->left
$1 = (struct node *) 0x80496a8
(gdb) p *$
$2 = {val = 5, left = 0x0, right = 0x0}

5.修改被调试程序运行时的变量值 :set x = 12。

6.利用自定义变量方便调试:例如,

  1: int
 w[4] = {12,5,8,29};
  2: main()
  3: {
  4:    w[2] = 88;
  5: }

我们设置i,然后利用这个变量对这个数组进行遍历:

(gdb) set $i = 0
(gdb) p w[$i++]
$1=12
(gdb)
$2=5
(gdb)
$3=88
(gdb)
$4=29

7.强制类型打印

p {type}address:把address指定的内存解释为type类型(类似于强制转型,更加强)

8.设置一些常见选项

  • 1) set print array:打开数组显示,打开后当数组显示时,每个元素占一行,如果不打开的话,每个元素则以逗号分隔。默认关闭
  • 2) set print elements num-of-elements:设置GDB打印数据时显示元素的个数,缺省为200,设为0表示不限制(unlimited)
  • 3) set print null-stop:设置GDB打印字符数组的时候,遇到NULL时停止,缺省是关闭的
  • 4) set print pretty:设置GDB打印结构的时候,每行一个成员,并且有相应的缩进,缺省是关闭的
  • 5) set print object:设置GDB打印多态类型的时候,打印实际的类型,缺省为关闭
  • 6) set print static-members:设置GDB打印结构的时候,是否打印static成员,缺省是打开的
  • 7) set print vtbl:GDB将用比较规整的格式来显示虚函数表,缺省是关闭的

参考文献:
《Art of Debugging》
《Linux® Debugging and Performance Tuning: Tips and Techniques》

Author:gnuhpc
WebSite:blog.csdn.net/gnuhpc

你可能感兴趣的:(gdb调试技术3)