Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / debugging

Collect .NET Applications Traces with sysinternals Tools

5.00/5 (5 votes)
30 Jul 2014MIT4 min read 20.2K  
How you may noninvasively trace .NET applications with sysinternals tools

In this short post, I would like to show you how, with sysinternals tools, you may noninvasively trace .NET applications. This is especially useful in production environment where you can’t install your favorite debugger and hang whole IIS to diagnose an issue. We will work with three tools: dbgview, procdump and procmon. Let’s start with the first one.

DebugView (dbgview.exe)

According to its description on sysinternals site: “DebugView is an application that lets you monitor debug output on your local system, or any computer on the network that you can reach via TCP/IP.” DebugView captures text messages sent by OutputDebugString and DbgPrint Win32Api methods. Interestingly .NET’s DefaultTraceListener under the hood posts trace messages using the OutputDebugString method. So by enabling .NET trace sources, we could have a peak at how the application interacts with .NET Framework APIs. Some interesting trace source names are (based on http://msdn.microsoft.com/en-us/library/ty48b824.aspx):

  • System.Net – traces from some public methods of the HttpWebRequest, HttpWebResponse, FtpWebRequest and FtpWebResponse classes, and SSL debug information (invalid certificates, missing issuers list and client certificate errors)
  • System.Net.Sockets – traces from some public methods of the Socket, TcpListener, TcpClient, and Dns classes
  • System.Net.HttpListener – traces from some public methods of the HttpListener, HttpListenerRequest and HttpListenerResponse
  • System.Net.Cache – traces from some private and internal methods in System.Net.Cache
  • System.Net.Http – traces from some public methods of the HttpClient, DelegatingHandler, HttpClientHandler, HttpMessageHandler, MessageProcessingHandler, and WebRequestHandler classes
  • System.Net.WebSockets – traces from some public methods of the ClientWebSocket and WebSocket classes
  • Microsoft.Owin – traces from Owin infrastructure in recent ASP.NET applications
  • System.IdentityModel – Windows Identity Foundation traces
  • System.Runtime.Serialization – runtime serializers logs
  • System.ServiceModel – logs all stages of WCF processing, whenever configuration is read, a message is processed in transport, security processing, a message is dispatched in user code, and so on
  • System.ServiceModel.MessageLogging – logs all messages that flow through the system
  • System.ServiceModel.Activation – WCF activation logs

REMARK: I mentioned WCF traces above, but they have some specific switch settings which I won’t describe in this post – if you need to work with WCF traces, read this MSDN article.

We will see how to collect .NET traces by examining a sample erroneous situation I ran into in one of our applications. We implemented a Google authentication based on Microsoft.Owin.Security.Google. Unfortunately, on our test server, the application was not able to authenticate the user – only the external authentication cookie was set. I couldn’t run Fiddler on the server so I added the following lines to the application web.config file:

XML
<system.diagnostics>
    <trace autoflush="true" />
    <sharedListeners>
    </sharedListeners>
    <sources>
        <source name="Thinktecture.IdentityModel" switchValue="Verbose">
        </source>
        <source name="System.IdentityModel" switchValue="Verbose">
        </source>
        <source name="Microsoft.Owin" switchValue="Verbose">
        </source>
        <source name="System.Net" switchValue="Verbose">
        </source>
    </sources>
</system.diagnostics>

Then I ran dbgview.exe as an administrator and select Capture Global Win32 from the Capture menu. When I tried to sign in with Google again traces started to appear. Below, you may find a snippet of the output (I used x to hide sensitive info):

00000005	0.01719604	[5280] System.Net Verbose: 0 :
00000006	0.01721783	[5280] [8136] Exiting HttpWebRequest#32103595::HttpWebRequest()
00000007	0.01734075	[5280] System.Net Verbose: 0 :
00000008	0.01738405	[5280] [8136] HttpWebRequest#32103595::HttpWebRequest
   (uri: 'https://accounts.google.com/o/oauth2/token', connectionGroupName: '9777040')
00000009	0.01745697	[5280] System.Net Verbose: 0 :
00000010	0.01747121	[5280] [8136] Exiting HttpWebRequest#32103595::HttpWebRequest()
00000011	0.03358723	[5280] System.Net Verbose: 0 :
00000012	0.03368026	[5280] [8136] HttpWebRequest#32103595::BeginGetRequestStream()
00000013	0.03388838	[5280] System.Net Verbose: 0 :
00000014	0.03393281	[5280] [8136] ServicePoint#40870089::ServicePoint(accounts.google.com:443)
00000015	0.03459155	[5280] System.Net Information: 0 :
00000016	0.03465440	[5280] [8136] Associating HttpWebRequest#32103595 with ServicePoint#40870089
00000017	0.03490024	[5280] System.Net Information: 0 :
00000018	0.03493740	[5280] [8136] Associating Connection#19415024 with HttpWebRequest#32103595
00000019	0.04254256	[5280] System.Net Verbose: 0 :
00000020	0.04256323	[5280] [8136] 
            Exiting HttpWebRequest#32103595::BeginGetRequestStream()  -> ContextAwareResult#40053370
00000021	0.06268339	[5280] System.Net Information: 0 :
00000022	0.06276719	[5280] [2988] 
             Connection#19415024 - Created connection from x.x.x.x:59905 to 173.194.70.84:443.
00000023	0.06294571	[5280] System.Net Information: 0 :
00000024	0.06302002	[5280] [2988] TlsStream#43170133::.ctor(host=accounts.google.com, #certs=0)
00000025	0.06314965	[5280] System.Net Information: 0 :
00000026	0.06323793	[5280] [2988] Associating HttpWebRequest#32103595 with ConnectStream#64411991
00000027	0.06338710	[5280] System.Net Information: 0 :
00000028	0.06345862	[5280] [2988] HttpWebRequest#32103595 - Request: POST /o/oauth2/token HTTP/1.1
00000029	0.06345862	[5280]
00000030	0.06362708	[5280] System.Net Information: 0 :
00000031	0.06370223	[5280] [2988] ConnectStream#64411991 - Sending headers
00000032	0.06370223	[5280] {
00000033	0.06370223	[5280] Content-Type: application/x-www-form-urlencoded
00000034	0.06370223	[5280] Host: accounts.google.com
00000035	0.06370223	[5280] Content-Length: 299
00000036	0.06370223	[5280] Expect: 100-continue
00000037	0.06370223	[5280] Connection: Keep-Alive
00000038	0.06370223	[5280] }.
...
0.65584564	[5280] [8136] ConnectStream#64411991::BeginWrite()
00000107	0.65595937	[5280] System.Net Verbose: 0 :
00000108	0.65602893	[5280] [8136] Data from ConnectStream#64411991::BeginWrite
00000109	0.65620744	[5280] System.Net Verbose: 0 :
00000110	0.65627921	[5280] [8136] 00000000 : 
            67 72 61 6E 74 5F 74 79-70 65 3D 61 75 74 68 6F : grant_type=autho
00000111	0.65640384	[5280] System.Net Verbose: 0 :
00000112	0.65647143	[5280] [8136] 00000010 : 
            72 69 7A 61 74 69 6F 6E-5F 63 6F 64 65 26 63 6F : rization_code&co
00000113	0.65660554	[5280] System.Net Verbose: 0 :
00000114	0.65667343	[5280] [8136] 00000020 : 
            64 65 XX XX XX XX XX XX-XX XX XX XX XX XX XX XX : de=xxxxxxxxxxxxx
00000115	0.65679383	[5280] System.Net Verbose: 0 :
00000122	0.65743381	[5280] [8136] 00000060 : 
            XX XX XX 26 72 65 64 69-72 65 63 74 5F 75 72 69 : xxx&redirect_uri
00000123	0.65756148	[5280] System.Net Verbose: 0 :
...
00000220	0.75980693	[5280] Microsoft.Owin.Security.Google.GoogleOAuth2AuthenticationMiddleware Error: 0 :
00000221	0.75987005	[5280] Authentication failed
00000222	0.75987005	[5280] System.Net.Http.HttpRequestException: 
                 Response status code does not indicate success: 400 (Bad Request).
...

As you can see, in verbose mode, whole HTTP requests are logged – with the trace from dbgview, it was easy to conclude that our application was sending an invalid redirect uri to Google when generating authentication token.

ProcDump and Process Monitor (procmon.exe)

Another great tool to diagnose applications on production is procdump. Its main purpose is to create dumps in different buggy situations, but today, I will focus only on its logging capabilities. You may not know but procdump has a special mode in which no dumps are collected but profiling messages are sent to procmon. You enable this mode with following switches: -f “” -e 1. I usually also add -l switch which makes procdump collect debug log output of our application (i.e., traces I described in the previous paragraph). Procdump is a bit more invasive than dbgview as it attaches itself (as a debugger) to the examined process, but it shouldn’t have a big impact on your application performance.

Let’s have a look at an example. Attach procdump to some buggy application:

PS windows> procdump -e 1 -f "" -l ExceptionTest.exe

Then, run procmon and make sure that you included events from: your application, procdump.exe and procdump64.exe (for x64 machines):

Image 1

Procmon is itself an excellent diagnosing tool and exception logs from procdump make it even more usable. With these logs, you should be able to correlate exceptions in your application with system events (such as I/O, registry or network operations). I need to mention that procdump logs appear in procmon as Profiling Events so remember to enable them:

Image 2

I hope hints presented in this post will help you in your own diagnosing battles. :) And if you know any .NET trace sources that I’ve forgotten in my list, please write them in the comments and I will add them.

License

This article, along with any associated source code and files, is licensed under The MIT License