NetExt – SOS on steroids


I have been playing recently with quite a new windbg extension (released by Rodney Viana from Microsoft) called NetExt. Rodney Viana published an introductory post about it, which you may find on his blog. In this post I would like to show you my usage samples as well as encourage you to start using it by yourself. Netext documentation is thorough and nicely organized which is good because at the beginning you probably will spend a lot of time on this page 🙂 In paragraphs that follow I will focus mainly on dump debugging, but most of the techniques presented here should work as well in live debugging sessions.

Finding yourself in a dump

Starting steps depend on the type of diagnosis you need to perform. Though it is almost always worth to know the CLR Runtime version (!wver):

!wver
Runtime(s) Found: 1
0: Filename: mscordacwks_amd64_Amd64_4.0.30319.34209.dll Location: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscordacwks.dll
.NET Version: v4.0.30319.34209
NetExt (this extension) Version: 2.0.1.5550

and the Windows version (vertarget and version):

0:000> vertarget
Windows 7 Version 7601 (Service Pack 1) MP (4 procs) Free x64
Product: WinNt, suite: SingleUserTS
kernel32.dll version: 6.1.7601.18869 (win7sp1_gdr.150525-0603)
Machine Name:
Debug session time: Wed Jul  8 12:40:05.000 2015 (UTC + 2:00)
System Uptime: 0 days 4:40:15.908
Process Uptime: 0 days 0:16:10.000
  Kernel time: 0 days 0:00:00.000
  User time: 0 days 0:00:02.000

Information about the debuggee process (such as the command line, environment variables, security context etc.) can be revealed with the help of the !peb command and finally you may list appdomains hosted by this process with !wdomain.

Scanning through threads (exceptions, locks)

Knowing what threads are doing is helpful when you are diagnosing application failures or hangs. To list threads currently running in the application use the !wthreads command. This command will show you also a number of locks acquired by a given thread as well as information about the thread type and the last exception that occurred in it. Example output:

0:000> !wthreads
   Id OSId Address          Domain           Allocation Start:End              COM  GC Type  Locks Type / Status             Last Exception
    1 23e4 0000000001f76e50 0000000001f69720 0000000000000000:0000000000000000 MTA  Preemptive   0 Background
    2 1fc0 0000000001fa9200 0000000001f69720 0000000000000000:0000000000000000 MTA  Preemptive   0 Background|Finalizer
    3 0a3c 0000000001ff8830 0000000001f69720 0000000000000000:0000000000000000 MTA  Preemptive   0 Background|Timer|Worker
    4 2124 0000000001ff9000 0000000001f69720 0000000000000000:0000000000000000 NONE Preemptive   0 Background
    6 0e3c 0000000001ff9fa0 0000000001f69720 0000000000000000:0000000000000000 NONE Preemptive   0 Background|Wait|Worker
    7 ---- 0000000001ffa770 0000000001f69720 0000000000000000:0000000000000000 MTA  Preemptive   0 IOCPort|Worker|Terminated
    8 ---- 0000000001ffaf40 0000000001f69720 0000000000000000:0000000000000000 MTA  Preemptive   0 Worker|Terminated

Next step would be to dump managed stacks for all the running threads ~*e!wclrstack. To dump the exception data use the !wpe command (it requires an exception address, but I’ve already created a pull request to the netext repo so it could also print the current exception in the thread) or !wdae to dump all the exceptions in the heap. Maybe you don’t find those commands very innovative, but in my opinion they produce more readable output than their SOS alternatives. Unfortunately, to diagnose problems with locks you still need to stick to SOS or SOSEX commands (an example of such a diagnosis session can be found in one of my recent posts).

Analyzing the GC heap

The NetExt extension unveils its real power when it comes to the GC heap analysis. Apart from standard commands such as !wdo (dump object), !wclass (dump class definition), !wheap (with interesting switches: -detailsonly, -type {partial-type-name} or -mt) we have at our disposal a heap-query language! To make it work we need to first create a heap index using the !windex command. If our dump file is huge and generating the index takes a lot of time it might be worth to save it (with the -save {file-name} switch) and later load it (-load {file-name}) each time we analyze the dump. !windex command has also an interesting -implement {partial-type-name} switch which dumps all the objects implementing a given type. Finally it has a switch to filter objects by type (-type {partial-type-name}) or method table (-mt {method-table}). With the generated index we are now ready to query our heap. We can start with the basic !wselect, but the most powerful of the NetExt commands is !wfrom. Its syntax is as follows:

!wfrom [/nofield] [/withpointer] [/type <string>]
       [/mt <string>] [/fieldname <string>] [/fieldtype <string>]
       [/implement <string>] [/obj <expr>]
       [where (<condition>)] select <expr1>, ..., <exprN>

There is a big number of functions you may use in the where and select parts and they are all listed in the documentation. Below I present you some example queries to show you how flexible this command is.

Show execution contexts bound to running threads

You may check the SOS way of finding this information in my debug recipe and compare.

