【问题标题】:What is your favourite Windbg tip/trick? [closed]你最喜欢的 Windbg 提示/技巧是什么? [关闭]
【发布时间】:2010-09-12 18:09:44
【问题描述】:

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

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

【问题讨论】:

    标签: c++ windows debugging windbg


    【解决方案1】:

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

    【讨论】:

    • 不知道为什么,但是在WinDbg:6.12.0002.633中似乎不可用,当输入.cmdtree时弹出帮助文件。
    • 是否包含文件参数?我成功地使用了与该命令相同版本的 WinDbg。
    • 你是对的,我错过了文件参数。
    • 非常酷!有关您可能想要放入该文件的有用命令列表,请查看我的帖子:ohadsoft.com/2014/10/some-windbg-tips
    【解决方案2】:

    调查崩溃转储中的内存泄漏(因为到目前为止,我更喜欢 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 )
    
    • 然后,如果您的对象有 vtable,只需使用 dps 命令在 DumpHeapEntries.log 中查找一些 0x24 字节的堆分配,以了解占用所有内存的对象的类型。
    0:075> dps 3be7f7e8
    3be7f7e8  00020006
    3be7f7ec  090c01e7
    3be7f7f0  0b40fe94 SomeDll!SomeType::`vftable'
    3be7f7f4  00000000
    3be7f7f8  00000000
    

    这很俗气,但很有效:)

    【讨论】:

    • 这是史诗般的人物,非常感谢您发布它。
    • 我一直在尝试自己实现这个,但我很困惑。你如何得到'3be7f7e8'地址给dds?这只是 !heap 输出中的第一列吗?这意味着您在原始日志中搜索该大小的分配,获取地址,然后对其进行 dds 处理?
    • 没错,在日志中,每次内存分配都会出现这样的一行:“3be7f7e8: 00038 . 00040 [107] - busy (24)”。 24 是我们在这里搜索的值,从上表中得到,告诉我们大部分内存被 0x24 字节分配使用。然后,我使用 cygwin 的 less 使用命令“/(24)”在 DumpHeapEntriesStats.log 中搜索这些行,选择一些匹配的地址并将它们 dds 到 cdb/WinDBG 中。
    【解决方案3】:

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

    dpp esp 范围


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

    windbg -z mylib.dll


    使用以下方法查询 GetLastError():

    !gle


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

    !error error_number

    【讨论】:

      【解决方案4】:

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

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

      解释

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

      【讨论】:

      • kM 似乎是 WinDbg Preview 中的默认行为(可从 Microsoft 商店获得),因此 k 5 和 kM 5 将给出相同的结果
      【解决方案5】:

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

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

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

      【讨论】:

      • alt+2, alt+1, alt+2, ctrl+F4 Lame hack... 即使光标在命令窗口中也可以工作。 :P
      【解决方案6】:

      一个词(好吧,三个):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> " }
      

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

      【讨论】:

        【解决方案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
        

        【讨论】:

          【解决方案8】:

          另一个答案提到了命令窗口和 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 工具来查找实际的类名。

          【讨论】:

            【解决方案9】:

            基于 .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}
            

            【讨论】:

              【解决方案10】:

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

              【讨论】:

              • 断点命令语法很难掌握。如果您在此处添加一个添加 oneshot 断点的断点示例,那就太好了。
              【解决方案11】:

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

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

              【讨论】:

                【解决方案12】:

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

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

                【讨论】:

                  【解决方案13】:

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

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

                  这是一个示例用法:

                  0:000> !ds 00000000023620b8
                  
                  MaxConcurrentInstances
                  

                  【讨论】:

                    猜你喜欢
                    • 2010-10-25
                    • 2010-10-06
                    • 2010-09-13
                    • 2010-11-29
                    • 2010-09-25
                    • 2010-09-06
                    • 2010-09-05
                    • 2010-10-08
                    • 2011-07-18
                    相关资源
                    最近更新 更多