调试命令(三)

disassemble

  • 当进行一些高级调试时,我们可能需要查看某段代码的汇编指令去排查问题,或者是在调试一些没有调试信息的发布版程序时,也只能通过反汇编代码去定位问题,那么 disassemble 命令就派上用场了。
initServer () at server.c:1839
1839        createSharedObjects();
(gdb) disassemble
Dump of assembler code for function initServer:
   0x000000000042f450 <+0>:     push   %r12
   0x000000000042f452 <+2>:     mov    $0x1,%esi
   0x000000000042f457 <+7>:     mov    $0x1,%edi
   0x000000000042f45c <+12>:    push   %rbp
   0x000000000042f45d <+13>:    push   %rbx
   0x000000000042f45e <+14>:    callq  0x421eb0 <signal@plt>
   0x000000000042f463 <+19>:    mov    $0x1,%esi
   0x000000000042f468 <+24>:    mov    $0xd,%edi
   0x000000000042f46d <+29>:    callq  0x421eb0 <signal@plt>
   0x000000000042f472 <+34>:    callq  0x42f3a0 <setupSignalHandlers>
   0x000000000042f477 <+39>:    mov    0x316d52(%rip),%r8d        # 0x7461d0 <server+2128>
   0x000000000042f47e <+46>:    test   %r8d,%r8d
   0x000000000042f481 <+49>:    jne    0x42f928 <initServer+1240>
   0x000000000042f487 <+55>:    callq  0x421a50 <getpid@plt>
  • GDB 默认反汇编为 AT&T 格式的指令,可以通过 show disassembly-flavor 查看,如果习惯 intel 汇编格式可以用命令 set disassembly-flavor intel 来设置。
(gdb) set disassembly-flavor intel
(gdb) disassemble
Dump of assembler code for function initServer:
   0x000000000042f450 <+0>:     push   r12
   0x000000000042f452 <+2>:     mov    esi,0x1
   0x000000000042f457 <+7>:     mov    edi,0x1
   0x000000000042f45c <+12>:    push   rbp
   0x000000000042f45d <+13>:    push   rbx
   0x000000000042f45e <+14>:    call   0x421eb0 <signal@plt>
   0x000000000042f463 <+19>:    mov    esi,0x1
   0x000000000042f468 <+24>:    mov    edi,0xd
   0x000000000042f46d <+29>:    call   0x421eb0 <signal@plt>
   0x000000000042f472 <+34>:    call   0x42f3a0 <setupSignalHandlers>
   0x000000000042f477 <+39>:    mov    r8d,DWORD PTR [rip+0x316d52]        # 0x7461d0 <server+2128>
   0x000000000042f47e <+46>:    test   r8d,r8d
   0x000000000042f481 <+49>:    jne    0x42f928 <initServer+1240>
   0x000000000042f487 <+55>:    call   0x421a50 <getpid@plt>
   0x000000000042f48c <+60>:    mov    QWORD PTR [rip+0x316701],0x0        # 0x745b98 <server+536>
   0x000000000042f497 <+71>:    mov    DWORD PTR [rip+0x3164e3],eax  

set args 和 show args

  • 很多程序需要我们传递命令行参数。在 GDB 调试中,很多人会觉得可以使用 gdb filename args 这种形式来给 GDB 调试的程序传递命令行参数,这样是不行的。正确的做法是在用 GDB 附加程序后,在使用 run 命令之前,使用“set args 参数内容”来设置命令行参数

  • 还是以 redis-server 为例,Redis 启动时可以指定一个命令行参数,它的默认配置文件位于 redis-server 这个文件的上一层目录,因此我们可以在 GDB 中这样传递这个参数:set args …/redis.conf(即文件 redis.conf 位于当前程序 redis-server 的上一层目录),可以通过 show args 查看命令行参数是否设置成功。

(gdb) set args ../redis.conf
(gdb) show args
Argument list to give program being debugged when it is started is "../redis.conf ".
(gdb)
  • 如果单个命令行参数之间含有空格,可以使用引号将参数包裹起来。
(gdb) set args "999 xx" "hu jj"
(gdb) show args
Argument list to give program being debugged when it is started is ""999 xx" "hu jj"".
(gdb)
  • 如果想清除掉已经设置好的命令行参数,使用 set args 不加任何参数即可。
(gdb) set args
(gdb) show args
Argument list to give program being debugged when it is started is "".
(gdb)

tbreak

  • tbreak 命令也是添加一个断点,第一个字母“t”的意思是 temporarily(临时的),也就是说这个命令加的断点是临时的,所谓临时断点,就是一旦该断点触发一次后就会自动删除。添加断点的方法与上面介绍的 break 命令一模一样,这里不再赘述。
