Beispiel #1
0
        public static EventListener Create(Guid eventSourceGuid, string sessionName, double eventCounterIntervalSeconds)
        {
            var session = new TraceEventSession(sessionName);
            TraceEventProviderOptions options = new TraceEventProviderOptions("EventCounterIntervalSec", eventCounterIntervalSeconds.ToString());             //I'm just guessing what the args should be...

            session.EnableProvider(eventSourceGuid, providerLevel: TraceEventLevel.Verbose, matchAnyKeywords: ulong.MaxValue, options: options);
            var source = new ETWTraceEventSource(sessionName, TraceEventSourceType.Session);
            var e      = new EventListener(session, source);

            source.Dynamic.All += delegate(TraceEvent data)
            {
                if (data.ProviderGuid != eventSourceGuid)
                {
                    throw new Exception();
                }

                if (data.EventName == "EventCounters")
                {
                    string           s  = data.PayloadString(0).Replace("∞", "0");
                    EventCounterData ed = Parse <EventCounterData>(s);
                    e.EventCounterEvent?.Invoke(MsSinceUnixEpoch(data.TimeStamp), ed);
                }
            };
            return(e);
        }
        static void RegisterEventListener(TraceEventSession userSession)
        {
            var options = new TraceEventProviderOptions();

            options.AddArgument("EventCounterIntervalSec", _config.EventCounterIntervalInSeconds.ToString());

            foreach (var eventSource in _config.EventSources)
            {
                userSession.EnableProvider(eventSource.Name, TraceEventLevel.Always, (ulong)EventKeywords.None, options);

                // Create a stream of the 'EventCounters' event source events
                IObservable <TraceEvent> eventStream = userSession.Source.Dynamic.Observe(eventSource.Name, "EventCounters");
                eventStream.Subscribe(onNext: traceEvent =>
                {
                    var payload = traceEvent.PayloadValue(0) as IDictionary <string, object>;
                    if (payload != null)
                    {
                        var key             = $"{traceEvent.ProviderName}-{payload["Name"]}";
                        var eventCounterRow = _eventCounters[key];

                        for (var i = 0; i < _config.Columns.Count; i++)
                        {
                            var rowDataCell = eventCounterRow[i];
                            rowDataCell.UpdateData(payload[_config.Columns[i].Name]?.ToString());
                        }
                    }
                });
            }

            // OK we are all set up, time to listen for events and pass them to the observers.
            userSession.Source.Process();
        }
Beispiel #3
0
    /// <summary>
    /// Dump the  dot net Heap for process 'processID' to the etl file name 'etlFileName'.  Send diagnostics to 'log'.
    /// If 'memoryGraph is non-null also update it to contain the heap Dump.  If null you get just the ETL file.
    /// returns true if successful.
    /// </summary>
    static public bool DumpAsEtlFile(int processID, string etlFileName, TextWriter log, MemoryGraph memoryGraph = null, DotNetHeapInfo dotNetInfo = null)
    {
        bool success = false;

        log.WriteLine("Starting ETW logging on File {0}", etlFileName);
        using (var session = new TraceEventSession("PerfViewGCHeapETLSession", etlFileName))
        {
            session.EnableKernelProvider(KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.Thread | KernelTraceEventParser.Keywords.ImageLoad);

            // Isolate this to a single process.
            var options = new TraceEventProviderOptions()
            {
                ProcessIDFilter = new List <int>()
                {
                    processID
                }
            };

            // For non-project N we need module rundown to figure out the correct module name
            session.EnableProvider(ClrRundownTraceEventParser.ProviderGuid, TraceEventLevel.Verbose,
                                   (ulong)(ClrRundownTraceEventParser.Keywords.Loader | ClrRundownTraceEventParser.Keywords.ForceEndRundown), options);

            session.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Informational,
                                   (ulong)(ClrTraceEventParser.Keywords.GCHeapDump | ClrTraceEventParser.Keywords.GC | ClrTraceEventParser.Keywords.Type | ClrTraceEventParser.Keywords.Type | ClrTraceEventParser.Keywords.GCHeapAndTypeNames), options);
            // Project  N support.
            session.EnableProvider(ClrTraceEventParser.NativeProviderGuid, TraceEventLevel.Informational,
                                   (ulong)(ClrTraceEventParser.Keywords.GCHeapDump | ClrTraceEventParser.Keywords.GC | ClrTraceEventParser.Keywords.Type | ClrTraceEventParser.Keywords.Type | ClrTraceEventParser.Keywords.GCHeapAndTypeNames), options);

            success = Dump(processID, memoryGraph, log, dotNetInfo);
            log.WriteLine("Stopping ETW logging on {0}", etlFileName);
        }

        log.WriteLine("DumpAsETLFile returns.  Success={0}", success);
        return(success);
    }
Beispiel #4
0
        internal static void EnableProvider(
            TraceEventSession session,
            Guid providerId,
            EventLevel level,
            EventKeywords matchAnyKeyword,
            IEnumerable <KeyValuePair <string, string> > arguments,
            IEnumerable <string> processNamesToFilter,
            bool sendManifest = true)
        {
            // Make explicit the invocation for requesting the manifest from the EventSource (Provider).
            var argumentsDictionary = arguments.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

            if (sendManifest)
            {
                argumentsDictionary["Command"] = "SendManifest";
            }

            var options =
                new TraceEventProviderOptions
            {
                Arguments         = argumentsDictionary,
                ProcessNameFilter = processNamesToFilter.ToArray()
            };

            session.EnableProvider(providerId, (TraceEventLevel)level, (ulong)matchAnyKeyword, options);
        }
        internal static void EnableProvider(
            TraceEventSession session,
            Guid providerId,
            EventLevel level,
            EventKeywords matchAnyKeyword,
            IEnumerable<KeyValuePair<string, string>> arguments,
            IEnumerable<string> processNamesToFilter,
            bool sendManifest = true)
        {
            // Make explicit the invocation for requesting the manifest from the EventSource (Provider).
            var argumentsDictionary = arguments.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
            if (sendManifest)
            {
                argumentsDictionary["Command"] = "SendManifest";
            }

            var options =
                new TraceEventProviderOptions
                {
                    Arguments = argumentsDictionary,
                    ProcessNameFilter = processNamesToFilter.ToArray()
                };

            session.EnableProvider(providerId, (TraceEventLevel)level, (ulong)matchAnyKeyword, options);
        }
Beispiel #6
0
        private void ProcessEtwEvents()
        {
            // setup process filter if any
            TraceEventProviderOptions options = null;

            if (_processId != -1)
            {
                options = new TraceEventProviderOptions()
                {
                    ProcessIDFilter = new List <int>()
                    {
                        _processId
                    },
                };
            }

            // register handlers for events on the session source
            // --------------------------------------------------
            RegisterListeners(_session.Source);

            // decide which provider to listen to with filters if needed
            _session.EnableProvider(
                ClrTraceEventParser.ProviderGuid,  // CLR provider
                ((_filter & EventFilter.AllocationTick) == EventFilter.AllocationTick) ?
                TraceEventLevel.Verbose : TraceEventLevel.Informational,
                GetKeywords(),
                options
                );


            // this is a blocking call until the session is disposed
            _session.Source.Process();
        }
Beispiel #7
0
        private void SetupSession()
        {
            session = new TraceEventSession("DelphiTestProvider");

            // ETW buffers events and only delivers them after buffering up for some amount of time.  Thus
            // there is a small delay of about 2-4 seconds between the timestamp on the event (which is very
            // accurate), and the time we actually get the event.
            session.Source.Dynamic.All += delegate(TraceEvent data)
            {
                Interlocked.Increment(ref EventCounter);
                var str = data.ToString();
                Debug.WriteLine(string.Format("GOT Event {0} ", str));

                OnEventAny?.Invoke(this, data.ToString());
            };

            session.Source.Dynamic.AddCallbackForProviderEvent("Delphi-Test-Provider", "Test/Random", delegate(TraceEvent data)
            {
                var index = Interlocked.Increment(ref EventCounter);

                try
                {
                    var obj = new TestEventData(index, data);
                    Debug.WriteLine(obj);
                    OnEvent?.Invoke(this, obj);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("Exception while parsing event: " + ex);
                }
            });

            session.Source.UnhandledEvents += delegate(TraceEvent data)
            {
                if ((int)data.ID != 0xFFFE)         // The EventSource manifest events show up as unhanded, filter them out.
                {
                    Debug.WriteLine("GOT UNHANDLED EVENT: " + data.Dump());
                }
            };

            var traceOptions = new TraceEventProviderOptions
            {
                StacksEnabled = false
            };

            session.EnableProvider("Delphi-Test-Provider", options: traceOptions);

            // go into a loop processing events can calling the callbacks.  Because this is live data (not from a file)
            // processing never completes by itself, but only because someone called 'source.Dispose()'.
            Task.Run(() =>
            {
                session.Source.Process();
            });
        }
