用gdb脚本进行自动化调试

本文首先介绍用于自动化调试的gdb脚本的基本语法和基本用法,然后给出程序实例和调试实例。

在使用gdb调试程序的时候,如果想让调试自动化,该怎么办呢?
比如,想关闭 confirm,关闭 pagination,打开 print pretty, 设置 system root 为当前目录,等等。
如果是每次运行gdb都希望自动实现这些简单的设置,那么可以写在 ~/.gdbinit 文件中。如下:

#~/.gdbinit 

set sysroot
set confirm off
set pagination off
set print pretty

如果不是一个普通的需求,而是希望对一个特定的程序做一些自动的调试工作,比如,当遇到循环的时候,在循环体内自动地break和打印某些变量,那么该如何做呢?
编写一个gdb的命令脚本即可。

gdb脚本的基本语法

  1. 井号 # 表示注释

  2. set

1> 设置gdb的一些选项的值,如前面提及的pagination等
如果要查看当前的值,可以使用show命令,比如: show pagination

2> 创建调试使用的变量

set $a = i 

不带$的变量是被调试程序中的变量,如这里的i; 带$的变量为调试过程中定义的变量,如这里的$a
所有在gdb中创建的变量都是全局的

其他的例子还有:

(gdb) set $i = (char *)("Hello")

(gdb) print $i
$1 = 0x7ffff7fddf00 "Hello"

(gdb) printf "%s\n", $i
Hello

3> 访问寄存器

set $a = $eax
p $a

4> 修改寄存器

set $eax = 5

5> 修改内存

set *(unsigned char *)$addr = 0x90
  1. 函数声明语句
define func_name
    
end
  1. 函数帮助语句
document func_name
# write documents here 
end
  1. 打印语句
echo Hello\n

printf "a=%d\n", 10

  1. 条件语句
# e.g. if i == 10
if 
    # do something 
else 
    # do other things 
end 
  1. 循环语句
while  
    # do something 
end

举例:

set $i = 10
while $i > 0
    printf "%d\n", $i 
    set $i = $i - 1
end
  1. 普通的gdb命令

如,run, continue, info locals 等等

  1. 在 breakpoint 处执行调试语句

这一点是很常用的,尤其是在调试for循环的时候。
先看一个最简单的实例

break function_name
    command 1
    backtrace
    continue
end

以上代码,就是在指定的函数开始处设断点,当运行到此处后,打印backtrace,然后继续运行。
再看一个更加实用一点的例子:

set pagination off
set logging on gdb.output

set $var = 0

# 带 if 的 break 语句
break function1 if param1 == 32
    command 1
    print param2
    print param3->member1
    continue
end

break file.c:142 if x > 4
    command 2
    print y
    call checker_function
    continue
end

# 带 if 以及 ++ 运算的 break 语句
break function2 if $var++ < 3
    command 3
    print $var
    backtrace full
    continue
end

run

set logging off
quit

以上这段代码,可以保存进一个gdb脚本文件中了,比如,命名为 mycmd.gdb

gdb脚本的运行方法

那么如何使用gdb脚本呢,如上述的 mycmd.gdb?
有2种方法:

方法一: 在gdb的interactive界面运行 source mycmd.gdb

(gdb) file test.exe 

(gdb) source mycmd.gdb 

方法二: 在命令行中运行 gdb 的 batch 模式命令

gdb --batch --command=cmd.gdb --args test.exe  

三点说明:

  1. “–batch” 的含义是,该gdb命令将在命令行中运行,运行结束后,光标仍停留在命令行;
    假设不加 “–batch”, 则命令执行结束后,会停留再gdb的互动界面 (假设脚本最后没有 quit 语句。因以上脚本最后有quit,所以即使不在batch模式,也依然会退出gdb到达命令行。)

  2. “–command” 指定gdb脚本

  3. 若已在 “–args” 中指定了运行参数,则不要在gdb脚本的 run 命令后再指定运行参数了;
    否则,run命令中的运行参数会覆盖 “–args” 中的运行参数。

gdb脚本运用实例

以下给出一个C语言的示例程序,再给出一个gdb脚本,最后给出该脚本的运行结果。
C语言示例程序如下:

// test.c 
// gcc -g test.c -o test.exe 

#include 
#include 

int icount = 1; // default value

int main(int argc, char *argv[])
{
  int i, b;

  if (argc == 2) {
    icount = atoi(argv[1]);
  }

  i = icount;
  while (i > -1) {
    b = 5 / i;
    printf(" 5 / %d = %d \n", i, b );
    i--;
  }

  printf("Finished\n");
  return 0;
}

gdb命令文件 cmd.gdb

set $LINE_BP_1 = 20
set $LINE_BP_2 = 19

# at entry point - cmd1
b main
commands 1
  print argc
  continue
end

# printf line - cmd2
b test.c:$LINE_BP_1
commands 2
  p i
  p b
  continue
end

# int b = line - cmd3
b test.c:$LINE_BP_2
commands 3
  p i
  p b
  continue
end

# show arguments for program
show args
run

执行gdb自动运行命令:

gdb --batch --command=cmd.gdb --args test.exe 3

执行效果如下:

$gdb --batch --command=cmd.gdb --args test.exe 3
Breakpoint 1 at 0x719: file test.c, line 10.
Breakpoint 2 at 0x74f: file test.c, line 17.
Breakpoint 3 at 0x743: file test.c, line 16.
Argument list to give program being debugged when it is started is "3".
Missing separate debuginfo for /lib64/ld-linux-x86-64.so.2
Try: zypper install -C "debuginfo(build-id)=9487b554bdea197d4813797d7c37491ae1983345"
Missing separate debuginfo for /lib64/libc.so.6
Try: zypper install -C "debuginfo(build-id)=c688e67ac1e22ad79f4fd60d7fb012a267e83a29"

Breakpoint 1, main (argc=2, argv=0x7fffffffe188) at test.c:13
13        if (argc == 2) {
$1 = 2

Breakpoint 3, main (argc=2, argv=0x7fffffffe188) at test.c:19
19          b = 5 / i;
$2 = 3
$3 = 0

Breakpoint 2, main (argc=2, argv=0x7fffffffe188) at test.c:20
20          printf(" 5 / %d = %d \n", i, b );
$4 = 3
$5 = 1
 5 / 3 = 1

Breakpoint 3, main (argc=2, argv=0x7fffffffe188) at test.c:19
19          b = 5 / i;
$6 = 2
$7 = 1

Breakpoint 2, main (argc=2, argv=0x7fffffffe188) at test.c:20
20          printf(" 5 / %d = %d \n", i, b );
$8 = 2
$9 = 2
 5 / 2 = 2

Breakpoint 3, main (argc=2, argv=0x7fffffffe188) at test.c:19
19          b = 5 / i;
$10 = 1
$11 = 2

Breakpoint 2, main (argc=2, argv=0x7fffffffe188) at test.c:20
20          printf(" 5 / %d = %d \n", i, b );
$12 = 1
$13 = 5
 5 / 1 = 5

Breakpoint 3, main (argc=2, argv=0x7fffffffe188) at test.c:19
19          b = 5 / i;
$14 = 0
$15 = 5

Program received signal SIGFPE, Arithmetic exception.
0x0000555555554749 in main (argc=2, argv=0x7fffffffe188) at test.c:19
16          b = 5 / i;

笔者的gdb版本为8.1,试验平台为SUSE15.

(完)

你可能感兴趣的:(调试,gdb)