private async Task HandleProcessInfo(EventPipeEventSource source, Func <Task> stopFunc, CancellationToken token)
        {
            string commandLine = null;
            Action <TraceEvent, Action> processInfoHandler = (TraceEvent traceEvent, Action taskComplete) =>
            {
                commandLine = (string)traceEvent.PayloadByName("CommandLine");
                taskComplete();
            };

            // Completed when the ProcessInfo event of the Microsoft-DotNETCore-EventPipe event provider is handled
            using var processInfoTaskSource = new EventTaskSource <Action <TraceEvent> >(
                      taskComplete => traceEvent => processInfoHandler(traceEvent, taskComplete),
                      handler => source.Dynamic.AddCallbackForProviderEvent(MonitoringSourceConfiguration.EventPipeProviderName, "ProcessInfo", handler),
                      handler => source.Dynamic.RemoveCallback(handler),
                      token);

            // Completed when any trace event is handled
            using var anyEventTaskSource = new EventTaskSource <Action <TraceEvent> >(
                      taskComplete => traceEvent => taskComplete(),
                      handler => source.Dynamic.All += handler,
                      handler => source.Dynamic.All -= handler,
                      token);

            // Wait for any trace event to be processed
            await anyEventTaskSource.Task;

            // Stop the event pipe session
            await stopFunc();

            // Wait for the ProcessInfo event to be processed
            await processInfoTaskSource.Task;

            // Notify of command line information
            await _processInfoCallback(commandLine);
        }
Example #2
0
        private async Task HandleGCEvents(EventPipeEventSource source, int pid, Func <Task> stopFunc, CancellationToken token)
        {
            int gcNum = -1;

            Action <GCStartTraceData, Action> gcStartHandler = (GCStartTraceData data, Action taskComplete) =>
            {
                if (data.ProcessID != pid)
                {
                    return;
                }

                taskComplete();

                if (gcNum < 0 && data.Depth == 2 && data.Type != GCType.BackgroundGC)
                {
                    gcNum = data.Count;
                }
            };

            Action <GCBulkNodeTraceData, Action> gcBulkNodeHandler = (GCBulkNodeTraceData data, Action taskComplete) =>
            {
                if (data.ProcessID != pid)
                {
                    return;
                }

                taskComplete();
            };

            Action <GCEndTraceData, Action> gcEndHandler = (GCEndTraceData data, Action taskComplete) =>
            {
                if (data.ProcessID != pid)
                {
                    return;
                }

                if (data.Count == gcNum)
                {
                    taskComplete();
                }
            };

            // Register event handlers on the event source and represent their completion as tasks
            using var gcStartTaskSource = new EventTaskSource <Action <GCStartTraceData> >(
                      taskComplete => data => gcStartHandler(data, taskComplete),
                      handler => source.Clr.GCStart += handler,
                      handler => source.Clr.GCStart -= handler,
                      token);
            using var gcBulkNodeTaskSource = new EventTaskSource <Action <GCBulkNodeTraceData> >(
                      taskComplete => data => gcBulkNodeHandler(data, taskComplete),
                      handler => source.Clr.GCBulkNode += handler,
                      handler => source.Clr.GCBulkNode -= handler,
                      token);
            using var gcStopTaskSource = new EventTaskSource <Action <GCEndTraceData> >(
                      taskComplete => data => gcEndHandler(data, taskComplete),
                      handler => source.Clr.GCStop += handler,
                      handler => source.Clr.GCStop -= handler,
                      token);
            using var sourceCompletedTaskSource = new EventTaskSource <Action>(
                      taskComplete => taskComplete,
                      handler => source.Completed += handler,
                      handler => source.Completed -= handler,
                      token);

            // A task that is completed whenever GC data is received
            Task gcDataTask = Task.WhenAny(gcStartTaskSource.Task, gcBulkNodeTaskSource.Task);
            Task gcStopTask = gcStopTaskSource.Task;

            DotNetHeapDumpGraphReader dumper = new DotNetHeapDumpGraphReader(TextWriter.Null)
            {
                DotNetHeapInfo = new DotNetHeapInfo()
            };

            dumper.SetupCallbacks(_gcGraph, source, pid.ToString(CultureInfo.InvariantCulture));

            // The event source will not always provide the GC events when it starts listening. However,
            // they will be provided when the event source is told to stop processing events. Give the
            // event source some time to produce the events, but if it doesn't start producing them within
            // a short amount of time (5 seconds), then stop processing events to allow them to be flushed.
            Task eventsTimeoutTask = Task.Delay(TimeSpan.FromSeconds(5), token);
            Task completedTask     = await Task.WhenAny(gcDataTask, eventsTimeoutTask);

            token.ThrowIfCancellationRequested();

            // If started receiving GC events, wait for the GC Stop event.
            if (completedTask == gcDataTask)
            {
                await gcStopTask;
            }

            // Stop receiving events; if haven't received events yet, this will force flushing of events.
            await stopFunc();

            // Wait for all received events to be processed.
            await sourceCompletedTaskSource.Task;

            // Check that GC data and stop events were received. This is done by checking that the
            // associated tasks have ran to completion. If one of them has not reached the completion state, then
            // fail the GC dump operation.
            if (gcDataTask.Status != TaskStatus.RanToCompletion ||
                gcStopTask.Status != TaskStatus.RanToCompletion)
            {
                throw new InvalidOperationException("Unable to create GC dump due to incomplete GC data.");
            }

            dumper.ConvertHeapDataToGraph();

            _gcGraph.AllowReading();
        }