gnu2和gnu3区别_GNU调试器的5个技巧

gnu2和gnu3区别

GNU调试器 (gdb)是一种宝贵的工具,可用于在开发程序时检查正在运行的进程并解决问题。

您可以在特定位置设置断点(按函数名称,行号等),启用和禁用这些断点,显示和更改变量值,并执行所有调试器希望执行的所有标准操作。 但是它还具有您可能没有尝试过的许多其他功能。 这里有五个供您尝试。

条件断点

设置断点是您将要学习的使用GNU调试器的第一步。 程序在达到断点时停止,您可以运行gdb命令对其进行检查或更改变量,然后再允许该程序继续运行。

例如,您可能知道经常调用的函数有时会崩溃,但仅当它获得某个参数值时才会崩溃。 您可以在该函数的开始处设置一个断点并运行程序。 每次遇到断点时都会显示函数参数,如果未提供触发崩溃的参数值,则可以继续操作,直到再次调用该函数为止。 当麻烦的参数触发崩溃时,您可以单步执行代码以查看问题所在。


   
   
( gdb ) break sometimes_crashes
Breakpoint 1 at 0x40110e: file prog.c, line 5 .
( gdb ) run
[ ... ]
Breakpoint 1 , sometimes_crashes ( f =0x7fffffffd1bc ) at prog.c: 5
5      fprintf ( stderr,
( gdb ) continue
Breakpoint 1 , sometimes_crashes ( f =0x7fffffffd1bc ) at prog.c: 5
5      fprintf ( stderr,
( gdb ) continue

为了使此方法更具可重复性,您可以在您感兴趣的特定调用之前计算该函数被调用的次数,并在该断点处设置一个计数器(例如,“ continue 30”以使其在到达下一个29次时忽略它)断点)。

但是,断点真正强大的地方在于它们在运行时评估表达式的能力,这使您可以自动化这种测试。 输入:条件断点。


   
   
break [ LOCATION ] if CONDITION

( gdb ) break sometimes_crashes if ! f
Breakpoint 1 at 0x401132: file prog.c, line 5 .
( gdb ) run
[ ... ]
Breakpoint 1 , sometimes_crashes ( f =0x0 ) at prog.c: 5
5      fprintf ( stderr,
( gdb )

有条件断点使您不必让gdb每次调用该函数时都去做,而是让条件断点仅在特定表达式的值为true时才使gdb停止在该位置。 如果执行到达条件断点位置,但表达式的计算结果为false,则执行

断点命令

GNU调试器中断点的一个甚至更复杂的功能是能够编写对到达断点的响应的脚本。 断点命令使您可以编写GNU调试器命令列表,以在到达断点时运行。

我们可以使用它来解决有时已经出现的bug,并在它提供空指针的情况下无害地从该函数返回,而该臭虫有时会在功能中返回。

我们可以使用silent作为第一行来更好地控制输出。 否则,每次碰到断点时,即使在运行断点命令之前,也会显示堆栈帧。


   
   
( gdb ) break sometimes_crashes
Breakpoint 1 at 0x401132: file prog.c, line 5 .
( gdb ) commands 1
Type commands for breakpoint ( s ) 1 , one per line.
End with a line saying just "end" .
> silent
> if ! f
  > frame
  > printf "Skipping call \n "
  > return 0
  > continue
  > end
> printf "Continuing \n "
> continue
> end
( gdb ) run
Starting program: / home / twaugh / Documents / GDB / prog
warning: Loadable section ".note.gnu.property" outside of ELF segments
Continuing
Continuing
Continuing
#0  sometimes_crashes (f=0x0) at prog.c:5
5      fprintf ( stderr,
Skipping call
[ Inferior 1 ( process 9373 ) exited normally ]
( gdb )

转储二进制内存

GNU Debugger内置支持使用x命令以各种格式检查内存,包括八进制,十六进制等。 但是我喜欢并排看到两种格式:左侧为十六进制字节,右侧为相同字节表示的ASCII字符。

当我想逐字节查看文件的内容时,经常使用hexdump -C (hexdump来自util-linux软件包)。 这是gdb的x命令,显示十六进制字节:


   
   
( gdb ) x / 33xb mydata
0x404040 < mydata > :    0x02    0x01    0x00    0x02    0x00    0x00    0x00    0x01
0x404048 < mydata+ 8 > :    0x01    0x47    0x00    0x12    0x61    0x74    0x74    0x72
0x404050 < mydata+ 16 > :    0x69    0x62    0x75    0x74    0x65    0x73    0x2d    0x63
0x404058 < mydata+ 24 > :    0x68    0x61    0x72    0x73    0x65    0x75    0x00    0x05
0x404060 < mydata+ 32 > :    0x00

如果您可以教gdb像hexdump一样显示内存怎么办? 您可以并且实际上可以将这种方法用于您喜欢的任何格式。

通过结合使用dump命令将字节存储在文件中,将shell命令在文件上运行hexdump以及define命令结合在一起,我们可以使自己的新hexdump命令使用hexdump显示内存内容。


   
   
( gdb ) define hexdump
Type commands for definition of "hexdump" .
End with a line saying just "end" .
> dump binary memory / tmp / dump.bin $arg0 $arg0 + $arg1
> shell hexdump -C / tmp / dump.bin
> end

这些命令甚至可以放在〜/ .gdbinit文件中,以永久定义hexdump命令。 它在起作用:


   
   
( gdb ) hexdump mydata sizeof ( mydata )
00000000  02 01 00 02 00 00 00 01  01 47 00 12 61 74 74 72   | .........G..attr |
00000010   69 62 75 74 65 73 2d 63   68 61 72 73 65 75 00 05   | ibutes-charseu.. |
00000020  00                                                 | . |
00000021

内联拆卸

有时您想更多地了解导致崩溃的原因,而源代码还不够。 您想查看在CPU指令级别发生了什么。

使用disassemble命令可以查看实现功能的CPU指令。 但是有时输出可能很难遵循。 通常,我想查看哪些指令对应于函数中源代码的特定部分。 为此,请使用/ s修饰符在反汇编中包含源代码行。


   
   
( gdb ) disassemble / s main
Dump of assembler code for function main:
prog.c:
11     {
   0x0000000000401158 < + 0 > :    push   % rbp
   0x0000000000401159 < + 1 > :    mov      % rsp, % rbp
   0x000000000040115c < + 4 > :    sub      $0 x10, % rsp

12      int n = 0 ;
   0x0000000000401160 < + 8 > :    movl   $0 x0,-0x4 ( % rbp )

13      sometimes_crashes ( & n ) ;
   0x0000000000401167 < + 15 > :    lea     -0x4 ( % rbp ) , % rax
   0x000000000040116b < + 19 > :    mov     % rax, % rdi
   0x000000000040116e < + 22 > :    callq  0x401126 < sometimes_crashes >
[ ...snipped... ]

这与信息寄存器一起查看所有CPU寄存器的当前值以及命令stepi一次执行一条指令之类的命令,可以使您对程序有更详细的了解。

反向调试

有时,您希望自己可以倒转时间。 想象一下,您已经达到了变量的监视点。 监视点就像一个断点,但不是在程序中的某个位置设置,而是在表达式上设置(使用watch命令)。 每当表达式的值更改时,执行就会停止,调试器将接管控制权。

想象一下您已经达到了这个观察点,并且变量使用的内存已更改值。 事实证明,这可能是由更早发生的事情引起的。 例如,内存已释放,现在正在重新使用。 但是何时,为什么释放了它?

GNU调试器甚至可以解决此问题,因为您可以反向运行程序!

它通过在每个步骤中仔细记录程序的状态来实现此目的,以便可以恢复以前记录的状态,从而产生时间倒流的错觉。

要启用此状态记录,请使用target record-full命令。 然后,您可以使用听起来不太可行的命令,例如:

  • 反向步骤 ,倒退到上一个源代码行
  • reverse-next ,它倒退到上一个源代码行,向后跳过函数调用
  • 反向完成 ,返回到当前函数即将​​被调用的位置
  • 反向继续 ,返回到程序中的先前状态,该状态将(现在)触发断点(或其他导致断点停止的状态)

这是一个实际的反向调试示例:


   
   
( gdb ) b main
Breakpoint 1 at 0x401160: file prog.c, line 12 .
( gdb ) r
Starting program: / home / twaugh / Documents / GDB / prog
[ ... ]

Breakpoint 1 , main ( ) at prog.c: 12
12      int n = 0 ;
( gdb ) target record-full
( gdb ) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x0000000000401154 in sometimes_crashes ( f =0x0 ) at prog.c: 7
7       return * f;
( gdb ) reverse-finish
Run back to call of #0  0x0000000000401154 in sometimes_crashes (f=0x0)
        at prog.c: 7
0x0000000000401190 in main ( ) at prog.c: 16
16      sometimes_crashes ( 0 ) ;

这些只是GNU调试器可以做的一些有用的事情。 还有更多发现。 您最喜欢gdb的哪个隐藏,鲜为人知或仅是惊人的惊人功能? 请在评论中分享。

翻译自: https://opensource.com/article/19/9/tips-gnu-debugger

gnu2和gnu3区别

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值