45

我开始意识到 Windbg 是一个非常强大的 Windows 平台调试器,我偶尔会学到一些关于它的新东西。Windbg 用户可以分享他们的一些疯狂技能吗?

ps:我不是在寻找一个漂亮的命令,这些可以在文档中找到。分享一些无法想象可以用 windbg 完成的事情的技巧怎么样?例如,当进程在windbg 下运行时,某种方式可以生成有关内存分配的统计信息。

4

13 回答 13

30

我最喜欢的是命令.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

于 2008-09-24T15:28:29.573 回答
28

调查崩溃转储中的内存泄漏(因为到目前为止我更喜欢 UMDH 用于实时进程)。该策略是相同类型的对象都以相同的大小分配。

  • !heap -h 0命令提供给 WinDbg 的命令行版本 cdb.exe(以获得更快的速度)以获取所有堆分配:
"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 = $8; gsub(/\(|\)/, "", str); print "0x" str " 0x" $4 }' \
| sort \
| uniq -c \
| gawk '{ printf "%10.2f %10d %10d ( %s = %d )\n", $1*strtonum($3)/1024, $1, strtonum($3), $2, strtonum($2) }' \
| 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 )
  • 然后,如果您的对象有 vtables,只需使用该dps命令在 DumpHeapEntries.log 中查找一些 0x24 字节的堆分配,以了解占用所有内存的对象的类型。
0:075> dps 3be7f7e8
3be7f7e8  00020006
3be7f7ec  090c01e7
3be7f7f0  0b40fe94 SomeDll!SomeType::`vftable'
3be7f7f4  00000000
3be7f7f8  00000000

这很俗气,但它有效:)

于 2009-02-11T16:37:34.583 回答
20

在使用 vtables 在堆栈上查找 C++ 对象时,以下命令非常方便,尤其是在使用发布版本时,当很多东西都被优化掉时。

dpp esp范围


能够将任意 PE 文件作为转储加载是很巧妙的:

windbg -z mylib.dll


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

!gle


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

!error error_number
于 2008-10-01T19:50:56.080 回答
18

我每天使用的几乎 60% 的命令..

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

解释

  1. dv /i /t [文档]
    1. dv- 显示当前范围内局部变量的名称和值
    2. /i- 指定变量的类型:局部、全局、参数、函数或未知
    3. /t- 显示变量的数据类型
  2. ?? this [文档]
    1. ??- 评估 C++ 表达式
    2. this- C++ 这个指针
  3. kM [文档]
    1. k- 显示堆栈回溯
    2. M- DML 模式。帧号是特定帧的超链接。有关 kM 的更多信息,请参阅http://windbg.info/doc/1-common-cmds.html
  4. .frame x [文档]
    1. 切换到帧号 x。0 是堆栈顶部的帧,1 是第 0 帧下方的第 1 帧,依此类推。
    2. 要显示堆栈上另一帧的局部变量,首先切换到该帧 - .frame x,然后使用dv /i /t. 默认情况下d将显示来自顶部框架的信息。
  5. !analyze -v [doc1] [doc2 - 使用 !analyze 扩展]
    1. !analyze-analyze扩展。显示有关当前异常或错误检查的信息。请注意,要运行扩展,我们添加前缀!.
    2. -v- 详细输出
  6. !lmi [文档]
    1. !lmi-lmi扩展。显示有关模块的详细信息。
  7. ~ [文档]
    1. ~- 显示指定线程或当前进程中所有线程的状态。
于 2008-10-01T20:12:09.230 回答
10

我最常使用的“提示”可以让您不必经常触摸那个讨厌的鼠标:Alt+1

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

Peeve:为什么在焦点位于源窗口时忽略按键?这不像您可以从 WinDbg 内部编辑源代码。Alt+1救援。

于 2009-02-11T06:15:59.820 回答
8

一个词(好吧,三个):DML,即Debugger Markup Language

这是对 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> " }

显然,这是一个非常固定的例子,但这些东西对我来说真的是无价之宝。无需在非常复杂的对象中寻找正确的数据成员(这通常需要一分钟以及各种强制转换和取消引用技巧),只需单击一下即可自动完成!

于 2009-08-03T10:38:36.057 回答
7
  • .prefer_dml 1

    这会修改许多内置命令(例如lm)以显示 DML 输出,从而允许您单击链接而不是运行命令。很方便...

  • .reload /f /o file.dll/o将覆盖您拥有的符号的当前副本)

  • .enable_unicode 1//将调试器切换为默认为字符串的Unicode,因为所有 Windows 组件内部都使用 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
于 2010-04-28T16:10:33.670 回答
4

另一个答案提到了命令窗口和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+ up/down滚动命令输出窗口的一行,使用Alt+ PgDn/PgUp滚动一屏。

注意:似乎不同版本的 WinDbg 对窗口和控件有不同的类名,因此您可能需要先使用 AutoHotkey 提供的 window spy 工具来查找实际的类名。

于 2009-11-28T22:50:46.110 回答
4

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

!for_each_module .if(($sicmp( "@#ModuleName" , "mscorwks") = 0) ) 
{.loadby sos mscorwks} .elsif ($sicmp( "@#ModuleName" , "clr") = 0) 
{.loadby sos clr}
于 2010-09-13T14:30:14.950 回答
3

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

于 2008-12-22T20:37:43.660 回答
1

不要使用WinDbg 的.heap -stat命令。它有时会给你不正确的输出。相反,使用 DebugDiags 内存报告。

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

于 2008-10-05T12:59:56.833 回答
1

适用于 x86/x64 的托管代码的平台无关转储字符串:

j $ptrsize = 8 'aS !ds .printf "%mu \n", c+';'aS !ds .printf "%mu \n", 10+'

这是一个示例用法:

0:000> !ds 00000000023620b8

MaxConcurrentInstances
于 2011-02-02T01:33:14.590 回答
1

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

每当您需要执行相同的旧例程时运行它,而无需启动 WinDbg 并手动执行操作。太糟糕了,当您不确定要查找的内容时,这不起作用,或者某些命令参数需要手动分析才能找到/获取。

于 2010-07-16T15:19:58.113 回答