14
14
using Microsoft . Playwright ;
15
15
using Xunit ;
16
16
using Xunit . Abstractions ;
17
+ using Microsoft . Diagnostics . Tracing . Etlx ;
17
18
18
19
#nullable enable
19
20
@@ -27,121 +28,140 @@ public EventPipeDiagnosticsTests(ITestOutputHelper output, SharedBuildPerTestCla
27
28
_enablePerTestCleanup = true ;
28
29
}
29
30
30
- [ Theory ]
31
- [ InlineData ( Configuration . Debug ) ]
32
- [ InlineData ( Configuration . Release ) ]
33
- public async Task BlazorEventPipeTestWithCpuSamples ( Configuration config )
34
- { const string tracesPath = "traces" ;
31
+ [ Fact ]
32
+ public async Task BlazorEventPipeTestWithCpuSamples ( )
33
+ {
34
+ const string tracesPath = "traces" ;
35
35
Directory . CreateDirectory ( tracesPath ) ;
36
36
37
37
string extraProperties = @"
38
38
<WasmPerfInstrumentation>all,interval=0</WasmPerfInstrumentation>
39
39
<WasmPerfTracing>true</WasmPerfTracing>
40
40
" ;
41
41
42
- ProjectInfo info = CopyTestAsset ( config , aot : false , TestAsset . BlazorBasicTestApp , "blazor_eventpipe" , extraProperties : extraProperties ) ;
42
+ ProjectInfo info = CopyTestAsset ( Configuration . Release , aot : false , TestAsset . BlazorBasicTestApp , "blazor_eventpipe" , extraProperties : extraProperties ) ;
43
+
44
+ UpdateFile ( Path . Combine ( "Pages" , "Counter.razor" ) , new Dictionary < string , string > {
45
+ {
46
+ @"currentCount++;" ,
47
+ """
48
+ for(int i = 0; i < 1000; i++)
49
+ {
50
+ Console.WriteLine($"Incrementing count: {i}");
51
+ }
52
+ currentCount++;
53
+ """
54
+ }
55
+ } ) ;
56
+
43
57
44
58
// Build the project
45
- BuildProject ( info , config , new BuildOptions ( AssertAppBundle : false ) ) ;
59
+ BuildProject ( info , Configuration . Release , new BuildOptions ( AssertAppBundle : false ) ) ;
46
60
47
61
// Setup the environment for file uploads
48
- string traceFilePath = Path . Combine ( tracesPath , "cpuprofile.nettrace" ) ;
49
62
var serverEnv = new Dictionary < string , string >
50
63
{
51
- [ "FILE_UPLOAD_PATH" ] = tracesPath ,
52
- [ "TRACE_FILE_PATH" ] = traceFilePath
64
+ [ "DEVSERVER_UPLOAD_PATH" ] = tracesPath
53
65
} ;
54
66
55
67
// Create a custom test handler that will navigate to Counter page, collect CPU samples,
56
68
// click the button, and upload the trace
57
69
async Task CpuProfileTest ( IPage page )
58
70
{
71
+ await Task . Delay ( 1000 ) ;
72
+ _testOutput . WriteLine ( "XXXXXXX 1" ) ;
59
73
// Navigate to the Counter page
60
74
await page . Locator ( "text=Counter" ) . ClickAsync ( ) ;
61
75
62
76
// Verify we're on the Counter page
63
77
var txt = await page . Locator ( "p[role='status']" ) . InnerHTMLAsync ( ) ;
64
78
Assert . Equal ( "Current count: 0" , txt ) ;
79
+ _testOutput . WriteLine ( "XXXXXXX 2" ) ;
65
80
66
81
// Collect CPU samples for 5 seconds
67
82
await page . EvaluateAsync ( @"
68
- window.cpuSamplesPromise = globalThis.getDotnetRuntime(0).collectCpuSamples({durationSeconds: 5});
83
+ console.log(`AAAAAAAAAAAAAAAAAAAAAA`);
84
+ globalThis.getDotnetRuntime(0)
85
+ .collectCpuSamples({durationSeconds: 2, skipDownload:true}).then(traces => {
86
+ console.log(`DDDDDDDDDDDDDDDDDDDDDDD`);
87
+ // concatenate the buffers into a single Uint8Array
88
+ const concatenated = new Uint8Array(traces.reduce((acc, curr) => acc + curr.byteLength, 0));
89
+ let offset = 0;
90
+ for (const trace of traces) {
91
+ concatenated.set(new Uint8Array(trace), offset);
92
+ offset += trace.byteLength;
93
+ }
94
+ console.log(`EEEEEEEEEEEEEEEEEEEEEEEEE`);
95
+
96
+ return fetch('/upload/cpuprofile.nettrace', {
97
+ headers: {
98
+ 'Content-Type': 'application/octet-stream'
99
+ },
100
+ method: 'POST',
101
+ body: concatenated
102
+ });
103
+ }).then(() => {
104
+ console.log(`XXXXXXXXXXXXXXX`);
105
+ }).catch(err => {
106
+ console.log(`ERROR: ${err}`);
107
+ });
108
+ console.log(`BBBBBBBBBBBBBBBBBBBBBBBB`);
69
109
" ) ;
110
+ _testOutput . WriteLine ( "XXXXXXX 2a" ) ;
70
111
71
112
// Click the button a few times
72
113
for ( int i = 0 ; i < 5 ; i ++ )
73
114
{
115
+ _testOutput . WriteLine ( "XXXXXXX 4 " + i ) ;
74
116
await page . Locator ( "text=\" Click me\" " ) . ClickAsync ( ) ;
75
117
await Task . Delay ( 300 ) ;
76
- } // Wait for the CPU samples promise to complete
77
- await page . EvaluateAsync ( @"
78
- window.cpuSamplesPromise.then(trace => {
79
- return fetch('/upload', {
80
- method: 'POST',
81
- headers: {
82
- 'File-Name': 'cpuprofile.nettrace'
83
- },
84
- body: trace
85
- });
86
- });
87
- " ) ;
118
+ }
119
+ _testOutput . WriteLine ( "XXXXXXX 3" ) ;
88
120
121
+ var txt2 = await page . Locator ( "p[role='status']" ) . InnerHTMLAsync ( ) ;
122
+ _testOutput . WriteLine ( "XXXXXXX T " + txt2 ) ;
123
+ Assert . NotEqual ( "Current count: 0" , txt2 ) ;
124
+
125
+ _testOutput . WriteLine ( "XXXXXXX 6" ) ;
89
126
// Give time for the upload to complete
90
- await Task . Delay ( 1000 ) ;
127
+ await Task . Delay ( 5000 ) ;
91
128
}
92
129
130
+ string extraArgs = " --web-server-use-cors --web-server-use-https" ;
131
+
132
+ _testOutput . WriteLine ( "XXXXXXX 7" ) ;
93
133
// Run the test using the custom handler
94
134
await RunForBuildWithDotnetRun ( new BlazorRunOptions (
95
- Configuration : config ,
135
+ ExtraArgs : extraArgs ,
136
+ Configuration : Configuration . Release ,
96
137
Test : CpuProfileTest ,
97
138
CheckCounter : false ,
98
139
ServerEnvironment : serverEnv
99
140
) ) ;
100
141
142
+ _testOutput . WriteLine ( "XXXXXXX 8" ) ;
143
+
101
144
// Verify the trace file was created
145
+ var traceFilePath = Path . Combine ( tracesPath , "cpuprofile.nettrace" ) ;
102
146
Assert . True ( File . Exists ( traceFilePath ) , $ "Trace file { traceFilePath } was not created") ;
147
+ var converted = TraceLog . CreateFromEventTraceLogFile ( traceFilePath ) ;
148
+ Assert . True ( File . Exists ( converted ) , $ "Trace file { converted } was not created") ;
149
+ _testOutput . WriteLine ( "XXXXXXX 9" ) ;
103
150
104
- // Analyze the trace file
105
- using ( var source = new ETWTraceEventSource ( traceFilePath ) )
151
+ var methodFound = false ;
152
+ using ( var source = TraceLog . OpenOrConvert ( converted ) )
106
153
{
107
- var methodFound = false ;
108
- var sampledMethodNames = new List < string > ( ) ;
109
-
110
- // Get all sample profile events
111
- source . Clr . All += ( TraceEvent data ) =>
154
+ methodFound = source . CallStacks . Any ( stack => stack . CodeAddress . FullMethodName == "BlazorBasicTestApp.Counter.IncrementCount()" ) ;
155
+ if ( ! methodFound )
112
156
{
113
- if ( data . EventName == "Sample" || data . EventName == "GC/SampledObjectAllocation" )
157
+ foreach ( var stack in source . CallStacks )
114
158
{
115
- var stackEvent = data as ClrStackTraceTraceData ;
116
- if ( stackEvent != null && stackEvent . CallStack != null )
117
- {
118
- var stack = stackEvent . CallStack ;
119
- while ( stack != null )
120
- {
121
- var methodName = stack . CodeAddress . FullMethodName ;
122
- sampledMethodNames . Add ( methodName ) ;
123
-
124
- // Check if IncrementCount method is in the stack
125
- if ( methodName . Contains ( "IncrementCount" ) )
126
- {
127
- methodFound = true ;
128
- }
129
- stack = stack . Caller ;
130
- }
131
- }
159
+ _testOutput . WriteLine ( $ "Stack: { stack . CodeAddress . FullMethodName } ") ;
132
160
}
133
- } ;
134
-
135
- source . Process ( ) ;
136
-
137
- // Output all sampled method names for diagnostic purposes
138
- foreach ( var methodName in sampledMethodNames . Distinct ( ) . Take ( 20 ) )
139
- {
140
- _testOutput . WriteLine ( $ "Sampled method: { methodName } ") ;
141
161
}
142
-
143
- Assert . True ( methodFound , "The trace should contain stack frames for the 'IncrementCount' method" ) ;
144
162
}
163
+
164
+ Assert . True ( methodFound , "The trace should contain stack frames for the 'IncrementCount' method" ) ;
145
165
}
146
166
}
147
167
}
0 commit comments