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

45

我意识到Windbg是Windows平台上非常强大的调试器,我偶尔会学到一些新知识。是否有其他Windbg用户可以分享一些他们的技巧呢?

附注:我不是在寻找巧妙的命令,这些可以在文档中找到。如何分享使用Windbg完成一些无法想象的事情的技巧呢?例如,在Windbg下运行进程时生成有关内存分配的统计信息的方法。

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

示例: alt text http://blogs.msdn.com/photos/debuggingtoolbox/images/8954736/original.aspx


4
不知道为什么,在WinDbg:6.12.0002.633中似乎无法使用命令".cmdtree",输入该命令时没有弹出帮助文件。 - Dyno Fu
4
文件参数是否包含?我使用同样版本的WinDbg并成功执行了该命令。 - Kris Kumler
你是对的,我漏掉了文件参数。 - Dyno Fu
非常酷!如果您想在该文件中添加一些有用的命令,请查看我的帖子:https://www.ohadsoft.com/2014/10/some-windbg-tips/,其中列出了一些命令。 - Ohad Schneider

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
您会得到一个类似这样的表格,例如告诉我们25529270个0x24字节的分配占用了近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 )
  • 如果您的对象具有虚函数表,则只需使用dps命令在DumpHeapEntries.log中查找一些0x24字节的堆分配,以了解占用所有内存的对象类型。
0:075> dps 3be7f7e8
3be7f7e8  00020006
3be7f7ec  090c01e7
3be7f7f0  0b40fe94 SomeDll!SomeType::`vftable'
3be7f7f4  00000000
3be7f7f8  00000000

这很俗套,但它有效 :)


这太棒了,伙计,非常感谢你的发布。 - pj4533
我一直在尝试自己实现这个,但是我感到困惑。你是如何通过'3be7f7e8'地址来获取dds的呢?那只是!heap输出中的第一列吗?也就是说,你要在原始日志中搜索该大小的分配,获取地址,然后对其执行dds操作? - pj4533
在日志中,每次内存分配都会得到一行类似于这样的内容:“3be7f7e8: 00038 . 00040 [107] - busy (24)”。 这里我们要搜索的是值为24,从上面的表格中可以看出大部分内存都是由0x24字节的分配使用的。 然后我使用cygwin的less命令在DumpHeapEntriesStats.log中搜索这些行,使用命令“/(24)”选择一些匹配的地址并在cdb/WinDBG中进行dds操作。 - jturcotte

20

当查找带有虚函数表的C++对象时,以下命令非常有用,特别是在使用发布版本进行优化时。

dpp esp 范围


可以将任意PE文件加载为转储文件:

windbg -z mylib.dll


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

!gle


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

!error error_number

18

我每天使用的命令中,将近60%都是关于IT技术的。

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++ 中的 this 指针。
  3. kM [文档]
    1. k - 显示堆栈回溯信息。
    2. M - DML 模式。帧编号是到特定帧的超链接。有关 kM 的更多信息,请参见http://windbg.info/doc/1-common-cmds.html
  4. .frame x [文档]
    1. 切换到第 x 个帧。0 表示堆栈顶部的帧,1 表示 0 帧的下面一帧,依此类推。
    2. 要显示堆栈上另一个帧的局部变量,请先切换到该帧 - .frame x,然后使用 dv /i /t。默认情况下,d 显示来自顶部帧的信息。
  5. !analyze -v [文档1] [文档2 - 使用 !analyze 扩展名]
    1. !analyze - analyze 扩展名。显示有关当前异常或 bug check 的信息。请注意,要运行扩展名,我们需要在前面加上 !
    2. -v - 详细输出。
  6. !lmi [文档]
    1. !lmi - lmi 扩展名。显示有关模块的详细信息。
  7. ~ [文档]
    1. ~ - 显示指定线程或当前进程中所有线程的状态。

kM 似乎是 WinDbg 预览版(可从 Microsoft 商店获取)的默认行为,因此 k 5 和 kM 5 将给出相同的结果。 - Sahil Singh

10
我最常用的“提示”是一种可以让您避免经常触摸那个烦人鼠标的技巧:Alt+1Alt+1将焦点放在命令窗口中,以便您实际输入命令并使向上箭头滚动到命令历史记录。但是,如果您的焦点已经在可滚动的命令历史记录中,则不起作用。
抱怨:为什么在焦点在源窗口时按键被忽略?毕竟你无法从WinDbg内部编辑源代码。Alt+1来解救。

1
alt+2, alt+1, alt+2, ctrl+F4。这是一个简单的技巧...即使光标在命令窗口中也可以使用。 :P - anishsane

8

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

这是WinDbg中相对较新的功能,帮助文件中没有记录。但在Windows调试工具的安装目录下有“dml.doc”文档提供了一些说明。

基本上,这是一种类似HTML的语法,您可以将其添加到调试器脚本中进行格式化和链接。您可以使用链接调用其他脚本,甚至是同一个脚本。

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

现在,有了DML,我能够在输出中添加链接,允许在相关对象上再次调用同一个脚本。这样可以更快地探索模型。

这里是一个简化的例子。假设正在检查的对象与另一个对象有一个名为“reference”的关系。 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> " }

显然,这是一个相当通用的例子,但对我来说,这些内容非常宝贵。与在非常复杂的对象中寻找正确的数据成员(通常需要一分钟和各种类型转换和取消引用技巧)相比,一切都可以自动完成一次点击!


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

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}

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 + 上箭头/下箭头来滚动命令输出窗口的一行内容,Alt + PgDn/PgUp来滚动一个屏幕。
注意:不同版本的WinDbg将具有不同的窗口和控件类名,因此您可能需要使用AutoHotkey提供的窗口间谍工具先找到实际的类名。

3

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


断点命令语法很难掌握。如果您在这里添加一个添加一次性断点的断点示例,那就太好了。 - JeffJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接