Skip to content

feat: cross-language LTO to inline C TLS shim into Rust FFI#1982

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 18 commits into
mainfrom
yannham/thread-ctx-inlining-exp
Jun 2, 2026
Merged

feat: cross-language LTO to inline C TLS shim into Rust FFI#1982
gh-worker-dd-mergequeue-cf854d[bot] merged 18 commits into
mainfrom
yannham/thread-ctx-inlining-exp

Conversation

@yannham

@yannham yannham commented May 13, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Add opt-in cross-language LTO so the C TLS shim (libdd_get_otel_thread_ctx_v1) is inlined directly into the Rust FFI functions, eliminating a function-call indirection on every TLS access.

The mode is controlled by the LIBDD_OTEL_THREAD_CTX_INLINE env var. A build-optimized.sh wrapper script sets the env var and the target-scoped RUSTFLAGS needed for cross-language LTO. Without the env var, the build behaves exactly as before.

Motivation

The OTel thread-level context spec requires TLSDESC for the TLS variable, which forces us through a C shim since stable rustc doesn't expose TLS dialect control. This adds a function call on every attach/detach. Cross-language LTO eliminates that call.

Benchmark results (median of 3 batch runs on x86-64):

Benchmark Default Inline (LTO) Improvement
single/in_place ~17.1 ns ~13.5 ns ~20%
single/swap ~21.9 ns ~20.1 ns ~8%
multi/swap/4 ~7.4 ns ~5.3 ns ~28%
multi/swap/16 ~14.5 ns ~8.7 ns ~40%

