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, token); }
protected override async Task OnEventSourceAvailable(EventPipeEventSource eventSource, Func <Task> stopSessionAsync, CancellationToken token) { ExecuteCounterLoggerAction((metricLogger) => metricLogger.PipelineStarted()); eventSource.Dynamic.All += traceEvent => { try { if (traceEvent.TryGetCounterPayload(_filter, out ICounterPayload counterPayload)) { ExecuteCounterLoggerAction((metricLogger) => metricLogger.Log(counterPayload)); } } catch (Exception) { } }; using var sourceCompletedTaskSource = new EventTaskSource <Action>( taskComplete => taskComplete, handler => eventSource.Completed += handler, handler => eventSource.Completed -= handler, token); await sourceCompletedTaskSource.Task; ExecuteCounterLoggerAction((metricLogger) => metricLogger.PipelineStopped()); }
private async Task HandleGCEvents(EventPipeEventSource source, Func <Task> stopFunc, CancellationToken token) { int gcNum = -1; Action <GCStartTraceData, Action> gcStartHandler = (GCStartTraceData data, Action taskComplete) => { taskComplete(); if (gcNum < 0 && data.Depth == 2 && data.Type != GCType.BackgroundGC) { gcNum = data.Count; } }; Action <GCBulkNodeTraceData, Action> gcBulkNodeHandler = (GCBulkNodeTraceData data, Action taskComplete) => { taskComplete(); }; Action <GCEndTraceData, Action> gcEndHandler = (GCEndTraceData data, Action taskComplete) => { 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); // 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(); }
protected override async Task OnEventSourceAvailable(EventPipeEventSource eventSource, Func <Task> stopSessionAsync, CancellationToken token) { ExecuteCounterLoggerAction((metricLogger) => metricLogger.PipelineStarted()); eventSource.Dynamic.All += traceEvent => { try { // Metrics if (traceEvent.EventName.Equals("EventCounters")) { IDictionary <string, object> payloadVal = (IDictionary <string, object>)(traceEvent.PayloadValue(0)); IDictionary <string, object> payloadFields = (IDictionary <string, object>)(payloadVal["Payload"]); //Make sure we are part of the requested series. If multiple clients request metrics, all of them get the metrics. string series = payloadFields["Series"].ToString(); if (GetInterval(series) != CounterIntervalSeconds * 1000) { return; } string counterName = payloadFields["Name"].ToString(); if (!_filter.IsIncluded(traceEvent.ProviderName, counterName)) { return; } float intervalSec = (float)payloadFields["IntervalSec"]; string displayName = payloadFields["DisplayName"].ToString(); string displayUnits = payloadFields["DisplayUnits"].ToString(); double value = 0; CounterType counterType = CounterType.Metric; if (payloadFields["CounterType"].Equals("Mean")) { value = (double)payloadFields["Mean"]; } else if (payloadFields["CounterType"].Equals("Sum")) { counterType = CounterType.Rate; value = (double)payloadFields["Increment"]; if (string.IsNullOrEmpty(displayUnits)) { displayUnits = "count"; } //TODO Should we make these /sec like the dotnet-counters tool? } // Note that dimensional data such as pod and namespace are automatically added in prometheus and azure monitor scenarios. // We no longer added it here. var counterPayload = new CounterPayload(traceEvent.TimeStamp, traceEvent.ProviderName, counterName, displayName, displayUnits, value, counterType, intervalSec); ExecuteCounterLoggerAction((metricLogger) => metricLogger.Log(counterPayload)); } } catch (Exception) { } }; using var sourceCompletedTaskSource = new EventTaskSource <Action>( taskComplete => taskComplete, handler => eventSource.Completed += handler, handler => eventSource.Completed -= handler, token); await sourceCompletedTaskSource.Task; ExecuteCounterLoggerAction((metricLogger) => metricLogger.PipelineStopped()); }