December 29th, 2015

Using ASP.Net Module to Debug Async Calls

I had a web application that generously used async/await keywords. The application encountered a slow response possibly due to a pending backend database call. I attached a debugger to the application. There was, however, nothing runningĀ in the process (see Figure 1), which was expected due to the calls being async and the waiting threads were no longer active.

It is frustrating seeing no code running when debugging applications using asynchronous methods. What can I do?

Figure 1 ā€œNothingā€ is running in the process. The ‘Location’ column is empty.

Introducing TPLEventListenerModule

Andrew Stasyuk has a good post (https://msdn.microsoft.com/en-us/magazine/JJ891052.aspx) introducing a way to get the call stacks (or causality chains) of async tasks. I figured I could leverage this method in my web application.

In a nutshell, the .NET Framework defines an event source, TplEtwProvider, which fires events that allow you to track Task lifetime. In .NET 4.5 and above, it is possible to subscribe to the event source to get the events. Here is what I do. I create an ASP.Net HttpModule, TPLEventListenerModule, which contains an event listener listening to TPL (Task Parallel Library) events.Ā The listener captures two kinds of TPL event, TaskWaitBegin and TaskWaitEnd. Upon capturing an event, the listener stores the Taskā€™s information — such as, the Id of the task, the StackTrace where the event happens, the created time of the event, the type of the event, and so on — in memory. The module ensures the listener initializedĀ during BeginRequest event ofĀ requests. And I create an HttpHandler for viewing the stored information. The task information can also be accessed via APIs or be found in a dump of the web application.

TheĀ solution of the moduleĀ is attached to this blog post. Here are some more details about the module.

Ā  Ā 1. The module is an ASP.Net HttpModule. To use it I just need to register the module in my web applicationā€™s web.config. The section to be added in web.config looks like this,Ā 

<system.webServer> 
    … 
    <modules>
      <add name="TPLEventListenerModule" type="TaskEventListenerModule.TPLEventListenerModule, TaskEventListenerModule"/>
    </modules>
    …
  </system.webServer>

Ā  Ā 2. The captured event info is stored in the following two ways:Ā 

Ā Ā Ā Each HttpContext instance has a SingleRequestTaskStore instance that stores the current active Taskā€™s info for the corresponding request.

Figure 2 A SingleRequestTaskStore instance is stored in the HttpContext’s Items bag.

Ā Ā Ā Each HttpContext instance has a DebuggingInfoList containing all events for the corresponding request. Ā 

Ā 

Figure 3 A DebuggingInfoList contains all Task Begin/End events for the corresponding request.

Ā  Ā 3. The module puts/removes every requestā€™s HttpContext into/from a ConcurrentItemStore in the requestā€™s BeginRequest/EndRequest event. I can access the stored HttpContext via the CurrentContextViewer.Ā 

Figure 4 The Contexts property contains HttpContexts for all active requests.

Ā  Ā 4. I wrote an HttpHandler, ViewAsyncTaskInfoHandler. The handler can give me a view of all active requests and all async Task info if there is any. To enable the handler in my application, in web.config I added a section like this,

<system.webServer>
    …
    <handlers>
      <add type="TaskEventListenerModule.ViewAsyncTaskInfoHandler, TaskEventListenerModule" name="ViewAsyncTaskInfoHandler"
           resourceType="Unspecified" path="vat.axd" verb="POST,GET,HEAD"></add>
    </handlers>
    …

Trying it Out

The attached solution includes Ā a demo project. The default page has two hyperlinks, ā€œAsync Pageā€ and ā€œView Async Tasksā€. Start the application and browse the default page, you can try clicking ā€œAsync Pageā€ a few times and then click ā€œView Async Tasksā€, andĀ ā€œView Async Tasksā€ pageĀ will show youĀ a list of requests running async tasks.

With the TPLEventListenerModule in place, I can now know what async task my request is awaiting even though I do not see any active thread. If you ever have the same problem, get a copy of the module into your project today.

Ā 

TaskEventListenerModule.zip

Category
ASP.NET

0 comments

Discussion are closed.