1
1
using FastSerialization ;
2
+ using Microsoft . Diagnostics . NETCore . Client ;
2
3
using Microsoft . Diagnostics . Tracing ;
3
4
using Microsoft . Diagnostics . Tracing . Etlx ;
4
5
using Microsoft . Diagnostics . Tracing . EventPipe ;
7
8
using System ;
8
9
using System . Collections . Generic ;
9
10
using System . Diagnostics ;
11
+ using System . Diagnostics . Tracing ;
10
12
using System . IO ;
11
13
using System . Linq ;
12
14
using System . Text ;
15
+ using System . Threading . Tasks ;
13
16
using Xunit ;
14
17
using Xunit . Abstractions ;
18
+ using static Microsoft . Diagnostics . Tracing . Etlx . TraceLog ;
15
19
16
20
namespace TraceEventTests
17
21
{
@@ -20,15 +24,54 @@ public class EventPipeParsing : EventPipeTestBase
20
24
private class EventRecord
21
25
{
22
26
public int TotalCount ;
23
- public string FirstSeriazliedSample ;
27
+ public string FirstSerializedSample ;
28
+ }
29
+
30
+ private class EventStatistics
31
+ {
32
+ public SortedDictionary < string , EventRecord > Records = new SortedDictionary < string , EventRecord > ( StringComparer . Ordinal ) ;
33
+
34
+ public void Record ( TraceEvent data ) => Record ( data . ProviderName + "/" + data . EventName , data ) ;
35
+
36
+ public void Record ( string eventName , TraceEvent data )
37
+ {
38
+ if ( Records . ContainsKey ( eventName ) )
39
+ {
40
+ Records [ eventName ] . TotalCount ++ ;
41
+ }
42
+ else
43
+ {
44
+ Records [ eventName ] = new EventRecord ( )
45
+ {
46
+ TotalCount = 1 ,
47
+ FirstSerializedSample = new String ( data . ToString ( ) . Replace ( "\n " , "\\ n" ) . Replace ( "\r " , "\\ r" ) . Take ( 1000 ) . ToArray ( ) )
48
+ } ;
49
+ }
50
+ }
51
+
52
+ override
53
+ public string ToString ( )
54
+ {
55
+ StringBuilder sb = new StringBuilder ( 1024 * 1024 ) ;
56
+ foreach ( var item in Records )
57
+ {
58
+ sb . AppendLine ( $ "{ item . Key } , { item . Value . TotalCount } , { item . Value . FirstSerializedSample } ") ;
59
+ }
60
+
61
+ return sb . ToString ( ) ;
62
+ }
24
63
}
25
64
26
65
public EventPipeParsing ( ITestOutputHelper output )
27
66
: base ( output )
28
67
{
29
68
}
30
-
31
- [ Theory ( ) ]
69
+
70
+ #if NETCOREAPP3_0_OR_GREATER
71
+ [ Theory ( Skip = "Snapshot difs due to increased float accuracy on newer .NET versions." ) ]
72
+ #else
73
+ [ Theory ]
74
+ #endif
32
75
[ MemberData ( nameof ( TestEventPipeFiles ) ) ]
33
76
public void Basic ( string eventPipeFileName )
34
77
{
@@ -38,30 +81,13 @@ public void Basic(string eventPipeFileName)
38
81
string eventPipeFilePath = Path . Combine ( UnZippedDataDir , eventPipeFileName ) ;
39
82
40
83
Output . WriteLine ( string . Format ( "Processing the file {0}, Making ETLX and scanning." , Path . GetFullPath ( eventPipeFilePath ) ) ) ;
41
-
42
- var eventStatistics = new SortedDictionary < string , EventRecord > ( StringComparer . Ordinal ) ;
84
+ var eventStatistics = new EventStatistics ( ) ;
43
85
44
86
using ( var traceLog = new TraceLog ( TraceLog . CreateFromEventPipeDataFile ( eventPipeFilePath ) ) )
45
87
{
46
88
var traceSource = traceLog . Events . GetSource ( ) ;
47
89
48
- traceSource . AllEvents += delegate ( TraceEvent data )
49
- {
50
- string eventName = data . ProviderName + "/" + data . EventName ;
51
-
52
- if ( eventStatistics . ContainsKey ( eventName ) )
53
- {
54
- eventStatistics [ eventName ] . TotalCount ++ ;
55
- }
56
- else
57
- {
58
- eventStatistics [ eventName ] = new EventRecord ( )
59
- {
60
- TotalCount = 1 ,
61
- FirstSeriazliedSample = new String ( data . ToString ( ) . Replace ( "\n " , "\\ n" ) . Replace ( "\r " , "\\ r" ) . Take ( 1000 ) . ToArray ( ) )
62
- } ;
63
- }
64
- } ;
90
+ traceSource . AllEvents += eventStatistics . Record ;
65
91
66
92
// Process
67
93
traceSource . Process ( ) ;
@@ -79,7 +105,7 @@ public void Streaming(string eventPipeFileName)
79
105
80
106
string eventPipeFilePath = Path . Combine ( UnZippedDataDir , eventPipeFileName ) ;
81
107
Output . WriteLine ( string . Format ( "Processing the file {0}" , Path . GetFullPath ( eventPipeFilePath ) ) ) ;
82
- var eventStatistics = new SortedDictionary < string , EventRecord > ( StringComparer . Ordinal ) ;
108
+ var eventStatistics = new EventStatistics ( ) ;
83
109
84
110
long curStreamPosition = 0 ;
85
111
using ( MockStreamingOnlyStream s = new MockStreamingOnlyStream ( new FileStream ( eventPipeFilePath , FileMode . Open , FileAccess . Read , FileShare . Read ) ) )
@@ -95,7 +121,6 @@ public void Streaming(string eventPipeFileName)
95
121
Assert . InRange ( newStreamPosition , curStreamPosition , curStreamPosition + 103_000 ) ;
96
122
curStreamPosition = newStreamPosition ;
97
123
98
-
99
124
string eventName = data . ProviderName + "/" + data . EventName ;
100
125
101
126
// For whatever reason the parse filtering below produces a couple extra events
@@ -108,18 +133,7 @@ public void Streaming(string eventPipeFileName)
108
133
eventName == "Microsoft-Windows-DotNETRuntime/Method" )
109
134
return ;
110
135
111
- if ( eventStatistics . ContainsKey ( eventName ) )
112
- {
113
- eventStatistics [ eventName ] . TotalCount ++ ;
114
- }
115
- else
116
- {
117
- eventStatistics [ eventName ] = new EventRecord ( )
118
- {
119
- TotalCount = 1 ,
120
- FirstSeriazliedSample = new String ( data . ToString ( ) . Replace ( "\n " , "\\ n" ) . Replace ( "\r " , "\\ r" ) . Take ( 1000 ) . ToArray ( ) )
121
- } ;
122
- }
136
+ eventStatistics . Record ( eventName , data ) ;
123
137
} ;
124
138
125
139
// this is somewhat arbitrary looking set of parser event callbacks empirically
@@ -148,6 +162,61 @@ public void Streaming(string eventPipeFileName)
148
162
ValidateEventStatistics ( eventStatistics , eventPipeFileName ) ;
149
163
}
150
164
165
+ #if NETCOREAPP3_0_OR_GREATER
166
+ [ Theory ]
167
+ #else
168
+ [ Theory ( Skip = "EventPipeSession connection is only available to target apps on .NET Core 3.0 or later" ) ]
169
+ #endif
170
+ [ InlineData ( true ) ]
171
+ [ InlineData ( false ) ]
172
+ public async Task SessionStreaming ( bool initialRundown )
173
+ {
174
+ var client = new DiagnosticsClient ( Process . GetCurrentProcess ( ) . Id ) ;
175
+ var rundownConfig = initialRundown ? EventPipeRundownConfiguration . Enable ( client ) : EventPipeRundownConfiguration . None ( ) ;
176
+ var providers = new [ ]
177
+ {
178
+ new EventPipeProvider ( SampleProfilerTraceEventParser . ProviderName , EventLevel . Informational ) ,
179
+ } ;
180
+ using ( var session = client . StartEventPipeSession ( providers , requestRundown : false ) )
181
+ {
182
+ using ( var traceSource = CreateFromEventPipeSession ( session , rundownConfig ) )
183
+ {
184
+ var sampleEventParser = new SampleProfilerTraceEventParser ( traceSource ) ;
185
+
186
+ // Signal that we have received the first event.
187
+ var eventCallStackIndex = new TaskCompletionSource < CallStackIndex > ( ) ;
188
+ sampleEventParser . ThreadSample += delegate ( ClrThreadSampleTraceData e )
189
+ {
190
+ eventCallStackIndex . TrySetResult ( e . CallStackIndex ( ) ) ;
191
+ } ;
192
+
193
+ // Process in the background (this is blocking).
194
+ var processingTask = Task . Run ( traceSource . Process ) ;
195
+
196
+ // Verify the event can be symbolicated on the fly if (initialRundown == true).
197
+ var callStackIndex = await eventCallStackIndex . Task ;
198
+ Assert . NotEqual ( CallStackIndex . Invalid , callStackIndex ) ;
199
+ var codeAddressIndex = traceSource . TraceLog . CallStacks . CodeAddressIndex ( callStackIndex ) ;
200
+ Assert . NotEqual ( CodeAddressIndex . Invalid , codeAddressIndex ) ;
201
+ var methodIndex = traceSource . TraceLog . CodeAddresses . MethodIndex ( codeAddressIndex ) ;
202
+ if ( initialRundown )
203
+ {
204
+ Assert . NotEqual ( MethodIndex . Invalid , methodIndex ) ;
205
+ var method = traceSource . TraceLog . CodeAddresses . Methods [ methodIndex ] ;
206
+ Assert . NotEmpty ( method . FullMethodName ) ;
207
+ }
208
+ else
209
+ {
210
+ Assert . Equal ( MethodIndex . Invalid , methodIndex ) ;
211
+ }
212
+
213
+ // Stop after receiving the first event.
214
+ session . Stop ( ) ;
215
+ await processingTask ;
216
+ }
217
+ }
218
+ }
219
+
151
220
[ Fact ]
152
221
public void CanParseHeaderOfV1EventPipeFile ( )
153
222
{
@@ -248,7 +317,7 @@ public void V4EventPipeFileHasProcNumbers()
248
317
// Process
249
318
traceSource . Process ( ) ;
250
319
251
- for ( int i = 0 ; i < traceSource . NumberOfProcessors ; i ++ )
320
+ for ( int i = 0 ; i < traceSource . NumberOfProcessors ; i ++ )
252
321
{
253
322
Assert . NotEqual ( 0 , counts [ i ] ) ;
254
323
}
@@ -463,7 +532,7 @@ public void WellKnownDiagnosticSourceEventsHavePayloads()
463
532
// and ignore the empty parameter metadata provided in the stream, treating the events
464
533
// as if the runtime had provided the correct parameter schema.
465
534
//
466
- // I am concurrently working on a runtime fix and updated file format revision which can
535
+ // I am concurrently working on a runtime fix and updated file format revision which can
467
536
// correctly encode these parameter types. However for back-compat with older runtimes we
468
537
// need this.
469
538
@@ -570,15 +639,9 @@ private void Dynamic_All(TraceEvent obj)
570
639
throw new NotImplementedException ( ) ;
571
640
}
572
641
573
- private void ValidateEventStatistics ( SortedDictionary < string , EventRecord > eventStatistics , string eventPipeFileName )
642
+ private void ValidateEventStatistics ( EventStatistics eventStatistics , string eventPipeFileName )
574
643
{
575
- StringBuilder sb = new StringBuilder ( 1024 * 1024 ) ;
576
- foreach ( var item in eventStatistics )
577
- {
578
- sb . AppendLine ( $ "{ item . Key } , { item . Value . TotalCount } , { item . Value . FirstSeriazliedSample } ") ;
579
- }
580
-
581
- string actual = sb . ToString ( ) ;
644
+ string actual = eventStatistics . ToString ( ) ;
582
645
string baselineFile = Path . Combine ( TestDataDir , eventPipeFileName + ".baseline.txt" ) ;
583
646
string expected = File . ReadAllText ( baselineFile ) ;
584
647
@@ -603,7 +666,7 @@ public MockStreamingOnlyStream(Stream innerStream)
603
666
{
604
667
_innerStream = innerStream ;
605
668
}
606
- public long TestOnlyPosition { get { return _innerStream . Position ; } }
669
+ public long TestOnlyPosition { get { return _innerStream . Position ; } }
607
670
608
671
public override bool CanRead => true ;
609
672
public override bool CanSeek => false ;
@@ -662,7 +725,7 @@ public void Write(string arg)
662
725
public void WriteArray < T > ( T [ ] elements , Action < T > writeElement )
663
726
{
664
727
WriteArrayLength ( elements . Length ) ;
665
- for ( int i = 0 ; i < elements . Length ; i ++ )
728
+ for ( int i = 0 ; i < elements . Length ; i ++ )
666
729
{
667
730
writeElement ( elements [ i ] ) ;
668
731
}
@@ -779,7 +842,7 @@ private static void Align(BinaryWriter writer, long previousBytesWritten)
779
842
}
780
843
}
781
844
782
- public static void WriteBlock ( BinaryWriter writer , string name , Action < BinaryWriter > writeBlockData ,
845
+ public static void WriteBlock ( BinaryWriter writer , string name , Action < BinaryWriter > writeBlockData ,
783
846
long previousBytesWritten = 0 )
784
847
{
785
848
Debug . WriteLine ( $ "Starting block { name } position: { writer . BaseStream . Position + previousBytesWritten } ") ;
@@ -931,7 +994,7 @@ MemoryStream GetFirstChunk()
931
994
EventPipeWriter . WriteNetTraceHeader ( writer ) ;
932
995
EventPipeWriter . WriteFastSerializationHeader ( writer ) ;
933
996
EventPipeWriter . WriteTraceObject ( writer ) ;
934
- EventPipeWriter . WriteMetadataBlock ( writer ,
997
+ EventPipeWriter . WriteMetadataBlock ( writer ,
935
998
new EventMetadata ( 1 , "Provider" , "Event" , 1 ) ) ;
936
999
ms . Position = 0 ;
937
1000
return ms ;
@@ -950,16 +1013,16 @@ MemoryStream GetNextChunk()
950
1013
else
951
1014
{
952
1015
// 20 blocks, each with 20 events in them
953
- for ( int i = 0 ; i < 20 ; i ++ )
1016
+ for ( int i = 0 ; i < 20 ; i ++ )
954
1017
{
955
- EventPipeWriter . WriteEventBlock ( writer ,
1018
+ EventPipeWriter . WriteEventBlock ( writer ,
956
1019
w =>
957
1020
{
958
1021
for ( int j = 0 ; j < 20 ; j ++ )
959
1022
{
960
1023
EventPipeWriter . WriteEventBlob ( w , 1 , _sequenceNumber ++ , payloadSize , WriteEventPayload ) ;
961
1024
}
962
- } ,
1025
+ } ,
963
1026
_bytesWritten ) ;
964
1027
}
965
1028
}
@@ -987,7 +1050,7 @@ public override void Flush()
987
1050
public override int Read ( byte [ ] buffer , int offset , int count )
988
1051
{
989
1052
int ret = _currentChunk . Read ( buffer , offset , count ) ;
990
- if ( ret == 0 )
1053
+ if ( ret == 0 )
991
1054
{
992
1055
_currentChunk = GetNextChunk ( ) ;
993
1056
_bytesWritten += _currentChunk . Length ;
0 commit comments