Oracle® Developer Studio 12.5: dbxtool 教程

退出打印视图

更新时间: 2016 年 6 月
 
 

使用断点和步进

使用断点可以将程序停止在错误位置之前,并单步执行代码以查找何处出错。

如果您尚未取消停靠 "Output"(输出)窗口,请执行该操作。

您早先是从命令行运行的程序。通过在 dbxtool 中运行程序来重现错误。

  1. 在工具栏中单击 "Restart"(重新启动)按钮 image: 或在 "Debugger Console"(调试器控制台)窗口中键入 run

  2. 在 "Debugger Console"(调试器控制台)窗口中按回车键。

    警报框会提供有关 SEGV 的信息。

    image:显示 SEGV 的 “Signal Caught“(捕获的信号)警报窗口
  3. 在警报框中,单击 "Discard and Pause"(放弃并暂停)。

    "Editor"(编辑器)窗口会再次在 Interp::find() 中突出显示对 strcmp() 的调用。

  4. 在工具栏中单击 "Make Caller Current"(使调用方成为当前调用方)按钮 image: 以转至您先前在 Interp::dispatch() 中看到的未知代码。

  5. 在下一节,您将在调用 find() 之前一个位的位置设置断点,这样您就可以单步执行代码以了解为何出错。

设置断点

可以通过多种方式设置断点,如行断点或函数断点。以下列表介绍了创建断点的多种方式。


注 -  如果未显示行号,请通过在左边界中右键单击并选择 "Show Line Numbers"(显示行号)选项,在编辑器中启用行号。
  • 设置行断点

    通过在第 127 行旁边的左边界中单击,开启/关闭行断点。

    image:在第 127 行上包含行断点的编辑器窗口
  • 设置函数断点

    设置函数断点

    1. 在编辑器窗口中选择 Interp::dispatch

    2. 选择 "Debug"(调试)> "New Breakpoint"(新建断点)或右键单击并选择 "New Breakpoint"(新建断点)。

      将显示 "New Breakpoint"(新建断点)对话框。

      image:“New Breakpoint“(新建断点)对话框

      请注意,"Function"(函数)字段随选定的函数名称进行填充。

    3. 单击 "OK"(确定)。

  • 从命令行设置断点

    设置函数断点最容易的方法是从 dbx 命令行进行设置。在 "Debugger Console"(调试器控制台)窗口中键入 stop in 命令:

    (dbx) stop in dispatch                 
    (4) stop in Interp::dispatch(char*) 
    (dbx) 

    请注意,不必键入 Interp::dispatch。只需键入函数名称即可。

断点窗口和编辑器的外观可能如下所示:

image:杂乱的编辑器窗口

要避免编辑器中变得杂乱,请使用 "Breakpoints"(断点)窗口。

  1. 单击 "Breakpoints"(断点)选项卡(如果先前最小化了该选项卡,请先将其最大化)。

  2. 选择行断点和其中一个函数断点,右键单击,然后选择 "Delete"(删除)。

有关断点的更多信息,请参见Oracle Developer Studio 12.5:使用 dbx 调试程序 中的 第 6 章, 设置断点和跟踪

函数断点的优点

通过在编辑器中切换来设置行断点可能比较直观。但是,许多 dbx 用户喜欢使用函数断点是由于以下原因:

  • 在 "Debugger Console"(调试器控制台)窗口中键入 si dispatch 意味着您不必在编辑器中打开文件并滚动到某行设置断点。

  • 由于您可以通过在编辑器中选择任何文本来创建函数断点,因此可以从函数的调用点在此函数上设置断点,而不必打开文件。


    提示  -  sistop in 的别名。大多数 dbx 用户都会定义许多别名,并将这些别名放置在 dbx 配置文件 ~/.dbxrc 中。一些常见的示例有:
    alias si stop in
    alias sa stop at
    alias s step
    alias n next
    alias r run

    有关定制 .dbxrc 文件和 dbxenv 变量的更多信息,请参见Oracle Developer Studio 12.5:使用 dbx 调试程序 中的 设置 dbxenv 变量

  • 在 "Breakpoints"(断点)窗口中,函数断点的名称是描述性的。行断点的名称不是描述性的,但可以通过在 "Breakpoints"(断点)窗口中右键单击行断点并选择 "Go To Source"(转至源)或双击断点来了解第 127 行中的内容。

  • 函数断点更为稳定。由于 dbxtool 会保留断点,因此,如果您编辑了代码或执行了源代码控制合并,行断点可能容易发生偏移。而函数名称则不太容易受到编辑操作的影响。

使用监视和步进

既然您在 Interp::dispatch() 处设置了单个断点,那么如果您再次单击 "Restart"(重新启动)image: 并在 "Debugger Console"(调试器控制台)窗口中按回车键,程序会在 dispatch() 函数包含可执行代码的第一行停止。

