2020
2121namespace Microsoft . Diagnostics . Tracing
2222{
23+ public class RuntimeLoaderStats : Dictionary < int , CLRRuntimeActivityComputer . PerThreadStartStopData >
24+ {
25+ public TraceLogEventSource EventSource ;
26+ }
27+
28+
2329 public class CLRRuntimeActivityComputer
2430 {
31+ public struct EventUID : IComparable < EventUID > , IEquatable < EventUID >
32+ {
33+ public EventUID ( TraceEvent evt ) : this ( evt . EventIndex , evt . TimeStampRelativeMSec ) { }
34+
35+ public EventUID ( EventIndex eventId , double time )
36+ {
37+ EventId = eventId ;
38+ Time = time ;
39+ }
40+
41+ public readonly EventIndex EventId ;
42+ public readonly double Time ;
43+
44+ public int CompareTo ( EventUID other )
45+ {
46+ int timeCompare = Time . CompareTo ( other . Time ) ;
47+ if ( timeCompare != 0 )
48+ return timeCompare ;
49+
50+ return EventId . CompareTo ( other . EventId ) ;
51+ }
52+
53+ public override int GetHashCode ( )
54+ {
55+ return ( int ) EventId ;
56+ }
57+
58+ public override bool Equals ( object obj )
59+ {
60+ if ( ! ( obj is EventUID ) )
61+ return false ;
62+ return this . CompareTo ( ( EventUID ) obj ) == 0 ;
63+ }
64+
65+ public bool Equals ( EventUID other )
66+ {
67+ return this . CompareTo ( other ) == 0 ;
68+ }
69+ }
70+
71+ public struct StartStopThreadEventData : IComparable < StartStopThreadEventData >
72+ {
73+ public StartStopThreadEventData ( EventUID start , EventUID end , string name )
74+ {
75+ Start = start ;
76+ End = end ;
77+ Name = name ;
78+ StackDepth = 0 ;
79+ }
80+
81+ public EventUID Start ;
82+ public EventUID End ;
83+ public int StackDepth ;
84+ public string Name ;
85+
86+ int IComparable < StartStopThreadEventData > . CompareTo ( StartStopThreadEventData other )
87+ {
88+ return Start . CompareTo ( other . Start ) ;
89+ }
90+ }
91+
92+
2593 struct IdOfIncompleteAction : IEquatable < IdOfIncompleteAction >
2694 {
2795 public long Identifier ;
@@ -48,19 +116,32 @@ public bool Equals(IdOfIncompleteAction other)
48116
49117 struct IncompleteActionDesc
50118 {
51- public StartStopStackMingledComputer . EventUID Start ;
119+ public EventUID Start ;
52120 public string OperationType ;
53121 public string Name ;
54122 }
55123
124+ public class PerThreadStartStopData
125+ {
126+ public int Offset ;
127+
128+ public StartStopThreadEventData [ ] Data ;
129+ public StartStopThreadEventData [ ] SplitUpData ;
130+ public double [ ] SplitUpDataStarts ;
131+ }
132+
133+ RuntimeLoaderStats _startStopData = new RuntimeLoaderStats ( ) ;
134+ public RuntimeLoaderStats StartStopData => _startStopData ;
135+
56136 Dictionary < IdOfIncompleteAction , IncompleteActionDesc > _incompleteJitEvents = new Dictionary < IdOfIncompleteAction , IncompleteActionDesc > ( ) ;
57137 Dictionary < IdOfIncompleteAction , IncompleteActionDesc > _incompleteR2REvents = new Dictionary < IdOfIncompleteAction , IncompleteActionDesc > ( ) ;
58138 Dictionary < IdOfIncompleteAction , IncompleteActionDesc > _incompleteTypeLoadEvents = new Dictionary < IdOfIncompleteAction , IncompleteActionDesc > ( ) ;
59139
60- public Dictionary < int , List < StartStopStackMingledComputer . StartStopThreadEventData > > StartStopEvents { get ; } = new Dictionary < int , List < StartStopStackMingledComputer . StartStopThreadEventData > > ( ) ;
140+ public Dictionary < int , List < StartStopThreadEventData > > StartStopEvents { get ; } = new Dictionary < int , List < StartStopThreadEventData > > ( ) ;
61141
62142 public CLRRuntimeActivityComputer ( TraceLogEventSource source )
63143 {
144+ _startStopData . EventSource = source ;
64145 source . Clr . MethodJittingStarted += Clr_MethodJittingStarted ;
65146 source . Clr . MethodR2RGetEntryPoint += Clr_MethodR2RGetEntryPoint ;
66147 source . Clr . MethodLoadVerbose += Clr_MethodLoadVerbose ;
@@ -78,21 +159,74 @@ public CLRRuntimeActivityComputer(TraceLogEventSource source)
78159 source . Clr . LoaderAssemblyLoad -= Clr_LoaderAssemblyLoad ;
79160 source . Clr . TypeLoadStart -= Clr_TypeLoadStart ;
80161 source . Clr . TypeLoadStop -= Clr_TypeLoadStop ;
162+
163+ HashSet < EventUID > interestingEvents = new HashSet < EventUID > ( ) ;
164+ foreach ( var entry in StartStopEvents )
165+ {
166+ StartStopThreadEventData [ ] data = entry . Value . ToArray ( ) ;
167+ Array . Sort ( data ) ;
168+ PerThreadStartStopData perThread = new PerThreadStartStopData ( ) ;
169+ perThread . Data = data ;
170+ for ( int i = 0 ; i < data . Length ; i ++ )
171+ {
172+ interestingEvents . Add ( data [ i ] . Start ) ;
173+ interestingEvents . Add ( data [ i ] . End ) ;
174+ }
175+ _startStopData . Add ( entry . Key , perThread ) ;
176+ }
177+
178+ foreach ( var entry in _startStopData )
179+ {
180+ List < StartStopThreadEventData > splitUpStartStopData = new List < StartStopThreadEventData > ( ) ;
181+
182+ Stack < StartStopThreadEventData > currentPerThreadProcessingState = new Stack < StartStopThreadEventData > ( ) ;
183+ for ( int i = 0 ; i < entry . Value . Data . Length ; i ++ )
184+ {
185+ var startStop = entry . Value . Data [ i ] ;
186+
187+ if ( currentPerThreadProcessingState . Count > 0 )
188+ {
189+ while ( ( currentPerThreadProcessingState . Count > 0 ) && ( currentPerThreadProcessingState . Peek ( ) . End . CompareTo ( startStop . Start ) < 0 ) )
190+ {
191+ // Current stack top event finished before this event happened.
192+ var poppedEvent = currentPerThreadProcessingState . Pop ( ) ;
193+ EventUID lastEventProcessedEnd = poppedEvent . End ;
194+ if ( currentPerThreadProcessingState . Count > 0 )
195+ {
196+ var tempPoppedEvent = currentPerThreadProcessingState . Pop ( ) ;
197+ tempPoppedEvent . Start = lastEventProcessedEnd ;
198+ splitUpStartStopData . Add ( tempPoppedEvent ) ;
199+ currentPerThreadProcessingState . Push ( tempPoppedEvent ) ;
200+ }
201+ }
202+ }
203+
204+ startStop . StackDepth = currentPerThreadProcessingState . Count ;
205+ splitUpStartStopData . Add ( startStop ) ;
206+ currentPerThreadProcessingState . Push ( startStop ) ;
207+ }
208+ entry . Value . SplitUpData = splitUpStartStopData . ToArray ( ) ;
209+ entry . Value . SplitUpDataStarts = new double [ entry . Value . SplitUpData . Length ] ;
210+ for ( int i = 0 ; i < entry . Value . SplitUpDataStarts . Length ; i ++ )
211+ {
212+ entry . Value . SplitUpDataStarts [ i ] = entry . Value . SplitUpData [ i ] . Start . Time ;
213+ }
214+ }
81215 }
82216
83- private void AddStartStopData ( int threadId , StartStopStackMingledComputer . EventUID start , StartStopStackMingledComputer . EventUID end , string name )
217+ private void AddStartStopData ( int threadId , EventUID start , EventUID end , string name )
84218 {
85219 if ( ! StartStopEvents . ContainsKey ( threadId ) )
86- StartStopEvents [ threadId ] = new List < StartStopStackMingledComputer . StartStopThreadEventData > ( ) ;
220+ StartStopEvents [ threadId ] = new List < StartStopThreadEventData > ( ) ;
87221
88- List < StartStopStackMingledComputer . StartStopThreadEventData > startStopData = StartStopEvents [ threadId ] ;
89- startStopData . Add ( new StartStopStackMingledComputer . StartStopThreadEventData ( start , end , name ) ) ;
222+ List < StartStopThreadEventData > startStopData = StartStopEvents [ threadId ] ;
223+ startStopData . Add ( new StartStopThreadEventData ( start , end , name ) ) ;
90224 }
91225
92226 private void Clr_LoaderAssemblyLoad ( AssemblyLoadUnloadTraceData obj )
93227 {
94228 // Since we don't have start stop data, simply treat the assembly load event as a point in time so that it is visible in the textual load view
95- StartStopStackMingledComputer . EventUID eventTime = new StartStopStackMingledComputer . EventUID ( obj ) ;
229+ EventUID eventTime = new EventUID ( obj ) ;
96230 AddStartStopData ( obj . ThreadID , eventTime , eventTime , $ "ASMLOAD({ obj . FullyQualifiedAssemblyName } ,{ obj . AssemblyID } )") ;
97231 }
98232
@@ -116,14 +250,14 @@ private void MethodJittedEvent(TraceEvent evt, long methodID)
116250 // JitStart is processed, don't process it again.
117251 _incompleteJitEvents . Remove ( id ) ;
118252
119- AddStartStopData ( id . ThreadID , jitStartData . Start , new StartStopStackMingledComputer . EventUID ( evt ) , jitStartData . OperationType + "(" + jitStartData . Name + ")" ) ;
253+ AddStartStopData ( id . ThreadID , jitStartData . Start , new EventUID ( evt ) , jitStartData . OperationType + "(" + jitStartData . Name + ")" ) ;
120254 }
121255 }
122256
123257 private void Clr_MethodJittingStarted ( MethodJittingStartedTraceData obj )
124258 {
125259 IncompleteActionDesc incompleteDesc = new IncompleteActionDesc ( ) ;
126- incompleteDesc . Start = new StartStopStackMingledComputer . EventUID ( obj ) ;
260+ incompleteDesc . Start = new EventUID ( obj ) ;
127261 incompleteDesc . Name = JITStats . GetMethodName ( obj ) ;
128262 incompleteDesc . OperationType = "JIT" ;
129263
@@ -137,7 +271,7 @@ private void Clr_MethodJittingStarted(MethodJittingStartedTraceData obj)
137271 private void Clr_R2RGetEntryPointStarted ( R2RGetEntryPointStartedTraceData obj )
138272 {
139273 IncompleteActionDesc incompleteDesc = new IncompleteActionDesc ( ) ;
140- incompleteDesc . Start = new StartStopStackMingledComputer . EventUID ( obj ) ;
274+ incompleteDesc . Start = new EventUID ( obj ) ;
141275 incompleteDesc . Name = "" ;
142276 incompleteDesc . OperationType = "R2R" ;
143277
@@ -156,7 +290,7 @@ private void Clr_MethodR2RGetEntryPoint(R2RGetEntryPointTraceData obj)
156290
157291 // If we had a R2R start lookup event, capture that start time, otherwise, use the R2REntrypoint
158292 // data as both start and stop
159- StartStopStackMingledComputer . EventUID startUID = new StartStopStackMingledComputer . EventUID ( obj ) ;
293+ EventUID startUID = new EventUID ( obj ) ;
160294 if ( _incompleteR2REvents . TryGetValue ( id , out IncompleteActionDesc r2rStartData ) )
161295 {
162296 startUID = r2rStartData . Start ;
@@ -166,18 +300,18 @@ private void Clr_MethodR2RGetEntryPoint(R2RGetEntryPointTraceData obj)
166300 if ( obj . EntryPoint == 0 )
167301 {
168302 // If Entrypoint is null then the search failed.
169- AddStartStopData ( id . ThreadID , startUID , new StartStopStackMingledComputer . EventUID ( obj ) , "R2R_Failed" + "(" + JITStats . GetMethodName ( obj ) + ")" ) ;
303+ AddStartStopData ( id . ThreadID , startUID , new EventUID ( obj ) , "R2R_Failed" + "(" + JITStats . GetMethodName ( obj ) + ")" ) ;
170304 }
171305 else
172306 {
173- AddStartStopData ( id . ThreadID , startUID , new StartStopStackMingledComputer . EventUID ( obj ) , "R2R_Found" + "(" + JITStats . GetMethodName ( obj ) + ")" ) ;
307+ AddStartStopData ( id . ThreadID , startUID , new EventUID ( obj ) , "R2R_Found" + "(" + JITStats . GetMethodName ( obj ) + ")" ) ;
174308 }
175309 }
176310
177311 private void Clr_TypeLoadStart ( TypeLoadStartTraceData obj )
178312 {
179313 IncompleteActionDesc incompleteDesc = new IncompleteActionDesc ( ) ;
180- incompleteDesc . Start = new StartStopStackMingledComputer . EventUID ( obj ) ;
314+ incompleteDesc . Start = new EventUID ( obj ) ;
181315 incompleteDesc . Name = "" ;
182316 incompleteDesc . OperationType = "TypeLoad" ;
183317
@@ -196,14 +330,14 @@ private void Clr_TypeLoadStop(TypeLoadStopTraceData obj)
196330
197331 // If we had a TypeLoad start lookup event, capture that start time, otherwise, use the TypeLoadStop
198332 // data as both start and stop
199- StartStopStackMingledComputer . EventUID startUID = new StartStopStackMingledComputer . EventUID ( obj ) ;
333+ EventUID startUID = new EventUID ( obj ) ;
200334 if ( _incompleteTypeLoadEvents . TryGetValue ( id , out IncompleteActionDesc typeLoadStartData ) )
201335 {
202336 startUID = typeLoadStartData . Start ;
203337 _incompleteTypeLoadEvents . Remove ( id ) ;
204338 }
205339
206- AddStartStopData ( id . ThreadID , startUID , new StartStopStackMingledComputer . EventUID ( obj ) , $ "TypeLoad ({ obj . TypeName } , { obj . LoadLevel } )") ;
340+ AddStartStopData ( id . ThreadID , startUID , new EventUID ( obj ) , $ "TypeLoad ({ obj . TypeName } , { obj . LoadLevel } )") ;
207341 }
208342 }
209343}
0 commit comments