@@ -12,31 +12,32 @@ namespace Sentry.Profiling;
12
12
internal class SampleProfilerSession : IDisposable
13
13
{
14
14
private readonly EventPipeSession _session ;
15
- private readonly TraceLogEventSource _eventSource ;
16
15
private readonly SampleProfilerTraceEventParser _sampleEventParser ;
17
16
private readonly IDiagnosticLogger ? _logger ;
18
17
private readonly SentryStopwatch _stopwatch ;
19
18
private bool _stopped = false ;
19
+ private Task _processing ;
20
20
21
- private SampleProfilerSession ( SentryStopwatch stopwatch , EventPipeSession session , TraceLogEventSource eventSource , IDiagnosticLogger ? logger )
21
+ private SampleProfilerSession ( SentryStopwatch stopwatch , EventPipeSession session , TraceLogEventSource eventSource , Task processing , IDiagnosticLogger ? logger )
22
22
{
23
23
_session = session ;
24
24
_logger = logger ;
25
- _eventSource = eventSource ;
26
- _sampleEventParser = new SampleProfilerTraceEventParser ( _eventSource ) ;
25
+ EventSource = eventSource ;
26
+ _sampleEventParser = new SampleProfilerTraceEventParser ( EventSource ) ;
27
27
_stopwatch = stopwatch ;
28
+ _processing = processing ;
28
29
}
29
30
30
31
// Exposed only for benchmarks.
31
32
internal static EventPipeProvider [ ] Providers = new [ ]
32
33
{
33
- // Note: all events we need issued by "DotNETRuntime" provider are at " EventLevel.Informational"
34
- // see https://learn.microsoft.com/en-us/dotnet/fundamentals/diagnostics/runtime-events
35
- // TODO replace Keywords.Default with a subset. Currently it is:
36
- // Default = GC | Type | GCHeapSurvivalAndMovement | Binder | Loader | Jit | NGen | SupressNGen
37
- // | StopEnumeration | Security | AppDomainResourceManagement | Exception | Threading | Contention | Stack | JittedMethodILToNativeMap
38
- // | ThreadTransfer | GCHeapAndTypeNames | Codesymbols | Compilation,
39
- new EventPipeProvider ( ClrTraceEventParser . ProviderName , EventLevel . Informational , ( long ) ClrTraceEventParser . Keywords . Default ) ,
34
+ new EventPipeProvider ( ClrTraceEventParser . ProviderName , EventLevel . Verbose , ( long ) (
35
+ ClrTraceEventParser . Keywords . Jit
36
+ | ClrTraceEventParser . Keywords . NGen
37
+ | ClrTraceEventParser . Keywords . Loader
38
+ | ClrTraceEventParser . Keywords . Binder
39
+ | ClrTraceEventParser . Keywords . JittedMethodILToNativeMap
40
+ ) ) ,
40
41
new EventPipeProvider ( SampleProfilerTraceEventParser . ProviderName , EventLevel . Informational ) ,
41
42
// new EventPipeProvider(TplEtwProviderTraceEventParser.ProviderName, EventLevel.Informational, (long) TplEtwProviderTraceEventParser.Keywords.Default)
42
43
} ;
@@ -46,11 +47,14 @@ private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession sessio
46
47
// need a large buffer if we're connecting righ away. Leaving it too large increases app memory usage.
47
48
internal static int CircularBufferMB = 16 ;
48
49
50
+ // Exposed for tests
51
+ internal TraceLogEventSource EventSource { get ; }
52
+
49
53
public SampleProfilerTraceEventParser SampleEventParser => _sampleEventParser ;
50
54
51
55
public TimeSpan Elapsed => _stopwatch . Elapsed ;
52
56
53
- public TraceLog TraceLog => _eventSource . TraceLog ;
57
+ public TraceLog TraceLog => EventSource . TraceLog ;
54
58
55
59
// default is false, set 1 for true.
56
60
private static int _throwOnNextStartupForTests = 0 ;
@@ -86,7 +90,7 @@ public static SampleProfilerSession StartNew(IDiagnosticLogger? logger = null)
86
90
var eventSource = TraceLog . CreateFromEventPipeSession ( session , TraceLog . EventPipeRundownConfiguration . Enable ( client ) ) ;
87
91
88
92
// Process() blocks until the session is stopped so we need to run it on a separate thread.
89
- Task . Factory . StartNew ( eventSource . Process , TaskCreationOptions . LongRunning )
93
+ var processing = Task . Factory . StartNew ( eventSource . Process , TaskCreationOptions . LongRunning )
90
94
. ContinueWith ( _ =>
91
95
{
92
96
if ( _ . Exception ? . InnerException is { } e )
@@ -95,7 +99,7 @@ public static SampleProfilerSession StartNew(IDiagnosticLogger? logger = null)
95
99
}
96
100
} , TaskContinuationOptions . OnlyOnFaulted ) ;
97
101
98
- return new SampleProfilerSession ( stopWatch , session , eventSource , logger ) ;
102
+ return new SampleProfilerSession ( stopWatch , session , eventSource , processing , logger ) ;
99
103
}
100
104
catch ( Exception ex )
101
105
{
@@ -108,15 +112,15 @@ public async Task WaitForFirstEventAsync(CancellationToken cancellationToken = d
108
112
{
109
113
var tcs = new TaskCompletionSource ( ) ;
110
114
var cb = ( TraceEvent _ ) => { tcs . TrySetResult ( ) ; } ;
111
- _eventSource . AllEvents += cb ;
115
+ EventSource . AllEvents += cb ;
112
116
try
113
117
{
114
118
// Wait for the first event to be processed.
115
119
await tcs . Task . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
116
120
}
117
121
finally
118
122
{
119
- _eventSource . AllEvents -= cb ;
123
+ EventSource . AllEvents -= cb ;
120
124
}
121
125
}
122
126
@@ -128,8 +132,9 @@ public void Stop()
128
132
{
129
133
_stopped = true ;
130
134
_session . Stop ( ) ;
135
+ _processing . Wait ( ) ;
131
136
_session . Dispose ( ) ;
132
- _eventSource . Dispose ( ) ;
137
+ EventSource . Dispose ( ) ;
133
138
}
134
139
catch ( Exception ex )
135
140
{
0 commit comments