Beispiel #8
0
 /// <summary>
 /// Interface to enable user providers.
 /// </summary>
 /// <param name="providerGuid">The Guid that represents the event provider to be enabled.</param>
 /// <param name="providerLevel">The verbosity to turn on.</param>
 /// <param name="matchAnyKeywords">A bitvector representing the areas to turn on. Only the
 /// low 32 bits are used by classic providers and passed as the 'flags' value. Zero
 /// is a special value which is a provider defined default, which is usually 'everything'.
 /// </param>
 /// <param name="options">Additional options for the provider.</param>
 public void EnableProvider(
     Guid providerGuid,
     TraceEventLevel providerLevel     = TraceEventLevel.Verbose,
     ulong matchAnyKeywords            = ulong.MaxValue,
     TraceEventProviderOptions options = null)
 {
     if (TraceEventSession.EnableProvider(providerGuid, providerLevel, matchAnyKeywords, options))
     {
         Debug.WriteLine("The session already existed and needed to be restarted.");
     }
 }
        protected void EnabledDefaultProviders(TraceEventSession session, TraceEventProviderOptions options = null)
        {
            // This provider is required to get ActivityID support on TraceEvents.
            session.EnableProvider(
                TplEtwProviderTraceEventParser.ProviderGuid,
                TraceEventLevel.Informational,
                (ulong)TplEtwProviderTraceEventParser.Keywords.TasksFlowActivityIds,
                options);

            session.EnableProvider(DotNetRuntimeProviderGuid, options: options);
            session.EnableProvider(XamlProviderGuid, options: options);
        }
        protected override void EnableProviders(TraceEventSession session)
        {
            var options = new TraceEventProviderOptions()
            {
                ProcessNameFilter = new string[] { "Quadrant.exe" }
            };

            Guid eventSource = TraceEventProviders.GetEventSourceGuidFromName(QuadrantProvider);

            session.EnableProvider(eventSource, options: options);

            EnabledDefaultProviders(session, options);
        }
 public static Task <bool> RealTimeTraceEventSessionAsync
 (
     string providerName
     , string sessionName
     , string tracingFileName = null
     , string[] traceEvents   = null
     , Action
     <
         long
         , TraceEventDispatcher
         , TraceEventSession
         , TraceEvent
     > onOneEventTracedOnceProcessAction = null
     , TraceEventProviderOptions traceEventProviderOptions = null
     , TraceEventSessionOptions traceEventSessionOptions   = TraceEventSessionOptions.Create
     , TraceEventSourceType traceEventSourceType           = TraceEventSourceType.MergeAll
     , TraceEventLevel traceEventLevel = TraceEventLevel.Always
     , ulong matchKeywords             = ulong.MaxValue
     , bool needCountHits = false
 )
 {
     return
         (Task
          .Factory
          .StartNew <bool>
          (
              () =>
     {
         return
         RealTimeTraceEventSession
         (
             providerName
             , sessionName
             , tracingFileName
             , traceEvents
             , onOneEventTracedOnceProcessAction
             , traceEventProviderOptions
             , traceEventSessionOptions
             , traceEventSourceType
             , traceEventLevel
             , matchKeywords
             , needCountHits
         );
     }
              ,
              TaskCreationOptions.LongRunning
              |
              TaskCreationOptions.DenyChildAttach
          ));
 }
Beispiel #12
0
        static void Main(string[] args)
        {
            using (var session = new TraceEventSession("MonitorKernelAndClrEventsSession"))
            {
                Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs cancelArgs) =>
                {
                    session.Dispose();
                    cancelArgs.Cancel = true;
                };

                //session.EnableKernelProvider(KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.Thread);

                var optionsWithStacks = new TraceEventProviderOptions()
                {
                    StacksEnabled = true
                };
                //session.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)ClrTraceEventParser.Keywords.Default);
                //session.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)ClrTraceEventParser.Keywords.Exception, optionsWithStacks);

                //session.EnableProvider(ClrRundownTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)ClrRundownTraceEventParser.Keywords.Default);

                session.EnableProvider("Microsoft-Windows-TCPIP", TraceEventLevel.Verbose);
#if TRACELOG
                using (TraceLogEventSource traceLogSource = TraceLog.CreateFromTraceEventSession(session))
                {
                    traceLogSource.Clr.ExceptionStart += ClrOnExceptionStart;
                    traceLogSource.Process();
                }
#else
                session.Source.Clr.GCStart     += ClrOnGcStart;
                session.Source.Clr.GCStop      += ClrOnGcStop;
                session.Source.Clr.GCHeapStats += ClrOnGcHeapStats;

                session.Source.Dynamic.All += delegate(Microsoft.Diagnostics.Tracing.TraceEvent data)
                {
                    // ETW buffers events and only delivers them after buffering up for some amount of time.  Thus
                    // there is a small delay of about 2-4 seconds between the timestamp on the event (which is very
                    // accurate), and the time we actually get the event.  We measure that delay here.
                    var delay = (DateTime.Now - data.TimeStamp).TotalSeconds;
                    //Console.WriteLine("GOT Event Delay={0:f1}sec: {1}/{2} ", delay, data.ProviderName, data.EventName);
                };
                session.Source.Dynamic.AddCallbackForProviderEvent("Microsoft-Windows-TCPIP", "TcpSendTransmitted",
                                                                   data => Console.WriteLine($"PID {data.ProcessID} sent {data.PayloadByName("NumBytes")} B"));

                //session.Source.Kernel.ProcessStart += KernelOnProcessStart;
                //session.Source.Kernel.ProcessStop += KernelOnProcessStop;
                session.Source.Process();
#endif
            }
        }
        public bool RealTimeTraceEventSession
        (
            string providerName
            , string sessionName
            , string tracingFileName = null
            , string[] traceEvents   = null
            , Action
            <
                long
                , TraceEventDispatcher
                , TraceEventSession
                , TraceEvent
            > onOneEventTracedOnceProcessAction = null
            , TraceEventProviderOptions traceEventProviderOptions = null
            , TraceEventSessionOptions traceEventSessionOptions   = TraceEventSessionOptions.Create
            , TraceEventSourceType traceEventSourceType           = TraceEventSourceType.MergeAll
            , TraceEventLevel traceEventLevel = TraceEventLevel.Always
            , ulong matchKeywords             = ulong.MaxValue
            , bool needCountHits = false)
        {
            var r = false;

            if
            (
                traceEvents != null
                &&
                traceEvents.Length > 0
                &&
                onOneEventTracedOnceProcessAction != null
            )
            {
                r = TraceEventsHelper
                    .RealTimeTraceEventSession
                    (
                    providerName
                    , sessionName
                    , tracingFileName
                    , traceEvents
                    , onOneEventTracedOnceProcessAction
                    , traceEventProviderOptions
                    , traceEventSessionOptions
                    , traceEventSourceType
                    , traceEventLevel
                    , matchKeywords
                    , needCountHits
                    );
            }
            return(r);
        }
        public static void EnableEventSources(this TraceEventSession session, IEnumerable <EventSourceDefinition> eventSources)
        {
            foreach (var eventSource in eventSources)
            {
                TraceEventProviderOptions providerOptions = new TraceEventProviderOptions()
                {
                    EventIDsToEnable  = eventSource.Ids,
                    ProcessIDFilter   = eventSource.ProcessIds,
                    ProcessNameFilter = eventSource.ProcessNames,
                };

                // If this API fails, it will throw an exception.
                // https://github.com/Microsoft/perfview/blob/master/src/TraceEvent/TraceEventSession.cs
                session.EnableProvider(eventSource.ProviderGuid, eventSource.TraceLevel, eventSource.Keywords, providerOptions);
            }
        }
Beispiel #15
0
        private void RunAsync()
        {
            var elevated = TraceEventSession.IsElevated();

            var eventSourceGuid = TraceEventProviders.GetProviderGuidByName(providerName);

            session       = new TraceEventSession(sessionName, "data.etl");
            kernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName, "data.kernel.etl");

            kernelSession.EnableKernelProvider(KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.Thread);

            var optionsWithStacks = new TraceEventProviderOptions()
            {
                StacksEnabled = true
            };

            session.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)ClrTraceEventParser.Keywords.Default);
            session.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)ClrTraceEventParser.Keywords.GC, optionsWithStacks);
        }
