11using Microsoft . Diagnostics . Tracing ;
2+ using Microsoft . Diagnostics . Tracing . Etlx ;
23using Microsoft . Diagnostics . Tracing . Parsers ;
34using Microsoft . Diagnostics . Tracing . Parsers . GCDynamic ;
45using System ;
@@ -143,6 +144,79 @@ public void GCDynamicCommittedUsageWithValidDataIsDispatched()
143144 } ) ;
144145 }
145146
147+ [ Fact ]
148+ public void GCDynamicCommittedUsageReplayDecodesWithoutFixupData ( )
149+ {
150+ byte [ ] data = CreateCommittedUsageData ( 1 , 100 , 200 , 300 , 400 , 500 ) ;
151+ byte [ ] payload = CreatePayload ( "CommittedUsage" , data . Length , data , 7 ) ;
152+
153+ WithGCDynamicReplayEvent ( payload , delegate ( GCDynamicTraceEventImpl traceEvent )
154+ {
155+ CommittedUsageTraceEvent committedUsage = ( CommittedUsageTraceEvent ) traceEvent . EventPayload ;
156+
157+ Assert . Equal ( 1 , committedUsage . Version ) ;
158+ Assert . Equal ( 100 , committedUsage . TotalCommittedInUse ) ;
159+ Assert . Equal ( 500 , committedUsage . TotalBookkeepingCommitted ) ;
160+ } ) ;
161+ }
162+
163+ [ Fact ]
164+ public void GCDynamicCommittedUsageReplayDoesNotReusePreviousPayload ( )
165+ {
166+ byte [ ] firstData = CreateCommittedUsageData ( 1 , 100 , 200 , 300 , 400 , 500 ) ;
167+ byte [ ] secondData = CreateCommittedUsageData ( 2 , 600 , 700 , 800 , 900 , 1000 ) ;
168+ byte [ ] firstPayload = CreatePayload ( "CommittedUsage" , firstData . Length , firstData , 7 ) ;
169+ byte [ ] secondPayload = CreatePayload ( "CommittedUsage" , secondData . Length , secondData , 8 ) ;
170+ List < short > versions = new List < short > ( ) ;
171+ List < long > committedInUse = new List < long > ( ) ;
172+
173+ WithReusedGCDynamicReplayEvent ( firstPayload , secondPayload , delegate ( GCDynamicTraceEventImpl traceEvent )
174+ {
175+ CommittedUsageTraceEvent committedUsage = ( CommittedUsageTraceEvent ) traceEvent . EventPayload ;
176+ versions . Add ( committedUsage . Version ) ;
177+ committedInUse . Add ( committedUsage . TotalCommittedInUse ) ;
178+ } ) ;
179+
180+ Assert . Equal ( new short [ ] { 1 , 2 } , versions ) ;
181+ Assert . Equal ( new long [ ] { 100 , 600 } , committedInUse ) ;
182+ }
183+
184+ [ Fact ]
185+ public void GCDynamicCommittedUsageReplayWithTruncatedDataIsSafe ( )
186+ {
187+ byte [ ] payload = CreatePayload ( "CommittedUsage" , 1 , new byte [ ] { 1 } , 7 ) ;
188+
189+ WithGCDynamicReplayEvent ( payload , delegate ( GCDynamicTraceEventImpl traceEvent )
190+ {
191+ CommittedUsageTraceEvent committedUsage = ( CommittedUsageTraceEvent ) traceEvent . EventPayload ;
192+ Assert . Equal ( 0 , committedUsage . Version ) ;
193+ Assert . Equal ( 0 , committedUsage . TotalCommittedInUse ) ;
194+ Assert . Equal ( 0 , committedUsage . TotalCommittedInGlobalDecommit ) ;
195+ Assert . Equal ( 0 , committedUsage . TotalCommittedInFree ) ;
196+ Assert . Equal ( 0 , committedUsage . TotalCommittedInGlobalFree ) ;
197+ Assert . Equal ( 0 , committedUsage . TotalBookkeepingCommitted ) ;
198+
199+ List < KeyValuePair < string , object > > values = new List < KeyValuePair < string , object > > ( ) ;
200+ Exception payloadValuesException = Record . Exception ( delegate
201+ {
202+ foreach ( KeyValuePair < string , object > value in committedUsage . PayloadValues )
203+ {
204+ values . Add ( value ) ;
205+ }
206+ } ) ;
207+ Assert . Null ( payloadValuesException ) ;
208+ Assert . Equal ( 6 , values . Count ) ;
209+ Assert . Equal ( ( short ) 0 , values [ 0 ] . Value ) ;
210+ for ( int i = 1 ; i < values . Count ; i ++ )
211+ {
212+ Assert . Equal ( ( long ) 0 , values [ i ] . Value ) ;
213+ }
214+
215+ Exception toXmlException = Record . Exception ( delegate { traceEvent . ToXml ( new StringBuilder ( ) ) ; } ) ;
216+ Assert . Null ( toXmlException ) ;
217+ } ) ;
218+ }
219+
146220 /// <summary>
147221 /// Regression test for the propagated-exception path through
148222 /// PayloadValues on a malformed payload. ToXml iterates
@@ -243,6 +317,18 @@ private static byte[] CreatePayload(string name, int dataSize, byte[] data, shor
243317 return payload . ToArray ( ) ;
244318 }
245319
320+ private static byte [ ] CreateCommittedUsageData ( short version , long committedInUse , long committedInGlobalDecommit , long committedInFree , long committedInGlobalFree , long bookkeepingCommitted )
321+ {
322+ byte [ ] data = new byte [ CommittedUsageTraceEvent . MinimumDataSize ] ;
323+ BitConverter . GetBytes ( version ) . CopyTo ( data , 0 ) ;
324+ BitConverter . GetBytes ( committedInUse ) . CopyTo ( data , 2 ) ;
325+ BitConverter . GetBytes ( committedInGlobalDecommit ) . CopyTo ( data , 10 ) ;
326+ BitConverter . GetBytes ( committedInFree ) . CopyTo ( data , 18 ) ;
327+ BitConverter . GetBytes ( committedInGlobalFree ) . CopyTo ( data , 26 ) ;
328+ BitConverter . GetBytes ( bookkeepingCommitted ) . CopyTo ( data , 34 ) ;
329+ return data ;
330+ }
331+
246332 private static unsafe void WithGCDynamicEvent ( byte [ ] payload , Action < GCDynamicTraceEventImpl > action )
247333 {
248334 fixed ( byte * payloadBytes = payload )
@@ -260,5 +346,44 @@ private static unsafe void WithGCDynamicEvent(byte[] payload, Action<GCDynamicTr
260346 action ( traceEvent ) ;
261347 }
262348 }
349+
350+ private static unsafe void WithGCDynamicReplayEvent ( byte [ ] payload , Action < GCDynamicTraceEventImpl > action )
351+ {
352+ WithReusedGCDynamicReplayEvent ( payload , null , action ) ;
353+ }
354+
355+ private static unsafe void WithReusedGCDynamicReplayEvent ( byte [ ] firstPayload , byte [ ] secondPayload , Action < GCDynamicTraceEventImpl > action )
356+ {
357+ fixed ( byte * firstPayloadBytes = firstPayload )
358+ fixed ( byte * secondPayloadBytes = secondPayload )
359+ {
360+ TraceLog source = ( TraceLog ) Activator . CreateInstance ( typeof ( TraceLog ) , true ) ;
361+ source . _QPCFreq = 1 ;
362+ source . _syncTimeQPC = 1 ;
363+ source . _syncTimeUTC = DateTime . UtcNow ;
364+ source . sessionStartTimeQPC = 1 ;
365+
366+ TraceEventNativeMethods . EVENT_RECORD eventRecord = new TraceEventNativeMethods . EVENT_RECORD ( ) ;
367+ eventRecord . EventHeader . ProviderId = GCDynamicTraceEventParser . ProviderGuid ;
368+ eventRecord . EventHeader . Id = ( ushort ) GCDynamicEventBase . CommittedUsageTemplate . ID ;
369+
370+ GCDynamicTraceEventImpl traceEvent = new GCDynamicTraceEventImpl ( null , ( int ) GCDynamicEventBase . CommittedUsageTemplate . ID , 1 , "GC" , Guid . Empty , 41 , "CommittedUsage" , GCDynamicTraceEventParser . ProviderGuid , "Microsoft-Windows-DotNETRuntime" ) ;
371+ traceEvent . eventRecord = & eventRecord ;
372+ traceEvent . traceEventSource = source ;
373+
374+ eventRecord . UserDataLength = ( ushort ) firstPayload . Length ;
375+ eventRecord . UserData = ( IntPtr ) firstPayloadBytes ;
376+ traceEvent . userData = eventRecord . UserData ;
377+ action ( traceEvent ) ;
378+
379+ if ( secondPayload != null )
380+ {
381+ eventRecord . UserDataLength = ( ushort ) secondPayload . Length ;
382+ eventRecord . UserData = ( IntPtr ) secondPayloadBytes ;
383+ traceEvent . userData = eventRecord . UserData ;
384+ action ( traceEvent ) ;
385+ }
386+ }
387+ }
263388 }
264389}
0 commit comments