我们最喜欢的Windbg技巧/窍门是什么?

时间:2020-03-06 14:39:59  来源:igfitidea点击:

我已经意识到,Windbg是Windows平台上非常强大的调试器,而且我偶尔也会学到一些新知识。 Windbg的其他用户可以分享一些疯狂的技能吗?

ps:我不是在寻找漂亮的命令,可以在文档中找到这些命令。如何分享使用windbg可以做的事情的提示?例如在windbg下运行进程时,生成有关内存分配的统计信息的某种方法。

解决方案

我最喜欢的是命令.cmdtree <file>(未记录,但在以前的发行说明中已引用)。这可以帮助调出另一个窗口(可以停靠)以显示有用的或者常用的命令。这可以帮助用户提高使用该工具的效率。

最初是在这里用参数<file>的示例来讨论的:
http://blogs.msdn.com/debuggingtoolbox/archive/2008/09/17/special-command-execute-commands-from-a-customized-user-interface-with-cmdtree.aspx

例子:
替代文字http://blogs.msdn.com/photos/debuggingtoolbox/images/8954736/original.aspx

当在堆栈上查找带有vtable的C ++对象时,以下命令非常方便,尤其是当使用发行版构建时,许多事情已被优化时。

dpp esp Range

能够以转储的方式加载任意PE文件非常简单:

windbg -z mylib.dll

使用以下命令查询GetLastError():

!gle

这有助于解码常见的错误代码:

!error error_number

我每天都会使用近60%的命令。

dv /i /t
?? this
kM (kinda undocumented) generates links to frames
.frame x
!analyze -v
!lmi
~

不要使用WinDbg的.heap -stat命令。有时会给我们不正确的输出。而是,使用DebugDiags内存报告。

有了正确的数字,我们就可以使用WinDbg的.heap -flt ...命令了。

我喜欢使用高级断点命令,例如使用断点创建新的一次性断点。

我最常使用的"技巧"是一种可以避免我们如此频繁地触摸讨厌的鼠标的技巧:" Alt" +" 1"

Alt + 1将焦点放在命令窗口中,以便我们可以实际键入命令,并且向上箭头可在命令历史记录中滚动。但是,如果焦点已经在可滚动的命令历史记录中,则此方法将不起作用。

Peeve:为什么焦点在源代码窗口中时,为什么忽略了按键?并不是说我们可以从WinDbg内部编辑源代码。救援时按Alt + 1.

调查崩溃转储中的内存泄漏(因为到目前为止,我更喜欢UMDH作为活动进程)。
策略是相同类型的对象都分配有相同的大小。

  • 向WinDbg的命令行版本cdb.exe提供"!heap -h 0"命令(以提高速度)以获取所有堆分配:
"C:\Program Files\Debugging Tools for Windows\cdb.exe" -c "!heap -h 0;q" -z [DumpPath] > DumpHeapEntries.log
  • 使用Cygwin来grep分配列表,并按大小对它们进行分组:
grep "busy ([[:alnum:]]\+)" DumpHeapEntries.log \
| gawk '{ str = ; gsub(/\(|\)/, "", str); print "0x" str " 0x"  }' \
| sort \
| uniq -c \
| gawk '{ printf "%10.2f %10d %10d ( %s = %d )\n", *strtonum()/1024, , strtonum(), , strtonum() }' \
| sort > DumpHeapEntriesStats.log
  • 例如,我们得到一个表,该表告诉我们0x24字节的25529270分配占用了近1.2 GB的内存。
8489.52        707      12296 ( 0x3000 = 12288 )
  11894.28       5924       2056 ( 0x800 = 2048 )
  13222.66     846250         16 ( 0x2 = 2 )
  14120.41     602471         24 ( 0x2 = 2 )
  31539.30    2018515         16 ( 0x1 = 1 )
  38902.01    1659819         24 ( 0x1 = 1 )
  40856.38        817      51208 ( 0xc800 = 51200 )
1196684.53   25529270         48 ( 0x24 = 36 )
  • 然后,如果对象具有vtable,只需使用dps命令在DumpHeapEntries.log中查找一些0x24字节的堆分配,以了解占用所有内存的对象的类型。
