Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
f8f5aff
Add HybridUnwinder for ARM64 only
gleocadie Mar 19, 2026
a1210af
Add CI job
gleocadie Mar 19, 2026
055da31
Make sure profiler is not disabled in the code
gleocadie Mar 19, 2026
4020dd0
Fix path construction when running the integration tests
gleocadie Mar 19, 2026
a425839
Fix bug in ManagedCodeCache
gleocadie Mar 19, 2026
e40bd0d
Fix native loader
gleocadie Mar 19, 2026
41835f6
Fix tracer/profiler test
gleocadie Mar 19, 2026
e3f94ac
Fix bugs
gleocadie Mar 19, 2026
04f0483
Fix framework for test + check lr-fallback approach
gleocadie Mar 20, 2026
2e844bb
Make it better
gleocadie Mar 20, 2026
fd15b53
Display in CI stack mismatch
gleocadie Mar 20, 2026
0bcec1e
Try fixing stack walking with jit stub/helper
gleocadie Mar 20, 2026
c67a0a8
Try fixing net9 issue with stack collection
gleocadie Mar 20, 2026
64028b1
Try something: more native frames mixed with managed frames
gleocadie Mar 23, 2026
8a62927
Fix deadlock
gleocadie Mar 23, 2026
9c2a923
Try fixing crash in libunwind
gleocadie Mar 24, 2026
b012166
TO run CI
gleocadie Mar 24, 2026
a585d18
Add UnwindTracer
gleocadie Mar 25, 2026
00901c0
Fix build + adjust libunwind mirrored data structures
gleocadie Mar 27, 2026
bb48018
Try another one fix
gleocadie Mar 28, 2026
2a65d9d
Try my fix
gleocadie Mar 28, 2026
3c3530d
Try something else with UnwindTracersProvider
gleocadie Mar 30, 2026
bfda141
Build ME
gleocadie Mar 31, 2026
4c82baa
Try new cursor snapshot api + cfa update in unw_step
gleocadie Apr 3, 2026
a554f25
add RecordStart with ucontext_t address
gleocadie Apr 13, 2026
e964931
Finish early TimerCreate-based CPU profiler
gleocadie Apr 13, 2026
20fad51
Add Sanitizers jobs
gleocadie Apr 13, 2026
bde77c0
Try fixigin ubsan job
gleocadie Apr 13, 2026
9df6d75
Fix asan job
gleocadie Apr 13, 2026
6511650
Investigate crash
gleocadie Apr 14, 2026
8aaaf6b
Try fixing ubsan
gleocadie Apr 16, 2026
bfbd08d
Fix build
gleocadie Apr 17, 2026
37ab55f
Start clean up a bit
gleocadie Apr 20, 2026
0b7e0db
Fix FrameStore
gleocadie Apr 21, 2026
e52c5dd
Try fixing test
gleocadie Apr 21, 2026
fbf0c63
Clean up
gleocadie Apr 22, 2026
cda3349
More cleanup
gleocadie Apr 22, 2026
54f1bba
fix(profiler): restore unique-ownership semantics on ScopedTracer (ar…
gleocadie Apr 22, 2026
9be2542
chore(profiler): arm64 polish pass (logs, comments, atomics, EOF newl…
gleocadie Apr 22, 2026
044a856
docs(profiler): capture arm64 build-system gotchas (C7, E1..E4)
gleocadie Apr 22, 2026
b5e8b5f
test(profiler): extend FrameStoreTest with fake-IP / cached-path null…
gleocadie Apr 22, 2026
942d618
Fixing Integration tests
gleocadie Apr 23, 2026
5ef61b7
Fix UBsan job
gleocadie Apr 23, 2026
c00db67
Handle ARM64 case for callstack comparison
gleocadie Apr 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .azure-pipelines/steps/update-github-pipeline-status.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ stages:
- integration_tests_linux_debugger
- profiler_integration_tests_windows
- profiler_integration_tests_linux
- profiler_integration_tests_arm64
- asan_profiler_tests
- asan_arm64_profiler_tests
- ubsan_arm64_profiler_tests
- ubsan_profiler_tests
- tsan_profiler_tests
- integration_tests_arm64
Expand Down Expand Up @@ -118,7 +121,10 @@ stages:
in(dependencies.integration_tests_linux_debugger.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.profiler_integration_tests_windows.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.profiler_integration_tests_linux.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.profiler_integration_tests_arm64.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.asan_profiler_tests.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.asan_arm64_profiler_tests.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.ubsan_arm64_profiler_tests.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.ubsan_profiler_tests.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.tsan_profiler_tests.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.integration_tests_arm64.result, 'Succeeded','SucceededWithIssues','Skipped'),
Expand Down Expand Up @@ -209,7 +215,10 @@ stages:
- integration_tests_linux_debugger
- profiler_integration_tests_windows
- profiler_integration_tests_linux
- profiler_integration_tests_arm64
- asan_profiler_tests
- asan_arm64_profiler_tests
- ubsan_arm64_profiler_tests
- ubsan_profiler_tests
- tsan_profiler_tests
- integration_tests_arm64
Expand Down Expand Up @@ -288,7 +297,10 @@ stages:
in(dependencies.integration_tests_linux_debugger.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.profiler_integration_tests_windows.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.profiler_integration_tests_linux.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.profiler_integration_tests_arm64.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.asan_profiler_tests.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.asan_arm64_profiler_tests.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.ubsan_arm64_profiler_tests.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.ubsan_profiler_tests.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.tsan_profiler_tests.result, 'Succeeded','SucceededWithIssues','Skipped'),
in(dependencies.integration_tests_arm64.result, 'Succeeded','SucceededWithIssues','Skipped'),
Expand Down
209 changes: 209 additions & 0 deletions .azure-pipelines/ultimate-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2907,6 +2907,107 @@ stages:
testResultsFiles: profiler/build_data/results/**/*.trx
condition: succeededOrFailed()

- stage: profiler_integration_tests_arm64
condition: >
and(
succeeded(),
or(
eq(variables.isMainOrReleaseBranch, true),
eq(dependencies.generate_variables.outputs['generate_variables_job.generate_variables_step.IsProfilerChanged'], 'True')
)
)
dependsOn: [package_arm64, generate_variables, merge_commit_id]
variables:
targetShaId: $[ stageDependencies.merge_commit_id.fetch.outputs['set_sha.sha']]
targetBranch: $[ stageDependencies.merge_commit_id.fetch.outputs['set_sha.branch']]
jobs:
- template: steps/update-github-status-jobs.yml
parameters:
jobs: [Test]

- job: Test
timeoutInMinutes: 60 #default value
strategy:
matrix:
arm64:
baseImage: debian
artifactSuffix: linux-arm64
alpine:
baseImage: alpine
artifactSuffix: linux-musl-arm64

variables:
IncludeMinorPackageVersions: $[eq(variables.perform_comprehensive_testing, 'true')]

pool:
name: $(linuxArm64Pool)

steps:
- template: steps/clone-repo.yml
parameters:
targetShaId: $(targetShaId)
targetBranch: $(targetBranch)

- template: steps/restore-working-directory.yml
parameters:
artifact: build-$(artifactSuffix)-working-directory

- template: steps/download-artifact.yml
parameters:
artifact: linux-monitoring-home-$(artifactSuffix)
path: $(monitoringHome)

- template: steps/download-artifact.yml
parameters:
artifact: linux-profiler-symbols-$(artifactSuffix)
path: $(monitoringHome)

- template: steps/run-in-docker.yml
parameters:
build: true
baseImage: $(baseImage)
command: "BuildProfilerSamples"
apiKey: $(DD_LOGGER_DD_API_KEY)
retryCountForRunCommand: 3

- template: steps/run-in-docker.yml
parameters:
baseImage: $(baseImage)
command: "BuildAndRunProfilerCpuLimitTests"
extraArgs: "--cpus 2 --env CONTAINER_CPUS=1"
apiKey: $(DD_LOGGER_DD_API_KEY)
retryCountForRunCommand: 3

- template: steps/run-in-docker.yml
parameters:
baseImage: $(baseImage)
command: "BuildAndRunProfilerCpuLimitTests"
extraArgs: "--cpus 0.5 --env CONTAINER_CPUS=0.5"
apiKey: $(DD_LOGGER_DD_API_KEY)
retryCountForRunCommand: 3

- script: |
docker-compose -f docker-compose.yml -p $(DockerComposeProjectName) \
run --rm \
-e baseImage=$(baseImage) \
ProfilerIntegrationTests
displayName: docker-compose run --no-deps ProfilerIntegrationTests
env:
DD_LOGGER_DD_API_KEY: $(ddApiKey)
baseImage: $(baseImage) # for interpolation in the docker-compose file

- publish: profiler/build_data
artifact: _$(System.StageName)_$(Agent.JobName)_logs_$(System.JobAttempt)
condition: always()
continueOnError: true

- task: PublishTestResults@2
displayName: publish test results
inputs:
testResultsFormat: VSTest
testResultsFiles: profiler/build_data/results/**/*.trx
condition: succeededOrFailed()

- stage: asan_profiler_tests
#address sanitizer tests
condition: >
Expand Down Expand Up @@ -2992,6 +3093,114 @@ stages:
condition: always()
continueOnError: false

- stage: asan_arm64_profiler_tests
#address sanitizer tests on arm64
condition: >
and(
succeeded(),
eq(dependencies.generate_variables.outputs['generate_variables_job.generate_variables_step.IsProfilerChanged'], 'True')
)
dependsOn: [merge_commit_id, generate_variables]
variables:
targetShaId: $[ stageDependencies.merge_commit_id.fetch.outputs['set_sha.sha']]
targetBranch: $[ stageDependencies.merge_commit_id.fetch.outputs['set_sha.branch']]
jobs:
- template: steps/update-github-status-jobs.yml
parameters:
jobs: [Linux]

- job: Linux
timeoutInMinutes: 60

pool:
name: $(linuxArm64Pool)

steps:
- template: steps/clone-repo.yml
parameters:
targetShaId: $(targetShaId)
targetBranch: $(targetBranch)

- template: steps/run-in-docker.yml
parameters:
build: true
baseImage: debian
command: "BuildProfilerAsanTest -Framework net7.0"
apiKey: $(DD_LOGGER_DD_API_KEY)

- template: steps/run-in-docker.yml
parameters:
build: true
baseImage: debian
command: "BuildProfilerSampleForSanitiserTests -Framework net7.0"
apiKey: $(DD_LOGGER_DD_API_KEY)

- template: steps/run-in-docker.yml
parameters:
baseImage: debian
command: "RunSampleWithProfilerAsan -Framework net7.0"
apiKey: $(DD_LOGGER_DD_API_KEY)

- publish: profiler/build_data
displayName: Uploading Address sanitizer test results
artifact: _$(System.StageName)_$(Agent.JobName)_test_results_$(System.JobAttempt)
condition: always()
continueOnError: false

- stage: ubsan_arm64_profiler_tests
#undefined behavior sanitizer tests on arm64
condition: >
and(
succeeded(),
eq(dependencies.generate_variables.outputs['generate_variables_job.generate_variables_step.IsProfilerChanged'], 'True')
)
dependsOn: [merge_commit_id, generate_variables]
variables:
targetShaId: $[ stageDependencies.merge_commit_id.fetch.outputs['set_sha.sha']]
targetBranch: $[ stageDependencies.merge_commit_id.fetch.outputs['set_sha.branch']]
jobs:
- template: steps/update-github-status-jobs.yml
parameters:
jobs: [Linux]

- job: Linux
timeoutInMinutes: 60

pool:
name: $(linuxArm64Pool)

steps:
- template: steps/clone-repo.yml
parameters:
targetShaId: $(targetShaId)
targetBranch: $(targetBranch)

- template: steps/run-in-docker.yml
parameters:
build: true
baseImage: debian
command: "BuildProfilerUbsanTest -Framework net7.0"
apiKey: $(DD_LOGGER_DD_API_KEY)

- template: steps/run-in-docker.yml
parameters:
build: true
baseImage: debian
command: "BuildProfilerSampleForSanitiserTests -Framework net7.0"
apiKey: $(DD_LOGGER_DD_API_KEY)

- template: steps/run-in-docker.yml
parameters:
baseImage: debian
command: "RunSampleWithProfilerUbsan -Framework net7.0"
apiKey: $(DD_LOGGER_DD_API_KEY)

- publish: profiler/build_data
displayName: Uploading test results
artifact: _$(System.StageName)_$(Agent.JobName)_logs_$(System.JobAttempt)
condition: always()
continueOnError: false

- stage: ubsan_profiler_tests
#undefined behavior sanitizer tests
condition: >
Expand Down
23 changes: 21 additions & 2 deletions build/cmake/FindLibunwind.cmake
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
SET(LIBUNWIND_VERSION "v1.8.3")
# We intentionally pin to the DataDog/libunwind fork on tag
Copy link
Copy Markdown
Collaborator Author

@gleocadie gleocadie Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will upgrade later in this PR.
Just as a reminder.

# gleocadie/v1.8.1-custom-3 rather than upstream libunwind v1.8.1 or v1.8.3.
# The fork carries the following patches that the Continuous Profiler's
# HybridUnwinder depends on (arm64 Linux):
# - unw_cursor_snapshot_t / unw_cursor_snapshot(): a signal-safe public API
# to inspect the dwarf cursor (CFA, loc_fp/loc_lr/loc_sp, frame_type,
# cfa_reg_sp, cfa_reg_offset, dwarf_step_ret, step_method, loc_info).
# Used by UnwinderTracer.h / HybridUnwinder.cpp without having to mirror
# libunwind's internal layouts.
# - unw_init_local2() + UNW_INIT_SIGNAL_FRAME flag: lets us initialize the
# cursor directly from a signal-delivered ucontext_t and flag the first
# frame as a signal frame so libunwind returns the interrupted PC instead
# of the signal-trampoline PC.
# - Additional accessors on the tdep_frame (fp_cfa_offset / lr_cfa_offset /
# sp_cfa_offset, cfa_is_unreliable, next_to_signal_frame) consumed by the
# tracer for diagnostics.
# Upstream libunwind 1.8.3 does not expose these APIs. When they land upstream
# (or when the fork is rebased on top of a newer upstream tag), update this
# file accordingly.
SET(LIBUNWIND_VERSION "v1.8.1-custom-3")

SET(LIBUNWIND_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/libunwind-prefix/src/libunwind-build)

ExternalProject_Add(libunwind
GIT_REPOSITORY https://github.com/DataDog/libunwind.git
GIT_TAG gleocadie/v1.8.3
GIT_TAG gleocadie/v1.8.1-custom-3
GIT_PROGRESS true
INSTALL_COMMAND ""
UPDATE_COMMAND ""
Expand Down
10 changes: 8 additions & 2 deletions profiler/src/Demos/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

Expand All @@ -7,7 +7,13 @@
<PropertyGroup>
<!-- only run .NET Framework tests on Windows -->
<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">net48;netcoreapp3.1;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">netcoreapp3.1;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT' And '$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)' != 'Arm64'">netcoreapp3.1;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<!--
netcoreapp3.1 is deliberately excluded on arm64 Linux: the Continuous Profiler
refuses to initialize when major < 5 on that platform (see CorProfilerCallback::Initialize),
so running the demos against .NET Core 3.1 arm64 would produce no profiles at all.
-->
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT' And '$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)' == 'Arm64'">net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<Platforms>AnyCPU;x64;x86</Platforms>

<!-- Hide warnings for EOL .NET Core targets (e.g. netcoreapp3.0) -->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">netcoreapp3.1;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT' And '$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)' != 'Arm64'">netcoreapp3.1;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT' And '$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)' == 'Arm64'">net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<Platforms>AnyCPU;x64;x86</Platforms>

