Skip to content

Commit a2b3664

Browse files
committed
fix: net8 unknown stack trace methods for JIT methods
1 parent f89815a commit a2b3664

File tree

2 files changed

+53
-15
lines changed

2 files changed

+53
-15
lines changed

src/Sentry.Profiling/SampleProfilerSession.cs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ namespace Sentry.Profiling;
1212
internal 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;
@@ -22,21 +21,21 @@ private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession sessio
2221
{
2322
_session = session;
2423
_logger = logger;
25-
_eventSource = eventSource;
26-
_sampleEventParser = new SampleProfilerTraceEventParser(_eventSource);
24+
EventSource = eventSource;
25+
_sampleEventParser = new SampleProfilerTraceEventParser(EventSource);
2726
_stopwatch = stopwatch;
2827
}
2928

3029
// Exposed only for benchmarks.
3130
internal static EventPipeProvider[] Providers = new[]
3231
{
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),
32+
new EventPipeProvider(ClrTraceEventParser.ProviderName, EventLevel.Verbose, (long) (
33+
ClrTraceEventParser.Keywords.Jit
34+
| ClrTraceEventParser.Keywords.NGen
35+
| ClrTraceEventParser.Keywords.Loader
36+
| ClrTraceEventParser.Keywords.Binder
37+
| ClrTraceEventParser.Keywords.JittedMethodILToNativeMap
38+
)),
4039
new EventPipeProvider(SampleProfilerTraceEventParser.ProviderName, EventLevel.Informational),
4140
// new EventPipeProvider(TplEtwProviderTraceEventParser.ProviderName, EventLevel.Informational, (long) TplEtwProviderTraceEventParser.Keywords.Default)
4241
};
@@ -46,11 +45,14 @@ private SampleProfilerSession(SentryStopwatch stopwatch, EventPipeSession sessio
4645
// need a large buffer if we're connecting righ away. Leaving it too large increases app memory usage.
4746
internal static int CircularBufferMB = 16;
4847

48+
// Exposed for tests
49+
internal TraceLogEventSource EventSource { get; }
50+
4951
public SampleProfilerTraceEventParser SampleEventParser => _sampleEventParser;
5052

5153
public TimeSpan Elapsed => _stopwatch.Elapsed;
5254

53-
public TraceLog TraceLog => _eventSource.TraceLog;
55+
public TraceLog TraceLog => EventSource.TraceLog;
5456

5557
// default is false, set 1 for true.
5658
private static int _throwOnNextStartupForTests = 0;
@@ -108,15 +110,15 @@ public async Task WaitForFirstEventAsync(CancellationToken cancellationToken = d
108110
{
109111
var tcs = new TaskCompletionSource();
110112
var cb = (TraceEvent _) => { tcs.TrySetResult(); };
111-
_eventSource.AllEvents += cb;
113+
EventSource.AllEvents += cb;
112114
try
113115
{
114116
// Wait for the first event to be processed.
115117
await tcs.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
116118
}
117119
finally
118120
{
119-
_eventSource.AllEvents -= cb;
121+
EventSource.AllEvents -= cb;
120122
}
121123
}
122124

@@ -128,8 +130,8 @@ public void Stop()
128130
{
129131
_stopped = true;
130132
_session.Stop();
133+
EventSource.Dispose();
131134
_session.Dispose();
132-
_eventSource.Dispose();
133135
}
134136
catch (Exception ex)
135137
{

test/Sentry.Profiling.Tests/SamplingTransactionProfilerTests.cs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.IO.Abstractions.TestingHelpers;
1+
using Microsoft.Diagnostics.Tracing;
2+
using Microsoft.Diagnostics.Tracing.Parsers.Clr;
23
using Sentry.Internal.Http;
34

45
namespace Sentry.Profiling.Tests;
@@ -180,6 +181,41 @@ public async Task Profiler_AfterTimeout_Stops()
180181
}
181182
}
182183

184+
[SkippableFact]
185+
public async Task EventPipeSession_ReceivesExpectedCLREvents()
186+
{
187+
SampleProfilerSession? session = null;
188+
SkipIfFailsInCI(() => session = SampleProfilerSession.StartNew(_testOutputLogger));
189+
using (session)
190+
{
191+
var eventsReceived = new HashSet<string>();
192+
session!.EventSource.Clr.All += (TraceEvent ev) => eventsReceived.Add(ev.EventName);
193+
194+
var loadedMethods = new HashSet<string>();
195+
session!.EventSource.Clr.MethodLoadVerbose += (MethodLoadUnloadVerboseTraceData ev) => loadedMethods.Add(ev.MethodName);
196+
197+
198+
await session.WaitForFirstEventAsync(CancellationToken.None);
199+
var limitMs = 50;
200+
var sut = new SamplingTransactionProfiler(_testSentryOptions, session, limitMs, CancellationToken.None);
201+
RunForMs(limitMs * 5);
202+
MethodToBeLoaded(100);
203+
RunForMs(limitMs * 5);
204+
sut.Finish();
205+
206+
Assert.Contains("Method/LoadVerbose", eventsReceived);
207+
Assert.Contains("Method/ILToNativeMap", eventsReceived);
208+
Assert.Contains("Method/UnloadVerbose", eventsReceived);
209+
210+
Assert.Contains("MethodToBeLoaded", loadedMethods);
211+
}
212+
}
213+
214+
private static long MethodToBeLoaded(int n)
215+
{
216+
return -n;
217+
}
218+
183219
[SkippableTheory]
184220
[InlineData(true)]
185221
[InlineData(false)]

0 commit comments

Comments
 (0)