How do I listen to TPL TaskStarted/TaskCompleted ETW events

Your question challanged me to look into ETW (which I've been wanting to look into for a while). I was able to capture "task start" and "task end" using 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 what happens is:

  1. We start new a live event capturing session using TraceEventSession where we pass it TraceEventLevel.Always to print out all messages (we could narrow this down to TranceEventLevel.Information, but for the example I chose all).

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

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

  4. We print out once we're inside the event.

Note my use of Task.Run was simply because session.Source.Process() is a blocking call, and I wanted it to run in the background.