[Profiler] Add support for ARM64#8338
Conversation
BenchmarksBenchmark execution time: 2026-05-11 09:05:33 Comparing candidate commit 3d2d5b9 in PR branch Some scenarios are present only in baseline or only in candidate runs. If you didn't create or remove some scenarios in your branch, this maybe a sign of crashed benchmarks 💥💥💥 Scenarios present only in baseline:
Found 4 performance improvements and 2 performance regressions! Performance is the same for 48 metrics, 18 unstable metrics, 88 known flaky benchmarks, 38 flaky benchmarks without significant changes.
|
Execution-Time Benchmarks Report ⏱️Execution-time results for samples comparing This PR (8338) and master. ✅ No regressions detected - check the details below Full Metrics ComparisonFakeDbCommand
HttpMessageHandler
Comparison explanationExecution-time benchmarks measure the whole time it takes to execute a program, and are intended to measure the one-off costs. Cases where the execution time results for the PR are worse than latest master results are highlighted in **red**. The following thresholds were used for comparing the execution times:
Note that these results are based on a single point-in-time result for each branch. For full results, see the dashboard. Graphs show the p99 interval based on the mean and StdDev of the test run, as well as the mean value of the run (shown as a diamond below the graph). Duration chartsFakeDbCommand (.NET Framework 4.8)gantt
title Execution time (ms) FakeDbCommand (.NET Framework 4.8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8338) - mean (75ms) : 69, 80
master - mean (75ms) : 71, 80
section Bailout
This PR (8338) - mean (79ms) : 76, 82
master - mean (78ms) : 75, 82
section CallTarget+Inlining+NGEN
This PR (8338) - mean (1,133ms) : 1069, 1198
master - mean (1,125ms) : 1071, 1179
FakeDbCommand (.NET Core 3.1)gantt
title Execution time (ms) FakeDbCommand (.NET Core 3.1)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8338) - mean (115ms) : 111, 120
master - mean (115ms) : 108, 123
section Bailout
This PR (8338) - mean (115ms) : 112, 118
master - mean (115ms) : 112, 118
section CallTarget+Inlining+NGEN
This PR (8338) - mean (811ms) : 782, 839
master - mean (808ms) : 783, 834
FakeDbCommand (.NET 6)gantt
title Execution time (ms) FakeDbCommand (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8338) - mean (105ms) : 99, 111
master - mean (103ms) : 98, 109
section Bailout
This PR (8338) - mean (103ms) : 99, 108
master - mean (104ms) : 99, 109
section CallTarget+Inlining+NGEN
This PR (8338) - mean (947ms) : 915, 979
master - mean (946ms) : 910, 981
FakeDbCommand (.NET 8)gantt
title Execution time (ms) FakeDbCommand (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8338) - mean (100ms) : 96, 104
master - mean (100ms) : 96, 104
section Bailout
This PR (8338) - mean (104ms) : 99, 108
master - mean (104ms) : 99, 110
section CallTarget+Inlining+NGEN
This PR (8338) - mean (828ms) : 785, 871
master - mean (835ms) : 778, 892
HttpMessageHandler (.NET Framework 4.8)gantt
title Execution time (ms) HttpMessageHandler (.NET Framework 4.8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8338) - mean (195ms) : 190, 200
master - mean (195ms) : 191, 198
section Bailout
This PR (8338) - mean (198ms) : 195, 200
master - mean (199ms) : 196, 201
section CallTarget+Inlining+NGEN
This PR (8338) - mean (1,210ms) : 1154, 1267
master - mean (1,212ms) : 1173, 1251
HttpMessageHandler (.NET Core 3.1)gantt
title Execution time (ms) HttpMessageHandler (.NET Core 3.1)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8338) - mean (280ms) : 275, 285
master - mean (277ms) : 269, 284
section Bailout
This PR (8338) - mean (281ms) : 277, 285
master - mean (278ms) : 274, 282
section CallTarget+Inlining+NGEN
This PR (8338) - mean (957ms) : 937, 977
master - mean (964ms) : 938, 990
HttpMessageHandler (.NET 6)gantt
title Execution time (ms) HttpMessageHandler (.NET 6)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8338) - mean (274ms) : 261, 288
master - mean (276ms) : 263, 290
section Bailout
This PR (8338) - mean (273ms) : 265, 282
master - mean (273ms) : 268, 278
section CallTarget+Inlining+NGEN
This PR (8338) - mean (1,147ms) : 1108, 1185
master - mean (1,144ms) : 1097, 1191
HttpMessageHandler (.NET 8)gantt
title Execution time (ms) HttpMessageHandler (.NET 8)
dateFormat x
axisFormat %Q
todayMarker off
section Baseline
This PR (8338) - mean (279ms) : 269, 289
master - mean (277ms) : 266, 287
section Bailout
This PR (8338) - mean (272ms) : 268, 277
master - mean (275ms) : 264, 287
section CallTarget+Inlining+NGEN
This PR (8338) - mean (1,028ms) : 985, 1072
master - mean (1,023ms) : 980, 1067
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ba7a85e to
c4d42ed
Compare
876012b to
774f6b9
Compare
f3d5056 to
4350315
Compare
4350315 to
3e6ce93
Compare
…m64) UnwindTracersProvider::ScopedTracer has a user-declared destructor that pushes its TracerNode back onto the free-list. It had no explicit move ops, which meant the compiler kept the implicit (deprecated) copy-assign and omitted the implicit move. TimerCreateCpuProfiler::Collect() uses assignment on every arm64 CPU sample when debug logging is on, so the temporary and the local both ended up releasing the same node, corrupting the free-list. - Delete the copy ctor/assignment and define explicit move ctor/assignment that null out the source's _provider/_node. - Default-initialize _provider/_node in the class body so the ScopedTracer(nullptr) overload no longer leaves _node indeterminate. Made-with: Cursor
…ines)
- [F3] Rewrite the .NET<5 arm64 Log::Warn in CorProfilerCallback::Initialize
to use "Continuous Profiler" and "arm64 Linux" per AGENTS.md guidelines.
- [B4] Expand the no_sanitize("vptr") rationale above the first call-site
to explicitly cover the three clang+arm64 suppression points below.
- [F4] Document that TimerCreateCpuProfiler::_useUnwinderTracer is a
one-shot snapshot of Log::IsDebugEnabled() captured at construction.
- [F5] Collapse the duplicated AddModuleRangesToCache declaration in
ManagedCodeCache.h by toggling visibility with a single #ifdef DD_TEST
public: / #else private: / #endif block.
- [F6] Fix the "GetfunctionId" typo and clarify the comment around the
defensive !isR2r.has_value() guard.
- [F7] Drop the stray blank line introduced in LinuxStackFramesCollector
after RegisterHandler.
- [F8] Default-initialize StackFramesCollectorBase::_tracer{nullptr} to
remove any future indeterminate-value hazard.
- [C4] Append a trailing newline to Backtrace2Unwinder.h, HybridUnwinder.h,
IUnwinder.h, LinuxStackFramesCollector.h, TimerCreateCpuProfiler.h.
Made-with: Cursor
- [C7] Explain in profiler/src/Demos/Directory.Build.props why netcoreapp3.1 is excluded on arm64 Linux (the Continuous Profiler refuses to initialize on .NET Core 3.1 / arm64). - [E1] Clarify in PublishProfilerLinux that the linux-arm64 and linux-musl-arm64 RID folders are both populated, via the glibc and Alpine/musl CI legs respectively. - [E2] Add a TODO + tracking-issue placeholder next to the TSAN-disabled-on-arm64 conditions (48-bit VMA requirement on CI hosts). - [E3] Document why we preload libasan.so.5 on arm64 (older gcc 9 image) vs libasan.so.6 on x64. - [E4] Describe in build/cmake/FindLibunwind.cmake the patches carried by DataDog/libunwind@gleocadie/v1.8.1-custom-3 and why we can't move to upstream v1.8.3 yet. Made-with: Cursor
…opt cases [D1]
Regression guards for two behaviors that previously caused the arm64
exception profiler to surface a long tail of NotResolvedFrame entries:
- Fake IPs (FakeUnknownIP / FakeLockContentionIP / FakeAllocationIP)
short-circuit to {isResolved=true, <corresponding placeholder>} without
calling ICorProfilerInfo::GetFunctionFromIP.
- On Windows, if the cached GetFunctionFromIP path throws an SEH
exception (simulated), FrameStore::GetFrame translates the resulting
std::nullopt from ManagedCodeCache into {isResolved=true, NotResolvedFrame}
so the legacy placeholder behavior is preserved.
Made-with: Cursor
Co-authored-by: Andrew Lock <[email protected]>
Co-authored-by: Andrew Lock <[email protected]>
16e8dde to
3d2d5b9
Compare
Summary of changes
Add support for .NET profiler on aarch64.
Reason for change
Customers ask :)
Implementation details
The implementation relies on the
ManagedCodeCache. We added a new unwinderHybridUnwinderwhich first uses libunwind to get passed the native frame. Once we hit a managed frame (thanks toManagedCodeCache::IsManaged), we walk the callstack using the FP-chain. .NET Jit is known to push the frame pointer on the stack.Additional:
ManagedCodeCache::IsManagedreturns anstd::optional<bool>because in some contention case, we may not know for sure if an instruction pointer is native or managed. In case we do not know, we implemented a retry mechanism. In the if we still do not know, we stop the unwinding to avoid crashing the app.UnwindingRecorderclass is enabled and used only if the Debug log is enabled. For now, we do not write the recording into a file. This will be done in a later PR.Test coverage
Using the same tests we have + minor adjustments:
Due to contention between the
ManagedCodeCache::Workerand the unwinder, at startup we may have truncated callstack. This is fine for. A rework of theManagedCodeCachewill be done to remove that limitation.This fact is taken into account in the assertions.
Other details
To enable profiler on ARM64, we have a this env var
DD_INTERNAL_PROFILING_ENABLED_ARM64which must set to1.