@@ -180,9 +180,34 @@ public static TraceLogEventSource CreateFromTraceEventSession(TraceEventSession
180
180
/// the .Log Property) which lets you get at aggregated information (Processes, threads, images loaded, and perhaps most
181
181
/// importantly TraceEvent.CallStack() will work. Thus you can get real time stacks from events).
182
182
/// </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 )
184
202
{
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 ;
186
211
}
187
212
188
213
/// <summary>
@@ -197,6 +222,83 @@ public static TraceLogEventSource CreateFromEventPipeEventSource(EventPipeEventS
197
222
return traceLog . realTimeSource ;
198
223
}
199
224
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
+
200
302
/// <summary>
201
303
/// Creates a ETLX file an Lttng Text file 'filePath'.
202
304
/// </summary>
@@ -593,8 +695,6 @@ private unsafe TraceLog(TraceEventDispatcher source)
593
695
realTimeQueue = new Queue < QueueEntry > ( ) ;
594
696
realTimeFlushTimer = new Timer ( _ => FlushRealTimeEvents ( 1000 ) , null , 1000 , 1000 ) ;
595
697
596
- //double lastTime = 0;
597
-
598
698
// Set up callbacks - we use the session's source for our input.
599
699
rawEventSourceToConvert = source ;
600
700
SetupCallbacks ( rawEventSourceToConvert ) ;
@@ -1077,6 +1177,21 @@ private void AddMarkThread(int threadID, long timeStamp, int heapNum)
1077
1177
thread . process . markThreadsInGC [ threadID ] = heapNum ;
1078
1178
}
1079
1179
}
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
+
1080
1195
/// <summary>
1081
1196
/// SetupCallbacks installs all the needed callbacks for TraceLog Processing (stacks, process, thread, summaries etc)
1082
1197
/// on the TraceEventSource rawEvents.
@@ -1097,27 +1212,13 @@ private unsafe void SetupCallbacks(TraceEventDispatcher rawEvents)
1097
1212
jsJittedMethods = new List < MethodLoadUnloadJSTraceData > ( ) ;
1098
1213
sourceFilesByID = new Dictionary < JavaScriptSourceKey , string > ( ) ;
1099
1214
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
-
1115
1215
if ( rawEvents is EventPipeEventSource eventPipeEventSource )
1116
1216
{
1117
- eventPipeEventSource . HeadersDeserialized += copyHeaders ;
1118
- } else
1217
+ eventPipeEventSource . HeadersDeserialized += delegate ( ) { CopyHeadersFrom ( rawEvents ) ; } ;
1218
+ }
1219
+ else
1119
1220
{
1120
- copyHeaders ( ) ;
1221
+ CopyHeadersFrom ( rawEvents ) ;
1121
1222
}
1122
1223
1123
1224
// 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()
4315
4416
TraceLog . rawKernelEventSource . Process ( ) ;
4316
4417
TraceLog . rawEventSourceToConvert . StopProcessing ( ) ;
4317
4418
} ) ;
4318
- kernelTask . Start ( ) ;
4319
4419
}
4320
4420
TraceLog . rawEventSourceToConvert . Process ( ) ;
4321
4421
if ( kernelTask != null )
@@ -9094,7 +9194,7 @@ internal void AddILMapping(MethodILToNativeMapTraceData data)
9094
9194
{
9095
9195
var ilMap = new ILToNativeMap ( ) ;
9096
9196
ilMap . Next = ILMapIndex . Invalid ;
9097
- var process = log . Processes . GetProcess ( data . ProcessID , data . TimeStampQPC ) ;
9197
+ var process = log . Processes . GetOrCreateProcess ( data . ProcessID , data . TimeStampQPC ) ;
9098
9198
if ( process == null )
9099
9199
{
9100
9200
return ;
0 commit comments