Skip to content

Commit 031ce2d

Browse files
committed
TraceLog support for initial rundown session
1 parent fafcc00 commit 031ce2d

File tree

1 file changed

+124
-24
lines changed

1 file changed

+124
-24
lines changed

src/TraceEvent/TraceLog.cs

+124-24
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,34 @@ public static TraceLogEventSource CreateFromTraceEventSession(TraceEventSession
180180
/// the .Log Property) which lets you get at aggregated information (Processes, threads, images loaded, and perhaps most
181181
/// importantly TraceEvent.CallStack() will work. Thus you can get real time stacks from events).
182182
/// </summary>
183-
public static TraceLogEventSource CreateFromEventPipeSession(EventPipeSession session)
183+
/// <param name="rundownSession">
184+
/// If given, the rundownSession is used to initialize module and method information and then the session is closed.
185+
/// This only makes sense in realtime sessions when you need to resolve function names.
186+
/// </param>
187+
/// <example>
188+
/// var client = new DiagnosticsClient(Process.GetCurrentProcess().Id);
189+
/// using var rundownSession = client.StartEventPipeSession(
190+
/// new EventPipeProvider(ClrTraceEventParser.ProviderName, EventLevel.Informational, (long) ClrTraceEventParser.Keywords.Default),
191+
/// requestRundown: true
192+
/// );
193+
/// EventPipeProvider[] providers = new[]
194+
/// { new EventPipeProvider(ClrTraceEventParser.ProviderName, EventLevel.Informational, (long) ClrTraceEventParser.Keywords.Default),
195+
/// new EventPipeProvider(SampleProfilerTraceEventParser.ProviderName, EventLevel.Informational),
196+
/// };
197+
/// var session = client.StartEventPipeSession(providers, requestRundown: false);
198+
/// var eventSource = TraceLog.CreateFromEventPipeSession(session, rundownSession);
199+
/// eventSource.Process();
200+
/// </example>
201+
public static TraceLogEventSource CreateFromEventPipeSession(EventPipeSession session, EventPipeSession rundownSession = null)
184202
{
185-
return CreateFromEventPipeEventSource(new EventPipeEventSource(session.EventStream));
203+
var tlEventSource = CreateFromEventPipeEventSource(new EventPipeEventSource(session.EventStream));
204+
205+
if (rundownSession != null)
206+
{
207+
tlEventSource.TraceLog.ProcessInitialRundown(rundownSession);
208+
}
209+
210+
return tlEventSource;
186211
}
187212

188213
/// <summary>
@@ -197,6 +222,83 @@ public static TraceLogEventSource CreateFromEventPipeEventSource(EventPipeEventS
197222
return traceLog.realTimeSource;
198223
}
199224

225+
private void ProcessInitialRundown(EventPipeSession session)
226+
{
227+
using (var source = new EventPipeEventSource(session.EventStream))
228+
{
229+
SetupInitialRundownCallbacks(source);
230+
231+
// Only stopping the session will cause the rundown events to be sent.
232+
// However, we cannot stop it before starting to process the source.
233+
// Therefore, we need attach to the first event and only then stop the session.
234+
var completionSource = new TaskCompletionSource<bool>();
235+
source.AllEvents += delegate (TraceEvent _)
236+
{
237+
completionSource.TrySetResult(true);
238+
};
239+
var task = Task.Run(source.Process);
240+
completionSource.Task.Wait();
241+
session.Stop();
242+
task.Wait();
243+
}
244+
}
245+
246+
/// <summary>
247+
/// Attaches callbacks to initialize modules and methods based on the rundown events.
248+
/// Unlike SetupCallbacks(), these callbacks are attached before any events are processed so
249+
/// they do not rely on ForAllUnresolvedCodeAddressesInRange() and instead record all the methods.
250+
/// </summary>
251+
private void SetupInitialRundownCallbacks(EventPipeEventSource rawEvents)
252+
{
253+
rawEvents.HeadersDeserialized += delegate () { CopyHeadersFrom(rawEvents); };
254+
255+
var clrRundownParser = new ClrRundownTraceEventParser(rawEvents);
256+
Action<ModuleLoadUnloadTraceData> onLoaderRundown = delegate (ModuleLoadUnloadTraceData data)
257+
{
258+
TraceProcess process = processes.GetOrCreateProcess(data.ProcessID, data.TimeStampQPC);
259+
process.LoadedModules.ManagedModuleLoadOrUnload(data, false, true);
260+
};
261+
262+
clrRundownParser.LoaderModuleDCStop += onLoaderRundown;
263+
clrRundownParser.LoaderModuleDCStart += onLoaderRundown;
264+
265+
// TODO We don't seem to be getting any of these events, only MethodDCStopVerbose.
266+
// clrRundownParser.MethodDCStartVerbose += delegate (MethodLoadUnloadVerboseTraceData data)
267+
// {
268+
// if (data.IsJitted)
269+
// {
270+
// TraceProcess process = processes.GetOrCreateProcess(data.ProcessID, data.TimeStampQPC);
271+
// process.InsertJITTEDMethod(data.MethodStartAddress, data.MethodSize, delegate ()
272+
// {
273+
// TraceManagedModule module = process.LoadedModules.GetOrCreateManagedModule(data.ModuleID, data.TimeStampQPC);
274+
// MethodIndex methodIndex = CodeAddresses.Methods.NewMethod(TraceLog.GetFullName(data), module.ModuleFile.ModuleFileIndex, data.MethodToken);
275+
// return new TraceProcess.MethodLookupInfo(data.MethodStartAddress, data.MethodSize, methodIndex);
276+
// });
277+
278+
// jittedMethods.Add((MethodLoadUnloadVerboseTraceData)data.Clone());
279+
// }
280+
// };
281+
282+
clrRundownParser.MethodILToNativeMapDCStop += delegate (MethodILToNativeMapTraceData data)
283+
{
284+
codeAddresses.AddILMapping(data);
285+
};
286+
287+
clrRundownParser.MethodDCStopVerbose += delegate (MethodLoadUnloadVerboseTraceData data)
288+
{
289+
if (data.IsJitted)
290+
{
291+
TraceProcess process = processes.GetOrCreateProcess(data.ProcessID, data.TimeStampQPC);
292+
process.InsertJITTEDMethod(data.MethodStartAddress, data.MethodSize, delegate ()
293+
{
294+
TraceManagedModule module = process.LoadedModules.GetOrCreateManagedModule(data.ModuleID, data.TimeStampQPC);
295+
MethodIndex methodIndex = CodeAddresses.Methods.NewMethod(TraceLog.GetFullName(data), module.ModuleFile.ModuleFileIndex, data.MethodToken);
296+
return new TraceProcess.MethodLookupInfo(data.MethodStartAddress, data.MethodSize, methodIndex);
297+
});
298+
}
299+
};
300+
}
301+
200302
/// <summary>
201303
/// Creates a ETLX file an Lttng Text file 'filePath'.
202304
/// </summary>
@@ -593,8 +695,6 @@ private unsafe TraceLog(TraceEventDispatcher source)
593695
realTimeQueue = new Queue<QueueEntry>();
594696
realTimeFlushTimer = new Timer(_ => FlushRealTimeEvents(1000), null, 1000, 1000);
595697

596-
//double lastTime = 0;
597-
598698
// Set up callbacks - we use the session's source for our input.
599699
rawEventSourceToConvert = source;
600700
SetupCallbacks(rawEventSourceToConvert);
@@ -1077,6 +1177,21 @@ private void AddMarkThread(int threadID, long timeStamp, int heapNum)
10771177
thread.process.markThreadsInGC[threadID] = heapNum;
10781178
}
10791179
}
1180+
1181+
private void CopyHeadersFrom(TraceEventDispatcher rawEvents)
1182+
{
1183+
pointerSize = rawEvents.PointerSize;
1184+
_syncTimeUTC = rawEvents._syncTimeUTC;
1185+
_syncTimeQPC = rawEvents._syncTimeQPC;
1186+
_QPCFreq = rawEvents._QPCFreq;
1187+
sessionStartTimeQPC = rawEvents.sessionStartTimeQPC;
1188+
sessionEndTimeQPC = rawEvents.sessionEndTimeQPC;
1189+
cpuSpeedMHz = rawEvents.CpuSpeedMHz;
1190+
numberOfProcessors = rawEvents.NumberOfProcessors;
1191+
eventsLost = rawEvents.EventsLost;
1192+
osVersion = rawEvents.OSVersion;
1193+
}
1194+
10801195
/// <summary>
10811196
/// SetupCallbacks installs all the needed callbacks for TraceLog Processing (stacks, process, thread, summaries etc)
10821197
/// on the TraceEventSource rawEvents.
@@ -1097,27 +1212,13 @@ private unsafe void SetupCallbacks(TraceEventDispatcher rawEvents)
10971212
jsJittedMethods = new List<MethodLoadUnloadJSTraceData>();
10981213
sourceFilesByID = new Dictionary<JavaScriptSourceKey, string>();
10991214

