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