我如何收听TPL TaskStarted / TaskCompleted ETW事件

问题描述 投票:8回答:3

我有兴趣听ETW(Windows的事件跟踪)TPL事件,特别是我想知道Task何时开始以及何时停止。

这是我用于测试的示例程序:

   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();
            }
         }
      }
   }

我的机器上的示例输出如下:

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

因此,Invoke方法会导致事件触发,但任务不会触发任何事件。查看任务源(例如reference source),代码与Parallel.Invoke事件的触发方式没有什么不同。

上面有什么问题,或者,我如何监听TaskStarted和TaskCompleted事件(或任何与此相关的任务相关事件)?

c# .net task-parallel-library etw
3个回答
6
投票

你的问题让我想到了ETW(我一直想要研究一下)。我能够使用Microsoft.Diagnostics.Tracing.TraceEvent NuGet library捕获“任务开始”和“任务结束”,并使用以下简单代码:

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();
}

基本上会发生什么:

  1. 我们使用TraceEventSession开始新的实时事件捕获会话,我们将它传递给TraceEventLevel.Always以打印出所有消息(我们可以将其缩小到TranceEventLevel.Information,但是对于我选择所有的示例)。
  2. 我们通过将其Guid传递给session.EnableProvider来启用TplEventSource提供程序。
  3. 我们注册一个回调被调用一旦TplEventSource(显然是TPL的事件源)发射TaskExecute/StartTaskExecute/Stop事件(取自reference source
  4. 一旦我们进入活动,我们就会打印出来。

注意我使用Task.Run只是因为session.Source.Process()是一个阻塞调用,我希望它在后台运行。


0
投票

我写的代码的Here is an example只使用BCL监听TPL ETW事件。

part that tripped me up是你需要运行这段代码的事实:

// Cause the type initializer for System.Threading.Tasks.TplEtwProvider to run.
// Otherwise async method builders starting events will be missed.
Type.GetType("System.Threading.Tasks.TplEtwProvider, mscorlib", true).GetField("Log").GetValue(null);

否则,一切似乎都按照你期望的方式运作。不需要第三方库!


0
投票

基于Yuvals answer不适合我,因为事件名称似乎不同,我提出了一个类型安全的解决方案,不使用魔法guid或字符串:

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.Diagnostics.Tracing;
using Microsoft.Diagnostics.Tracing.Parsers;
using Microsoft.Diagnostics.Tracing.Parsers.Tpl;
using Microsoft.Diagnostics.Tracing.Session;

...

Task.Run(() =>
{
    using (var session = new TraceEventSession("TplCaptureSession"))
    {
        session.EnableProvider(TplEtwProviderTraceEventParser.ProviderGuid, TraceEventLevel.Always);

        var parser = new TplEtwProviderTraceEventParser(session.Source);
parser.AddCallbackForEvent<TaskStartedArgs>(
            null,
            @event =>
            {
                Console.WriteLine($"Task {@event.TaskID} started by {@event.OriginatingTaskID}");
            });

        parser.AddCallbackForEvent<TaskCompletedArgs>(
            null,
            @event =>
            {
                Console.WriteLine($"Task {@event.TaskID} completed");
            });

        session.Source.Process();
    }
});

为了完整起见,这里是我看到由System.Threading.Tasks.TplEventSource源生成的事件名称列表:

  • 等待任务继续计划/发送
  • DebugFacilityMessage
  • DebugFacilityMessage1
  • DebugMessage
  • EventSourceMessage
  • ForkJoin /启动
  • ForkJoin /停止
  • IncompleteAsyncMethod
  • 调用/启动
  • 调用/停止
  • 循环/启动
  • 循环/停止
  • ManifestData
  • 变化
  • RunningContinuation
  • RunningContinuationList
  • SetActivityId
  • TaskCompleted
  • TaskScheduled /发送
  • TaskStarted
  • TaskWait /发送
  • TaskWaitContinuationComplete
  • TaskWaitContinuationStarted
  • TaskWaitEnd
  • TraceOperationBegin
  • TraceOperationEnd
  • TraceOperationRelation
  • TraceSynchronousWorkBegin
  • TraceSynchronousWorkEnd
© www.soinside.com 2019 - 2024. All rights reserved.