Sample Windbg extension to recurse, filter and pipe commands

I didn't post to this blog during many months, mainly because I moved from my Escalation Engineer position to an Application Development Consultant position (aka Technical Account Manager Dev).
I missed some free time for that kind of leisure, and as many other interesting blogs now spread over the web, I felt unuseful to post things that may not really help someone.

That's why I come back today, to post a debugger extension I wrote some months ago to help me to automate my debugging sessions.
I always felt a lack in windbg to easily filter other debugger extensions outputs and automate recursing commands with previous results.

Others like Doug & Tess are able to use both !foreach, .shell - -ci or even logparser to filter outputs, but I'm too bad/lazy for that and preferred having my own debugger syntax :)
After having shown this extension to some debugging class I held in France, I decided to share it publicly.

The binary version I'm posting here is the initial version of this extension, that I wanted to improve before posting, but as I never had time I'm posting it here as-is..
It contains in this version only 1 debugger command called !sosexec, with this awful syntax:

     

!sosexec /c “command1|filter1”->”command2 [tokenID1]|filter2”->”command3 [tokenID2]”->....

 

The parameter [tokenID1] is the column index for the token you’re looking for, starting from 0.
For example in the bellow output, 0 is the ‘MT’ token, 1 is the ‘Field’ token, and thus 6 is the tokenID for ‘Value’. Note those indexes are changing depending on sos version so take care to check them first.

 

0:021> !do 0acca994

Name: System.Web.HttpContext

MethodTable: 663a2750

EEClass: 663a26e0

Size: 168(0xa8) bytes

 (C:\WINDOWS\assembly\GAC_32\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)

Fields:

      MT Field Offset Type VT Attr Value Name

663ad210 4000fc7 4 ...IHttpAsyncHandler 0 instance 08a308d0 _asyncAppHandler

663a206c 4000fc8 8 ...b.HttpApplication 0 instance 08a308d0 _appInstance

663a3a9c 4000fc9 c ....Web.IHttpHandler 0 instance 0accb490 _handler

663a2ba0 4000fca 10 ...m.Web.HttpRequest 0 instance 0accaa3c _request

A real world example for ASP.NET 2.0 application could be the one bellow to list the current ASPX pages easily (note1 663a2750 was my methodtable for HttpContext) (note2 that’s just a sample, Tess debugging script makes it lots better)

0:021> !sosexec /c "!dumpheap -mt 663a2750 -short"->"!do [0]|_request"->"!do [6]|_path"->"!do [6]|String:"

Executing !do 06c35cb0

  Executing !do 06c35d58

    Executing !do 06c34538

      String: E:\Applications.Net\CRM Cog Integration Web\coghierarchy.aspx

Executing !do 0acca994

    Executing !do 0acc91e4

      String: E:\Applications.Net\CRM Cog Integration Web\cogcompanyinformation.aspx

Executing !do 0ca32e98

  Executing !do 0ca32f40

    Executing !do 0ca31310

      String: E:\Applications.Net\CRM Cog Integration Web\cogcompanyinformation.aspx

Executing !do 0caa95fc

  Executing !do 0caa96a4

    Executing !do 0caa7e4c

      String: E:\Applications.Net\CRM Cog Integration Web\cogcompanyinformation.aspx

 

This command will:

1.     Execute !dumpheap -mt 663a2750 -short (I got MT from an earlier !dumpheap –stat)

3.     Execute !do with the 1th token (ID 0) for each output lines of step 1

4.     Filter this output for lines containing string “_request”

5.     Execute !do again with the 7th token (ID 6) of each filtered line of step 4 (the ‘Value’ token)

6.     Filter output with lines containing the string ‘_path’

7. Execute !do with again the 7th token (ID 6) of each filtered line of step 6

8. Filter output from step 7. to only show the string content, that starts with “String:”

Another example bellow will list SQL commands executed by current threads:

!sosexec /c "~*e !dso|System.Data.SqlClient.SqlCommand"->"!do [1]|cmdText"->"!do [5]"

Executing !do 0x2645ab0c

  Executing !do 0x143f9908

    String: sql_PrestataireLoadPatente

Executing !do 0x111bbc18

  Executing !do 0x1c442bdc

    String: sql_FactureLoad

Executing !do 0x14efddc0

  Executing !do 0x143f5f44

    String: sql_Vue_facture_impayee

 

The nice thing is that it works for both user (native & managed) and kernel debugging (live or postmortem).

Another example will dump all ASP templates compiled in an IIS process using the iisinfo.dll debugger extension brought by DebugDiag.

.logopen c:\temp\aspcode.txt

!sosexec /c "!iisinfo.templates -v|C:\"->"!templatecode [1]"

.logclose

 

You can also simply use it to filter your debugger output, for example listing all DLLs named ora* (don’t ask me why :-p)

0:029> !sosexec /c "lmnt|ora"

03f70000 041c7000 orageneric9 orageneric9.dll Mon May 06 02:23:24 2002 (3CD5CCFC)

60500000 60590000 oracommon9 oracommon9.dll Mon Apr 29 01:12:14 2002 (3CCC81CE)

60800000 60806000 oravsn9 oravsn9.dll Mon Apr 29 01:13:48 2002 (3CCC822C)

60810000 60816000 orawtc9 orawtc9.dll Mon Apr 29 01:13:50 2002 (3CCC822E)

61400000 6142c000 oranl9 oranl9.dll Sat Apr 27 04:30:17 2002 (3CCA0D39)

61480000 61534000 oran9 oran9.dll Sat Apr 27 04:31:08 2002 (3CCA0D6C)

 

I highly recommend to write and test those commands step by step, as a simple mistake can make this version of the extension loop infinitely executing wrong commands and thus looking for unknown symbols… J

When I'll have a better version, I'll post it with source to codeplex to also let other people improve it, as it exposes lightweight framework of C++ objects to allow to be reused in other debugger extensions, such as my only !sosexec command is only made of:

  HRESULT CALLBACK

  sosexec(PDEBUG_CLIENT4 Client, PCSTR args)

  {

     INIT_API();

 

     // Read arguments command line

     CArgs* oArgs = new CArgs(args);

     vector<string> sCommands = oArgs->getCommands();

 

     // Split args into commands/filters/tokens

     vector<string> sFilters = oArgs->getFilters();

     vector<int> iTokens = oArgs->getTokens();

 

     // Recursively execute commands

     CExecutor *oExec1 = new CExecutor(Client, sCommands,sFilters,iTokens);

     oExec1->RecurseExec();

     free(oExec1);

 

     EXIT_API();

     return S_OK;

  }

 

On the feature list there is currently some more switches like /unique, /silent, ability to use multiple filters, and skip headers or footer lines.
As I’ll try to improve it and post source code for good, feel free to share ideas, feedbacks or sample commands that you find useful to share.

Enjoy!

soshelp.dll