加入收藏 | 设为首页 | 会员中心 | 我要投稿 应用网_扬州站长网 (https://www.0514zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > Asp教程 > 正文

手把手教你使用 GNU 调试器 | Linux 中国

发布时间:2022-11-25 14:48:17 所属栏目:Asp教程 来源:
导读:  GNU 调试器是一个发现程序缺陷的强大工具。

  如果你是一个程序员,想在你的软件增加某些功能,你首先考虑实现它的方法:例如写一个方法、定义一个类,或者创建新的数据类型。然后你用编译器或解释器可以理
  GNU 调试器是一个发现程序缺陷的强大工具。
 
  如果你是一个程序员,想在你的软件增加某些功能,你首先考虑实现它的方法:例如写一个方法、定义一个类,或者创建新的数据类型。然后你用编译器或解释器可以理解的编程语言来实现这个功能。但是,如果你觉得你所有代码都正确,但是编译器或解释器依然无法理解你的指令怎么办?如果软件大多数情况下都运行良好,但是在某些环境下出现缺陷怎么办?这种情况下,你得知道如何正确使用调试器找到问题的根源。
 
  GNU 调试器(GNU Project Debugger)(GDB)是一个发现项目缺陷的强大工具。它通过追踪程序运行过程中发生了什么来帮助你发现程序错误或崩溃的原因。(LCTT 校注:GDB 全程是“GNU Project Debugger”,即 “GNU 项目调试器”,但是通常我们简称为“GNU 调试器”)
 
  本文是 GDB 基本用法的实践教程。请跟随示例,打开命令行并克隆此仓库:
 
  git clone https://github.com/hANSIc99/core_dump_example.git
  快捷方式
 
  GDB 的每条命令都可以缩短。例如:显示设定的断点的 info break 命令可以被缩短为 i break。你可能在其他地方看到过这种缩写,但在本文中,为了清晰展现使用的函数,我将所写出整个命令。
 
  命令行参数
 
  你可以将 GDB 附加到每个可执行文件。进入你克隆的仓库(core_dump_example),运行 make 进行编译。你现在能看到一个名为 coredump 的可执行文件。(更多信息,请参考我的文章《创建和调试 Linux 的转储文件》。)
 
  要将 GDB 附加到这个可执行文件,请输入: gdb coredump。
 
  你的输出应如下所示:
 
  gdb coredump output
 
  返回结果显示没有找到调试符号。
 
  调试信息是目标文件(object file)(可执行文件)的组成部分,调试信息包括数据类型、函数签名、源代码和操作码之间的关系。此时,你有两种选择:
 
  使用调试信息进行编译
 
  为了在二进制文件中包含调试信息,你必须重新编译。打开 Makefile,删除第 9 行的注释标签(#)后重新编译:
 
  CFLAGS =-Wall -Werror -std=c++11 -g
  -g 告诉编译器包含调试信息。运行 make clean,接着运行 make,然后再次调用 GDB。你得到如下输出后就可以调试代码了:
 
  GDB output with symbols
 
  新增的调试信息会增加可执行文件的大小。在这种情况下,执行文件增加了 2.5 倍(从 26,088 字节 增加到 65,480 字节)。
 
  输入 run -c1,使用 -c1 开关启动程序。当程序运行到达 State_4 时将崩溃:
 
  gdb output crash on c1 switch
 
  你可以检索有关程序的其他信息,info source 命令提供了当前文件的信息:
 
  gdb info source output
 
  info shared 命令打印了动态库列表机器在虚拟地址空间的地址,它们在启动时被加载到该地址,以便程序运行:
 
  gdb info shared output
 
  如果你想了解 Linux 中的库处理方式,请参见我的文章 在 Linux 中如何处理动态库和静态库。
 
  调试程序
 
  你可能已经注意到,你可以在 GDB 中使用 run 命令启动程序。run 命令接受命令行参数,就像从控制台启动程序一样。-c1 开关会导致程序在第 4 阶段崩溃。要从头开始运行程序,你不用退出 GDB,只需再次运行 run 命令。如果没有 -c1 开关,程序将陷入死循环,你必须使用 Ctrl+C 来结束死循环。
 
  gdb output stopped by sigint
 
  你也可以一步一步运行程序。在 C/C++ 中,入口是 main 函数。使用 list main 命令打开显示 main 函数的部分源代码:
 
  gdb output list main
 
  main 函数在第 33 行,因此可以输入 break 33 在 33 行添加断点:
 
  gdb output breakpoint added
 
  输入 run 运行程序。正如预期的那样,程序在 main 函数处停止。输入 layout src 并排查看源代码:
 
  gdb output break at main
 
  你现在处于 GDB 的文本用户界面(TUI)模式。可以使用键盘向上和向下箭头键滚动查看源代码。
 
  GDB 高亮显示当前执行行。你可以输入 next(n)命令逐行执行命令。如果你没有指定新的命令,GBD 会执行上一条命令。要逐行运行代码,只需按回车键。
 
  有时,你会发现文本的输出有点显示不正常:
 
  gdb output corrupted
 
  如果发生这种情况,请按 Ctrl+L 重置屏幕。
 
  使用 Ctrl+X+A 可以随时进入和退出 TUI 模式。你可以在手册中找到 其他的键绑定 。
 
  要退出 GDB,只需输入 quit。
 
  设置监察点
 
  这个示例程序的核心是一个在无限循环中运行的状态机。n_state 变量枚举了当前所有状态:
 
  while(true){
          switch(n_state){
          case State_1:
                  std::cout << "State_1 reached" << std::flush;
                  n_state = State_2;
                  break;
          case State_2:
                  std::cout << "State_2 reached" << std::flush;
                  n_state = State_3;
                  break;
          
          (.....)
          
          }
  }
  如果你希望当 n_state 的值为 State_5 时停止程序。为此,请在 main 函数处停止程序并为 n_state 设置监察点:
 
  watch n_state == State_5
  只有当所需的变量在当前上下文中可用时,使用变量名设置监察点才有效。
 
  当你输入 continue 继续运行程序时,你会得到如下输出:

  gdb output stop on watchpoint_1
 
  如果你继续运行程序,当监察点表达式评估为 false 时 GDB 将停止:
 
  gdb output stop on watchpoint_2
 
  你可以为一般的值变化、特定的值、读取或写入时来设置监察点。
 
  更改断点和监察点
 
  输入 info watchpoints 打印先前设置的监察点列表:
 
  gdb output info watchpoints
 
  删除断点和监察点
 
  如你所见,监察点就是数字。要删除特定的监察点,请先输入 delete 后输入监察点的编号。例如,我的监察点编号为 2;要删除此监察点,输入 delete 2。
 
  注意: 如果你使用 delete 而没有指定数字asp调试器,所有 监察点和断点将被删除。
 
  这同样适用于断点。在下面的截屏中,我添加了几个断点,输入 info breakpoint 打印断点列表:
 
  gdb output info breakpoints
 
  要删除单个断点,请先输入 delete 后输入断点的编号。另外一种方式:你可以通过指定断点的行号来删除断点。例如,clear 78 命令将删除第 78 行设置的断点号 7。
 
  禁用或启用断点和监察点
 
  除了删除断点或监察点之外,你可以通过输入 disable,后输入编号禁用断点或监察点。在下文中,断点 3 和 4 被禁用,并在代码窗口中用减号标记:

  disabled breakpoints
 
  也可以通过输入类似 disable 2 - 4 修改某个范围内的断点或监察点。如果要重新激活这些点,请输入 enable,然后输入它们的编号。
 
  条件断点
 
  首先,输入 delete 删除所有断点和监察点。你仍然想使程序停在 main 函数处,如果你不想指定行号,可以通过直接指明该函数来添加断点。输入 break main 从而在 main 函数处添加断点。
 
  输入 run 从头开始运行程序,程序将在 main 函数处停止。
 
  main 函数包括变量 n_state_3_count,当状态机达到状态 3 时,该变量会递增。
 
  基于 n_state_3_count 的值添加一个条件断点,请输入:
 
  break 54 if n_state_3_count == 3

  Set conditional breakpoint
 
  继续运行程序。程序将在第 54 行停止之前运行状态机 3 次。要查看 n_state_3_count 的值,请输入:
 
  print n_state_3_count

  print variable
 
  使断点成为条件断点
 
  你也可以使现有断点成为条件断点。用 clear 54 命令删除最近添加的断点,并通过输入 break 54 命令添加一个简单的断点。你可以输入以下内容使此断点成为条件断点:
 
  condition 3 n_state_3_count == 9
  3 指的是断点编号。
 
  modify breakpoint
 
  在其他源文件中设置断点
 
  如果你的程序由多个源文件组成,你可以在行号前指定文件名来设置断点,例如,break main. cpp:54。
 
  捕捉点
 
  除了断点和监察点之外,你还可以设置捕获点。捕获点适用于执行系统调用、加载共享库或引发异常等事件。
 
  要捕获用于写入 STDOUT 的 write 系统调用,请输入:
 
  catch syscall write

  catch syscall write output
 
  每当程序写入控制台输出时,GDB 将中断执行。
 
  在手册中,你可以找到一整章关于 断点、监察点和捕捉点 的内容。
 
  评估和操作符号
 
  用 print 命令可以打印变量的值。一般语法是 print 。修改变量的值,请输入:
 
  set variable  .
  在下面的截屏中,我将变量 n_state_3_count 的值设为 123。
 
  catch syscall write output
 
  /x 表达式以十六进制打印值;使用 & 运算符,你可以打印虚拟地址空间内的地址。
 
  如果你不确定某个符号的数据类型,可以使用 whatis 来查明。
 
  whatis output
 
  如果你要列出 main 函数范围内可用的所有变量,请输入 info scope main :
 
  info scope main output
 
  DW_OP_fbreg 值是指基于当前子程序的堆栈偏移量。
 
  或者,如果你已经在一个函数中并且想要列出当前堆栈帧上的所有变量,你可以使用 info locals :
 
  info locals output。
 

(编辑:应用网_扬州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!