概述
使用高级断点技巧
本节演示了使用断点的一些高级技巧:
使用断点计数
使用受限制的断点
拾取有用的断点计数
监视点
使用断点条件
使用弹出进行微重播
使用修复并继续
本节和示例程序受到了在 dbx 中发现的一个实际错误的启发,该错误的发现顺序与本节中所述的顺序相同。
注 -
要获取本节中显示的正确输出,示例程序必须仍“包含错误”。如果修复了错误,请从程序示例重新下载 OracleDeveloperStudio12.5-Samples 目录。
源代码包含一个名为 in 的样例输入文件,该文件会在示例程序中触发一个错误。in 包含以下代码:
display nonexistent_var# should yield an error
display var
stop in X# will cause one "stopped" message and display
stop in Y# will cause second "stopped" message and display
run
cont
cont
run
cont
cont
使用该输入文件运行程序时,输出如下所示:
$ a.out < in
> display nonexistent_var
error: Don't know about 'nonexistent_var'
> display var
will display 'var'
> stop in X
> stop in Y
> run
running ...
stopped in X
var = {
a = '100'
b = '101
c = ''
d = '102
e = '103'
f = '104'
}
> cont
stopped in Y
var = {
a = '105'
b = '106'
c = ''
d = '107'
e = '108'
f = '109'
}
> cont
exited
> run
running ...
stopped in X
var = {
a = '110'
b = '111'
c = ''
d = '112'
e = '113'
f = '114'
}
> cont
stopped in Y
var = {
a = '115'
b = '116'
c = ''
d = '117'
e = '118'
f = '119'
}
> cont
exited
> quit
Goodby
此输出可能看起来很长,但此示例的重点是展示长时间运行的复杂程序所使用的技术,在这些程序中,单步执行代码或仅仅进行跟踪是不切实际的。
请注意,显示字段 c 的值时,您将获取 的值。如果该字段包含错误地址,可能会发生这样的情形。
问题
请注意,当您第二次运行程序时,您收到了在第一次运行时没有收到的其他错误消息:
error: cannot get value of 'var.c'
error() 函数使用变量 err_silent 使错误消息在某些情况下不再出现。例如,对于 display 命令,不会显示错误消息,而会将问题显示为 c = ''。
步骤 1:反复性
第一步是设置一个调试目标并配置该目标,以便可通过单击 "Restart"(重新启动)
轻松地重复错误。
按如下方式开始调试程序:
如果您尚未编译示例程序,请按照 程序示例 中的说明进行操作。
选择 "Debug"(调试)> "Debug Executable"(调试可执行文件)。
在 "Debug Executable"(调试可执行文件)对话框中,浏览可执行文件或键入可执行文件的路径。
在 "Arguments"(参数)字段中,键入:
< in
可执行文件路径的目录部分显示在 "Working Directory"(工作目录)字段中。
单击 "Debug"(调试)。
在真实情形中,您可能还希望填充 "Environment"(环境)字段。
调试程序时,dbxtool 会创建一个调试目标。您可以通过选择 "Debug"(调试)> "Debug Recent"(调试近来的)并选择所需的可执行文件来使用同一调试配置。
您可以从 dbx 命令行设置其中的多个属性。这些属性将存储在调试目标配置中。
以下技巧有助于轻松保持可重复性。添加断点时,可以通过单击 "Restart"(重新启动)快速转至某个感兴趣的位置,而不必在出现各种中间断点时单击 "Continue"(继续)。
步骤 2:第一个断点
在 error() 函数输出一条错误消息时,在该函数内放置第一个断点。此断点将成为第 33 行上的一个行断点。
在较大的程序中,您可以通过键入以下内容(例如,在 "Debugger Console"(调试器控制台)窗口中)来轻松更改编辑器窗口中的当前函数:
(dbx) func error
淡紫色条表示 func 命令所找到的匹配项。
通过在编辑器窗口左边界中数字 33 的上面单击,创建行断点。
单击 "Restart"(重新启动)
以运行该程序,命中断点时,堆栈跟踪会显示由于 in 文件中的模拟命令而生成的错误消息:
> display var# should yield an error
调用 error() 是预期行为。
单击 "Continue"(继续)
继续执行该进程并再次命中该断点。
此时将显示一条意外的错误消息。
步骤 3:断点计数
最好是在每次运行时都能反复到达此位置,而不必在第一次命中断点之后由于以下命令而单击 "Continue"(继续):
> display var # should yield an error
您可以编辑程序或输入脚本,然后消除第一个麻烦的 display 命令。但是,您正在处理的特定输入顺序可能是重现此错误的关键,因此您不需要更改输入。
由于您关注的是第二次到达此断点,因此,可将其计数设置为 2。
在 "Breakpoints"(断点)窗口中,右键单击该断点,然后选择 "Customize"(定制)。
在 "Customize Breakpoint"(定制断点)对话框中,在 "Count Limit"(计数限制)字段中键入 2。
单击 "OK"(确定)。
现在,您就可以反复到达您关注的位置了。
在本例中,是否选择计数 2 无关紧要。但是,有时会对某个感兴趣的位置进行多次调用。请参阅步骤 7:确定计数值以轻松选择合适的计数值。但现在,您可以先了解一下如何通过另一种方式仅在您关注的调用中在 error() 中停止。
步骤 4:受限制的断点
对于 error() 内部的断点,打开 "Customize Breakpoint"(定制断点)对话框,然后通过从 "Count Limit"(计数限制)的下拉式列表中选择 "Always stop"(总是停止)禁用断点计数。
重新运行程序。
注意两次在 error() 中停止的堆栈跟踪。第一次,在 error() 中的停止与以下屏幕相似:
第二次,在 error() 中的停止与以下屏幕相似:
要安排为在从 runProgram(第 [7] 帧)调用时在此断点处停止,请再次打开 "Customize Breakpoint"(定制断点)对话框并将 "While In"(满足条件)字段设置为 runProgram。
步骤 5:查找原因
由于 err_silent 不 > 0,因此发出了不需要的错误消息。通过气球表达式求值看一下 err_silent 的值。
将光标放在第 31 行中 err_silent 的上方,然后等待其值显示出来。
跟随堆栈看一下设置 err_silent 的位置。
单击 "Make Caller Current"(使调用方成为当前调用方)
两次以到达 evaluateField(),该函数已调用 evaluateFieldPrepare(),模拟一个可能正在处理 err_silent 的复杂函数。
再次单击 "Make Caller Current"(使调用方成为当前调用方)以到达 printField(),此处 err_silent 正在递增。printField() 也已调用 printFieldPrepare(),printFieldPrepare 也模拟一个可能正在处理 err_silent 的复杂函数。
请注意 err_silent++ 和 err_silent-- 如何将某些代码包围起来。
err_silent 可能在 printFieldPrepare() 或 evaluateFieldPrepare() 中出错,或者当控制到达 printField() 时 err_silent 已经出错。
步骤 6:更多断点计数
要查明 err_silent 是在对 printField() 的调用之前还是之后出错,请在 printField() 中放置一个断点。
选择 printField(),右键单击,然后选择 "New Breakpoint"(新建断点)。
新的断点类型已预先选择,并且 "Function"(函数)字段已使用 printfield 预先填充。
单击 "OK"(确定)。
单击 "Restart"(重新启动)
。
第一次命中断点的时间是第一次运行期间第一次停止在第一个字段 var.a 上时。err_silent 为 0 是可以接受的。
单击 "Continue"(继续)。
err_silent 仍可以接受。
再次单击 "Continue"(继续)。
err_silent 仍可以接受。
到达对 printField() 的特定调用(该调用导致了不需要的错误消息)可能需要一段时间。您需要在 printField 断点上使用断点计数。可是应该将计数设置成什么呢?在该简单示例中,您可以尝试对运行和停止以及显示的字段进行计数,但实际上该过程可能会更加困难。有一种方法可以半自动地确定该计数。
步骤 7:确定计数值
打开 "Customize Breakpoint"(定制断点)对话框,找到 printField() 上的断点,然后将 "Count Limit"(计数限制)字段设置为无限大。
此设置意味着您将永远不会在此断点处停止。但是,仍将进行计数。
将 "Breakpoints"(断点)窗口设置为显示更多属性(如计数)。
单击 "Breakpoints"(断点)窗口右上角的 "Change Visible Columns"(更改可视列)按钮
。
选择 "Count Limit"(计数限制)、"Count"(计数)和 "While In"(满足条件)。
单击 "OK"(确定)。
再次运行程序。您将命中 error() 内部的断点,也就是受 runProgram() 限制的断点。
现在来看一下 printField() 上断点的计数。
计数为 15。
再次在 "Customize Breakpoint"(定制断点)窗口中单击 "Count Limit"(计数限制)列中的下拉式列表,选择 "Use current Count value"(使用当前计数值)将当前计数传送给计数限制,然后单击 "OK"(确定)。
现在如果运行程序,您将在最后一次调用 printField() 时在该函数中停止,然后显示意外的错误消息。
步骤 8:确定具体原因
再次使用气球表达式求值检查 err_silent。现在的值为 -1。最有可能的原因是,在您到达 printField() 之前,一个 err_silent-- 执行得太多,或者一个 err_silent++ 执行得太少。
您可以通过仔细检查代码,在与该示例类似的小程序中找到这个不匹配的 err_silent 对。但是,大型程序可能包含大量的以下配对:
err_silent++;
err_silent--;
更为快捷地找到不匹配的 err_silent 对的方法是使用监视点。
错误的原因可能根本不是不匹配的 err_silent++; 和 err_silent--; 对,而是一个覆盖了 err_silent 内容的异常指针。在捕获此类问题时,监视点会比较有效。
步骤 9:使用监视点
在 err_silent 上创建监视点:
选择 err_silent 变量,右键单击,然后选择 "New Breakpoint"(新建断点)。
将 "Breakpoint Type"(断点类型)设置为 "Access"(访问)。
请注意 "Settings"(设置)部分如何变化以及 "Address"(地址)字段是如何成为 & err_silent 的。
在 "When"(时间)字段中选择 "After"(之后)。
在 "Operation"(操作)字段中选择 "Write"(写入)。
单击 "OK"(确定)。
运行程序。
您在 init() 处停止。err_silent 递增到了 1,之后就停止了执行。
单击 "Continue"(继续)。
您再次在 init() 中停止。
再次单击 "Continue"(继续)。
您再次在 init() 中停止。
再次单击 "Continue"(继续)。
您再次在 init() 中停止。
再次单击 "Continue"(继续)。
现在您将在 stopIn() 中停止。此时看起来也是一切正常,没有出现 -1。
可以设置断点条件,而不是反复地单击 "Continue"(继续),直到将 err_silent 设置为 -1。
步骤 10:断点条件
为您的监视点添加一个条件:
在 "Breakpoints"(断点)窗口中,右键单击 "After"(之后)写入断点,然后选择 "Customize"(定制)。
验证是否在 "When"(时间)字段中选择了 "After"(之后)。
通过选择 "After"(之后),您可以查看更改后的 err_silent 的值。
将 "Condition"(条件)字段设置为 err_silent == -1。
单击 "OK"(确定)。
再次运行程序。
您在 checkThings() 处停止,这是第一次将 err_silent 设置为 -1。在您查找匹配的 err_silent++ 时,您会看清错误:err_silent 仅在该函数的 else 部分中递增。
这是您所要寻找的错误吗?
步骤 11:通过弹出堆栈来验证诊断
有一种方法可以核实您是否确实检查完函数的 else 块,那就是在 checkThings() 上设置一个断点并运行程序。但 checkThings() 可能会被多次调用。您可以使用断点计数或受限制的断点来实现对 checkThings() 的正确调用,但重播最近所执行内容的更快方法是弹出堆栈。
选择 "Debug"(调试)> "Stack"(堆栈)> "Pop Topmost Call"(弹出最顶层调用)。
请注意 "Pop Topmost Call"(弹出最顶层调用)不会撤消任何内容。尤其是,err_silent 的值已出错,因为您正从数据调试切换到控制流调试。
进程状态恢复到包含对 checkThings() 的调用的行的开始处。
单击 "Step Into"(步入)
并在再次调用 checkThings() 时进行观察。
在单步执行 checkThings() 时,您可以验证该进程是否执行了 if 块(此处 err_silent 没有递增,并且接着会递减至 -1)。
尽管您似乎已找到编程错误,但您可能需要反复对其进行检查。
步骤 12:使用修复进一步验证诊断
请修复代码并验证错误确实已经消除。
通过将 err_silent++ 置于 if 语句的上方来修复代码。
选择 "Debug"(调试)> "Apply Code Changes"(应用代码更改),或者按 "Apply Code Changes"(应用代码更改)按钮
。
禁用 printField 断点和监视点,但保留 error() 中断点的启用状态。
再次运行程序。
请注意,程序已完成但没有命中 error() 中的断点,其输出符合预期。
讨论
该示例说明了与使用断点和步进结尾处所讨论的模式相同的模式,即,用户在出错之前的某个点停止行为异常的程序,然后单步执行代码,将代码的本意与代码实际的行为相比较。主要差异在于,查找出错之前的点的过程要复杂一些。
最后
以上就是鲤鱼白羊为你收集整理的oracle异常错误断点,使用高级断点技巧 - Oracle® Developer Studio 12.5: dbxtool 教程的全部内容,希望文章能够帮你解决oracle异常错误断点,使用高级断点技巧 - Oracle® Developer Studio 12.5: dbxtool 教程所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复