1100-
// If this is a ETL file, we also need to compute all the normal TraceLog stuff the raw stream
1101-
Action copyHeaders = delegate ()
1102-
{
1103-
pointerSize = rawEvents.PointerSize;
1104-
_syncTimeUTC = rawEvents._syncTimeUTC;
1105-
_syncTimeQPC = rawEvents._syncTimeQPC;
1106-
_QPCFreq = rawEvents._QPCFreq;
1107-
sessionStartTimeQPC = rawEvents.sessionStartTimeQPC;
1108-
sessionEndTimeQPC = rawEvents.sessionEndTimeQPC;
1109-
cpuSpeedMHz = rawEvents.CpuSpeedMHz;
1110-
numberOfProcessors = rawEvents.NumberOfProcessors;
1111-
eventsLost = rawEvents.EventsLost;
1112-
osVersion = rawEvents.OSVersion;
1113-
};
1114-
11151215
if (rawEvents is EventPipeEventSource eventPipeEventSource)
11161216
{
1117-
eventPipeEventSource.HeadersDeserialized += copyHeaders;
1118-
} else
1217+
eventPipeEventSource.HeadersDeserialized += delegate () { CopyHeadersFrom(rawEvents); };
1218+
}
1219+
else
11191220
{
1120-
copyHeaders();
1221+
CopyHeadersFrom(rawEvents);
11211222
}
11221223

11231224
// These parsers create state and we want to collect that so we put it on our 'parsers' list that we serialize.
@@ -4315,7 +4416,6 @@ public override bool Process()
43154416
TraceLog.rawKernelEventSource.Process();
43164417
TraceLog.rawEventSourceToConvert.StopProcessing();
43174418
});
4318-
kernelTask.Start();
43194419
}
43204420
TraceLog.rawEventSourceToConvert.Process();
43214421
if (kernelTask != null)
@@ -9094,7 +9194,7 @@ internal void AddILMapping(MethodILToNativeMapTraceData data)
90949194
{
90959195
var ilMap = new ILToNativeMap();
90969196
ilMap.Next = ILMapIndex.Invalid;
9097-
var process = log.Processes.GetProcess(data.ProcessID, data.TimeStampQPC);
9197+
var process = log.Processes.GetOrCreateProcess(data.ProcessID, data.TimeStampQPC);
90989198
if (process == null)
90999199
{
91009200
return;

0 commit comments

Comments
 (0)