> !wfrom -nospace -nofield -type System.Threading.Thread select "System TID: ", 
     $thread(DONT_USE_InternalThread), ", Managed TID: ", m_ManagedThreadId, ", address: ", $addr(), ",
     execution context: ", m_ExecutionContext

System TID: #INVALID#, Managed TID: 0n22, address: 0000000100066D48, execution context: 0000000000000000
System TID: 28, Managed TID: 0n23, address: 0000000100068D48, execution context: 0000000000000000
System TID: 29, Managed TID: 0n26, address: 0000000100098E18, execution context: 0000000000000000
System TID: 7, Managed TID: 0n1, address: 00000001FFE674E8, execution context: 00000002FFEB2CD8

Show all recently run SQL queries

List all SQL queries with the addresses of their parameters arrays:

> !wfrom -nospace -nofield -type System.Data.SqlClient.SqlCommand select $addr(), 
   ", params: ", _parameters._items._items, ", sql: ", _commandText

00000002000198E8, params: 0000000200019AE0, sql: dbo.TempResetTimeout
00000003000682A8, params: 0000000300068528, sql: SELECT * FROM ttt WHERE category=@category AND parent_id=@parent_id AND (enabled = @enabled Or enabled = 1) ORDER BY sortorder
...

For a given query we may then list its parameter values:

> !wselect _parameterName, _value from 0000000300068528

[System.Data.SqlClient.SqlParameter[]]
***************
[0]: 00000003000683c0
[System.Data.SqlClient.SqlParameter[]]
(string)System.String _parameterName = @category
System.Object _value = 00000000fff0c620 testcat
***************
[1]: 0000000300068568
[System.Data.SqlClient.SqlParameter[]]
(string)System.String _parameterName = @parent_id
System.Object _value = 0000000300068210 5
***************
[2]: 0000000300068678
[System.Data.SqlClient.SqlParameter[]]
(string)System.String _parameterName = @enabled
System.Object _value = 00000000ffeb4950 False

Show all current HTTP requests data

This example is taken from the Rodney’s post:

> !wfrom -nospace -nofield -type *.HttpContext select $rpad($addr(),10)," ",$if(!_thread, "  --",
    $lpad($thread(_thread.DONT_USE_InternalThread),4))," ",$if((_timeoutSet==1),$tickstotimespan(_timeout._ticks), 
    "Not set "), " ", $if(_response._completed || _finishPipelineRequestCalled,"Finished", 
    $tickstotimespan($now()-_utcTimestamp.dateData)), " ", $replace($lpad(_response._statusCode,8),
    "0n","")," ", $rpad($isnull(_request._httpMethod,"NA"),8), " ", $isnull(_request._url.m_String, 
   _request._filePath._virtualPath)

00000000FFF22F80   -- 00:00:00 Finished    200 GET      http://localhost:80/test/WebResource.axd?d=xxxx
0000000100061CE0   -- 00:00:00 Finished    200 GET      http://localhost:80/test/home.aspx

Show all open memory streams

> !wfrom -implement System.IO.MemoryStream where (_isOpen == 1) select $addr()

calculated: 0000000200027E58
calculated: 0000000200209B90
calculated: 000000020021C4A0
calculated: 00000002004FD540
calculated: 00000002004FDE20
calculated: 000000040176E498

6 Object(s) listed
17 Object(s) skipped by filter

Working with GUID/datetime/IP fields

It was always troublesome to display GUID or datetime fields in a readable format. I used to run John Robbin’s scripts to accomplish that. With NetExt you just use one of the available functions, for example to convert datetime ticks to a date use the $tickstodatetime function:

0:007> !wfrom -obj 00000002009df6d8 select $tickstodatetime(dateData)
calculated: 2015-06-02 08:18:25

1 Object(s) listed

For GUIDs use the $toguid function and for IP addresses the $ipaddress function. Other field functions can be found in the documentation.

Misc commands

To make the debugging experience even better some shortcut commands are available:

  • !wruntime to list active HTTP runtimes
  • !whttp – show current HTTP context objects
  • !wcookie – shows current HTTP cookies
  • !wconfig – shows config lines in the memory (useful when the only thing you have is a file dump)
  • !wdict – displays dictionary objects
  • !wkeyvalue – displays objects stored in a NamedValueCollection

Extending

What makes this extension even greater is the fact that its source code is available under the GNU GPLv2 licence. So if you happen to miss some functionality you may implement it by yourself. The setup might require few changes in the project configuration (there are some paths hardcoded and you need to download boost and tinyxml libraries), but it’s nothing too hard – in case you run into problems contact me and I will help you deal with them.

The solution is composed of two projects: a native NetExt and a managed NetExtShim, where NetExt references NetExtShim and at the same time implements interfaces required to talk to the debugger engine (engextcpp.cpp). NetExtShim under the hood uses CLRMD to query the CLR runtime information and exports its managed functions to the native world with the help of the Unmanaged Exports package and COM interoping.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s