Beispiel #16
0
    /// <summary>
    /// Dump the  dot net Heap for process 'processID' to the etl file name 'etlFileName'.  Send diagnostics to 'log'.
    /// If 'memoryGraph is non-null also update it to contain the heap Dump.  If null you get just the ETL file.
    /// returns true if successful.
    /// </summary>
    static public bool DumpAsEtlFile(int processID, string etlFileName, TextWriter log, MemoryGraph memoryGraph = null, DotNetHeapInfo dotNetInfo = null)
    {
        bool success = false;

        log.WriteLine("Starting ETW logging on File {0}", etlFileName);
        using (var session = new TraceEventSession("PerfViewGCHeapETLSession", etlFileName))
        {
            session.BufferSizeMB = 256;
            session.EnableKernelProvider(KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.Thread | KernelTraceEventParser.Keywords.ImageLoad);

            // Isolate this to a single process.
            var options = new TraceEventProviderOptions()
            {
                ProcessIDFilter = new List <int>()
                {
                    processID
                }
            };

            // There is a bug in the runtime 4.6.2 and earlier where we only clear the table of types we have already emitted when you ENABLE
            // the Clr Provider WITHOUT the ClrTraceEventParser.Keywords.Type keyword. We achieve this by turning on just the GC events,
            // (which clears the Type table) and then turn all the events we need on.
            // Note we do this here, as well as in Dump() because it only works if the CLR Type keyword is off (and we turn it on below)
            session.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Informational, (ulong)ClrTraceEventParser.Keywords.GC, options);
            System.Threading.Thread.Sleep(50);      // Wait for it to complete (it is async)

            // For non-project N we need module rundown to figure out the correct module name
            session.EnableProvider(ClrRundownTraceEventParser.ProviderGuid, TraceEventLevel.Verbose,
                                   (ulong)(ClrRundownTraceEventParser.Keywords.Loader | ClrRundownTraceEventParser.Keywords.ForceEndRundown), options);

            session.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Informational,
                                   (ulong)(ClrTraceEventParser.Keywords.GCHeapDump | ClrTraceEventParser.Keywords.GC | ClrTraceEventParser.Keywords.Type | ClrTraceEventParser.Keywords.GCHeapAndTypeNames), options);
            // Project  N support.
            session.EnableProvider(ClrTraceEventParser.NativeProviderGuid, TraceEventLevel.Informational,
                                   (ulong)(ClrTraceEventParser.Keywords.GCHeapDump | ClrTraceEventParser.Keywords.GC | ClrTraceEventParser.Keywords.Type | ClrTraceEventParser.Keywords.GCHeapAndTypeNames), options);

            success = Dump(processID, memoryGraph, log, dotNetInfo);
            log.WriteLine("Stopping ETW logging on {0}", etlFileName);
        }

        log.WriteLine("DumpAsETLFile returns.  Success={0}", success);
        return(success);
    }
 public Task <bool> TraceETWTraceEventSourceAsync
 (
     string providerName
     , string tracedFileName
     , string[] traceEvents = null
     , Action
     <
         long
         , TraceEventDispatcher
         , TraceEventSession
         , TraceEvent
     > onOneEventTracedOnceProcessAction = null
     , TraceEventProviderOptions traceEventProviderOptions = null
     , TraceEventSourceType traceEventSourceType           = TraceEventSourceType.MergeAll
     , TraceEventLevel traceEventLevel = TraceEventLevel.Always
     , ulong matchKeywords             = ulong.MaxValue
     , bool needCountHits = false
 )
 {
     return
         (Task
          .Factory
          .StartNew <bool>
          (
              () =>
     {
         return
         TraceETWTraceEventSource
         (
             providerName
             , tracedFileName
             , traceEvents
             , onOneEventTracedOnceProcessAction
             , traceEventProviderOptions
             , traceEventSourceType
             , traceEventLevel
             , matchKeywords
             , needCountHits
         );
     }
          ));
 }
Beispiel #18
0
        public override Task StartCollectingAsync()
        {
            // TODO: Allow a file name to be provided
            var outputFile = _config.ProcessId == null?
                             Path.Combine(_config.OutputPath, "dotnet-collect.etl") :
                                 Path.Combine(_config.OutputPath, $"dotnet-collect.{_config.ProcessId.Value}.etl");

            if (File.Exists(outputFile))
            {
                throw new InvalidOperationException($"Target file already exists: {outputFile}");
            }
            _session = new TraceEventSession("dotnet-collect", outputFile);

            if (_config.CircularMB is int circularMb)
            {
                _session.CircularBufferMB = circularMb;
            }

            var options = new TraceEventProviderOptions();

            if (_config.ProcessId is int pid)
            {
                options.ProcessIDFilter = new List <int>()
                {
                    pid
                };
            }

            // Enable the providers requested
            foreach (var provider in _config.Providers)
            {
                _session.EnableProvider(provider.Provider, ConvertLevel(provider.Level), provider.Keywords, options);
            }

            return(Task.CompletedTask);
        }
        /// <summary>
        /// CollectData doe will turn on logging of data from 'eventSourceName' to the file 'dataFileName'.
        /// It will then call EventGenerator.CreateEvents and wait 12 seconds for it to generate some data.
        /// </summary>
        static void CollectData(string eventSourceName, string dataFileName)
        {
            // Today you have to be Admin to turn on ETW events (anyone can write ETW events).
            if (!(TraceEventSession.IsElevated() ?? false))
            {
                Out.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process.");
                Debugger.Break();
                return;
            }

            // As mentioned below, sessions can outlive the process that created them.  Thus you need a way of
            // naming the session so that you can 'reconnect' to it from another process.   This is what the name
            // is for.  It can be anything, but it should be descriptive and unique.   If you expect multiple versions
            // of your program to run simultaneously, you need to generate unique names (e.g. add a process ID suffix)
            // however this is dangerous because you can leave data collection on if the program ends unexpectedly.
            //
            // In this case we tell the session to place the data in MonitorToFileData.etl.
            var sessionName = "SimpleTraceLogSession";

            Out.WriteLine("Creating a '{0}' session writing to {1}", sessionName, dataFileName);
            Out.WriteLine("Use 'logman query -ets' to see active sessions.");
            Out.WriteLine("Use 'logman stop {0} -ets' to manually stop orphans.", sessionName);
            using (var session = new TraceEventSession(sessionName, dataFileName))      // Since we give it a file name, the data goes there.
                using (var kernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName, Path.ChangeExtension(dataFileName, ".kernel.etl")))
                {
                    /* BY DEFAULT ETW SESSIONS SURVIVE THE DEATH OF THE PROESS THAT CREATES THEM! */
                    // Unlike most other resources on the system, ETW session live beyond the lifetime of the
                    // process that created them.   This is very useful in some scenarios, but also creates the
                    // very real possibility of leaving 'orphan' sessions running.
                    //
                    // To help avoid this by default TraceEventSession sets 'StopOnDispose' so that it will stop
                    // the ETW session if the TraceEventSession dies.   Thus executions that 'clean up' the TraceEventSession
                    // will clean up the ETW session.   This covers many cases (including throwing exceptions)
                    //
                    // However if the process is killed manually (including control C) this cleanup will not happen.
                    // Thus best practices include
                    //
                    //     * Add a Control C handler that calls session.Dispose() so it gets cleaned up in this common case
                    //     * use the same session name run-to-run so you don't create many orphans.
                    //
                    // By default TraceEventSessions are in 'create' mode where it assumes you want to create a new session.
                    // In this mode if a session already exists, it is stopped and the new one is created.
                    //
                    // Here we install the Control C handler.   It is OK if Dispose is called more than once.
                    Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { session.Dispose(); kernelSession.Dispose(); };

                    // Enable kernel events.
                    kernelSession.EnableKernelProvider(KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.Thread);

                    // Enable my provider, you can call many of these on the same session to get events from other providers

                    // Turn on the eventSource given its name.
                    // Note we turn on Verbose level all keywords (ulong.MaxValue == 0xFFF....) and turn on stacks for
                    // this provider (for all events, until Windows 8.1 you can only turn on stacks for every event
                    // for a particular provider or no stacks)
                    var options = new TraceEventProviderOptions()
                    {
                        StacksEnabled = true
                    };
                    var restarted = session.EnableProvider(eventSourceName, TraceEventLevel.Verbose, ulong.MaxValue, options);
                    if (restarted)  // Generally you don't bother with this warning, but for the demo we do.
                    {
                        Out.WriteLine("The session {0} was already active, it has been restarted.", sessionName);
                    }

                    // We also turn on CLR events because we need them to decode Stacks and we also get exception events (and their stacks)
                    session.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)ClrTraceEventParser.Keywords.Default);

                    // Start another thread that Causes MyEventSource to create some events
                    // Normally this code as well as the EventSource itself would be in a different process.
                    EventGenerator.CreateEvents();

                    // Also generate some exceptions so we have interesting stacks to look at
                    Thread.Sleep(100);
                    EventGenerator.GenerateExceptions();

                    Out.WriteLine("Waiting 12 seconds for events to come in.");
                    Thread.Sleep(12000);

                    // Because the process in question (this process) lives both before and after the time the events were
                    // collected, we don't have complete information about JIT compiled methods in that method.   There are
                    // some methods that were JIT compiled before the session started (e.g. SimpleTraceLog.Main) for which
                    // we do not have information.   We collect this by forcing a CLR 'rundown' which will dump method information
                    // for JIT compiled methods that were not present.  If you know that the process of interest ends before
                    // data collection ended or that data collection started before the process started, then this is not needed.
                    Out.WriteLine("Forcing rundown of JIT methods.");
                    var rundownFileName = Path.ChangeExtension(dataFileName, ".clrRundown.etl");
                    using (var rundownSession = new TraceEventSession(sessionName + "Rundown", rundownFileName))
                    {
                        rundownSession.EnableProvider(ClrRundownTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)ClrRundownTraceEventParser.Keywords.Default);
                        // Poll until 2 second goes by without growth.
                        for (var prevLength = new FileInfo(rundownFileName).Length; ;)
                        {
                            Thread.Sleep(2000);
                            var newLength = new FileInfo(rundownFileName).Length;
                            if (newLength == prevLength)
                            {
                                break;
                            }
                            prevLength = newLength;
                        }
                    }
                    Out.WriteLine("Done with rundown.");
                }

            Out.WriteLine("Zipping the raw files into a single '{0}' file.", dataFileName);

            // At this point you have multiple ETL files that don't have all the information
            // inside them necessary for analysis off the currentn machine.    To do analsysis
            // of the machine you need to merge the ETL files (which can be done with
            //        TraceEventSession.MergeInPlace(dataFileName, Out);
            // However this does not get the symbolic information (NGEN PDBS) needed to
            // decode the stacks in the .NET managed framewrok on another machine.
            // To do the merging AND generate these NGEN images it is best to us ethe
            // ZipppedETLWriter that does all this (and compresses all the files in a ZIP archive).

            ZippedETLWriter writer = new ZippedETLWriter(dataFileName, Out);

            writer.WriteArchive();

            Out.WriteLine("Zip complete, output file = {0}", writer.ZipArchivePath);
        }
        public static bool TraceETWTraceEventSource
        (
            string providerName
            , string tracedFileName
            , string[] traceEvents = null
            , Action
            <
                long
                , TraceEventDispatcher
                , TraceEventSession
                , TraceEvent
            > onOneEventTracedOnceProcessAction = null
            , TraceEventProviderOptions traceEventProviderOptions = null
            , TraceEventSourceType traceEventSourceType           = TraceEventSourceType.MergeAll
            , TraceEventLevel traceEventLevel = TraceEventLevel.Always
            , ulong matchKeywords             = ulong.MaxValue
            , bool needCountHits = false
        )
        {
            var r = false;

            if (!(TraceEventSession.IsElevated() ?? false))
            {
                Out.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process.");
                return(r);
            }
            if
            (
                traceEvents != null
                &&
                traceEvents.Length > 0
                &&
                onOneEventTracedOnceProcessAction != null
            )
            {
                using (var source = new ETWTraceEventSource(tracedFileName, traceEventSourceType))
                {
                    //闭包
                    long sequence = 0;
                    RegisterCallbacks
                    (
                        providerName
                        , traceEvents
                        , source
                        , null
                        , (x, y, z) =>
                    {
                        long id = 0;
                        if (needCountHits)
                        {
                            id = Interlocked.Increment(ref sequence);
                        }
                        onOneEventTracedOnceProcessAction
                        (
                            id
                            , x
                            , y
                            , z
                        );
                    }
                    );
                    source.Process();   // call the callbacks for each event
                }
            }
            return(true);
        }