Additional Notes

  • Requires clang and lld (the toolchain's bundled rust-lld is used automatically).
  • A wrapper script is needed because build.rs cannot set rustc codegen flags (-Clinker-plugin-lto, -Clinker=clang). Those must come from RUSTFLAGS.
  • The build script validates prerequisites (clang available, LLD version) and fails with a clear error if something is missing.
  • The #[cfg(target_os/arch)] in build scripts are replaced with the correct CARGO_CFG_* env vars for cross-compilation correctness.

How to test the change?

# Default build (no inlining, same as before)
cargo build --release -p libdd-otel-thread-ctx-ffi
cargo test --release -p libdd-otel-thread-ctx-ffi

# Optimized build (cross-language LTO)
./libdd-otel-thread-ctx-ffi/build-optimized.sh

# Verify inlining succeeded (symbol should be absent)
nm target/x86_64-unknown-linux-gnu/release/liblibdd_otel_thread_ctx_ffi.so | grep libdd_get_otel_thread_ctx
# (no output = inlined)

# Verify TLSDESC preserved
readelf -r target/x86_64-unknown-linux-gnu/release/liblibdd_otel_thread_ctx_ffi.so | grep TLSDESC
# should show R_X86_64_TLSDESC for otel_thread_ctx_v1

@github-actions

github-actions Bot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Clippy Allow Annotation Report

Comparing clippy allow annotations between branches:

  • Base Branch: origin/main
  • PR Branch: origin/yannham/thread-ctx-inlining-exp

Summary by Rule

Rule Base Branch PR Branch Change

Annotation Counts by File

File Base Branch PR Branch Change

Annotation Stats by Crate

Crate Base Branch PR Branch Change
clippy-annotation-reporter 5 5 No change (0%)
datadog-ffe-ffi 1 1 No change (0%)
datadog-ipc 21 21 No change (0%)
datadog-live-debugger 6 6 No change (0%)
datadog-live-debugger-ffi 10 10 No change (0%)
datadog-profiling-replayer 4 4 No change (0%)
datadog-remote-config 3 3 No change (0%)
datadog-sidecar 57 57 No change (0%)
libdd-common 13 13 No change (0%)
libdd-common-ffi 12 12 No change (0%)
libdd-data-pipeline 5 5 No change (0%)
libdd-ddsketch 2 2 No change (0%)
libdd-dogstatsd-client 1 1 No change (0%)
libdd-profiling 13 13 No change (0%)
libdd-telemetry 20 20 No change (0%)
libdd-tinybytes 4 4 No change (0%)
libdd-trace-normalization 2 2 No change (0%)
libdd-trace-obfuscation 3 3 No change (0%)
libdd-trace-stats 1 1 No change (0%)
libdd-trace-utils 13 13 No change (0%)
Total 196 196 No change (0%)

About This Report

This report tracks Clippy allow annotations for specific rules, showing how they've changed in this PR. Decreasing the number of these annotations generally improves code quality.

@codecov-commenter

codecov-commenter commented May 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.38%. Comparing base (a84923e) to head (4eb82b7).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1982      +/-   ##
==========================================
- Coverage   73.40%   73.38%   -0.03%     
==========================================
  Files         464      465       +1     
  Lines       77970    77985      +15     
==========================================
- Hits        57236    57231       -5     
- Misses      20734    20754      +20     
Components Coverage Δ
libdd-crashtracker 65.47% <ø> (+0.01%) ⬆️
libdd-crashtracker-ffi 37.68% <ø> (ø)
libdd-alloc 98.77% <ø> (ø)
libdd-data-pipeline 86.46% <ø> (-0.02%) ⬇️
libdd-data-pipeline-ffi 77.03% <ø> (ø)
libdd-common 79.89% <ø> (ø)
libdd-common-ffi 74.41% <ø> (ø)
libdd-telemetry 73.34% <ø> (-0.03%) ⬇️
libdd-telemetry-ffi 31.36% <ø> (ø)
libdd-dogstatsd-client 82.64% <ø> (ø)
datadog-ipc 76.22% <ø> (ø)
libdd-profiling 81.69% <ø> (-0.02%) ⬇️
libdd-profiling-ffi 64.79% <ø> (ø)
libdd-sampling 97.41% <ø> (ø)
datadog-sidecar 34.60% <ø> (-0.05%) ⬇️
datdog-sidecar-ffi 8.61% <ø> (ø)
spawn-worker 48.86% <ø> (ø)
libdd-tinybytes 93.80% <ø> (ø)
libdd-trace-normalization 81.71% <ø> (ø)
libdd-trace-obfuscation 87.30% <ø> (ø)
libdd-trace-protobuf 68.25% <ø> (ø)
libdd-trace-utils 89.21% <ø> (ø)
libdd-tracer-flare 86.88% <ø> (ø)
libdd-log 74.83% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@datadog-prod-us1-6

datadog-prod-us1-6 Bot commented May 13, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 0.00%
Overall Coverage: 73.39% (-0.02%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 4eb82b7 | Docs | Datadog PR Page | Give us feedback!

@yannham yannham force-pushed the yannham/thread-ctx-inlining-exp branch 4 times, most recently from 9932cac to 9227252 Compare May 13, 2026 14:08
@dd-octo-sts

dd-octo-sts Bot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Artifact Size Benchmark Report

aarch64-alpine-linux-musl
Artifact Baseline Commit Change
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.so 7.63 MB 7.63 MB 0% (0 B) 👌
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.a 82.98 MB 82.98 MB 0% (0 B) 👌
aarch64-unknown-linux-gnu
Artifact Baseline Commit Change
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.a 94.05 MB 94.05 MB 0% (0 B) 👌
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.so 10.26 MB 10.26 MB 0% (0 B) 👌
libdatadog-x64-windows
Artifact Baseline Commit Change
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.dll 24.57 MB 24.57 MB 0% (0 B) 👌
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.lib 83.96 KB 83.96 KB 0% (0 B) 👌
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.pdb 178.35 MB 178.36 MB +0% (+8.00 KB) 👌
/libdatadog-x64-windows/debug/static/datadog_profiling_ffi.lib 915.73 MB 915.73 MB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.dll 8.03 MB 8.03 MB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.lib 83.96 KB 83.96 KB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.pdb 23.78 MB 23.78 MB 0% (0 B) 👌
/libdatadog-x64-windows/release/static/datadog_profiling_ffi.lib 47.44 MB 47.44 MB 0% (0 B) 👌
libdatadog-x86-windows
Artifact Baseline Commit Change
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.dll 21.28 MB 21.28 MB 0% (0 B) 👌
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.lib 85.29 KB 85.29 KB 0% (0 B) 👌
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.pdb 182.30 MB 182.29 MB -0% (-8.00 KB) 👌
/libdatadog-x86-windows/debug/static/datadog_profiling_ffi.lib 908.33 MB 908.33 MB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.dll 6.20 MB 6.20 MB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.lib 85.29 KB 85.29 KB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.pdb 25.49 MB 25.49 MB 0% (0 B) 👌
/libdatadog-x86-windows/release/static/datadog_profiling_ffi.lib 45.09 MB 45.09 MB 0% (0 B) 👌
x86_64-alpine-linux-musl
Artifact Baseline Commit Change
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.a 73.98 MB 73.98 MB 0% (0 B) 👌
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.so 8.53 MB 8.53 MB 0% (0 B) 👌
x86_64-unknown-linux-gnu
Artifact Baseline Commit Change
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.a 89.38 MB 89.38 MB 0% (0 B) 👌
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.so 10.37 MB 10.37 MB 0% (0 B) 👌

let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();

if target_os != "linux" {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This change is somehow unrelated, but using static cfg(target_os = "linux") is actually not the way to go in a build script (during cross-compilation typically, the target_os of the script and of the dynamic library could differ), so fixing it passing by.

@yannham yannham marked this pull request as ready for review May 18, 2026 09:45
@yannham yannham requested review from a team as code owners May 18, 2026 09:45
@yannham yannham requested review from ivoanjo and nsavoire May 18, 2026 09:46
@yannham

yannham commented May 18, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@yannham yannham requested a review from scottgerring May 21, 2026 08:16

@ivoanjo ivoanjo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I really like this optimization, left a few comments since I'm a bit... confused about some of the details :D

Comment thread libdd-otel-thread-ctx-ffi/build-optimized.sh
Comment thread libdd-otel-thread-ctx-ffi/build-optimized.sh Outdated
Comment thread libdd-otel-thread-ctx-ffi/build-optimized.sh Outdated
Comment thread libdd-otel-thread-ctx-ffi/README.md

@scottgerring scottgerring left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice - great to see the improvements to :D

Comment thread libdd-otel-thread-ctx-ffi/build-optimized.sh Outdated
Comment thread libdd-otel-thread-ctx-ffi/build-optimized.sh Outdated
Comment thread libdd-otel-thread-ctx-ffi/build-optimized.sh
Comment thread libdd-otel-thread-ctx/build.rs Outdated
Comment thread libdd-otel-thread-ctx-ffi/build-optimized.sh
Comment thread build-common/src/lib.rs Outdated
Comment thread libdd-otel-thread-ctx-ffi/build.rs Outdated

@hoolioh hoolioh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM. One caveat, probably the possibilities are scarce but I think it'd be worth to have an integration CI job that builds inline mode and asserts the invariants for the supported platforms. Since this depends on the external toolchains there could be situations in which the machinery could not work, we would silently default to not inlining the function and cause a regression. That said, I think it could be done in a following PR

yannham and others added 6 commits June 2, 2026 13:48
Add an opt-in inline mode via the LIBDD_OTEL_THREAD_CTX_INLINE env var
that uses cross-language LTO (clang + lld) to inline the C TLS shim
directly into the Rust FFI functions, eliminating a function-call
indirection on every TLS access.

When the env var is set, build.rs validates that clang and a suitable
LLD are available, compiles the C shim as LLVM bitcode (-flto=thin),
and emits the linker flags needed for cross-language LTO. A companion
build-optimized.sh wrapper sets the target-scoped RUSTFLAGS and env var.

When the env var is absent, the previous behavior is preserved: the
default cc compiles the shim with -mtls-dialect=gnu2 on x86-64, no LTO,
no inlining.

Also fixes #[cfg(target_os/arch)] in build scripts to use the correct
CARGO_CFG_* env vars for cross-compilation correctness.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After a successful build, verify with nm that the C TLS shim symbol was
actually inlined away. Warns and exits 1 if inlining failed, or warns
(but succeeds) if nm is not available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
yannham and others added 12 commits June 2, 2026 13:48
Deduplicate the function that locates the toolchain's bundled rust-lld
by moving it into build_common, where both build scripts can reuse it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Scott Gerring <scottgerring@users.noreply.github.com>
Co-Authored-By: Claude <noreply@anthropic.com>
@yannham yannham force-pushed the yannham/thread-ctx-inlining-exp branch from 1a5b30e to 4eb82b7 Compare June 2, 2026 11:48
@gh-worker-dd-mergequeue-cf854d gh-worker-dd-mergequeue-cf854d Bot merged commit a1da9fc into main Jun 2, 2026
141 checks passed
@gh-worker-dd-mergequeue-cf854d gh-worker-dd-mergequeue-cf854d Bot deleted the yannham/thread-ctx-inlining-exp branch June 2, 2026 16:28
gh-worker-dd-mergequeue-cf854d Bot pushed a commit that referenced this pull request Jun 19, 2026
# Release v36.0.0 
This release bumps the workspace version `35.0.0 → 36.0.0`. Below are the commits in `v35.0.0..HEAD` that directly modify the C ABI surface consumed by downstream SDKs.

## Major

| Commit | FFI crate(s) affected |
|---|---|
| `refactor(shm)!: Extract one_way_shared_memory to IPC and prepare libdd-remote-config for python` (#2121) | `datadog-sidecar-ffi` |
| `refactor(span)!: use VecMap for meta, metrics and meta_struct for v04 spans` (#2043) | `datadog-sidecar-ffi`, `libdd-data-pipeline-ffi` |
| `feat(data-pipeline)!: add fork safety hooks and cancellation token for trace exporter FFI` (#2051) | `libdd-data-pipeline-ffi`, `libdd-profiling-ffi` |

## Minor

| Commit | FFI crate(s) affected |
|---|---|
| `feat(sidecar): add retry interval configuration` (#2106) | `datadog-sidecar-ffi` |
| `feat(profiling): Add setting to omit local root span id from serialized pprof` (#2104) | `libdd-profiling-ffi` |
| `feat(live-debugger): agentless intake forwarding` (#2075) | `datadog-live-debugger-ffi` |
| `feat(sidecar): forward FFE exposures to EVP proxy` (#2026) | `datadog-sidecar-ffi` |
| `feat(sidecar): forward FFE evaluation metrics to OTLP intake` (#2052) | `datadog-sidecar-ffi` |
| `feat: cross-language LTO to inline C TLS shim into Rust FFI` (#1982) | `libdd-otel-thread-ctx-ffi` (build-only: build.rs / scripts / README) |

## Patch

| Commit | FFI crate(s) affected |
|---|---|
| `fix(ffe): honor shared fixture result metadata` (#2109) | `datadog-ffe-ffi` |
| `fix(sidecar): Dedup VecMap spans before serialization` (#2107) | `datadog-sidecar-ffi` |
| `fix(crashtracking): authenticate peer granted socket ptrace access` (#2098) | `libdd-crashtracker-ffi`, `datadog-sidecar-ffi` |
| `fix(remote-config): notification of multi-processing and runtime deduplication` (#2082) | `datadog-sidecar-ffi` |
| `fix(sidecar): configure OTLP endpoint for FFE metrics` (#2076) | `datadog-sidecar-ffi` |
| `refactor(datadog-remote-config): rename as libdd-remote-config` (#2085) | `datadog-sidecar-ffi` (incl. `cbindgen.toml`) |



Co-authored-by: julio.gonzalez <julio.gonzalez@datadoghq.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants