5
5
using System . Diagnostics ;
6
6
using System . Diagnostics . CodeAnalysis ;
7
7
using System . Diagnostics . Tracing ;
8
- using System . Net . Sockets ;
9
8
using Microsoft . Diagnostics . NETCore . Client ;
10
9
using Microsoft . Diagnostics . Tracing . Parsers ;
11
10
using Ultra . Sampler ;
@@ -37,8 +36,8 @@ public DiagnosticPortSession(int pid, bool sampler, string baseName, Cancellatio
37
36
_sampler = sampler ;
38
37
_baseName = baseName ;
39
38
_cancelConnectSource = new CancellationTokenSource ( ) ;
40
- _semaphoreSlim = new SemaphoreSlim ( 0 ) ;
41
- _connectTask = ConnectAndStartProfilingImpl ( pid , sampler , baseName , token ) ;
39
+ _semaphoreSlim = new SemaphoreSlim ( 1 ) ;
40
+ _connectTask = ConnectAndStartProfilingImpl ( token ) ;
42
41
}
43
42
44
43
public bool TryGetNettraceFilePathIfExists ( [ NotNullWhen ( true ) ] out string ? nettraceFilePath )
@@ -53,20 +52,20 @@ public bool TryGetNettraceFilePathIfExists([NotNullWhen(true)] out string? nettr
53
52
return nettraceFilePath is not null ;
54
53
}
55
54
56
- private async Task ConnectAndStartProfilingImpl ( int pid , bool sampler , string baseName , CancellationToken token )
55
+ private async Task ConnectAndStartProfilingImpl ( CancellationToken token )
57
56
{
58
57
CancellationTokenSource linkedCancellationTokenSource = CancellationTokenSource . CreateLinkedTokenSource ( token , _cancelConnectSource . Token ) ;
59
58
try
60
59
{
61
-
60
+
62
61
var connectCancellationToken = linkedCancellationTokenSource . Token ;
63
62
64
- if ( sampler )
63
+ if ( _sampler )
65
64
{
66
65
_cancelConnectSource . CancelAfter ( 500 ) ;
67
66
}
68
67
69
- var connectionAddress = await TryFindConnectionAddress ( pid , sampler , connectCancellationToken ) . ConfigureAwait ( false ) ;
68
+ var connectionAddress = await TryFindConnectionAddress ( _pid , _sampler , connectCancellationToken ) . ConfigureAwait ( false ) ;
70
69
if ( connectionAddress is null ) return ;
71
70
72
71
_diagnosticsClient = ( await DiagnosticsClientConnector . FromDiagnosticPort ( connectionAddress , connectCancellationToken ) . ConfigureAwait ( false ) ) ? . Instance ;
@@ -76,9 +75,9 @@ private async Task ConnectAndStartProfilingImpl(int pid, bool sampler, string ba
76
75
}
77
76
catch ( OperationCanceledException ex )
78
77
{
79
- if ( sampler && _cancelConnectSource is not null && _cancelConnectSource . IsCancellationRequested )
78
+ if ( _sampler && _cancelConnectSource is not null && _cancelConnectSource . IsCancellationRequested )
80
79
{
81
- throw new InvalidOperationException ( $ "Cannot connect to the diagnostic port socket for pid { pid } ", ex ) ;
80
+ throw new InvalidOperationException ( $ "Cannot connect to the diagnostic port socket for pid { _pid } ", ex ) ;
82
81
}
83
82
return ;
84
83
}
@@ -91,7 +90,9 @@ private async Task ConnectAndStartProfilingImpl(int pid, bool sampler, string ba
91
90
public async Task StartProfiling ( CancellationToken token )
92
91
{
93
92
// We want to make sure that we are not disposing while we are starting a session
93
+ Console . WriteLine ( $ "Before entering Start/Lock for { ( _sampler ? "sampler" : "clr" ) } ") ;
94
94
await _semaphoreSlim . WaitAsync ( token ) ;
95
+ Console . WriteLine ( $ "After entering Start/Lock for { ( _sampler ? "sampler" : "clr" ) } ") ;
95
96
96
97
try
97
98
{
@@ -102,9 +103,12 @@ public async Task StartProfiling(CancellationToken token)
102
103
103
104
_profilingTask = _connectTask . ContinueWith ( async task =>
104
105
{
106
+ if ( task . IsFaulted )
107
+ {
108
+ return ;
109
+ }
105
110
106
-
107
- _nettraceFilePath = Path . Combine ( Environment . CurrentDirectory , $ "{ _baseName } _{ ( _sampler ? "sampler" : "clr" ) } _{ _pid } .nettrace") ;
111
+ _nettraceFilePath = Path . Combine ( Environment . CurrentDirectory , $ "{ _baseName } _{ _pid } _{ ( _sampler ? "sampler" : "clr" ) } .nettrace") ;
108
112
_nettraceFileStream = new FileStream ( _nettraceFilePath , FileMode . Create , FileAccess . Write , FileShare . Read , 65536 , FileOptions . Asynchronous ) ;
109
113
110
114
long keywords = - 1 ;
@@ -143,11 +147,11 @@ public async Task StartProfiling(CancellationToken token)
143
147
144
148
public long GetNettraceFileLength ( ) => _nettraceFileStream ? . Length ?? 0 ;
145
149
146
- public async Task WaitForConnectAndStartSession ( )
150
+ public async Task WaitForConnect ( )
147
151
{
148
152
await _connectTask . ConfigureAwait ( false ) ;
149
153
}
150
-
154
+
151
155
private static async Task < string ? > TryFindConnectionAddress ( int pid , bool sampler , CancellationToken token )
152
156
{
153
157
var tempFolder = Path . GetTempPath ( ) ;
@@ -186,15 +190,16 @@ public async Task WaitForConnectAndStartSession()
186
190
187
191
// Let's increase the delay after each check to lower the overhead
188
192
waitForNextCheckDelayInMs += 10 ;
189
- waitForNextCheckDelayInMs = Math . Max ( waitForNextCheckDelayInMs , 100 ) ;
193
+ waitForNextCheckDelayInMs = Math . Min ( waitForNextCheckDelayInMs , 100 ) ;
190
194
}
191
195
192
196
return diagnosticPortSocket ;
193
197
}
194
198
195
199
public async ValueTask StopAndDisposeAsync ( )
196
200
{
197
- // We want to make sure that we are not disposing while we are connecting
201
+ // We want to make sure that we are not disposing while we are connecting/trying to start a session
202
+ // (This could happen if we are trying to profile a non .NET process with the CLR provider)
198
203
await _semaphoreSlim . WaitAsync ( CancellationToken . None ) ;
199
204
200
205
try
@@ -208,54 +213,57 @@ public async ValueTask StopAndDisposeAsync()
208
213
{
209
214
try
210
215
{
216
+ await _connectTask . ConfigureAwait ( false ) ;
217
+
211
218
// We wait for the session to start (we will close it right after below
212
219
await _profilingTask . ConfigureAwait ( false ) ;
213
220
}
214
- catch
221
+ catch ( Exception ex )
215
222
{
223
+ Console . WriteLine ( $ "Error while waiting for profiling { _baseName } { ( _sampler ? "sampler" : "clr" ) } to finish: { ex } ") ;
216
224
// Ignore
217
225
}
218
- }
219
226
220
- Debug . Assert ( _eventStreamCopyTask is not null ) ;
221
- try
222
- {
223
- await _eventStreamCopyTask . ConfigureAwait ( false ) ;
224
- }
225
- catch
226
- {
227
- // Ignore
228
- }
227
+ Debug . Assert ( _eventStreamCopyTask is not null ) ;
228
+ try
229
+ {
230
+ await _eventStreamCopyTask . ConfigureAwait ( false ) ;
231
+ }
232
+ catch
233
+ {
234
+ // Ignore
235
+ }
229
236
230
- Debug . Assert ( _nettraceFileStream is not null ) ;
231
- try
232
- {
233
- await _nettraceFileStream . DisposeAsync ( ) . ConfigureAwait ( false ) ;
234
- }
235
- catch
236
- {
237
- // Ignore
238
- }
237
+ Debug . Assert ( _nettraceFileStream is not null ) ;
238
+ try
239
+ {
240
+ await _nettraceFileStream . DisposeAsync ( ) . ConfigureAwait ( false ) ;
241
+ }
242
+ catch
243
+ {
244
+ // Ignore
245
+ }
239
246
240
- Debug . Assert ( _eventPipeSession is not null ) ;
241
- try
242
- {
243
- await _eventPipeSession . StopAsync ( CancellationToken . None ) . ConfigureAwait ( false ) ;
244
- }
245
- catch
246
- {
247
- // Ignore
248
- }
249
- finally
250
- {
247
+ Debug . Assert ( _eventPipeSession is not null ) ;
251
248
try
252
249
{
253
- _eventPipeSession . Dispose ( ) ;
250
+ await _eventPipeSession . StopAsync ( CancellationToken . None ) . ConfigureAwait ( false ) ;
254
251
}
255
252
catch
256
253
{
257
254
// Ignore
258
255
}
256
+ finally
257
+ {
258
+ try
259
+ {
260
+ _eventPipeSession . Dispose ( ) ;
261
+ }
262
+ catch
263
+ {
264
+ // Ignore
265
+ }
266
+ }
259
267
}
260
268
}
261
269
finally
0 commit comments