Beispiel #21
0
        static void Main(string[] args)
        {
            if (args.Length == 2 && args[0] == "--bodies" && System.IO.Directory.Exists(args[1]))
            {
                body_dir = args[1];
            }

            // read settings for custom ETW providers
            if (File.Exists(customProvidersConfigPath))
            {
                try
                {
                    customProviders = JsonConvert.DeserializeObject <Dictionary <string, CustomProvider> >(File.ReadAllText(customProvidersConfigPath));
                }
                catch (Exception e) {
                    Console.WriteLine("{0} Exception caught.", e);
                }
            }

            // perf optimization - warm up the Json serializer to avoid a big perf hit serializing the first event while the test is running
            // reduces the observer effect of the exe
            var serializedWinInetEvents = JsonConvert.SerializeObject(WinInetEvents);

            // create a real time user mode session
            using (session = new TraceEventSession("wpt-etw"))
            {
                session.StopOnDispose = true;
                // Set up Ctrl-C to stop the session
                Console.CancelKeyPress += (object s, ConsoleCancelEventArgs cancel_args) => session.Stop();

                session.Source.Dynamic.All += delegate(TraceEvent data)
                {
                    try
                    {
                        int    eventId   = (int)data.ID;
                        string eventName = null;
                        bool   keep      = false;
                        if (data.ProviderName == "Microsoft-Windows-WinINet-Capture")
                        {
                            if (data.ActivityID != Guid.Empty && data.EventName == "EventID(2004)")
                            {
                                var raw = data.PayloadByName("Payload") as byte[];
                                if (raw != null && raw.Length > 0)
                                {
                                    string activity = data.ActivityID.ToString("D");
                                    string path     = body_dir + "\\" + activity;
                                    try
                                    {
                                        using (var stream = new FileStream(path, FileMode.Append))
                                        {
                                            stream.Write(raw, 0, raw.Length);
                                        }
                                    }
                                    catch (Exception e) {
                                        Console.WriteLine("{0} Exception caught.", e);
                                    }
                                }
                            }
                        }
                        else if (data.ProviderName == "Microsoft-IE" &&
                                 IEEvents.ContainsKey(eventId))
                        {
                            keep      = true;
                            eventName = IEEvents[eventId];
                        }
                        else if (data.ProviderName == "Microsoft-Windows-WinINet" &&
                                 WinInetEvents.ContainsKey(eventId))
                        {
                            keep      = true;
                            eventName = WinInetEvents[eventId];
                        }
                        else if (customProviders.ContainsKey(data.ProviderName) &&
                                 (customProviders[data.ProviderName].EventNames == null ||
                                  customProviders[data.ProviderName].EventNames.Count() < 1 ||
                                  customProviders[data.ProviderName].EventNames.Contains(data.EventName)))
                        {
                            keep = true;
                        }

                        if (keep)
                        {
                            Dictionary <string, dynamic> evt = new Dictionary <string, dynamic>();
                            evt["Provider"] = data.ProviderName;
                            evt["Event"]    = eventName == null ? data.EventName : eventName;
                            evt["EID"]      = eventId;
                            evt["ts"]       = data.TimeStampRelativeMSec;
                            if (data.ActivityID != Guid.Empty)
                            {
                                evt["Activity"] = data.ActivityID.ToString("D");
                            }
                            if (data.RelatedActivityID != Guid.Empty)
                            {
                                evt["RelatedActivity"] = data.RelatedActivityID;
                            }
                            if (data.ProcessID >= 0)
                            {
                                evt["pid"] = data.ProcessID;
                            }
                            if (data.ThreadID >= 0)
                            {
                                evt["tid"] = data.ThreadID;
                            }
                            if (data.PayloadNames.Count() > 0)
                            {
                                Dictionary <string, dynamic> values = new Dictionary <string, dynamic>();
                                foreach (string name in data.PayloadNames)
                                {
                                    values[name] = data.PayloadByName(name);
                                }
                                evt["data"] = values;
                            }

                            //evt["ascii"] = System.Text.Encoding.ASCII.GetString(data.EventData());
                            //evt["raw"] = data.EventData();
                            string json = JsonConvert.SerializeObject(evt);
                            mutex.WaitOne();
                            events.Append(json).Append("\n");
                            mutex.ReleaseMutex();
                            //Debug.WriteLine(json.Trim());
                            //Console.WriteLine(json.Trim());
                        }
                    }
                    catch (Exception e1) {
                        Console.WriteLine("{0} Exception caught.", e1);
                    }
                };

                if (body_dir.Length > 0)
                {
                    session.EnableProvider("Microsoft-Windows-WinInet-Capture");
                }

                var WinInetProviderFilterOptions = new TraceEventProviderOptions()
                {
                    EventIDsToEnable = new List <int>(WinInetEvents.Keys)
                };
                session.EnableProvider("Microsoft-Windows-WinINet", TraceEventLevel.Informational, ulong.MaxValue, WinInetProviderFilterOptions);

                var IEProviderFilterOptions = new TraceEventProviderOptions()
                {
                    EventIDsToEnable = new List <int>(IEEvents.Keys)
                };
                session.EnableProvider("Microsoft-IE", TraceEventLevel.Informational, 0x4001302, IEProviderFilterOptions);

                if (customProviders.Count > 0)
                {
                    foreach (var provider in customProviders)
                    {
                        if (provider.Value.EventIDs == null || provider.Value.EventIDs.Count() < 1)
                        {
                            continue;
                        }
                        var customProviderFilterOptions = new TraceEventProviderOptions()
                        {
                            EventIDsToEnable = new List <int>(provider.Value.EventIDs)
                        };

                        session.EnableProvider(provider.Key,
                                               (TraceEventLevel)provider.Value.Verbosity,
                                               provider.Value.Filter,
                                               customProviderFilterOptions);
                    }
                }

                must_exit = false;
                var thread = new Thread(ThreadProc);
                thread.Start();
                try
                {
                    session.Source.Process();   // Listen (forever) for events
                }
                catch { }
                must_exit = true;
                thread.Join();
            }
        }
        public static bool RealTimeTraceEventSession
            (
                string providerName
                , string sessionName
                , string tracingFileName = null
                , string[] traceEvents = null
                , Action
                        <
                            long
                            , TraceEventDispatcher
                            , TraceEventSession
                            , TraceEvent
                        > onOneEventTracedOnceProcessAction = null
                , TraceEventProviderOptions traceEventProviderOptions = null
                , TraceEventSessionOptions traceEventSessionOptions = TraceEventSessionOptions.Create
                , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll
                , TraceEventLevel traceEventLevel = TraceEventLevel.Always
                , ulong matchKeywords = ulong.MaxValue
                , bool needCountHits = false
            )
        {
            var r = false;
            if (!(TraceEventSession.IsElevated() ?? false))
            {
                Out.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process.");
                return r;
            }
            var needTracingFile = !string.IsNullOrEmpty(tracingFileName);
            if
                (
                    traceEvents != null
                    &&
                    traceEvents.Length > 0
                    &&
                    onOneEventTracedOnceProcessAction != null
                )
            {
                using
                    (
                        var session =
                                (
                                    needTracingFile
                                    ?
                                    new TraceEventSession
                                                (
                                                    sessionName
                                                    , tracingFileName
                                                    , traceEventSessionOptions
                                                )
                                    {
                                        StopOnDispose = true
                                    }
                                    :
                                    new TraceEventSession
                                                (
                                                    sessionName
                                                    , traceEventSessionOptions
                                                )
                                    {
                                        StopOnDispose = true
                                    }
                                )
                    )
                {
                    using
                        (
                            var source =
                                        (
                                            needTracingFile
                                            ?
                                            new ETWTraceEventSource(tracingFileName)
                                            :
                                            session.Source
                                        )
                            )
                    {
                        long sequence = 0;
                        RegisterCallbacks
                            (
                                providerName
                                , traceEvents
                                , source
                                , session
                                , (x, y, z) =>
                                {
                                    long id = 0;
                                    if (needCountHits)
                                    {
                                        id = Interlocked.Increment(ref sequence);
                                    }
                                    onOneEventTracedOnceProcessAction
                                                    (
                                                        id
                                                        , x
                                                        , y
                                                        , z
                                                    );
                                }
                            );
                        var restarted = session
                                            .EnableProvider
                                                (
                                                    providerName
                                                    , traceEventLevel
                                                    , matchKeywords
                                                    , traceEventProviderOptions
                                                );
                        source
                            .Process();
                        r = true;
                    }
                }

            }
            return r;
        }
 public static bool TraceETWTraceEventSource
                     (
                         string providerName
                         , string tracedFileName
                         , string[] traceEvents = null
                         , Action
                                 <
                                     long
                                     , TraceEventDispatcher
                                     , TraceEventSession
                                     , TraceEvent
                                 > onOneEventTracedOnceProcessAction = null
                         , TraceEventProviderOptions traceEventProviderOptions = null
                         , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll
                         , TraceEventLevel traceEventLevel = TraceEventLevel.Always
                         , ulong matchKeywords = ulong.MaxValue
                         , bool needCountHits = false
                     )
 {
     var r = false;
     if (!(TraceEventSession.IsElevated() ?? false))
     {
         Out.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process.");
         return r;
     }
     if
         (
             traceEvents != null
             &&
             traceEvents.Length > 0
             &&
             onOneEventTracedOnceProcessAction != null
         )
     {
         using (var source = new ETWTraceEventSource(tracedFileName, traceEventSourceType))
         {
             //闭包
             long sequence = 0;
             RegisterCallbacks
                 (
                     providerName
                     , traceEvents
                     , source
                     , null
                     , (x, y, z) =>
                     {
                         long id = 0;
                         if (needCountHits)
                         {
                             id = Interlocked.Increment(ref sequence);
                         }
                         onOneEventTracedOnceProcessAction
                                         (
                                             id
                                             , x
                                             , y
                                             , z
                                         );
                     }
                 );
             source.Process();   // call the callbacks for each event
         }
     }
     return true;
 }
 public Task<bool> TraceETWTraceEventSourceAsync
             (
                 string providerName
                 , string tracedFileName
                 , string[] traceEvents = null
                 , Action
                         <
                             long
                             , TraceEventDispatcher
                             , TraceEventSession
                             , TraceEvent
                         > onOneEventTracedOnceProcessAction = null
                 , TraceEventProviderOptions traceEventProviderOptions = null
                 , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll
                 , TraceEventLevel traceEventLevel = TraceEventLevel.Always
                 , ulong matchKeywords = ulong.MaxValue
                 , bool needCountHits = false
             )
 {
     return
         Task
             .Factory
             .StartNew<bool>
                 (
                     () =>
                     {
                         return
                             TraceETWTraceEventSource
                             (
                                 providerName
                                 , tracedFileName
                                 , traceEvents
                                 , onOneEventTracedOnceProcessAction
                                 , traceEventProviderOptions
                                 , traceEventSourceType
                                 , traceEventLevel
                                 , matchKeywords
                                 , needCountHits
                             );
                     }
                 );
 }
