@@ -12,31 +12,32 @@ namespace Sentry.Profiling;
1212internal class SampleProfilerSession : IDisposable
1313{
1414 private readonly EventPipeSession _session ;
15- private readonly TraceLogEventSource _eventSource ;
1615 private readonly SampleProfilerTraceEventParser _sampleEventParser ;
1716 private readonly IDiagnosticLogger ? _logger ;
1817 private readonly SentryStopwatch _stopwatch ;
1918 private bool _stopped = false ;
19+ private Task _processing ;
2020
21- private SampleProfilerSession ( SentryStopwatch stopwatch , EventPipeSession session , TraceLogEventSource eventSource , IDiagnosticLogger ? logger )
21+ private SampleProfilerSession ( SentryStopwatch stopwatch , EventPipeSession session , TraceLogEventSource eventSource , Task processing , IDiagnosticLogger ? logger )
2222 {
2323 _session = session ;
2424 _logger = logger ;
25- _eventSource = eventSource ;
26- _sampleEventParser = new SampleProfilerTraceEventParser ( _eventSource ) ;
25+ EventSource = eventSource ;
26+ _sampleEventParser = new SampleProfilerTraceEventParser ( EventSource ) ;
2727 _stopwatch = stopwatch ;
28+ _processing = processing ;
2829 }
2930
3031 // Exposed only for benchmarks.
3132 internal static EventPipeProvider [ ] Providers = new [ ]
3233 {
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+ ) ) ,
4041 new EventPipeProvider ( SampleProfilerTraceEventParser . ProviderName , EventLevel . Informational ) ,
4142 // new EventPipeProvider(TplEtwProviderTraceEventParser.ProviderName, EventLevel.Informational, (long) TplEtwProviderTraceEventParser.Keywords.Default)
4243 } ;
@@ -46,11 +47,14 @@ private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession sessio
4647 // need a large buffer if we're connecting righ away. Leaving it too large increases app memory usage.
4748 internal static int CircularBufferMB = 16 ;
4849
50+ // Exposed for tests
51+ internal TraceLogEventSource EventSource { get ; }
52+
4953 public SampleProfilerTraceEventParser SampleEventParser => _sampleEventParser ;
5054
5155 public TimeSpan Elapsed => _stopwatch . Elapsed ;
5256
53- public TraceLog TraceLog => _eventSource . TraceLog ;
57+ public TraceLog TraceLog => EventSource . TraceLog ;
5458
5559 // default is false, set 1 for true.
5660 private static int _throwOnNextStartupForTests = 0 ;
@@ -86,7 +90,7 @@ public static SampleProfilerSession StartNew(IDiagnosticLogger? logger = null)
8690 var eventSource = TraceLog . CreateFromEventPipeSession ( session , TraceLog . EventPipeRundownConfiguration . Enable ( client ) ) ;
8791
8892 // 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 )
9094 . ContinueWith ( _ =>
9195 {
9296 if ( _ . Exception ? . InnerException is { } e )
@@ -95,7 +99,7 @@ public static SampleProfilerSession StartNew(IDiagnosticLogger? logger = null)
9599 }
96100 } , TaskContinuationOptions . OnlyOnFaulted ) ;
97101
98- return new SampleProfilerSession ( stopWatch , session , eventSource , logger ) ;
102+ return new SampleProfilerSession ( stopWatch , session , eventSource , processing , logger ) ;
99103 }
100104 catch ( Exception ex )
101105 {
@@ -108,15 +112,15 @@ public async Task WaitForFirstEventAsync(CancellationToken cancellationToken = d
108112 {
109113 var tcs = new TaskCompletionSource ( ) ;
110114 var cb = ( TraceEvent _ ) => { tcs . TrySetResult ( ) ; } ;
111- _eventSource . AllEvents += cb ;
115+ EventSource . AllEvents += cb ;
112116 try
113117 {
114118 // Wait for the first event to be processed.
115119 await tcs . Task . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
116120 }
117121 finally
118122 {
119- _eventSource . AllEvents -= cb ;
123+ EventSource . AllEvents -= cb ;
120124 }
121125 }
122126
@@ -128,8 +132,9 @@ public void Stop()
128132 {
129133 _stopped = true ;
130134 _session . Stop ( ) ;
135+ _processing . Wait ( ) ;
131136 _session . Dispose ( ) ;
132- _eventSource . Dispose ( ) ;
137+ EventSource . Dispose ( ) ;
133138 }
134139 catch ( Exception ex )
135140 {
0 commit comments