分类 调试 下的文章

使用WinDgb调试的时候,我们需要和各种结构体等符号打交道。包括系统的符号等等。有时候符号太多了,我们根本记不住或者只有模糊的印象,比如只记得其中的2个字母,怎么办?或者知道符号名,但不知道在哪个模块,特别是使用stl库的时候。这时候dt搜索就可以帮上忙了。
使用如下通配符命令即可列出所有的符号

dt  *!*XXX*  xxx为我们知道的仅有符号名字符

例如:

0:006> dt  *!*filesystem_error*
          xxx!filesystem_error
          xxx!std::tr2::sys::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >
          xxx!std::tr2::sys::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >
          MSVCP120!filesystem_error
          MSVCP120!std::tr2::sys::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >
          MSVCP120!std::tr2::sys::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >
          YYY!filesystem_error
          YYY!std::tr2::sys::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >
          YYY!std::tr2::sys::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >
105139e0  xxx!std::tr2::sys::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >
10513950  xxx!std::tr2::sys::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >
10514130  xxx!std::tr2::sys::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >::~basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >
10514d10  xxx!std::tr2::sys::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >::`scalar deleting destructor'
0f9c4140  YYY!std::tr2::sys::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >
0f9c05e0  YYY!std::tr2::sys::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >
0f9c4190  YYY!std::tr2::sys::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >::~basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >
0f9c0690  YYY!std::tr2::sys::basic_filesystem_error<std::tr2::sys::basic_path<std::basic_string<char,std::char_traits<char>,std::allocator<char> >,std::tr2::sys::path_traits> >::`scalar deleting destructor'

 

最近我写个例子程序研究下某个异常情况,故意制造了个崩溃。然后分析dmp文件。

当我执行!address -summary命令想观察下进程当前内存情况时,去报如下错误:

0:000> !address -summary

No symbols for ntdll. Cannot continue.

这意思是没有ntdll.dll模块的符号,不可能啊,因为有明显的证据证明该模块符号文件已经加载且匹配,如下:

1、比如我们从线程栈可以佐证

05 00affa24 7799662d 008e7000 808b6aa4 00000000 kernel32!BaseThreadInitThunk+0x19 (FPO: [Non-Fpo])
06 00affa80 779965fd ffffffff 779b51e4 00000000 ntdll!__RtlUserThreadStart+0x2f (FPO: [SEH])
07 00affa90 00000000 00da1091 008e7000 00000000ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])
只有加载了对应符号文件,才能看到这个正确的函数名。

2、执行!chksym ntdll

0:000> !chksym ntdll

ntdll.dll
    Timestamp: 6071CF9D
  SizeOfImage: 19C000
          pdb: wntdll.pdb
      pdb sig: F9EA7A7F-4120-6C21-D2ED-4E99993A9EF4
          age: 1

Loaded pdb is f:\debug_symbol\symbols32\wntdll.pdb\F9EA7A7F41206C21D2ED4E99993A9EF41\wntdll.pdb

wntdll.pdb
      pdb sig: F9EA7A7F-4120-6C21-D2ED-4E99993A9EF4
          age: 1

MATCH: wntdll.pdb and ntdll.dll
这条指令很明确的告诉我,不仅仅加载了,且是匹配的

可是为什么会报“No symbols for ntdll. Cannot continue.

在网上查了很多资料都没有什么帮助,后来在某篇文章上看到:该作者遇到了符号的其他问题,他得出结论说windbg的系统模块符号文件路径要设置为c:\symbols就解决了他面临的问题。

我想,我姑且也这样试试,于是我将我的符号路径改成如下:

然后,结果真的可以了,!address -summary命令正确执行和输出了。这是真的吗,真的是因为路径问题,真的是要c:\symbols吗?

根据我个人的经验,不太相信这个结论。我做了如下的验证步骤:

1、我注意到我原来的目录是两层的,会不会是因为这个原因呢,我将我原来的目录F:\Debug_Symbol\Symbols32调整为F:\Symbols32,执行相同命令,结果是报相同错误。

2、先删除现在c:\symbols目录,然后将我原来的符号目录考到c:盘,改名为symbols。然后执行!address -summary命令,我等待奇迹的出现,结果却是:

0:000> !address -summary

No symbols for ntdll. Cannot continue.

然后我懵逼了,但至少得出这样一个结论c:\symbols目录解决我了我的问题。然后继续折腾,结果在某次折腾里,我忘了修改windbg符号路径设置,但是目录下的相关符号被我移到了其他目录,也就是说F:\Debug_Symbol\Symbols32是空的,当调试时,ntdll.dll模块的符号文件是从微软符号服务器上拿的新的符号文件,也就是说问题的原因不在于目录而在于符号问见本身。

观察下两个符号文件:

原来的符号文件

 

这是新的,也是不出问题的符号文件

 

可以看到,这两个文件的的guid是一样的,但文件大小不一样,也就是说对应的版本还是不一样,旧的符号文件跟我系统里的ntdll其实是不匹配的。可是guid都一样,为什么不是正确的匹配版本呢。我懵逼了。

但是我们可以得出几点经验:

  • windbg告诉你符号文件匹配,但是那是不一定的
  • 以后遇到同样的问题,我们可以试着把原来所谓匹配的符号文件删除,然后执行同样的操作让windbg从新下载符号文件,可能会解决