Beispiel #25
0
    /// <summary>
    /// Add the nodes of the .NET heap for the process 'processID' to 'memoryGraph' sending diagnostic
    /// messages to 'log'.  If 'memoryGraph' is null, the ETW providers are triggered by we obviously
    /// don't update the memoryGraph.  Thus it is only done for the side effect of triggering a .NET heap
    /// dump. returns true if successful.
    /// </summary>
    static public bool Dump(int processID, MemoryGraph memoryGraph, TextWriter log, DotNetHeapInfo dotNetInfo = null)
    {
        var sw     = Stopwatch.StartNew();
        var dumper = new DotNetHeapDumpGraphReader(log);

        dumper.DotNetHeapInfo = dotNetInfo;
        bool dumpComplete         = false;
        bool listening            = false;
        TraceEventSession session = null;
        Task readerTask           = null;

        try
        {
            bool     etwDataPresent = false;
            TimeSpan lastEtwUpdate  = sw.Elapsed;
            // Set up a separate thread that will listen for ETW events coming back telling us we succeeded.
            readerTask = Task.Factory.StartNew(delegate
            {
                string sessionName   = "PerfViewGCHeapSession";
                session              = new TraceEventSession(sessionName, null);
                int gcNum            = -1;
                session.BufferSizeMB = 256;         // Events come pretty fast, so make the buffer bigger.

                // Start the providers and trigger the GCs.
                log.WriteLine("{0,5:n1}s: Requesting a .NET Heap Dump", sw.Elapsed.TotalSeconds);
                // Have to turn on Kernel provider first (before touching Source) so we do it here.
                session.EnableKernelProvider(KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.ImageLoad);

                session.Source.Clr.GCStart += delegate(GCStartTraceData data)
                {
                    if (data.ProcessID != processID)
                    {
                        return;
                    }
                    etwDataPresent = true;

                    if (gcNum < 0 && data.Depth == 2 && data.Type != GCType.BackgroundGC)
                    {
                        gcNum = data.Count;
                        log.WriteLine("{0,5:n1}s: Dot Net Dump Started...", sw.Elapsed.TotalSeconds);
                    }
                };

                session.Source.Clr.GCStop += delegate(GCEndTraceData data)
                {
                    if (data.ProcessID != processID)
                    {
                        return;
                    }

                    if (data.Count == gcNum)
                    {
                        log.WriteLine("{0,5:n1}s: DotNet GC Complete.", sw.Elapsed.TotalSeconds);
                        dumpComplete = true;
                    }
                };

                session.Source.Clr.GCBulkNode += delegate(GCBulkNodeTraceData data)
                {
                    if (data.ProcessID != processID)
                    {
                        return;
                    }
                    etwDataPresent = true;

                    if ((sw.Elapsed - lastEtwUpdate).TotalMilliseconds > 500)
                    {
                        log.WriteLine("{0,5:n1}s: Making  GC Heap Progress...", sw.Elapsed.TotalSeconds);
                    }
                    lastEtwUpdate = sw.Elapsed;
                };

                if (memoryGraph != null)
                {
                    dumper.SetupCallbacks(memoryGraph, session.Source, processID.ToString());
                }

                listening = true;
                session.Source.Process();
                log.WriteLine("{0,5:n1}s: ETW Listener dieing", sw.Elapsed.TotalSeconds);
            });

            // Wait for thread above to start listening (should be very fast)
            while (!listening)
            {
                readerTask.Wait(1);
            }
            Debug.Assert(session != null);

            // Request the heap dump.   We try to isolate this to a single process.
            var options = new TraceEventProviderOptions()
            {
                ProcessIDFilter = new List <int>()
                {
                    processID
                }
            };

            // There is a bug in the runtime 4.6.2 and earlier where we only clear the table of types we have already emitted when you ENABLE
            // the Clr Provider WITHOUT the ClrTraceEventParser.Keywords.Type keyword.  we achive this by turning on just the GC events,
            // (which clears the Type table) and then turn all the events we need on.
            session.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Informational, (ulong)ClrTraceEventParser.Keywords.GC, options);
            System.Threading.Thread.Sleep(50);      // Wait for it to complete (it is async)

            // For non-project N we need module rundown to figure out the correct module name
            session.EnableProvider(ClrRundownTraceEventParser.ProviderGuid, TraceEventLevel.Verbose,
                                   (ulong)(ClrRundownTraceEventParser.Keywords.Loader | ClrRundownTraceEventParser.Keywords.ForceEndRundown), options);

            session.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Informational, (ulong)ClrTraceEventParser.Keywords.GCHeapSnapshot, options);
            // Project N support.
            session.EnableProvider(ClrTraceEventParser.NativeProviderGuid, TraceEventLevel.Informational, (ulong)ClrTraceEventParser.Keywords.GCHeapSnapshot, options);

            for (;;)
            {
                if (readerTask.Wait(100))
                {
                    break;
                }

                if (!etwDataPresent && sw.Elapsed.TotalSeconds > 5)      // Assume it started within 5 seconds.
                {
                    log.WriteLine("{0,5:n1}s: Assume no Dot Heap", sw.Elapsed.TotalSeconds);
                    break;
                }
                if (sw.Elapsed.TotalSeconds > 100)       // Time out after 100 seconds.
                {
                    log.WriteLine("{0,5:n1}s: Timed out after 100 seconds", sw.Elapsed.TotalSeconds);
                    break;
                }
                // TODO FIX NOW, time out faster if we seek to be stuck
                if (dumpComplete)
                {
                    break;
                }
            }
            if (etwDataPresent)
            {
                dumper.ConvertHeapDataToGraph();        // Finish the conversion.
            }
        }
        finally
        {
            // Stop the ETW providers
            log.WriteLine("{0,5:n1}s: Shutting down ETW session", sw.Elapsed.TotalSeconds);
            if (session != null)
            {
                session.Dispose();
            }
        }
        if (readerTask != null)
        {
            log.WriteLine("{0,5:n1}s: Waiting for shutdown to complete.", sw.Elapsed.TotalSeconds);
            if (!readerTask.Wait(2000))
            {
                log.WriteLine("{0,5:n1}s: Shutdown wait timed out after 2 seconds.", sw.Elapsed.TotalSeconds);
            }
        }
        log.WriteLine("[{0,5:n1}s: Done Dumping .NET heap success={1}]", sw.Elapsed.TotalSeconds, dumpComplete);

        return(dumpComplete);
    }
