in src/TraceEvent/Computers/StartStopActivityComputer.cs [35:501]
public StartStopActivityComputer(TraceLogEventSource source, ActivityComputer taskComputer, bool ignoreApplicationInsightsRequestsWithRelatedActivityId = true)
{
m_ignoreApplicationInsightsRequestsWithRelatedActivityId = ignoreApplicationInsightsRequestsWithRelatedActivityId;
taskComputer.NoCache = true; // Can't cache start-stops (at the moment)
m_source = source;
m_activeStartStopActivities = new Dictionary<StartStopKey, StartStopActivity>();
m_taskComputer = taskComputer;
// Whenever a new Activity is created, propagate the start-stop activity from the creator
// to the created task.
taskComputer.Create += delegate (TraceActivity activity, TraceEvent data)
{
TraceActivity creator = activity.Creator;
if (creator == null)
{
return;
}
StartStopActivity startStopActivity = m_traceActivityToStartStopActivity.Get((int)creator.Index);
if (startStopActivity == null)
{
return;
}
m_traceActivityToStartStopActivity.Set((int)activity.Index, startStopActivity);
};
// Only need to fix up V4.6 Windows-ASP activity ids. It can be removed after we
// don't care about V4.6 runtimes (since it is fixed in V4.6.2 and beyond).
// It basically remembers the related activity ID of the last RequestSend event on
// each thread, which we use to fix the activity ID of the RequestStart event
KeyValuePair<Guid, Guid>[] threadToLastAspNetGuids = new KeyValuePair<Guid, Guid>[m_source.TraceLog.Threads.Count];
#if HTTP_SERVICE_EVENTS
// Sadly, the Microsoft-Windows-HttpService HTTP_OPCODE_DELIVER event gets logged in
// the kernel, so you don't know what process or thread is going to be get the request
// We hack around this by looking for a nearby ReadyTHread or CSwitch event. These
// variables remember the information needed to transfer these to cswitch in the
// correct process.
// remembers the last deliver event.
string lastHttpServiceDeliverUrl = null;
Guid lastHttpServiceDeliverActivityID = new Guid();
int lastHttpServiceReceiveRequestThreadId = 0;
int lastHttpServiceReceiveRequestTargetProcessId = 0;
Dictionary<long, int> mapConnectionToTargetProcess = new Dictionary<long, int>();
#endif
var dynamicParser = source.Dynamic;
dynamicParser.All += delegate (TraceEvent data)
{
// Special case IIS. It does not use start and stop opcodes (Ugg), but otherwise
// follows normal start-stop activity ID conventions. We also want it to work even
// though it is not a EventSource.
if (data.ID <= (TraceEventID)2 && data.ProviderGuid == MicrosoftWindowsIISProvider)
{
if (data.ID == (TraceEventID)1)
{
// TODO HACK. We have seen IIS Start and stop events that only have a
// context ID and no more. They also seem to be some sort of nested event
// It really looks like a bug that they were emitted. Ignore them.
if (16 < data.EventDataLength)
{
string extraStartInfo = data.PayloadByName("RequestURL") as string;
OnStart(data, extraStartInfo, null, null, null, "IISRequest");
}
}
else if (data.ID == (TraceEventID)2)
{
// TODO HACK. We have seen IIS Start and stop events that only have a
// context ID and no more. They also seem to be some sort of nested event
// It really looks like a bug that they were emitted. Ignore them.
if (16 < data.EventDataLength)
{
OnStop(data);
}
}
}
#if HTTP_SERVICE_EVENTS
else if (data.Task == (TraceEventTask)1 && data.ProviderGuid == MicrosoftWindowsHttpService)
{
if (data.ID == (TraceEventID)1) // HTTP_TASK_REQUEST / HTTP_OPCODE_RECEIVE_REQUEST
{
Debug.Assert(data.EventName == "HTTP_TASK_REQUEST/HTTP_OPCODE_RECEIVE_REQUEST");
lastHttpServiceReceiveRequestTargetProcessId = 0;
lastHttpServiceReceiveRequestThreadId = data.ThreadID;
object connectionID = data.PayloadByName("ConnectionId");
if (connectionID != null && connectionID is long)
mapConnectionToTargetProcess.TryGetValue((long)connectionID, out lastHttpServiceReceiveRequestTargetProcessId);
}
else if (data.ID == (TraceEventID)3) // HTTP_TASK_REQUEST/HTTP_OPCODE_DELIVER
{
Debug.Assert(data.EventName == "HTTP_TASK_REQUEST/HTTP_OPCODE_DELIVER");
if (lastHttpServiceReceiveRequestThreadId == data.ThreadID && lastHttpServiceReceiveRequestTargetProcessId != 0)
{
lastHttpServiceDeliverUrl = data.PayloadByName("Url") as string;
lastHttpServiceDeliverActivityID = data.ActivityID;
}
else
{
lastHttpServiceDeliverUrl = null;
lastHttpServiceReceiveRequestTargetProcessId = 0;
}
lastHttpServiceReceiveRequestThreadId = 0;
}
else if (data.ID == (TraceEventID)12 || data.ID == (TraceEventID)8) // HTTP_TASK_REQUEST/HTTP_OPCODE_FAST_SEND HTTP_TASK_REQUEST/HTTP_OPCODE_FAST_RESPONSE
{
if (data.ID == (TraceEventID)8)
{
object connectionID = data.PayloadByName("ConnectionId");
if (connectionID != null && connectionID is long)
mapConnectionToTargetProcess[(long)connectionID] = data.ProcessID;
}
Debug.Assert(data.ID != (TraceEventID)12 || data.EventName == "HTTP_TASK_REQUEST/HTTP_OPCODE_FAST_SEND");
Debug.Assert(data.ID != (TraceEventID)8 || data.EventName == "HTTP_TASK_REQUEST/HTTP_OPCODE_FAST_RESPONSE");
OnStop(data);
}
}
#endif
// TODO decide what the correct heuristic for deciding what start-stop events are interesting.
// Currently I only do this for things that might be an EventSource
if (!TraceEventProviders.MaybeAnEventSource(data.ProviderGuid))
{
return;
}
// Try to filter out things quickly. We really only care about start and stop events
// (except in special cases where the conventions were not followed and we fix them up).
if (data.Opcode != TraceEventOpcode.Start && data.Opcode != TraceEventOpcode.Stop)
{
// In V4.6 the activity ID for Microsoft-Windows-ASPNET/Request/Start is improperly set, but we can fix it by
// looking at the 'send' event that happens just before it. Can be removed when V4.6 no longer deployed.
// TODO remove (including threadToLastAspNetGuid) after 9/2016
if (data.Opcode == (TraceEventOpcode)9 && data.ProviderGuid == MicrosoftWindowsASPNetProvider)
{
TraceThread thread = data.Thread();
if (thread != null)
{
threadToLastAspNetGuids[(int)thread.ThreadIndex] = new KeyValuePair<Guid, Guid>(data.ActivityID, data.RelatedActivityID);
}
}
// These providers are weird in that they don't event do start and stop opcodes. This is unfortunate.
else if (data.Opcode == TraceEventOpcode.Info && data.ProviderGuid == AdoNetProvider)
{
FixAndProcessAdoNetEvents(data);
}
return;
}
// OK so now we only have EventSources with start and stop opcodes.
// There are a few that don't follow conventions completely, and then we handle the 'normal' case
if (data.ProviderGuid == MicrosoftApplicationInsightsDataProvider)
{
FixAndProcessAppInsightsEvents(data);
}
else if (data.ProviderGuid == FrameworkEventSourceTraceEventParser.ProviderGuid)
{
FixAndProcessFrameworkEvents(data);
}
else if (data.ProviderGuid == MicrosoftWindowsASPNetProvider)
{
FixAndProcessWindowsASP(data, threadToLastAspNetGuids);
}
else if (data.ProviderGuid == MicrosoftDiagnosticsActivityTrackingProvider)
{
ProcessActivityTrackingProviderEvents(data);
}
else // Normal case EventSource Start-Stop events that follow proper conventions.
{
// We currently only handle Start-Stops that use the ActivityPath convention
// We could change this, but it is not clear what value it has to do that.
Guid activityID = data.ActivityID;
if (StartStopActivityComputer.IsActivityPath(activityID, data.ProcessID))
{
if (data.Opcode == TraceEventOpcode.Start)
{
if (data.ProviderGuid == MicrosoftDiagnosticsDiagnosticSourceProvider)
{
// Inside the function, it will filter the events by 'EventName'.
// It will only process "Microsoft.EntityFrameworkCore.BeforeExecuteCommand" and "Microsoft.AspNetCore.Hosting.BeginRequest".
if (TryProcessDiagnosticSourceStartEvents(data))
{
return;
}
}
string extraStartInfo = null;
// Include the first argument in extraInfo if it is a string (e.g. a URL or other identifier).
if (0 < data.PayloadNames.Length)
{
try { extraStartInfo = data.PayloadValue(0) as string; }
catch (Exception) { }
if (extraStartInfo != null)
{
extraStartInfo = "/" + data.payloadNames[0] + "=" + extraStartInfo;
}
}
OnStart(data, extraStartInfo);
}
else
{
Debug.Assert(data.Opcode == TraceEventOpcode.Stop);
OnStop(data);
}
}
else
{
Trace.WriteLine("Skipping start at " + data.TimeStampRelativeMSec.ToString("n3") + " name = " + data.EventName);
}
}
};
#if HTTP_SERVICE_EVENTS
#if TODO_FIX_NOW // FIX NOW Use or remove.
// We monitor ReadyThread to make HttpService events more useful (see nodes above on lastHttpServiceUrl)
m_source.Kernel.DispatcherReadyThread += delegate (DispatcherReadyThreadTraceData data)
{
if (lastHttpServiceUrl == null)
return;
};
#endif
m_source.Kernel.ThreadCSwitch += delegate (CSwitchTraceData data)
{
// This code is to transfer information from the Microsoft-Windows-HttpService HTTP_TASK_REQUEST/HTTP_OPCODE_DELIVER
// event (that happens in the System process, not the target, and move it to the context switch that wakes up
// in order to service the event.
if (lastHttpServiceDeliverUrl == null)
return;
if (data.ProcessID != lastHttpServiceReceiveRequestTargetProcessId)
return;
// Test Stack
Guid activityID = lastHttpServiceDeliverActivityID;
OnStart(data, "url=" + lastHttpServiceDeliverUrl, &activityID, null, null, "HttpServiceRec");
lastHttpServiceDeliverUrl = null;
lastHttpServiceDeliverActivityID = Guid.Empty;
};
#endif
// Show the exception handling call stacks as a separate Activity.
// This can help users notice the time spent in the exception handling logic.
var clrExceptionParser = m_source.Clr;
clrExceptionParser.ExceptionCatchStart += delegate (ExceptionHandlingTraceData data)
{
OnStart(data, data.MethodName, null, null, null, "ExceptionHandling");
};
clrExceptionParser.ExceptionCatchStop += delegate (EmptyTraceData data)
{
OnStop(data);
};
var aspNetParser = new AspNetTraceEventParser(m_source);
aspNetParser.AspNetReqStart += delegate (AspNetStartTraceData data)
{
// if the related activity is not present, try using the context ID as the creator ID to look up.
// The ASPNet events reuse the IIS ID which means first stop kills both. As it turns out
// IIS stops before ASP (also incorrect) but it mostly this is benign...
StartStopActivity creator = null;
if (data.RelatedActivityID == Guid.Empty)
{
creator = GetActiveStartStopActivityTable(data.ContextId, data.ProcessID);
}
Guid activityId = data.ContextId;
OnStart(data, data.Path, &activityId, null, creator, null, false);
};
aspNetParser.AspNetReqStop += delegate (AspNetStopTraceData data)
{
Guid activityId = data.ContextId;
OnStop(data, &activityId);
};
// There are other ASP.NET events that have context information and this is useful
aspNetParser.AspNetReqStartHandler += delegate (AspNetStartHandlerTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqPipelineModuleEnter += delegate (AspNetPipelineModuleEnterTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqGetAppDomainEnter += delegate (AspNetGetAppDomainEnterTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
// These are probably not important, but they may help.
aspNetParser.AspNetReqRoleManagerBegin += delegate (AspNetRoleManagerBeginTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqRoleManagerGetUserRoles += delegate (AspNetRoleManagerGetUserRolesTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqRoleManagerEnd += delegate (AspNetRoleManagerEndTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqMapHandlerEnter += delegate (AspNetMapHandlerEnterTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqMapHandlerLeave += delegate (AspNetMapHandlerLeaveTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqHttpHandlerEnter += delegate (AspNetHttpHandlerEnterTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqHttpHandlerLeave += delegate (AspNetHttpHandlerLeaveTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqPagePreInitEnter += delegate (AspNetPagePreInitEnterTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqPagePreInitLeave += delegate (AspNetPagePreInitLeaveTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqPageInitEnter += delegate (AspNetPageInitEnterTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqPageInitLeave += delegate (AspNetPageInitLeaveTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqPageLoadEnter += delegate (AspNetPageLoadEnterTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqPageLoadLeave += delegate (AspNetPageLoadLeaveTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqPagePreRenderEnter += delegate (AspNetPagePreRenderEnterTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqPagePreRenderLeave += delegate (AspNetPagePreRenderLeaveTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqPageSaveViewstateEnter += delegate (AspNetPageSaveViewstateEnterTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqPageSaveViewstateLeave += delegate (AspNetPageSaveViewstateLeaveTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqPageRenderEnter += delegate (AspNetPageRenderEnterTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
aspNetParser.AspNetReqPageRenderLeave += delegate (AspNetPageRenderLeaveTraceData data)
{
SetThreadToStartStopActivity(data, data.ContextId);
};
var WCFParser = new ApplicationServerTraceEventParser(m_source);
WCFParser.WebHostRequestStart += delegate (Multidata69TemplateATraceData data)
{
OnStart(data, data.VirtualPath);
};
WCFParser.WebHostRequestStop += delegate (OneStringsTemplateATraceData data)
{
OnStop(data);
};
// Microsoft-Windows-Application Server-Applications/TransportReceive/Stop
WCFParser.MessageReceivedByTransport += delegate (Multidata29TemplateHATraceData data)
{
// This actually looks like a Stop opcode, but it is really a start because
// it has a RelatedActivityID
OnStart(data, data.ListenAddress, null, null, null, "OperationDispatch");
};
// These Stop the Starts. We don't want leaks in the common case.
WCFParser.DispatchFailed += delegate (Multidata38TemplateHATraceData data)
{
OnStop(data);
};
WCFParser.DispatchSuccessful += delegate (Multidata38TemplateHATraceData data)
{
OnStop(data);
};
WCFParser.OperationInvoked += delegate (Multidata24TemplateHATraceData data)
{
// The creator uses the same ID as myself.
OnStart(data, data.MethodName, null, null, GetActiveStartStopActivityTable(data.ActivityID, data.ProcessID));
};
WCFParser.OperationCompleted += delegate (Multidata28TemplateHATraceData data)
{
OnStop(data);
};
WCFParser.ServiceActivationStart += SetThreadToStartStopActivity;
WCFParser.ServiceHostFactoryCreationStart += SetThreadToStartStopActivity;
WCFParser.ServiceHostStarted += SetThreadToStartStopActivity;
WCFParser.HttpMessageReceiveStart += SetThreadToStartStopActivity;
WCFParser.HttpContextBeforeProcessAuthentication += SetThreadToStartStopActivity;
WCFParser.TokenValidationStarted += SetThreadToStartStopActivity;
WCFParser.MessageReadByEncoder += SetThreadToStartStopActivity;
WCFParser.HttpResponseReceiveStart += SetThreadToStartStopActivity;
WCFParser.SocketReadStop += SetThreadToStartStopActivity;
WCFParser.SocketAsyncReadStop += SetThreadToStartStopActivity;
WCFParser.SignatureVerificationStart += SetThreadToStartStopActivity;
WCFParser.SignatureVerificationSuccess += SetThreadToStartStopActivity;
WCFParser.ChannelReceiveStop += SetThreadToStartStopActivity;
WCFParser.DispatchMessageStart += SetThreadToStartStopActivity;
WCFParser.IncrementBusyCount += SetThreadToStartStopActivity;
WCFParser.DispatchMessageBeforeAuthorization += SetThreadToStartStopActivity;
WCFParser.ActionItemScheduled += SetThreadToStartStopActivity;
WCFParser.GetServiceInstanceStart += SetThreadToStartStopActivity;
WCFParser.GetServiceInstanceStop += SetThreadToStartStopActivity;
WCFParser.ActionItemCallbackInvoked += SetThreadToStartStopActivity;
WCFParser.ChannelReceiveStart += SetThreadToStartStopActivity;
WCFParser.OutgoingMessageSecured += SetThreadToStartStopActivity;
WCFParser.SocketWriteStart += SetThreadToStartStopActivity;
WCFParser.SocketAsyncWriteStart += SetThreadToStartStopActivity;
WCFParser.BinaryMessageEncodingStart += SetThreadToStartStopActivity;
WCFParser.MtomMessageEncodingStart += SetThreadToStartStopActivity;
WCFParser.TextMessageEncodingStart += SetThreadToStartStopActivity;
WCFParser.BinaryMessageDecodingStart += SetThreadToStartStopActivity;
WCFParser.MtomMessageDecodingStart += SetThreadToStartStopActivity;
WCFParser.TextMessageDecodingStart += SetThreadToStartStopActivity;
WCFParser.StreamedMessageWrittenByEncoder += SetThreadToStartStopActivity;
WCFParser.MessageWrittenAsynchronouslyByEncoder += SetThreadToStartStopActivity;
WCFParser.BufferedAsyncWriteStop += SetThreadToStartStopActivity;
WCFParser.HttpPipelineProcessResponseStop += SetThreadToStartStopActivity;
WCFParser.WebSocketAsyncWriteStop += SetThreadToStartStopActivity;
WCFParser.MessageSentByTransport += SetThreadToStartStopActivity;
WCFParser.HttpSendStop += SetThreadToStartStopActivity;
WCFParser.DispatchMessageStop += SetThreadToStartStopActivity;
WCFParser.DispatchSuccessful += SetThreadToStartStopActivity;
// Server-side quota information.
WCFParser.MaxReceivedMessageSizeExceeded += SetThreadToStartStopActivity;
WCFParser.MaxPendingConnectionsExceeded += SetThreadToStartStopActivity;
WCFParser.ReaderQuotaExceeded += SetThreadToStartStopActivity;
WCFParser.NegotiateTokenAuthenticatorStateCacheExceeded += SetThreadToStartStopActivity;
WCFParser.NegotiateTokenAuthenticatorStateCacheRatio += SetThreadToStartStopActivity;
WCFParser.SecuritySessionRatio += SetThreadToStartStopActivity;
WCFParser.PendingConnectionsRatio += SetThreadToStartStopActivity;
WCFParser.ConcurrentCallsRatio += SetThreadToStartStopActivity;
WCFParser.ConcurrentSessionsRatio += SetThreadToStartStopActivity;
WCFParser.ConcurrentInstancesRatio += SetThreadToStartStopActivity;
WCFParser.PendingAcceptsAtZero += SetThreadToStartStopActivity;
// WCF client operations.
// TODO FIX NOW, I have never run these! Get some data to test against.
WCFParser.ClientOperationPrepared += delegate (Multidata22TemplateHATraceData data)
{
string extraInformation = "/Action=" + data.ServiceAction + "/URL=" + data.Destination;
OnStart(data, extraInformation, null, null, GetActiveStartStopActivityTable(data.ActivityID, data.ProcessID), "ClientOperation");
};
WCFParser.ServiceChannelCallStop += delegate (Multidata22TemplateHATraceData data)
{
OnStop(data);
};
}