(gdb) tbreak main
Temporary breakpoint 1 at 0x423450: file server.c, line 3704.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/redis-4.0.9/src/redis-server
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Temporary breakpoint 1, main (argc=1, argv=0x7fffffffe588) at server.c:3704
3704    int main(int argc, char **argv) {
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/redis-4.0.9/src/redis-server
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
21652:C 14 Sep 07:05:39.288 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
21652:C 14 Sep 07:05:39.288 # Redis version=4.0.9, bits=64, commit=00000000, modified=0, pid=21652, just started
21652:C 14 Sep 07:05:39.288 # Warning: no config file specified, using the default config. In order to specify a config file use /root/redis-4.0.9/src/redis-server /path/to/redis.conf
21652:M 14 Sep 07:05:39.289 * Increased maximum number of open files to 10032 (it was originally set to 1024).
[New Thread 0x7ffff07ff700 (LWP 21653)]
[New Thread 0x7fffefffe700 (LWP 21654)]
[New Thread 0x7fffef7fd700 (LWP 21655)]
                _._
           _.-``__ ''-._
      _.-``    `.  `_.  ''-._           Redis 4.0.9 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 21652
  `-._    `-._  `-./  _.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |           http://redis.io
  `-._    `-._`-.__.-'_.-'    _.-'
 |`-._`-._    `-.__.-'    _.-'_.-'|
 |    `-._`-._        _.-'_.-'    |
  • 上述代码,我们使用 tbreak 命令在 main() 函数处添加了一个断点,当断点触发后,再次运行程序不再触发断点,因为这个临时断点已经被删除。

watch

  • watch 命令是一个强大的命令,它可以用来监视一个变量或者一段内存,当这个变量或者该内存处的值发生变化时,GDB 就会中断下来。被监视的某个变量或者某个内存地址会产生一个 watch point(观察点)。

  • watch 命令就可以通过添加硬件断点来达到监视数据变化的目的。watch 命令的使用方式是“watch 变量名或内存地址”,一般有以下几种形式:

    • 形式一:整型变量
int i;
watch i
    • 形式二:指针类型
char *p;
watch p 与 watch *p

注意:watch p 与 watch *p 是有区别的,前者是查看 *(&p),是 p 变量本身;后者是 p所指内存的内容。我们需要查看地址,因为目的是要看某内存地址上的数据是怎样变化的。

    • 形式三:watch 一个数组或内存区间
char buf[128];
watch buf
  • 这里是对 buf 的 128 个数据进行了监视,此时不是采用硬件断点,而是用软中断实现的。用软中断方式去检查内存变量是比较耗费 CPU 资源的,精确地指明地址是硬件中断。

注意:当设置的观察点是一个局部变量时,局部变量无效后,观察点也会失效。在观察点失效时 GDB 可能会提示如下信息:

Watchpoint 2 deleted because the program has left the block in&nbsp;which its expression is valid.

display

  • display 命令监视的变量或者内存地址,每次程序中断下来都会自动输出这些变量或内存的值。例如,假设程序有一些全局变量,每次断点停下来我都希望 GDB 可以自动输出这些变量的最新值,那么使用“display 变量名”设置即可。
Program received signal SIGINT, Interrupt.
0x00007ffff71e2483 in epoll_wait () from /lib64/libc.so.6
(gdb) display $ebx
1: $ebx = 7988560
(gdb) display /x $ebx
2: /x $ebx = 0x79e550
(gdb) display $eax
3: $eax = -4
(gdb) b main
Breakpoint 8 at 0x4201f0: file server.c, line 4003.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/redis-5.0.3/src/redis-server
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Breakpoint 8, main (argc=1, argv=0x7fffffffe4e8) at server.c:4003
4003    int main(int argc, char **argv) {
3: $eax = 4325872
2: /x $ebx = 0x0
1: $ebx = 0
(gdb)
  • 上述代码中,我使用 display 命令分别添加了寄存器 ebp 和寄存器 eax,ebp 寄存器分别使用十进制和十六进制两种形式输出其值,这样每次程序中断下来都会自动把这些值打印出来,可以使用 info display 查看当前已经自动添加了哪些值,使用 delete display 清除全部需要自动输出的变量,使用 delete diaplay 编号 删除某个自动输出的变量。
(gdb) delete display
Delete all auto-display expressions? (y or n) n
(gdb) delete display 3
(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
2:   y  $ebp
1:   y  $eax
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值