Beispiel #26
0
        public static string Start(string etlPath, IEnumerable<ProviderInfo> providerInfo, int bufferSizeMB = 64, bool stacksEnabled = false)
        {
            EnsureUnloadHandlerRegistered();

            var userSessionName = "xunit.performance.logger." + Guid.NewGuid().ToString();
            Sessions sessions = new Sessions();
            sessions.UserFileName = Path.ChangeExtension(etlPath, ".user.etl");
            sessions.KernelFileName = Path.ChangeExtension(etlPath, ".kernel.etl");
            sessions.MergedFileName = etlPath;

            var mergedProviderInfo = ProviderInfo.Merge(providerInfo);

            try
            {
                sessions.UserSession = new TraceEventSession(userSessionName, sessions.UserFileName);
                sessions.UserSession.BufferSizeMB = bufferSizeMB;

                if (IsWin8OrGreater)
                {
                    var availableCpuCounters = TraceEventProfileSources.GetInfo();
                    var cpuCounterIds = new List<int>();
                    var cpuCounterIntervals = new List<int>();
                    foreach (var cpuInfo in mergedProviderInfo.OfType<CpuCounterInfo>())
                    {
                        ProfileSourceInfo profInfo;
                        if (availableCpuCounters.TryGetValue(cpuInfo.CounterName, out profInfo))
                        {
                            cpuCounterIds.Add(profInfo.ID);
                            cpuCounterIntervals.Add(Math.Min(profInfo.MaxInterval, Math.Max(profInfo.MinInterval, cpuInfo.Interval)));
                        }
                    }

                    if (cpuCounterIds.Count > 0)
                        TraceEventProfileSources.Set(cpuCounterIds.ToArray(), cpuCounterIntervals.ToArray());
                }

                var kernelInfo = mergedProviderInfo.OfType<KernelProviderInfo>().FirstOrDefault();
                if (kernelInfo != null && NeedSeparateKernelSession(kernelInfo.Keywords))
                {
                    sessions.KernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName, sessions.KernelFileName);
                    sessions.KernelSession.BufferSizeMB = bufferSizeMB;
                }
                else
                {
                    sessions.KernelFileName = sessions.UserFileName;
                    sessions.KernelSession = sessions.UserSession;
                }

                if (kernelInfo != null)
                {
                    var kernelKeywords = (KernelTraceEventParser.Keywords)kernelInfo.Keywords;
                    var kernelStackKeywords = stacksEnabled ? (KernelTraceEventParser.Keywords)kernelInfo.StackKeywords : KernelTraceEventParser.Keywords.None;
                    sessions.KernelSession.EnableKernelProvider(kernelKeywords, kernelStackKeywords);
                }

                ulong profilerKeywords = 0;
                var stacksEnabledOptions = new TraceEventProviderOptions() { StacksEnabled = true };

                foreach (var userInfo in mergedProviderInfo.OfType<UserProviderInfo>())
                {
                    if (userInfo.StacksEnabled == true && stacksEnabled)
                        sessions.UserSession.EnableProvider(userInfo.ProviderGuid, userInfo.Level, userInfo.Keywords, stacksEnabledOptions);
                    else
                        sessions.UserSession.EnableProvider(userInfo.ProviderGuid, userInfo.Level, userInfo.Keywords);
                    if (userInfo.ProviderGuid == ETWClrProfilerTraceEventParser.ProviderGuid)
                        profilerKeywords |= userInfo.Keywords;
                }

                if (profilerKeywords != 0)
                    InstallETWClrProfiler((int)profilerKeywords);

                s_sessions[userSessionName] = sessions;
            }
            catch
            {
                sessions.Close();
                throw;
            }

            return userSessionName;
        }
Beispiel #27
0
        public static void Run(bool workAround)
        {
            Console.WriteLine("");
            Console.WriteLine($"Running {nameof(Repro)} with {nameof(Repro)}={workAround}");
            var eventSourceName = EventSource.GetName(typeof(ReproEventSource));
            var sessionName     = eventSourceName;

            Console.WriteLine("Creating a '{0}' session", sessionName);
            using (var session = new TraceEventSession(sessionName))
            {
                bool isProcessing = false;
                Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs e) => { session.Dispose(); };

                // Write events received and stop on StopEvent
                session.Source.Dynamic.All += (TraceEvent data) =>
                {
                    Console.WriteLine($"RECEIVED event {data.ID}");
                    if ((ushort)data.ID == ReproEventSource.Events.StopEvent)
                    {
                        Console.WriteLine($"End of {nameof(Repro)} - disposing session");
                        session.Dispose();
                    }
                };

                void EnableWithoutOptions()
                {
                    Console.WriteLine("Enabling provider WITHOUT options");
                    session.EnableProvider(eventSourceName);
                    Console.WriteLine("Provider enabled WITHOUT options");
                }

                void LoadEventSource()
                {
                    var dummy = ReproEventSource.Log;
                }

                void EnableWithOptions()
                {
                    Console.WriteLine("Enabling provider WITH options");
                    var options = new TraceEventProviderOptions();

                    options.EventIDsToEnable =
                        new int[] {
                        ReproEventSource.Events.Event1,
                        ReproEventSource.Events.StopEvent
                    };
                    session.EnableProvider(eventSourceName, options: options);
                    Console.WriteLine("Provider enabled WITH options");
                }

                void GenerateEvents()
                {
                    Task.Factory.StartNew(() =>
                    {
                        Console.WriteLine($"WRITING event {ReproEventSource.Events.Event1}");
                        ReproEventSource.Log.Event1();
                        Console.WriteLine($"WRITING event {ReproEventSource.Events.Event2}");
                        ReproEventSource.Log.Event2();
                        Console.WriteLine($"WRITING event {ReproEventSource.Events.StopEvent}");
                        ReproEventSource.Log.StopEvent();
                    });
                }

                void WaitALittle(TimeSpan timeSpan, string message)
                {
                    Console.WriteLine($"WAITING {timeSpan.ToString()} - {message}");
                    Thread.Sleep(timeSpan);
                    Console.WriteLine($"END WAITING {timeSpan.ToString()} - {message}");
                }

                void EnsureProcessing()
                {
                    if (!isProcessing)
                    {
                        Task.Factory.StartNew(() =>
                        {
                            WaitALittle(TimeSpan.FromMilliseconds(100), "before processing");
                            Console.WriteLine("Starting processing");
                            isProcessing = true;
                            session.Source.Process();
                        });
                    }
                }

                if (workAround)
                {
                    LoadEventSource();
                    EnableWithoutOptions();
                    EnsureProcessing();
                    WaitALittle(TimeSpan.FromSeconds(4), "processing warmup");
                }

                EnableWithOptions();
                EnsureProcessing();
                WaitALittle(TimeSpan.FromSeconds(4), "before generating events");
                GenerateEvents();
                WaitALittle(TimeSpan.FromSeconds(4), "processing events");
            }
        }
        public void ProcessEvents()
        {
            // setup process filter if any
            TraceEventProviderOptions options = null;

            if (_processId != -1)
            {
                options = new TraceEventProviderOptions()
                {
                    ProcessIDFilter = new List <int>()
                    {
                        _processId
                    },
                };
            }

            // register handlers for events on the session source
            // --------------------------------------------------

            // get exceptions
            _session.Source.Clr.ExceptionStart += OnExceptionStart;

            // get finalizers
            _session.Source.Clr.TypeBulkType     += OnTypeBulkType;
            _session.Source.Clr.GCFinalizeObject += OnGCFinalizeObject;

            // get thread contention time
            _session.Source.Clr.ContentionStart += OnContentionStart;
            _session.Source.Clr.ContentionStop  += OnContentionStop;

            // get GC details
            _session.Source.Clr.GCHeapStats      += OnGCHeapStats;
            _session.Source.Clr.GCStop           += OnGCStop;
            _session.Source.Clr.GCAllocationTick += ClrOnGcAllocationTick;

            // thread creation and run (but no exit)
            _session.Source.Clr.ThreadCreating += ClrOnThreadCreating;
            _session.Source.Clr.ThreadRunning  += ClrOnThreadRunning;

            // thread pool
            _session.Source.Clr.ThreadPoolWorkingThreadCountStart += ClrOnThreadPoolWorkingThreadCountStart;
            _session.Source.Clr.ThreadPoolWorkerThreadStart       += ClrOnThreadPoolWorkerThreadStart;
            _session.Source.Clr.ThreadPoolWorkerThreadStop        += ClrOnThreadPoolWorkerThreadStop;
            _session.Source.Clr.ThreadPoolEnqueue += ClrOnThreadPoolEnqueue;
            _session.Source.Clr.ThreadPoolDequeue += ClrOnThreadPoolDequeue;

            // decide which provider to listen to with filters if needed
            _session.EnableProvider(
                ClrTraceEventParser.ProviderGuid,  // CLR provider
                TraceEventLevel.Verbose,
                (ulong)(
                    ClrTraceEventParser.Keywords.Contention |         // thread contention timing
                    ClrTraceEventParser.Keywords.Threading |          // threadpool events
                    ClrTraceEventParser.Keywords.Exception |          // get the first chance exceptions
                    ClrTraceEventParser.Keywords.GCHeapAndTypeNames | // for finalizer type names
                    ClrTraceEventParser.Keywords.Type |               // for TypeBulkType definition of types
                    ClrTraceEventParser.Keywords.GC                   // garbage collector details
                    ),
                options
                );

            // this is a blocking call until the session is disposed
            _session.Source.Process();
        }
        public bool EnableProvider(string providerName, TraceEventLevel providerLevel = TraceEventLevel.Verbose, ulong matchAnyKeywords = ulong.MaxValue, TraceEventProviderOptions options = null)
        {
            if (this.isFakeAccessDenied)
            {
                throw new UnauthorizedAccessException("Access Denied.");
            }

            this.EnabledProviderNames.Add(providerName);
            return(true);
        }
