GDB-3——GDB实用调试技巧

 

1. 将 print 显示的字符串或字符数组显示完整

当我们使用 print 命令打印一个字符串或者字符数组时,如果该字符串太长,print 命令默认显示不全的,我们可以通过在 gdb 中输入 set print element 0 设置一下,这样再次使用 print 命令就能完整地显示该变量所有字符串了。

 

2. 让被gdb调试的程序接收信号

有两种方法:
(1) 在 gdb 中使用 signal 函数手动给我们的程序发送信号,这里就是 signal SIGINT。
(2) 改变 gdb 信号处理的设置,通过 handle SIGINT nostop print 告诉 gdb 在接收到 SIGINT 时不要停止、并把该信号传递给调试目标程序。(注:实测不行)

还有些程序如下配置,但是实验时都不好使。下面两个实测也没啥效果:
handle SIGPIPE noprint pass nostop
handle SIGUSR1 print pass nostop


3. 明明函数存在,添加断点时却无效

有时候,一个函数明明存在,并且我们的程序也存在调试符号,我们使用 break functionName 添加断点时,gdb 却提示:"Make breakpoint pending on future shared library load? y/n" 即使我们输入 y,添加的断点可能也不会被正确的触发。此时我们就需要改变添加断点的策略,使用该函数所在的代码文件和行号这种方式添加断点就能添加同样效果的断点。


4. 条件断点

实际调试中,我们一般会用到三种断点:普通断点、条件断点和硬件断点。
(1) 硬件断点又叫数据断点,这样的断点其实就是用 watch 命令添加的部分断点(为什么是部分,而不是全部,因为 watch 添加的断点,有部分是软中断实现的,不属于硬件断点)。硬件断点触发时机是当监视的内存地址或者变量值发送变化时,就会触发。
(2) 普通断点就是我们添加的断点除去条件断点和硬件断点以外的断点。
(3) 条件断点就是满足某个条件才会触发的断点。

这里重点来介绍一下条件断点,添加条件断点的命令之一是:break <lineNo> if <condition> 其中lineNo 是程序触发断点后需要停的位置,condition 是断点触发的条件。另一种方法是先添加一个普通断点,然后使用 condition <断点编号> <断点触发条件> 这样的格式来添加。

示例:

 1 #include <stdio.h>
 2 
 3 void do_something_func(int i)
 4 {
 5     printf("i*100 = %d\n", i*100);
 6 }
 7 
 8 int main()
 9 {
10     for(int i = 0; i < 10000; i++) {
11         do_something_func(i);
12     }
13     return 0;
14 }

调试记录:

//方法1:
lvm:~/origin_tmp/3.gdb$ gcc break_if.c -g -o break_if
lvm:~/origin_tmp/3.gdb$ gdb break_if
...
(gdb) b 11 if i==5 //直接设置为条件断点
Breakpoint 1 at 0x40055c: file break_if.c, line 11.
(gdb) r
Starting program: /origin_tmp/3.gdb/break_if 
i*100 = 0
i*100 = 100
i*100 = 200
i*100 = 300
i*100 = 400

Breakpoint 1, main () at break_if.c:11
11                      do_something_func(i);

//方法2:
(gdb) b 11
Breakpoint 1 at 0x40055c: file break_if.c, line 11.
(gdb) r
Starting program: /origin_tmp/3.gdb/break_if 

Breakpoint 1, main () at break_if.c:11
11                      do_something_func(i);
(gdb) condition 1 i==5  //将普通断点改为条件断点
(gdb) c
Continuing.
i*100 = 0
i*100 = 100
i*100 = 200
i*100 = 300
i*100 = 400

Breakpoint 1, main () at break_if.c:11
11                      do_something_func(i);

 

5. 自定义gdb调试命令

有些场景下,需要根据自己的程序情况,自定义一些可以在调试时输出我们程序特定信息的命令。这个在 gdb 中只要在 Linux 用户根目录下(root 用户就是 /root 目录,非 root 用户,是/home/用户名 这个目录)自定义一个 .gdbinit 文件,让后将命令定义在这个文件中。在 linux 系统中这是一个隐藏文件,需要使用 ls -a 命令查看,若是想在Window下修改,可以将前面的.去掉以便能显示出来,改过后再将.加上。

举个例子:
apache server的源码下载地址:http://httpd.apache.org/),在源码根目录下有个文件叫 .gdbinit,这个就是 apache server 自定义的 gdb 命令:

# gdb macros which may be useful for folks using gdb to debug
# apache. Delete it if it bothers you.
define dump_table
    set $t = (apr_table_entry_t *)((apr_array_header_t *)$arg0)->elts
    set $n = ((apr_array_header_t *)$arg0)->nelts
    set $i = 0
    while $i < $n
        if $t[$i].val == (void *)0L
            printf "[%u] '%s'=>NULL\n", $i, $t[$i].key
        else
            printf "[%u] '%s'='%s' [%p]\n", $i, $t[$i].key, $t[$i].val, $t[$i].val
        end
        set $i = $i + 1
    end
end
# 省略部分代码
# Set sane defaults for common signals:
handle SIGPIPE noprint pass nostop
handle SIGUSR1 print pass nostop

参考上面例子实现自己的gdb命令:

define judge_val
    set $val = $arg0
    if $val < 5
        printf "less than 5\n"
    else
        printf "bigger than 5\n"
    end
end

使用上面条件断点的程序进行实验:

lvm:~/origin_tmp/3.gdb$ gdb break_if
...
(gdb) b 11
Breakpoint 1 at 0x40055c: file break_if.c, line 11.
(gdb) r
Starting program: /origin_tmp/3.gdb/break_if 

Breakpoint 1, main () at break_if.c:11
11                      do_something_func(i);
(gdb) judge_val i
less than 5  //自己的命令执行成功了
(gdb) c
...
(gdb) judge_val i //当i大于5时也是执行成功的
bigger than 5

 

posted on 2022-11-08 11:51  Hello-World3  阅读(699)  评论(0编辑  收藏  举报

导航