Using WinDbg - Advanced commands
Did you know you can build your own advanced commands using for each, if, etc? The complete list of control tokens are:
- .if
- .else
- .elseif
- .foreach
- .for
- .while
- .do
- .break
- .continue
- .catch
- .leave
- .printf
- .block
Using these command tokes you can send quite advanced instructions to the debugger that not only will make your job a lot easier, but also impress your manager immensely. :)
.foreach
Let's begin with an easy example. Imagine you want to investigate all strings on the heap that are 6500 bytes or more. To list them you'd simply type !dumpheap -type System.String -min 6500. This will give you the following information:
0:000> !dumpheap -type System.String -min 6500
------------------------------
Heap 0
Address MT Size
790da154 790f9244 9280
0264c4d0 790f9244 32788
total 2 objects
------------------------------
Heap 1
Address MT Size
total 0 objects
------------------------------
Heap 2
Address MT Size
0b62e790 790f9244 11284
total 1 objects
------------------------------
Heap 3
Address MT Size
0e6839d0 790f9244 32788
0e717904 790f9244 32788
0fb2a320 790f9244 6828
total 3 objects
------------------------------
total 6 objects
Statistics:
MT Count TotalSize Class Name
790f9244 6 125756 System.String
Total 6 objects
So far, so good. The problem is that in order to investigate each string you'd have to run !dumpobject (!do) on every address. This might be acceptable now that we're only dealing with 6 strings, but what if it were 25, or 100? I don't know if you're aware of this, but if you pass the -short argument to !dumpheap it will give you the minimum information (just the addresses of the objects in question):
0:000> !dumpheap -type System.String -min 6500 -short
790da154
0264c4d0
0b62e790
0e6839d0
0e717904
0fb2a320
------------------------------
Now, let's use this information in a .foreach-clause:
0:000> .foreach(myVariable {!dumpheap -type System.String -min 6500 -short}){!do myVariable;.echo *************}
Name: System.String
MethodTable: 790f9244
EEClass: 790f91a4
Size: 9280(0x2440) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String:
WFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWGRkAiEPDxYCHwEFZFhYWFhYWFhYWFhYWFhYWFhYWFhYWF
hYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhY
WFhYWFhYWFhYWFhkZAInDw8WCh8CBQFFHwMFCjIyLzExLzIwMDcfBAUBVh8FBQFFHwYFASpkZAIpD2QWBGYPZBYEAg
ETC...
Fields:
MT Field Offset Type VT Attr Value Name
790fdb60 4000096 4 System.Int32 1 instance 4632 m_arrayLength
790fdb60 4000097 8 System.Int32 1 instance 4631 m_stringLength
790fad38 4000098 c System.Char 1 instance 3c m_firstChar
790f9244 4000099 10 System.String 0 shared static Empty
>> Domain:Value 000d5eb8:790d57b4 000fb4c0:790d57b4 000ca848:790d57b4 1d8334d8:790d57b4 <<
79122994 400009a 14 System.Char[] 0 shared static WhitespaceChars
>> Domain:Value 000d5eb8:026203f0 000fb4c0:02624504 000ca848:026745f0 1d8334d8:026dcef4 <<
*************
Name: System.String
MethodTable: 790f9244
EEClass: 790f91a4
Size: 32786(0x8012) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String:
g8PFgIfAQUHYWFhYWFhYWRkAgMPDxYCHwEFCWFhYWFhYWFhYWRkAgQPDxYCHwEFCjI4LzEyLzIwMDdkZAIvD2QWAmY
PZBYCZg9kFgICAw8PFgIfAQUFWFhYWFhkZAIxDw8WAh8JZ2QWBGYPZBYEAgEPZBYCZg9kFgQCAQ8WAh8IBQMxcHgWA
gIBDw8WAh8JaGRkAgMPZBYEAgEPDxYCHwEFCkFkZCBSZWNvcmRkZAIDDw8WAh8ABRZ+L0ltYWdlcy9UaXRsZS9OZXc
ETC...
Fields:
MT Field Offset Type VT Attr Value Name
790fdb60 4000096 4 System.Int32 1 instance 16385 m_arrayLength
790fdb60 4000097 8 System.Int32 1 instance 10960 m_stringLength
790fad38 4000098 c System.Char 1 instance 3c m_firstChar
790f9244 4000099 10 System.String 0 shared static Empty
>> Domain:Value 000d5eb8:790d57b4 000fb4c0:790d57b4 000ca848:790d57b4 1d8334d8:790d57b4 <<
79122994 400009a 14 System.Char[] 0 shared static WhitespaceChars
>> Domain:Value 000d5eb8:026203f0 000fb4c0:02624504 000ca848:026745f0 1d8334d8:026dcef4 <<
*************
Name: System.String
MethodTable: 790f9244
EEClass: 790f91a4
Size: 11282(0x2c12) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String:
WFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWGRkAiEPDxYCHwEFZFhYWFhYWFhYWFhYWFhYWFhYWFhYWFa
YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWF
hYWFhYWFhYWFhYWFhYWFhkZAInDw8WCh8CBQFFHwMFCjIyLzExLzIwMDcfBAUBVh8FBQFFHwYFASpkZAIpD2QWBGYPZ
ETC...
Fields:
MT Field Offset Type VT Attr Value Name
790fdb60 4000096 4 System.Int32 1 instance 5633 m_arrayLength
790fdb60 4000097 8 System.Int32 1 instance 3092 m_stringLength
790fad38 4000098 c System.Char 1 instance 5b m_firstChar
790f9244 4000099 10 System.String 0 shared static Empty
>> Domain:Value 000d5eb8:790d57b4 000fb4c0:790d57b4 000ca848:790d57b4 1d8334d8:790d57b4 <<
79122994 400009a 14 System.Char[] 0 shared static WhitespaceChars
>> Domain:Value 000d5eb8:026203f0 000fb4c0:02624504 000ca848:026745f0 1d8334d8:026dcef4 <<
*************
Name: System.String
MethodTable: 790f9244
EEClass: 790f91a4
Size: 32786(0x8012) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String:
SRU5UIFBBR0U6IDMgb2YgMTVkZAILD2QWAmYPZBYcAgcPDxYCHwEFZFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWF
hYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYW
FhYWFhkZAINDw8WCh4BRQUBRR4CVFQFCjIyLzExLzIwMDceAlRWBQFWHgJURQUBRR4CVFIFASpkZAITDw8WCh8CBQFF
ETC...
Fields:
MT Field Offset Type VT Attr Value Name
790fdb60 4000096 4 System.Int32 1 instance 16385 m_arrayLength
790fdb60 4000097 8 System.Int32 1 instance 10960 m_stringLength
790fad38 4000098 c System.Char 1 instance 3c m_firstChar
790f9244 4000099 10 System.String 0 shared static Empty
>> Domain:Value 000d5eb8:790d57b4 000fb4c0:790d57b4 000ca848:790d57b4 1d8334d8:790d57b4 <<
79122994 400009a 14 System.Char[] 0 shared static WhitespaceChars
>> Domain:Value 000d5eb8:026203f0 000fb4c0:02624504 000ca848:026745f0 1d8334d8:026dcef4 <<
*************
Name: System.String
MethodTable: 790f9244
EEClass: 790f91a4
Size: 32786(0x8012) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String:
0b25Ib21lBR9BcHBsaWNhdGlvbk5hdmlnYXRpb246aWJ0TG9nb3V0BTdfY3RsMDpQZXJzb25BcHBsaWNhdGlvbkRlZ3J
lZUxpc3Q6U3lzdGVtVGl0bGU6RWRpdEltYWdlBT5fY3RsMDpQZXJzb25BcHBsaWNhdGlvbkRlZ3JlZURpcGxvbWFMaXN
0OlN5c3RlbVRpdGxlOkVkaXRJbWFnZQVDX2N0bDA6UGVyc29uQXBwbGljYXRpb25PdGhlclF1YWxpZmljYXRpb25MaXN
ETC...
Fields:
MT Field Offset Type VT Attr Value Name
790fdb60 4000096 4 System.Int32 1 instance 16385 m_arrayLength
790fdb60 4000097 8 System.Int32 1 instance 10960 m_stringLength
790fad38 4000098 c System.Char 1 instance 3c m_firstChar
790f9244 4000099 10 System.String 0 shared static Empty
>> Domain:Value 000d5eb8:790d57b4 000fb4c0:790d57b4 000ca848:790d57b4 1d8334d8:790d57b4 <<
79122994 400009a 14 System.Char[] 0 shared static WhitespaceChars
>> Domain:Value 000d5eb8:026203f0 000fb4c0:02624504 000ca848:026745f0 1d8334d8:026dcef4 <<
*************
Name: System.String
MethodTable: 790f9244
EEClass: 790f91a4
Size: 6826(0x1aaa) bytes
(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String:
/wEPDwULLTEyMTQ5MDkyMjgPZBYCAgEPZBYGAgcPZBYEAgEPDxYCHghJbWFnZVVybAUrfi9pbWFnZXMvbmF2ZAILD2QW
aWdhdGlvbi9wYWdlcy9QYWdlMlByb2dyZXNzLmdpZmRkAgIPDxYCHgRUZXh0BRVDVVJSRU5UIFBBR0U6IDMgb2YgMTVk
ZAILD2QWAmYPZBYcAgcPDxYCHwEFZFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhY
ETC...
Fields:
MT Field Offset Type VT Attr Value Name
790fdb60 4000096 4 System.Int32 1 instance 3405 m_arrayLength
790fdb60 4000097 8 System.Int32 1 instance 3404 m_stringLength
790fad38 4000098 c System.Char 1 instance 2f m_firstChar
790f9244 4000099 10 System.String 0 shared static Empty
>> Domain:Value 000d5eb8:790d57b4 000fb4c0:790d57b4 000ca848:790d57b4 1d8334d8:790d57b4 <<
79122994 400009a 14 System.Char[] 0 shared static WhitespaceChars
>> Domain:Value 000d5eb8:026203f0 000fb4c0:02624504 000ca848:026745f0 1d8334d8:026dcef4 <<
*************
Unknown option: ------------------------------
*************
Let's analyze the exact syntax. Here's the command
.foreach(myVariable {!dumpheap -type System.String -min 6500 -short}){!do myVariable;.echo *************}
"myVariable" is, as the name implies, the name of the variable that I wish to use for the data generated by the first set of commands. The second set of commands is what I wish to execute for each token. First I run !do on the variable, and then I use the .echo-command to print a separator in order to make it a bit easier on the eyes.
There are additional parameters you can use. For example you can choose to skip every n number of variables, or specify a text file to be parsed and used as tokens instead. Take a look at the windbg documentation if you're interested.
.shell
I first saw this being used by my colleague Doug Stewart, who is a genius with these things.
0:000> .shell -i - -ci "!iisinfo.clientconns" FIND /c "Request active"
40
.shell: Process exited
What it does is, it runs !iisinfo.clientonns and uses the MS-DOS FIND-command to count the number of times the string "Request active" appears. Off course you could use it to search for certain strings from any type of output, like ".shell -i - -ci "!do 0b62e790" FIND /c /i "<table"" or whatever suits your needs.
Let's take a quick look at the syntax.
When it comes to ".shell" the -i option is mandatory. It specifies the file we want to use for input. In this scenario we don't want to use a file, so we add another hyphen, resulting in the following syntax: ".shell -i -"
We then add the -ci option, which states that we should treat the following commands as an input file instead. ".shell -i - -ci "!iisinfo.clientconns""
Finally we state what shell command we wish to run using this input. ".shell -i - -ci "!iisinfo.clientconns" FIND /c "Request active"".
Naturally we can use any complicated command we wish in the statement, so instead of !iisinfo.clientconns we could run one of our .foreach-loops instead.
/ Johan
Comments
- Anonymous
January 23, 2008
Slight correction:) the -i switch is optional so you can just run .shell -ci "!iisinfo.clientconns" FIND /c "Request active" if you want to
- Nitpicker:)
Anonymous
January 23, 2008
Nice informative post.Anonymous
January 23, 2008
I am learning debugging and its so easy to learn things with you and Tess. Keep up the great work!Anonymous
January 23, 2008
Thanks Sudeep! @Tess: I Stand corrected. :)Anonymous
January 31, 2008
Prerequisites This post will require some basic knowledge of windbg and the sos extension. For this IAnonymous
February 03, 2008
.NET Debugging Demos This is a series of debugging demos aimed to help you get some hands on experienceAnonymous
February 03, 2008
.NET Debugging Demos This is a series of debugging demos aimed to help you get some hands on experienceAnonymous
March 03, 2008
.NET:NETFoundations-Memorymodel(part2)Quake3Arena.NETPortisDone!lucene使用与优化Build...Anonymous
December 29, 2009
A bit after the fact... I know you guys work with these apps day in and day out. Were you familiar with WinDbg before you started working at Microsoft? How long did it take you to get a good ways up the learning curve? (I'm looking for hope on the horizon.) Thanks for the great posts. As mentioned before, you and Tess make WinDbg accessible to the rest of us. Kudoes and regards, WillAnonymous
April 10, 2012
Hi, .foreach(myVariable {!dumpheap -type System.String -min 6500 -short}){!do myVariable;.echo *************} For above command, how do I print myVariable itself?Anonymous
April 10, 2012
Never mind, I figured out. .It was simple (.echo myVariable). I was trying .printf