Beispiel #30
0
 public bool EnableProvider(TraceEventLevel level = TraceEventLevel.Verbose, ulong matchAnyKeywords = ulong.MaxValue, TraceEventProviderOptions options = null)
 {
     return(session.EnableProvider(WinINetProviderId, TraceEventLevel.Verbose, matchAnyKeywords, options));
 }
 public bool EnableProvider(string providerName, TraceEventLevel providerLevel = TraceEventLevel.Verbose, ulong matchAnyKeywords = ulong.MaxValue, TraceEventProviderOptions options = null)
 {
     return(this.session.EnableProvider(providerName, providerLevel, matchAnyKeywords, options));
 }
        public void ProcessEvents()
        {
            // setup process filter if any
            TraceEventProviderOptions options = null;

            if (_processId != -1)
            {
                options = new TraceEventProviderOptions()
                {
                    ProcessIDFilter = new List <int>()
                    {
                        _processId
                    },
                };
            }

            // register handlers for events on the session source
            // --------------------------------------------------

            if ((_filter & EventFilter.Exception) == EventFilter.Exception)
            {
                // get exceptions
                _session.Source.Clr.ExceptionStart += OnExceptionStart;
            }

            if ((_filter & EventFilter.Finalizer) == EventFilter.Finalizer)
            {
                // get finalizers
                _session.Source.Clr.TypeBulkType     += OnTypeBulkType;
                _session.Source.Clr.GCFinalizeObject += OnGCFinalizeObject;
            }

            if ((_filter & EventFilter.Contention) == EventFilter.Contention)
            {
                // get thread contention time
                _session.Source.Clr.ContentionStart += OnContentionStart;
                _session.Source.Clr.ContentionStop  += OnContentionStop;
            }

            if ((_filter & EventFilter.ThreadStarvation) == EventFilter.ThreadStarvation)
            {
                // detect ThreadPool starvation
                _session.Source.Clr.ThreadPoolWorkerThreadAdjustmentAdjustment += OnThreadPoolWorkerAdjustment;
            }

            if ((_filter & EventFilter.GC) == EventFilter.GC)
            {
                var source = _session.Source;
                source.NeedLoadedDotNetRuntimes();
                source.AddCallbackOnProcessStart((TraceProcess proc) =>
                {
                    if (proc.ProcessID != _processId)
                    {
                        return;
                    }

                    proc.AddCallbackOnDotNetRuntimeLoad((TraceLoadedDotNetRuntime runtime) =>
                    {
                        runtime.GCEnd += (TraceProcess p, TraceGC gc) =>
                        {
                            NotifyCollection(gc);
                        };
                    });
                });
            }

            if ((_filter & EventFilter.AllocationTick) == EventFilter.AllocationTick)
            {
                // sample every ~100 KB of allocations
                _session.Source.Clr.GCAllocationTick += OnGCAllocationTick;
            }

            // decide which provider to listen to with filters if needed
            _session.EnableProvider(
                ClrTraceEventParser.ProviderGuid,  // CLR provider
                ((_filter & EventFilter.AllocationTick) == EventFilter.AllocationTick) ?
                TraceEventLevel.Verbose : TraceEventLevel.Informational,
                (ulong)(
                    ClrTraceEventParser.Keywords.Contention |         // thread contention timing
                    ClrTraceEventParser.Keywords.Threading |          // threadpool events
                    ClrTraceEventParser.Keywords.Exception |          // get the first chance exceptions
                    ClrTraceEventParser.Keywords.GCHeapAndTypeNames | // for finalizer type names
                    ClrTraceEventParser.Keywords.Type |               // for TypeBulkType definition of types
                    ClrTraceEventParser.Keywords.GC                   // garbage collector details
                    ),
                options
                );


            // this is a blocking call until the session is disposed
            _session.Source.Process();
        }
 public bool TraceETWTraceEventSource
             (
                 string providerName
                 , string tracedFileName
                 , string[] traceEvents = null
                 , Action
                         <
                             long
                             , TraceEventDispatcher
                             , TraceEventSession
                             , TraceEvent
                         > onOneEventTracedOnceProcessAction = null
                 , TraceEventProviderOptions traceEventProviderOptions = null
                 , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll
                 , TraceEventLevel traceEventLevel = TraceEventLevel.Always
                 , ulong matchKeywords = ulong.MaxValue
                 , bool needCountHits = false
             )
 {
     var r = false;
     if
         (
             traceEvents != null
             &&
             traceEvents.Length > 0
             &&
             onOneEventTracedOnceProcessAction != null
         )
     {
         r = TraceEventsHelper
                     .TraceETWTraceEventSource
                         (
                             providerName
                             , tracedFileName
                             , traceEvents
                             , onOneEventTracedOnceProcessAction
                             , traceEventProviderOptions
                             , traceEventSourceType
                             , traceEventLevel
                             , matchKeywords
                             , needCountHits
                         );
     }
     return r;
 }
 public static bool EnableProviderForOperationalChannel(this TraceEventSession session, TraceEventProviderOptions options = null)
 {
     return(session.EnableProvider(ProviderName, TraceEventLevel.Informational, Operational.Keyword, options));
 }
 public static Task<bool> RealTimeTraceEventSessionAsync
                     (
                         string providerName
                         , string sessionName
                         , string tracingFileName = null
                         , string[] traceEvents = null
                         , Action
                                 <
                                     long
                                     , TraceEventDispatcher
                                     , TraceEventSession
                                     , TraceEvent
                                 > onOneEventTracedOnceProcessAction = null
                         , TraceEventProviderOptions traceEventProviderOptions = null
                         , TraceEventSessionOptions traceEventSessionOptions = TraceEventSessionOptions.Create
                         , TraceEventSourceType traceEventSourceType = TraceEventSourceType.MergeAll
                         , TraceEventLevel traceEventLevel = TraceEventLevel.Always
                         , ulong matchKeywords = ulong.MaxValue
                         , bool needCountHits = false
                     )
 {
     return
         Task
             .Factory
             .StartNew<bool>
                 (
                     () =>
                     {
                         return
                             RealTimeTraceEventSession
                                 (
                                     providerName
                                     , sessionName
                                     , tracingFileName
                                     , traceEvents
                                     , onOneEventTracedOnceProcessAction
                                     , traceEventProviderOptions
                                     , traceEventSessionOptions
                                     , traceEventSourceType
                                     , traceEventLevel
                                     , matchKeywords
                                     , needCountHits
                                 );
                     }
                     ,
                         TaskCreationOptions.LongRunning
                         |
                         TaskCreationOptions.DenyChildAttach
                 );
 }
        public static bool RealTimeTraceEventSession
        (
            string providerName
            , string sessionName
            , string tracingFileName = null
            , string[] traceEvents   = null
            , Action
            <
                long
                , TraceEventDispatcher
                , TraceEventSession
                , TraceEvent
            > onOneEventTracedOnceProcessAction = null
            , TraceEventProviderOptions traceEventProviderOptions = null
            , TraceEventSessionOptions traceEventSessionOptions   = TraceEventSessionOptions.Create
            , TraceEventSourceType traceEventSourceType           = TraceEventSourceType.MergeAll
            , TraceEventLevel traceEventLevel = TraceEventLevel.Always
            , ulong matchKeywords             = ulong.MaxValue
            , bool needCountHits = false
        )
        {
            var r = false;

            if (!(TraceEventSession.IsElevated() ?? false))
            {
                Out.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process.");
                return(r);
            }
            var needTracingFile = !string.IsNullOrEmpty(tracingFileName);

            if
            (
                traceEvents != null
                &&
                traceEvents.Length > 0
                &&
                onOneEventTracedOnceProcessAction != null
            )
            {
                using
                (
                    var session =
                        (
                            needTracingFile
                                    ?
                            new TraceEventSession
                            (
                                sessionName
                                , tracingFileName
                                , traceEventSessionOptions
                            )
                {
                    StopOnDispose = true
                }
                                    :
                            new TraceEventSession
                            (
                                sessionName
                                , traceEventSessionOptions
                            )
                {
                    StopOnDispose = true
                }
                        )
                )
                {
                    using
                    (
                        var source =
                            (
                                needTracingFile
                                            ?
                                new ETWTraceEventSource(tracingFileName)
                                            :
                                session.Source
                            )
                    )
                    {
                        long sequence = 0;
                        RegisterCallbacks
                        (
                            providerName
                            , traceEvents
                            , source
                            , session
                            , (x, y, z) =>
                        {
                            long id = 0;
                            if (needCountHits)
                            {
                                id = Interlocked.Increment(ref sequence);
                            }
                            onOneEventTracedOnceProcessAction
                            (
                                id
                                , x
                                , y
                                , z
                            );
                        }
                        );
                        var restarted = session
                                        .EnableProvider
                                        (
                            providerName
                            , traceEventLevel
                            , matchKeywords
                            , traceEventProviderOptions
                                        );
                        source
                        .Process();
                        r = true;
                    }
                }
            }
            return(r);
        }
        /// <summary>
        /// CollectData doe will turn on logging of data from 'eventSourceName' to the file 'dataFileName'.
        /// It will then call EventGenerator.CreateEvents and wait 12 seconds for it to generate some data. 
        /// </summary>
        static void CollectData(string eventSourceName, string dataFileName)
        {

            // Today you have to be Admin to turn on ETW events (anyone can write ETW events).   
            if (!(TraceEventSession.IsElevated() ?? false))
            {
                Out.WriteLine("To turn on ETW events you need to be Administrator, please run from an Admin process.");
                Debugger.Break();
                return;
            }

            // As mentioned below, sessions can outlive the process that created them.  Thus you need a way of 
            // naming the session so that you can 'reconnect' to it from another process.   This is what the name
            // is for.  It can be anything, but it should be descriptive and unique.   If you expect multiple versions
            // of your program to run simultaneously, you need to generate unique names (e.g. add a process ID suffix) 
            // however this is dangerous because you can leave data collection on if the program ends unexpectedly.  
            // 
            // In this case we tell the session to place the data in MonitorToFileData.etl.  
            var sessionName = "SimpleTraceLogSession";
            Out.WriteLine("Creating a '{0}' session writing to {1}", sessionName, dataFileName);
            Out.WriteLine("Use 'logman query -ets' to see active sessions.");
            Out.WriteLine("Use 'logman stop {0} -ets' to manually stop orphans.", sessionName);
            using (var session = new TraceEventSession(sessionName, dataFileName))      // Since we give it a file name, the data goes there. 
            using (var kernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName, Path.ChangeExtension(dataFileName, ".kernel.etl")))
            {
                /* BY DEFAULT ETW SESSIONS SURVIVE THE DEATH OF THE PROESS THAT CREATES THEM! */
                // Unlike most other resources on the system, ETW session live beyond the lifetime of the 
                // process that created them.   This is very useful in some scenarios, but also creates the 
                // very real possibility of leaving 'orphan' sessions running.  
                //
                // To help avoid this by default TraceEventSession sets 'StopOnDispose' so that it will stop
                // the ETW session if the TraceEventSession dies.   Thus executions that 'clean up' the TraceEventSession
                // will clean up the ETW session.   This covers many cases (including throwing exceptions)
                //  
                // However if the process is killed manually (including control C) this cleanup will not happen.  
                // Thus best practices include
                //
                //     * Add a Control C handler that calls session.Dispose() so it gets cleaned up in this common case
                //     * use the same session name run-to-run so you don't create many orphans. 
                //
                // By default TraceEventSessions are in 'create' mode where it assumes you want to create a new session.
                // In this mode if a session already exists, it is stopped and the new one is created.   
                // 
                // Here we install the Control C handler.   It is OK if Dispose is called more than once.  
                Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { session.Dispose(); kernelSession.Dispose(); };

                // Enable kernel events.  
                kernelSession.EnableKernelProvider(KernelTraceEventParser.Keywords.ImageLoad | KernelTraceEventParser.Keywords.Process | KernelTraceEventParser.Keywords.Thread);

                // Enable my provider, you can call many of these on the same session to get events from other providers  

                // Turn on the eventSource given its name.   
                // Note we turn on Verbose level all keywords (ulong.MaxValue == 0xFFF....) and turn on stacks for 
                // this provider (for all events, until Windows 8.1 you can only turn on stacks for every event 
                // for a particular provider or no stacks)
                var options = new TraceEventProviderOptions() { StacksEnabled = true };
                var restarted = session.EnableProvider(eventSourceName, TraceEventLevel.Verbose, ulong.MaxValue, options);
                if (restarted)      // Generally you don't bother with this warning, but for the demo we do.  
                    Out.WriteLine("The session {0} was already active, it has been restarted.", sessionName);

                // We also turn on CLR events because we need them to decode Stacks and we also get exception events (and their stacks)
                session.EnableProvider(ClrTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)ClrTraceEventParser.Keywords.Default);

                // Start another thread that Causes MyEventSource to create some events
                // Normally this code as well as the EventSource itself would be in a different process.  
                EventGenerator.CreateEvents();

                // Also generate some exceptions so we have interesting stacks to look at
                Thread.Sleep(100);
                EventGenerator.GenerateExceptions();

                Out.WriteLine("Waiting 12 seconds for events to come in.");
                Thread.Sleep(12000);

                // Because the process in question (this process) lives both before and after the time the events were 
                // collected, we don't have complete information about JIT compiled methods in that method.   There are 
                // some methods that were JIT compiled before the session started (e.g. SimpleTraceLog.Main) for which
                // we do not have information.   We collect this by forcing a CLR 'rundown' which will dump method information
                // for JIT compiled methods that were not present.  If you know that the process of interest ends before
                // data collection ended or that data collection started before the process started, then this is not needed.  
                Out.WriteLine("Forcing rundown of JIT methods.");
                var rundownFileName = Path.ChangeExtension(dataFileName, ".clrRundown.etl");
                using (var rundownSession = new TraceEventSession(sessionName + "Rundown", rundownFileName))
                {
                    rundownSession.EnableProvider(ClrRundownTraceEventParser.ProviderGuid, TraceEventLevel.Verbose, (ulong)ClrRundownTraceEventParser.Keywords.Default);
                    // Poll until 2 second goes by without growth.  
                    for (var prevLength = new FileInfo(rundownFileName).Length; ; )
                    {
                        Thread.Sleep(2000);
                        var newLength = new FileInfo(rundownFileName).Length;
                        if (newLength == prevLength) break;
                        prevLength = newLength;
                    }
                }
                Out.WriteLine("Done with rundown.");
            }

            Out.WriteLine("Merging the raw files into a single '{0}' file.", dataFileName);
            TraceEventSession.MergeInPlace(dataFileName, Out);
            Out.WriteLine("Merge complete.");
        }