0:075> dps 3be7f7e8
3be7f7e8  00020006
3be7f7ec  090c01e7
3be7f7f0  0b40fe94 SomeDll!SomeType::`vftable'
3be7f7f4  00000000
3be7f7f8  00000000

这很俗气,但有效:)

一个字(好吧,三个):DML,即调试器标记语言。

这是WinDbg的新增功能,并且没有记录在帮助文件中。但是,Windows调试工具的安装目录中的" dml.doc"中有一些文档。

基本上,这是一种类似于HTML的语法,我们可以将其添加到调试器脚本中以进行格式设置,更重要的是可以进行链接。我们可以使用链接来调用其他脚本,甚至是相同的脚本。

我的日常工作涉及对元建模器的维护,该元建模器为大型C ++软件提供了通用对象和对象之间的关系。首先,为了简化调试,我编写了一个简单的转储脚本,该脚本从这些对象中提取相关信息。

现在,使用DML,我已经能够向输出添加链接,从而允许在相关对象上再次调用相同的脚本。这样可以更快地探索模型。

这是一个简化的示例。假设内省的对象与另一个对象之间有一个称为"引用"的关系。
r @ $ t0 = $ arg1 $$ arg1是要检查的对象的地址

$$ dump some information from $t0

$$ allow the user to examine our reference
aS /x myref @@(&((<C++ type of the reference>*)@$t0)->reference )
.block { .printf /D "<link cmd=\"$$>a< <full path to this script> ${myref}\">dump Ref</link> " }

显然,这是一个很好的例子,但这对我来说确实是无价之宝。无需在非常复杂的对象中寻找合适的数据成员(通常要花一分钟,以及进行各种强制转换和取消引用技巧),所有操作都可以一键自动完成!

另一个答案提到了命令窗口和" Alt" +" 1"以专注于命令输入窗口。有没有人发现不使用鼠标就很难滚动命令输出窗口?

好吧,我最近使用AutoHotkey通过键盘滚动了命令输出窗口,而没有离开命令输入窗口。

; WM_VSCROLL = 0x115 (277)
ScrollUp(control="")
{
    SendMessage, 277, 0, 0, %control%, A
}

ScrollDown(control="")
{
    SendMessage, 277, 1, 0, %control%, A
}

ScrollPageUp(control="")
{
    SendMessage, 277, 2, 0, %control%, A
}

ScrollPageDown(control="")
{
    SendMessage, 277, 3, 0, %control%, A
}

ScrollToTop(control="")
{
    SendMessage, 277, 6, 0, %control%, A
}

ScrollToBottom(control="")
{   
    SendMessage, 277, 7, 0, %control%, A
}

#IfWinActive, ahk_class WinDbgFrameClass
    ; For WinDbg, when the child window is attached to the main window
    !UP::ScrollUp("RichEdit50W1")
    ^k::ScrollUp("RichEdit50W1")
    !DOWN::ScrollDown("RichEdit50W1")
    ^j::ScrollDown("RichEdit50W1")
    !PGDN::ScrollPageDown("RichEdit50W1")
    !PGUP::ScrollPageUp("RichEdit50W1")
    !HOME::ScrollToTop("RichEdit50W1")
    !END::ScrollToBottom("RichEdit50W1")
#IfWinActive, ahk_class WinBaseClass
    ; Also for WinDbg, when the child window is a separate window
    !UP::ScrollUp("RichEdit50W1")
    !DOWN::ScrollDown("RichEdit50W1")
    !PGDN::ScrollPageDown("RichEdit50W1")
    !PGUP::ScrollPageUp("RichEdit50W1")
    !HOME::ScrollToTop("RichEdit50W1")
    !END::ScrollToBottom("RichEdit50W1")

运行此脚本后,我们可以使用" Alt" +"向上" /"向下"滚动命令输出窗口的一行,使用" Alt" +" PgDn` / PgUp"滚动一个屏幕。

注意:看来不同版本的WinDbg的窗口和控件具有不同的类名,因此我们可能想使用AutoHotkey提供的窗口间谍工具首先查找实际的类名。

  • .prefer_dml 1这会修改许多内置命令(例如lm)以显示DML输出,该输出允许我们单击链接而不是运行命令。很方便...
  • .reload / f / o file.dll(/ o将覆盖我们拥有的符号的当前副本)
  • .enable_unicode 1//由于所有Windows组件内部都使用Unicode,因此将调试器的字符串默认设置为Unicode,这非常方便。
  • .ignore_missing_pages 1//如果进行了大量的内核转储分析,我们将看到很多关于内存被调出的错误。该命令将告诉调试器停止抛出此警告。

别名别名...

在调试器中节省一些时间。这是我的一些:

aS !p !process;
aS !t !thread;
aS .f .frame;
aS .p .process /p /r
aS .t .thread /p /r
aS dv dv /V /i /t //make dv do your favorite options by default
aS f !process 0 0 //f for find, e.g. f explorer.exe

对于使用调试器的命令和直接(静态或者自动)例程,能够将所有调试器命令都运行在文本命令文件中并通过kd.exe或者cdb.exe作为输入来运行是非常酷的。 ,可通过批处理脚本调用等。

在需要执行相同的旧例程时运行该脚本,而不必启动WinDbg并手动执行操作。不幸的是,如果我们不确定要查找的内容,或者某些命令参数需要手动分析才能查找/获取,则此方法将无效。

基于.NET Framework版本(v2.0 / v4.0)加载SOS的脚本:

!for_each_module .if(($sicmp( "@#ModuleName" , "mscorwks") = 0) ) 
{.loadby sos mscorwks} .elsif ($sicmp( "@#ModuleName" , "clr") = 0) 
{.loadby sos clr}