其实,这种情况还会引发其他指令无法执行,比如:

  • !teb
    0:000> !teb
    TEB at 008ea000
    error InitTypeRead( TEB )...
  • !heap
    0:000> !heap
    Invalid type information

后续我会继续研究这个问题,同时希望看到此文的其他人,能帮我解答。

无论是否有异常处理,用任何语言编写良好的错误处理代码都是困难的。当我考虑在一个给定的程序中需要实现什么样的异常处理时,我首先将可能捕获的每个异常分类到四个bucket中的一个,我将其标记为致命的、硬骨头般突出的、烦人的、外部的。

致命的异常不是你的错,你不能阻止它们,你也不能理智地清除它们。它们几乎总是发生,因为这一进程病入膏肓,即将摆脱痛苦。内存不足、线程中止等。捕捉这些是毫无意义的,因为你的用户代码所能做的一切都不能解决问题。就让你的“finally”块运行,并希望最好的。(或者,如果你真的很担心,快速失败和不让;在这一点上“finally”块运行,它们可能只会让事情变得更糟。但这是另一话题。)

硬骨头般突出的异常是您自己的该死的错误,您可以阻止它们,因此它们是代码中的错误。你不应该捕获它们;这样做是在你的代码中隐藏了一个错误。相反,您应该改写您的代码,这样就不可能在第一时间发生异常,因此不需要捕获异常。这个参数是空的,类型转换是坏的,索引超出范围,你试图除以零-这些都是你本来可以很容易地避免的问题,所以首先要防止混乱,而不是试图捕获它。

令人烦恼的异常是不幸的设计决策的结果。恼人的异常是在完全非异常的情况下抛出的,因此必须一直捕获和处理。典型的异常例子是Int32.Parse,如果给它一个不能被解析为整数的字符串,它就会抛出。但是这个方法99%的用例是转换用户输入的字符串,这可能是任何旧的东西,因此解析失败也不例外。更糟糕的是,如果不实现整个方法本身,调用者就无法提前确定其参数是否糟糕,在这种情况下,他们不需要首先调用它。这个不幸的设计决策非常令人恼火,当然,框架团队随后不久就实现了TryParse,这是正确的做法。你必须抓住令人恼火的异常,但这样做是令人恼火的。试着永远不要自己写一个抛出令人烦恼的例外的库。

最后,外部异常看起来有点像恼人的异常,只是它们不是不幸的设计选择的结果。相反,它们是凌乱的外部现实影响到你美丽、清晰的程序逻辑的结果。考虑这个伪C#代码,例如:

try
{
using ( File f = OpenFile(filename, ForReading) )
{
// Blah blah blah
}
}
catch (FileNotFoundException)
{
// Handle filename not found
}

.cmdtree

简介

使用形式

.cmdtree cmdfile

参数

  • cmdfile
    命令文件,包含多个你需要的命令。必须是一个文本档

使用步骤

1、使用命令创建文本文件test.wl,使用以下示例作为模板。您可以按所需方式修改{}之间的节:

windbg ANSI Command Tree 1.0title {"Common Commands"}

body

{
"Common Commands"}

{
"Information"}

{
"Time of dump"} {".time"}

{
"Process being debugged"} {"|"}

{
"Dump Location"} {"||"}

{
"Create server on port 9999"} {".server tcp:port=9999"}

{
"Show remote connections"} {".clients"}

{
"Process Environment Block"} {"!peb"}

{
"Logging"}

{
"Open Log"} {".logopen /t /u /d"}

{
"Close Log"} {".logclose"}

{
"Modules"}

{
"All Modules"} {"lm D sm"}

{
"Loaded Modules"} {"lmo D sm"}

{
"Loaded Modules (verbose)"} {"lmvo D sm"}

{
"Modules w/o symbols"} {"lme D sm"}

{
"Stacks"}

{
"Set frame length to 2000"} {".kframes 2000"}

{
"Dump current stack w/ DML"} {"kpM 1000"}

{
"Dump stacks without private info"} {"knL 1000"}

{
"Dump stacks with all parameters"} {"kPn 1000"}

{
"Dump stacks (distance from last frame)"} {"kf 1000"}

{
"Dump stacks with Frame Pointer Omission"} {"kvn 1000"}

{
"Dump all stack"} {"~*kbn 1000"}

{
"Dump unique stacks"} {"!uniqstack -pn"}

{
"Thread environment block"} {"!teb"}

{
"Move to next frame"} {".f+"}

{
"Move to previous frame"} {".f-"}

{
"Memory"}

{
"Dump heaps"} {"!heap -a"}

{
"Automated Task"}

{
"!analyze"} {"!analyze -v"}

{
"Locks"} {"!ntsdexts.locks"}

{
"CPU time for User and Kernel Mode"} {"!runaway 7"}

{
"Managed"}

{
"Load sos"} {".loadby sos mscorwks"}

{
"clrstack"} {"!clrstack"}

{
"Threads"} {"!threads"}

{
"Stack Objects"} {"!dso"}

{
"Exceptions"} {"!dae"}

微软符号服务器已经很久没ping通了,挂上全局代理可以下载符号,但是又不想总是开着全局代理。

后来找到一种替代方案,可以通过设置系统环境变量,来让下载符号的流量走代理服务器

_NT_SYMBOL_PROXY