<!--This is required for smoke test assembly discovery-->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">netcoreapp3.1;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT' And '$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)' != 'Arm64'">netcoreapp3.1;net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT' And '$([System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture)' == 'Arm64'">net6.0;net7.0;net8.0;net9.0;net10.0</TargetFrameworks>
<RootNamespace>Samples.Website_AspNetCore01</RootNamespace>
<Platforms>AnyCPU;x64;x86</Platforms>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ endif()
# ******************************************************

SET(API_WRAPPER_BASENAME Datadog.Linux.ApiWrapper)
# For arm64, we cannot replace the x64 suffix with arm64.
# This library is already shipped and used by customers. Their scripts expect the x64 suffix.
# SSI relies on this file.
# Changing this would bring little value but lots of headaches.
SET(API_WRAPPER_SHARED_LIB_NAME ${API_WRAPPER_BASENAME}.x64)

SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${DEPLOY_DIR})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@

Backtrace2Unwinder::Backtrace2Unwinder() = default;

std::int32_t Backtrace2Unwinder::Unwind(void* ctx, std::uintptr_t* buffer, std::size_t bufferSize) const
std::int32_t Backtrace2Unwinder::Unwind(void* ctx, std::uintptr_t* buffer, std::size_t bufferSize,
std::uintptr_t stackBase, std::uintptr_t stackEnd,
UnwinderTracer* tracer) const
{
// unw_backtrace2 handles the case ctx == nullptr
auto* context = reinterpret_cast<unw_context_t*>(ctx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class Backtrace2Unwinder : public IUnwinder
~Backtrace2Unwinder() override = default;

// Returns the number of frames unwound
std::int32_t Unwind(void* ctx, std::uintptr_t* buffer, std::size_t bufferSize) const override;
std::int32_t Unwind(void* ctx, std::uintptr_t* buffer, std::size_t bufferSize,
std::uintptr_t stackBase = 0, std::uintptr_t stackEnd = 0,
UnwinderTracer* tracer = nullptr) const override;

};
};
Loading
Loading