WinDbg和SoS,如何打印/转储一个大字符串?

17

我正在使用带有SoS扩展的WinDbg调试来自生产服务器的hangdump。

其中一个堆栈中有一个字符串参数,我需要知道它的值。 但是,它是一个相当大的字符串,并且当我使用时,WinDbg不会将其打印出来。这是的输出:

0:036> !do 00000001b30d8668
Name: System.String
MethodTable: 0000064278436728
EEClass: 000006427803e520
Size: 5125300(0x4e34b4) bytes
 (C:\WINDOWS\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: <String is invalid or too large to print>

Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
000006427843d998  4000096        8         System.Int32  1 instance          2562638 m_arrayLength
000006427843d998  4000097        c         System.Int32  1 instance          2562637 m_stringLength
0000064278438170  4000098       10          System.Char  1 instance               3c m_firstChar
0000064278436728  4000099       20        System.String  0   shared           static Empty
                                 >> Domain:Value  0000000000163260:000000007fff0370 00000000001a6760:000000007fff0370 <<
0000064278438020  400009a       28        System.Char[]  0   shared           static WhitespaceChars
                                 >> Domain:Value  0000000000163260:000000007fff0b60 00000000001a6760:000000007fff89f0 <<

我怎样才能获得这个字符串实例的值?最好将其转储到文件中。

4个回答

15

这是我编写的一个脚本,用于在Windbg中将字符串转储到文件中。

$$ Dumps the managed strings to a file
$$ Platform x86
$$ Usage $$>a<"c:\temp\dumpstringtofolder.txt" 6544f9ac 5000 c:\temp\stringtest
$$ First argument is the string method table pointer
$$ Second argument is the Min size of the string that needs to be used filter
$$ the strings
$$ Third is the path of the file
.foreach ($string {!dumpheap -short -mt ${$arg1}  -min ${$arg2}})
{ 

  $$ MT        Field      Offset               Type  VT     Attr    Value Name
  $$ 65452978  40000ed        4         System.Int32  1 instance    71117 m_stringLength
  $$ 65451dc8  40000ee        8          System.Char  1 instance       3c m_firstChar
  $$ 6544f9ac  40000ef        8        System.String  0   shared   static Empty

  $$ start of string is stored in the 8th offset, which can be inferred from above
  $$ Size of the string which is stored in the 4th offset
  r@$t0=  poi(${$string}+4)*2
  .writemem ${$arg3}${$string}.txt ${$string}+8 ${$string}+8+@$t0
}

以下是如何使用该命令:

$$>a<”c:\temp\dumpstringtofolder.txt” 6544f9ac 5000 c:\temp\stringtest

倒出的内容将以Unicode格式呈现,要查看其内容,请使用类似于以下代码的方法:Console.WriteLine(ASCIIEncoding.Unicode.GetString(File.ReadAllBytes(@"c:\temp\stringtest03575270.txt")));

希望对你有所帮助


1
我无法让脚本正常工作,但是根据你的示例,.writemem函数足够简单,我能够自己让它正常工作。 - Andrew Arnott
@AndrewArnott 我能够使用这个脚本转储字符串,我经常使用它。你遇到了什么问题?你是在x86/x64上使用它吗? - Naveen
1
不确定我们是否可以依赖于!Name2EE的输出,但是这可以用于自动获取方法表指针(r@$t1),因此我们可以摆脱参数1。 .foreach /pS 7 /ps 4 (methodtable {!Name2EE mscorlib.dll System.String}) { r@$t1= ${methodtable} } - Thomas Weller
@ThomasW. 我同意。可以使用 !Name2EE 进行自动化。 - Naveen
这里的 *2 是什么意思 r@$t0= poi(${$string}+4)*2 - Max Young
抱歉,我现在知道原因了,char 是2个字节。 - Max Young

9

如果你真的想要倾泻2562638个字符的文本,请三思而后行。但是,如果你真的想这样做,那么文本将存储在字符串实例的字段后面,因此你可以使用du <address+offset> <end address>来转储字符串的实际文本。输出将类似于以下内容:

00000000`132ab050  "this is an extremely long string"
00000000`132ab090  " of text, so don't even bother t"
00000000`132ab0d0  "o try to dump it to the screen -"

通过将会话输出记录到文件中,您可以轻松捕获输出并进行任何后处理。


对于非常长的字符串,由于限制,起始地址/结束地址语法将无法工作。您需要使用du <address+offset> L? <length in bytes>。 对于非常长的字符串,这实际上会花费太长时间,WinDbg不再响应。 - Thomas Weller
我在日志中得到了和命令窗口一样的消息:<字符串无效或过大无法打印>。 - Dave Black
一个我使用它的例子: du 0x181B4C71020+10 L? 10000。 不知道为什么,但我不得不将它偏移10才能使其正常工作。 - undefined

3
我已经将@Naveen的脚本修改以在x64平台上运行。
顺便说一下,这是一个很棒的脚本!
$$ Dumps the managed strings to a file
$$ Platform x64
$$ Usage $$>a<"c:\temp\dumpstringtofolder.txt" 00007ffa6c509808 5000 c:\temp\stringtest
$$ First argument is the string method table pointer
$$ Second argument is the Min size of the string that needs to be used filter
$$ the strings
$$ Third is the path of the file
.foreach ($string {!dumpheap -short -mt ${$arg1}  -min ${$arg2}})
{ 
    $$               MT    Field   Offset                 Type VT     Attr            Value Name
    $$ 00007ffa6c50c158  400027b        8         System.Int32  1 instance               18 m_stringLength
    $$ 00007ffa6c50a9c0  400027c        c          System.Char  1 instance               53 m_firstChar
    $$ 00007ffa6c509808  4000280       c0        System.String  0   shared           static Empty

    $$ start of string is stored in the 8th offset, which can be inferred from above
    $$ Size of the string which is stored in the c-th offset
    r@$t0= (poi(${$string}+8) & 00000000FFFFFFFF) *2
    .writemem ${$arg3}${$string}.txt (${$string}+c) (${$string}+c+@$t0)
}

似乎有一个非常大的字符串问题。我得到了这个字符串对象的输出,但是当我使用脚本时,它停在第三个。0:000> !dumpheap -mt 00007ff8ca9d10b0 -min 1000000 Address MT Size 000000670d17a4c8 00007ff8ca9d10b0 35377744 0000006717767bd8 00007ff8ca9d10b0 141515814 00000069686a1020 00007ff8ca9d10b0 1132138640 00000068c06a1020 00007ff8ca9d10b0 566068092 00000069d06a1020 00007ff8ca9d10b0 1132138588统计信息 MT Count TotalSize Class Name 00007ff8ca9d10b0 5 3007238878 System.String 总共有5个对象。 - bkqc
我尝试修改WriteMem,使用长度范围而不是地址范围,但似乎也不起作用。 .writemem ${$arg3}${$string}.txt (${$string}+c) L(@$t0) - bkqc
问题在于它超过了256MB并需要L? .writemem ${$arg3}${$string}.txt (${$string}+c) L?(@$t0) - bkqc

0
如果你很着急,可以在启用WinDbg日志后运行!do命令。在日志文件中,你将得到整个字符串。
在WinDbg菜单中,选择编辑->打开/关闭日志文件,设置日志文件路径。

1
我在日志和命令窗口中得到了相同的消息:<字符串无效或太大无法打印>。 - Dave Black

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