How to listen to TPL TaskStarted / TaskCompleted ETW events

I am interested in listening to TWE events (event tracking for Windows), in particular, I would like to know when Task starts and when it stops.

Here is an example of the program that I used for testing:

  using System; using System.Collections.Generic; using System.Diagnostics.Tracing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ConsoleApplication10 { class Listener : EventListener { private static readonly Guid tplGuid = new Guid("2e5dba47-a3d2-4d16-8ee0-6671ffdcd7b5"); protected override void OnEventSourceCreated(EventSource eventSource) { Console.WriteLine("Got guid: " + eventSource.Guid); EnableEvents(eventSource, EventLevel.LogAlways); } protected override void OnEventWritten(EventWrittenEventArgs eventData) { Console.WriteLine("Event: " + eventData.EventId); } } class Program { static void Main(string[] args) { using (var listener = new Listener()) { Action doIt = null; doIt = () => { Thread.Sleep(1000); Console.Write('.'); Task.Run(doIt); }; Task.Run(doIt); Parallel.Invoke(() => Console.WriteLine("invoke")); Console.Read(); } } } } 

An example output on my computer is as follows:

 Got guid: 8e9f5090-2d75-4d03-8a81-e5afbf85daf1 Got guid: 2e5dba47-a3d2-4d16-8ee0-6671ffdcd7b5 Event: 3 invoke Event: 4 ....... 

Thus, the Invoke method raises an event, but Tasks fire none. Looking at the source of a task (for example, a reference source ), the code is no different from how the Parallel.Invoke event fires.

What is wrong with the above, or how can I listen to the TaskStarted and TaskCompleted events (or any event related to the task, for that matter)?

+7
c # task-parallel-library etw
source share
1 answer

Your question made me look into the ETW (which I have long wanted to study). I was able to capture the “beginning of the task” and “the end of the task” using the Microsoft.Diagnostics.Tracing.TraceEvent NuGet library with the simple following code:

 private static void Main(string[] args) { Task.Run(() => { using (var session = new TraceEventSession("TplCaptureSession")) { session.EnableProvider(new Guid("2e5dba47-a3d2-4d16-8ee0-6671ffdcd7b5"), TraceEventLevel.Always); session.Source.Dynamic.AddCallbackForProviderEvent("System.Threading.Tasks .TplEventSource", "TaskExecute/Start", @event => { Console.WriteLine("Inside Task Started"); }); session.Source.Dynamic.AddCallbackForProviderEvent("System.Threading.Tasks .TplEventSource", "TaskExecute/Stop", @event => { Console.WriteLine("Inside Task Stopped"); }); session.Source.Process(); } }); var task = Task.Run(async () => { await Task.Delay(20000); }); task.Wait(); } 

Basically the following happens:

  • We start a new real-time event recording session using TraceEventSession , where we pass it to TraceEventLevel.Always to print all messages (we could narrow it down to TranceEventLevel.Information , but as an example I chose all).

  • We enable the TplEventSource provider by passing its Guid to session.EnableProvider .

  • We register a callback that needs to be called once. TplEventSource (which is the event source for TPL, obviously) fires the TaskExecute/Start or TaskExecute/Stop events (taken from the reference source )

  • We print when we are inside an event.

Note that using Task.Run was just because session.Source.Process() is a blocking call, and I wanted it to run in the background.

+4
source share

All Articles