Beispiel #38
0
        public static void ForceGC(string prefix, int processId, TextWriter tw, int completeTimeout = -1)
        {
            tw.WriteLine("{0}: proc {1}: starting", prefix, processId);

            const int startTimeout = 15000;

            completeTimeout = completeTimeout <= 0 ? 60000 : completeTimeout;
            bool interrupted = false;

            using (var startedEvent = new ManualResetEventSlim())
                using (var session = new TraceEventSession("TriggerGC-" + processId))
                {
                    session.Source.NeedLoadedDotNetRuntimes();
                    session.Source.AddCallbackOnProcessStart((TraceProcess proc) =>
                    {
                        proc.AddCallbackOnDotNetRuntimeLoad((TraceLoadedDotNetRuntime runtime) =>
                        {
                            runtime.GCStart += (TraceProcess p, TraceGC gc) =>
                            {
                                if (p.ProcessID == processId && gc.Reason.ToString().Contains("Induced"))
                                {
                                    string xprefix = $"{prefix}: proc {p.ProcessID}: ";
                                    tw.WriteLine("{0} GC #{1} {2} start at {3:N2}ms",
                                                 xprefix,
                                                 gc.Number,
                                                 gc.Reason,
                                                 gc.PauseStartRelativeMSec);
                                }
                            };
                            runtime.GCEnd += (TraceProcess p, TraceGC gc) =>
                            {
                                if (p.ProcessID == processId && gc.Reason.ToString().Contains("Induced"))
                                {
                                    string xprefix = $"{prefix}: proc {p.ProcessID}: ";
                                    tw.WriteLine("{0} GC #{1} {2} end, paused for {3:N2}ms",
                                                 xprefix,
                                                 gc.Number,
                                                 gc.Reason,
                                                 gc.PauseDurationMSec);

                                    WriteFormattedTraceGC(tw, xprefix, gc);
                                    WriteFormattedHeapStats(tw, xprefix, gc.HeapStats);

                                    session.Source.StopProcessing();
                                }
                            };
                        });
                    });

                    var options = new TraceEventProviderOptions
                    {
                        ProcessIDFilter = new List <int> {
                            processId
                        }
                    };

                    session.EnableProvider(
                        ClrTraceEventParser.ProviderGuid,
                        TraceEventLevel.Informational,
                        (ulong)(ClrTraceEventParser.Keywords.GC | ClrTraceEventParser.Keywords.GCHeapCollect),
                        options);

                    // Sample: direct invocation of GCHeapCollect with a specific Process ID.
                    // Not used here, since we also need GC-events to listen to.
                    //session.CaptureState(ClrTraceEventParser.ProviderGuid,
                    //  (long)(ClrTraceEventParser.Keywords.GCHeapCollect),
                    //  filterType: unchecked((int)0x80000004),
                    //  data: processId);

                    var eventReader = Task.Run(() =>
                    {
                        if (!Console.IsInputRedirected)
                        {
                            // Ensure we are cleanly detaching from victim process; otherwise it will be left hanging dormant.
                            Console.TreatControlCAsInput = false;
                            Console.CancelKeyPress      += (sender, args) =>
                            {
                                args.Cancel = true;
                                // session.Stop(), not source.StopProcessing(), because the former is immediate, while the later is not.
                                interrupted = true;
                                session.Stop();
                            };
                        }

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

                    if (!startedEvent.Wait(startTimeout))
                    {
                        tw.WriteLine("{0}: proc {1}: event reader did not start within {2:N2} seconds. Giving up.",
                                     prefix, processId, startTimeout / 1000);
                        session.Stop();
                        return;
                    }

                    if (!eventReader.Wait(completeTimeout))
                    {
                        tw.WriteLine("{0}: proc {1}: garbage collection did not finish within {2:N2} seconds. Giving up.",
                                     prefix, processId, completeTimeout / 1000);
                        session.Stop();
                    }
                    else if (interrupted)
                    {
                        tw.WriteLine("{0}: proc {1}: interrupted.", prefix, processId);
                    }
                    else
                    {
                        tw.WriteLine("{0}: proc {1}: complete.", prefix, processId);
                    }
                }
        }