标签:模式 如何 组成 启用 int 自动化 each 构造 参数
如果您调试了一段时间的崩溃转储,那么您可能遇到了这样的情况:调试器提供的初始转储上下文对应于在处理初始异常时发生的第二个异常,该异常可能更接近您正在调查的问题中的原始基础问题。
这可能很烦人,因为“.ecxr”命令将指向次要故障异常的位置,而不是原始异常上下文本身。然而,在大多数情况下,原始的、主要的异常上下文仍然在堆栈上;人们只需要知道如何找到它。
有两种方法可以解决这个问题:
如果堆栈展开失败,或者您正在处理其中一个转储,其中多个线程同时出现异常,这可能会有点乏味,这通常是由于崩溃转储写入失控。如果调试器能稍微自动化一下这个过程就好了。幸运的是,用一点蛮力的方法来做到这一点其实并不难。具体地说,只是一个普通的老“哑”内存扫描,以查找大多数上下文记录所共有的内容。这并不完全是一种巧妙的方法,但通常比手动在堆栈中查找要快得多,尤其是在涉及多个线程或多个嵌套异常的情况下。虽然可能会有误报,但通常很明显的一点是,涉及到一个活动异常有什么意义。然而,有时,快速而肮脏的暴力类型解决方案最终真的做到了这一点。
但是,为了基于内存搜索查找上下文记录,我们需要一些公共数据点,这些数据点通常对于所有上下文结构都是相同的,并且最好是连续的(为了便于使用“s”命令,调试器的内存搜索支持)。幸运的是,它以上下文结构的段寄存器的形式存在:
0:000> dt ntdll!_CONTEXT
+0x000 ContextFlags : Uint4B
[…]
+0x08c SegGs : Uint4B
+0x090 SegFs : Uint4B
+0x094 SegEs : Uint4B
+0x098 SegDs : Uint4B
[…]
现在,事实证明,对于给定进程中的所有线程,几乎总是具有相同的段选择器值,不包括异常的和非常不寻常的情况,如VDM进程。(x64上的段选择器值也是如此。)四个非零的32位值(实际上,零填充到32位的16位值)足以在不被误报的情况下合理地完成搜索。下面介绍如何使用臭名昭著的WinDbg调试器脚本(也适用于其他启用DbgEng的程序,如kd):
.foreach ( CxrPtr { s -[w1]d 0 l?ffffffff @gs @fs @es @ds } ) { .cxr CxrPtr – 8c }
这是一个有点冗长的命令,所以让我们把它分解成各个组件。首先,我们有一个“.foreach”构造,根据调试器文档,它遵循以下约定:
.foreach [Options] ( Variable { InCommands } ) { OutCommands }
这个命令字符串的最后一部分是output命令,它简单地指示调试器将当前上下文设置为输入命令输出替换宏的值,偏移量为0x8c。(如果调用,0x8c是从结构上下文的开始到SegGs成员的偏移量,这是我们搜索的第一个值;因此“s”命令返回的地址将是SegGs成员的地址。)请记住,我们将“s”命令的输出限制为仅作为地址本身,这样我们就可以轻松地将该地址传递给不同的命令(这可能会让人认为“s”和“.foreach”命令是一起工作的)。
将命令字符串放在一起,它指示调试器在连续内存中搜索由四个32位值(当前线程的gs、fs、es和ds段选择器值)组成的序列,并显示每个匹配项的包含上下文结构。
在执行这个comand时,除了异常相关的上下文记录之外,您还可以找到其他一些上下文记录(特别是,初始线程上下文是常见的),但是与错误相关的上下文记录通常是非常明显和不言而喻的。当然,这个方法并不是万无一失的,但是它让调试器为您做了一些艰苦的工作(这比在多个线程中手动地在损坏的堆栈中卑躬屈膝只为了提取一些上下文记录要好得多)。
当然,“.foreach”和“s”命令还有许多其他用途;不要害怕尝试使用它们。还有其他的助手可以自动执行某些任务(!for_each_frame, !for_each_local, !for_each_module, !for_each_process, 和 !for_each_thread,)除了一般的“.for each”外,每个“线程”都有。调试器脚本支持可能不是看起来最漂亮的,但是它可以非常方便地加速常见的、重复的任务。
一个带有“.foreach”的分隔提示(实际上是两个分隔提示):变量替换宏只有在用空格将其与其他符号分隔时才起作用。但是,在某些情况下,这可能是一个问题(在这种情况下,您需要对生成的展开宏执行某些运算,例如在这种情况下减去0x8c),因为宏符号展开时仍保留空格。有些命令,比如“dt”,不遵循标准的表达式解析规则(这让我非常恼火),如果它们用空格给出了参数,就会窒息。
但是,这些命令不会丢失所有内容;解决此问题的一种方法是将宏替换存储到伪寄存器中(例如,“r@$t0=ReplacementVariableMacro–0x8c”),并在实际输出命令中使用该伪寄存器,因为您可以在“输出命令”部分发出多个分号分隔的命令。
标签:模式 如何 组成 启用 int 自动化 each 构造 参数
原文地址:https://www.cnblogs.com/yilang/p/12155572.html