In my spare time I've been working on a WinDBG extension containing some useful methods that automate pretty common .NET debugging tasks for me, and hopefully other people as well. The extension right now targets .NET 4
/x64 only, as that's my primary development platform. I'll probably make a x86 build soon (especially if people request it), it shouldn't be very difficult to make it work on 32 bit platforms. Currently a few commands support DML (!DumpSqlConnectionPools is the most noticeable), I'll be adding support for DML to other commands soon.
A few of the commands are targeted at ASP.NET/server apps (!DumpHttpContext, !DumpASPNetRequests, !DumpSqlConnectionPools, !DumpThreadPool for example), but most are fairly general purpose. In the next few blog posts I'll go into details about the implementation of each of the commands, but for now here's a list of them with quick summaries and examples. Feel free to comment/email me with any comments/suggestions.
Download the extension here (x64)
And the x86 version here
Prints out all threads with a HTTP context.
ThreadID HttpContext StartTime URL + QS
0123 0000000002f62b38 05/29/2011 18:23:02 /hello/world.aspx
0124 0000000002f62c98 05/29/2011 18:23:05 /test.aspx?w00t=1
Prints the context information for the current thread, including the managed thread object address (System.Threading.Thread), ExecutionContext, and Logical/IllogicalCallContext.
OS ThreadId: e64, N: 0
Managed Thread @ 0000000002f58580
Thread ExecutionContext @ 0000000002f64af8
IllogicalCallContext @ 0000000002f64c40
HostContext (HttpContext) @ 0000000002f64d80
Note: of all the commands here, this is the most likely to break from service releases. It relies on reversed offsets to find the managed thread object from the unmanaged thread object.
!DumpDictionary (!DumpHashtable, !ddct, !dht)
Note: I used hashtable/dictionary pretty interchangeably for the rest of this post, anything that supports Hashtables also supports Dictionaries.
Iterates over all the key/value pairs in a dictionary, printing them out, and translating keys/values to strings if possible.
0:000> !ddct 0000000002f621c0
entry at: 0000000002f622e8
key: 0000000000000001 [(null), 1]
value: 0000000002f62128 [System.String, "test"]
entry at: 0000000002f62300
key: 0000000000000002 [(null), 2]
value: 0000000002f62150 [System.String, "two"]
These methods have a few switches you can use.
These switches are one of my favorite features and have saved me a ton of time already.
Note: DumpDictionary and DumpHashtable are both actually the same function, the export table links them together.
Note 2: This also supports the HybridDictionary class, just use any of the above functions on it.
Basically the same as !DumpDictionary, but goes through all the sub-hashtables inside the memory cache object. Doesn't support the same switches as !DumpDictionary yet though.
Other misc stuff
Prints out the mixed-mode callstack of the current thread.
Example: (parameters snipped by me so it doesn't wrap on my blog)
IP M Name
000000007745153a 0 ntdll!ZwRequestWaitReplyPort+a
0000000076d42f58 0 KERNEL32!ConsoleClientCallServer+54
0000000076d75321 0 KERNEL32!ReadConsoleInternal+1f1
0000000076d8a762 0 KERNEL32!ReadConsoleA+b2
0000000076d58e04 0 KERNEL32!TlsGetValue+899e
000007feef4f17c7 0 clr!DoNDirectCall__PatchGetThreadCall+7b
000007feec6834a1 1 DomainNeutralILStubClass.IL_STUB_PInvoke(...)+101
000007feece2f58a 1 System.IO.__ConsoleStream.ReadFileNative(...)+ba
000007feece2f3f2 1 System.IO.__ConsoleStream.Read(...)+62
(Note: I've seen some version of dbghelp have problems resolving native methods in CLR.dll to symbols. The latest version seems to work fine though.)
Given a System.DateTime or TimeSpan, formats it as a string MM/dd/yyyy hh:mi:ss. I think psscor2 had something similar back in the day.
Given an expression consisting of field access or hashtable lookups, evaluates the expression starting at a root object.
Fields are separated by periods just like C++/C#. Hashtable lookups are specified by the [ ] operator (like in C#) and are done in two ways.
- The first is if the hashtable/dictionary key is a string, you can use a quoted string inside the [ ] operator to tell the evaluator you want to match a string. For example _myHashtable["steve"].lastName will lookup the value in _myHashtable with the key of "steve", then evaluate the lastName field.
- The second is by hashcode. If the expression inside the [ ] operator is an unquoted number, it's used to do a lookup by hashcode.
Also, if the root object itself is a hashtable, it's legal for the expression to begin with simply a [ ] operator. For example, ["steve"].lastName.
Expressions ending with a "$" sign are evaluated to a string and the result string printed. If the final result isn't a string object, stuff will probably break.
Note: Hashtable lookups are actually O(n) (you'd need a full-out IL interpreter like in VS to do it in O(1)), so they might get slow on really big dictionaries.
Attempts to resolve a delegate to a method name. This should work at least for non-multicast delegates (eg delegates without an invocation list) to static or instance methods. For instance method calls, the value in _methodPtr is simply a pointer to the managed method and resolved in a way similar to ip2md. For static methods, the value in _methodPtrAux is an entry into the JIT stub table, and used to get a MethodDesc handle to the method, which is then resolved to a name.
Dumps the items currently in the .NET 4.0 ThreadPool queues, resolving work items to method names. This currently supports work items enqueued via ThreadPool.QueueUserWorkItem and Task[<T>]. These are the only two classes in .NET 4 that currently implement IThreadPoolWorkItem. This method only works if ThreadPoolGlobals.useNewWorkerQueue = true.
There's a also special case: if a work item was created via BeginInvoke, DumpThreadPool will attempt to resolve it to the real method being called by looking at the MethodCall object's MethodInfo field. For this to work, the method being called must be System.Runtime.Remoting.Proxies.AgileAsyncWorkerItem.ThreadPoolCallBack, and the callback argument must be a System.Runtime.Remoting.Proxies.AgileAsyncWorkerItem.
Dumps out all SQL connection pools in all app domains of the process.
 Factory @ 00000001df0614c8
Connection String: <snip>
Pool: 00000001bf077d08, State: 1, Waiters: 0, Size: 12
ConnPtr State #Async Created Command/Reader Timeout Text
 00000001bf0788e8 1 0 6/10/2011 17:30:32
 000000015f1ebf80 1 0 6/10/2011 17:30:33
 000000013f30da08 1 0 6/10/2011 17:30:39
 000000013f3315e0 1 0 6/10/2011 17:30:54
 000000011f4852f8 1 0 6/10/2011 17:30:54
 00000001df1b5998 1 0 6/10/2011 17:30:54
 00000000ff41da90 1 0 6/10/2011 17:30:54
 00000001bf1c08f8 1 0 6/10/2011 17:30:54
 000000019f5f8080 1 0 6/10/2011 17:30:54
 000000015f2b4f90 1 0 6/10/2011 17:30:54
[00a] 000000013f344120 1 0 6/10/2011 17:30:54
[00b] 000000019f60ab18 1 0 6/10/2011 17:30:54
Connection String: <snip>;
Pool: 000000017f4495b8, State: 1, Waiters: 0, Size: 4
ConnPtr State #Async Created Command/Reader Timeout Text
 000000017f449f50 1 0 6/10/2011 17:30:54
 000000017f471280 1 0 6/10/2011 17:30:54
 00000000ff4305b8 1 0 6/10/2011 17:30:54
 00000000ff442f50 1 1 6/10/2011 17:30:54 000000017f4372a0 50 do_something_prc
What are we seeing here? Connection pooling is basically a 5 level hierarchy:
- Connection String - top level, defines the server and connection options.
- Pool group - Second level, varies based on the identity used to connect to the DB if using SSPI
- Pool - Actually contains the SqlConnection instances.
- SqlConnection - The DB connection instance
- Active readers - SqlDataReaders that are actively doing something
Some fields that probably need some explaining:
- Pool -> State
- Corresponds to the System.Data.ProviderBase.DbConnectionPool+State enum, Initializing, Running, Shutting Down
- Connection -> State
- Corresponds to the System.Data.ConnectionState enum.
- Created is the time the connection was first opened in UTC.
- If Async = 1, the pointer is to a command, otherwise it's to a SqlDataReader. With a SqlCommand, you can get to the reader via the _cachedAsyncState field, with a SqlDataReader, you can get to the command via the _command field.
Note: This doesn't currently dump connections in the transacted connection pools.
(x86 only for now)
Dumps out all timers registered in the process.
Handle TimerObj ADID Period[ms] State Context Delegate Method
293e48 00000000022cbbb4 1 1000 00000000022cbb48 00000000022cbbc8 00000000022cbb6c SomeCallback(System.Object)
- The timer handle, can be used to cross reference with an existing TimerBase object
- A reference to the managed _TimerCallback object
- The appdomain ID the timer is associated with
- The state object associated with the constructor.
- The ExecutionContext captured by the timer.
- The delegate that will be called when the timer fires.
Prints out the built-in help.