image:执行在第 122 行停止的编辑器窗口

    由于您已经确定了传递给 find()argv[0] 问题,因此可以对 argv 使用监视:

  1. 在编辑器窗口中选择 argv 的一个实例。

  2. 右键单击并选择 "New Watch"(新建监视)。"New Watch"(新建监视)对话框将打开,并随选定的文本进行填充:

    image:“New Watch“(新建监视)对话框
  3. 单击 "OK"(确定)。

  4. 要打开 "Watches"(监视)窗口,请选择 "Window"(窗口)> "Watches"(监视)(Alt + Shift + 2 组合键)。

  5. 在 "Watches"(监视)窗口中,展开 argv

    image:argv 已展开的 “Watches“(监视)窗口

    请注意,argv 没有初始化,而且由于 argv 是局部变量,因此可能会从先前的调用中“继承”堆栈上留下的随机值。这会不会是问题的原因?


    注 -  可在 "Variables"(变量)窗口以及 "Watches"(监视)窗口中查看监视变量。
  6. 单击 "Step Over"(步过)(F8) image: 两次,直到绿色的 PC 箭头指向 int argc = 0;

  7. 由于 argc 将成为 argv 的索引,因此也为 argc 创建监视。请注意,argc 当前也未初始化,并且可能包含不需要的值。

    由于您在 argv 的监视之后为 argc 创建了监视,因此它会在 "Watches"(监视)窗口中显示在第二的位置。

  8. 要按照字母顺序对监视名称进行排序,请单击 "Name"(名称)列标题对该列进行排序。请注意下图中的排序三角形:

    image:包含已排序的监视的 “Watches“(监视)窗口
  9. 单击 "Step Over"(步过)(F8) image:

    argc 现在显示其初始化值 0 并以粗体显示,以表示该值刚刚更改过。

    image:argc 值以粗体显示的 “Watches“(监视)窗口

    应用程序将调用 strtok()

  10. 单击 "Step Over"(步过)步过该函数,并观察 token 是否为 NULL(例如,通过使用气球表达式求值)。

    strtok() 函数有助于拆分字符串,例如拆分为由 DELIMITERS 之一分隔的标记。有关更多信息,请参见 strtok(3) 手册页。

  11. 再次单击 "Step Over"(步过)将标记分配给 argv。循环中将存在对 strtok() 的调用。

    在步过时,您不会进入循环(不再有标记),系统会分配一个 NULL。

  12. 也步过该分配,以达到调用的阈值,从而查明样例程序发生崩溃的位置。

  13. 为了仔细检查程序是否在此处崩溃,步过对 find() 的调用。

    "Signal Caught"(捕获的信号)警报框再次显示。

    image:“Signal Caught“(捕获的信号)警报框
  14. 像以前一样单击 "Discard and Pause"(放弃并暂停)。

    Interp::dispatch() 中停止之后第一次对 find() 的调用实际上就是出错的位置。

    您可以快速返回到初始调用 find() 的位置。

    1. 单击 "Make Caller Current"(使调用方成为当前调用方)image:

    2. find() 的调用点处开启/关闭行断点。

    3. 打开 "Breakpoints"(断点)窗口并禁用 Interp::dispatch() 函数断点。

      dbxtool 的外观应如下所示:

      image:显示两个断点中的一个断点已禁用的编辑器窗口
    4. 向下的箭头表示在第 141 行上设置了两个断点,其中一个断点已禁用。

  15. 单击 "Restart"(重新启动)image: 并在 "Debugger Console"(调试器控制台)窗口中按回车键。

    程序在对 find() 的调用前返回。请注意,按 "Restart"(重新启动)按钮会导致重新启动。进行调试时,重新启动的频率要比刚开始时大很多。


    提示  -  如果您重新生成程序(例如,在搜索和修复错误之后),不需要退出 dbxtool 并重新启动。单击 "Restart"(重新启动)按钮时,dbx 会检测到程序(或其任何成员)已重新编译,因此会将其重新加载。 因此,请考虑将 dbxtool 放在桌面上(可以最小化),以便随时用于调试问题。
  16. 错误在何处?请再次查看监视:

    image:“Watches“(监视)窗口

    请注意,argv[0] 为 NULL,因为对 strtok() 的第一次调用返回 NULL(由于该行为空,没有标记)。

    如果您愿意,可以先修复此错误,然后再继续本教程的剩余部分。

    如果您要在调试器下运行该程序,则可以在调试器中修补代码(如使用断点脚本修补代码中所述)。

该示例代码的开发者很可能已测试此条件并绕过 Interp::dispatch() 的剩余部分。

讨论

该示例说明了最常见的调试模式,在该模式下,用户会在出错之前的某个点停止行为异常的程序,然后单步执行代码,将代码的本意与代码的实际行为相比较。

下一节介绍一些使用断点的高级技巧以避免在本示例中使用的某些单步执行和监视。