Skip to content

refactor(library-config): reorganize Linux process context#2228

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 2 commits into
mainfrom
glopes/otel-process-ctx-macos-win
Jul 16, 2026
Merged

refactor(library-config): reorganize Linux process context#2228
gh-worker-dd-mergequeue-cf854d[bot] merged 2 commits into
mainfrom
glopes/otel-process-ctx-macos-win

Conversation

@cataphract

@cataphract cataphract commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

This is 1/3 in the stacked process-context series.

It reorganizes the existing Linux process-context implementation without changing its behavior:

  • separates shared reader and writer logic into dedicated modules;
  • moves Linux-specific allocation, discovery, clock, and copy behavior behind platform modules;
  • introduces independent reader and writer Cargo features while preserving the existing Linux defaults at this layer;
  • adds platform-agnostic exports and retains the Linux compatibility path;
  • preserves the existing functionality exactly, including the edge cases and bugs addressed by the next PR.

Why?

This creates the module structure needed for additional operating systems without mixing a large code move with functional changes. Reviewers can evaluate the organization independently before the Linux implementation is updated and the new platforms are added.

Impact

There is no intended runtime behavior change. Linux remains the only supported implementation in this PR, and existing Linux callers retain the same behavior.

Validation

Validated independently at this commit with:

  • cargo check -p libdd-library-config --all-features;
  • cargo check -p libdd-library-config-ffi;
  • all-feature process-context tests;
  • reader-only process-context tests.

Stack

  1. refactor(library-config): reorganize Linux process context #2228 — reorganize the Linux process context
  2. fix(library-config): update Linux process context #2237 — update the Linux implementation
  3. feat(library-config): add macOS and Windows process context #2238 — add macOS and Windows support

BREAKING CHANGE: moves ProcessContextSelfReader from the Linux-specific module to the platform-agnostic process-context API, while retaining the deprecated Linux compatibility path.

@cataphract
cataphract requested a review from a team as a code owner July 10, 2026 16:49
@cataphract
cataphract requested review from vpellan and removed request for a team July 10, 2026 16:49
@cataphract cataphract changed the title otel process ctx support on macos win feat(library-config): support OTel process contexts on macOS and Windows Jul 10, 2026
@pr-commenter

pr-commenter Bot commented Jul 10, 2026

Copy link
Copy Markdown

Benchmarks

Comparison

Candidate

Candidate benchmark details

Baseline

Baseline benchmark details

@datadog-prod-us1-6

datadog-prod-us1-6 Bot commented Jul 10, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 77.14%
Overall Coverage: 74.79% (+0.33%)

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 19438b34d4

ℹ️ 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".

Comment thread libdd-library-config/src/tracer_metadata.rs
Comment thread libdd-library-config/src/otel_process_ctx/reader/macos.rs Outdated
let chunk_len = (len - offset).min(self.chunk_size);
let chunk_addr = addr.wrapping_add(offset);

// SAFETY: write asks the kernel to copy from chunk_addr. Invalid user memory is

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.

Reading the man page, it seems like error detection may occur but I don't see where it is guaranteed? POSIX does not appear to require EFAULT at all.

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.

We're only targeting Linux and macOS. Both Linux's and mac os man pages describe this return value.

})
}

fn copy(&self, addr: *const u8, len: usize) -> Result<Vec<u8>, PipeCopyError> {

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.

Does this function have SAFETY requirements?

@cataphract cataphract Jul 10, 2026

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.

The whole point of the function is that it should not generate a fault if attempting to read unmapped or otherwise inaccessible manner. So there is no safety contract that the caller must respect in order for the invocation of the function not to trigger UB.

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.

I think this contract (copy won't fail even for unmapped addresses or unitialized memory) should be specified at the trait level, in the documentation of copy. It's not obvious for anyone who hasn't sufficient context (as demonstrated by Daniel's question)

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.

I documented the contract on ProcessMemoryCopy::copy: callers do not need to establish that the range is valid, mapped, initialized, or remains mapped; inaccessible memory must return WouldBlock; success returns exactly len bytes; and the copy is not an atomic snapshot.

let chunk_addr = addr.wrapping_add(offset);

// SAFETY: write asks the kernel to copy from chunk_addr. Invalid user memory is
// reported as EFAULT or a short write without being dereferenced by Rust.

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.

What are the semantics in the case of a short write

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.

If there is a short write, then something interrupted the function after it's already written some data. It could have been interrupted, or it could have found an unreadable page.

But it turns out that, despite what the mac os man page says, write() always returns EFAULT upon encountering a fault, even if it's written something. This requires marking its error with pipe_dirty: true, which I'll do in a bit.

}

// SAFETY: every byte was initialized by the pipe reads above.
unsafe { bytes.set_len(len) };

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.

would it be safer to vec![0,len], or std::io::BorrowedBuf?

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.

I answered a similar comment when this code was introduced (it was just moved in this PR): https://github.com/DataDog/libdatadog/pull/2176/changes/BASE..12ca390b7683b6efaeae6cf06078b26eaf41a3d9#diff-e3f807d69a162e59e2e82257164c7642090139e9d90f6be2fecde5205ead3636 :

What difference does it make if it's initialized with zeros or not? The read syscall writes directly into the buffer and it should fill it to capacity. And if it there was a bug and it didn't, zeros or uninitialized memory would be equally wrong (I'd probably even prefer uninitialized memory because it's more likely to be caught by MSan).

BorrowedBuf would indeed be a small improvement, but it doesn't appear to be stable.

@yannham yannham Jul 15, 2026

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.

The difference is that you would get rid of this unsafe set_len I believe. You initialize it with the right length from the beginning and just use it as a fixed-sized array [u8; N]. No need to uphold any safety condition anymore.

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.

I changed the destination to vec![0; len], removed the unsafe set_len, and now construct a bounds-checked mutable slice before calling read. I also documented the slice-bounds proof.

@dd-octo-sts

dd-octo-sts Bot commented Jul 10, 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.88 MB 7.88 MB 0% (0 B) 👌
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.a 86.15 MB 86.15 MB +0% (+8.03 KB) 👌
aarch64-unknown-linux-gnu
Artifact Baseline Commit Change
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.a 97.40 MB 97.41 MB +0% (+8.82 KB) 👌
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.so 10.62 MB 10.62 MB +0% (+160 B) 👌
libdatadog-x64-windows
Artifact Baseline Commit Change
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.dll 25.54 MB 25.54 MB 0% (0 B) 👌
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.lib 88.44 KB 88.44 KB 0% (0 B) 👌
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.pdb 185.33 MB 185.34 MB +0% (+16.00 KB) 👌
/libdatadog-x64-windows/debug/static/datadog_profiling_ffi.lib 957.25 MB 957.26 MB +0% (+10.35 KB) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.dll 8.35 MB 8.35 MB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.lib 88.44 KB 88.44 KB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.pdb 24.72 MB 24.72 MB 0% (0 B) 👌
/libdatadog-x64-windows/release/static/datadog_profiling_ffi.lib 49.21 MB 49.21 MB -0% (-228 B) 👌
libdatadog-x86-windows
Artifact Baseline Commit Change
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.dll 22.19 MB 22.19 MB -0% (-512 B) 👌
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.lib 89.82 KB 89.82 KB 0% (0 B) 👌
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.pdb 189.78 MB 189.77 MB -0% (-8.00 KB) 👌
/libdatadog-x86-windows/debug/static/datadog_profiling_ffi.lib 945.93 MB 945.94 MB +0% (+10.35 KB) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.dll 6.46 MB 6.46 MB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.lib 89.82 KB 89.82 KB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.pdb 26.54 MB 26.54 MB 0% (0 B) 👌
/libdatadog-x86-windows/release/static/datadog_profiling_ffi.lib 46.82 MB 46.82 MB -0% (-200 B) 👌
x86_64-alpine-linux-musl
Artifact Baseline Commit Change
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.a 76.96 MB 76.97 MB +0% (+4.14 KB) 👌
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.so 8.81 MB 8.81 MB 0% (0 B) 👌
x86_64-unknown-linux-gnu
Artifact Baseline Commit Change
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.a 92.37 MB 92.38 MB +0% (+3.27 KB) 👌
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.so 10.72 MB 10.72 MB +.03% (+3.79 KB) 🔍

@cataphract
cataphract requested a review from a team as a code owner July 10, 2026 20:12
@cataphract
cataphract force-pushed the glopes/otel-process-ctx-macos-win branch from f1169de to f5eccae Compare July 11, 2026 18:57
@ivoanjo

ivoanjo commented Jul 13, 2026

Copy link
Copy Markdown
Member

This is very cool, and I definitely want to give it a pass ASAP. Having said that, I'd like to request a change in the way we refer to this.

Specifically, at this time, we're not trying or proposing upstream any way of using the OTel process context and thread context specifications on macOS and Windows.

Thus I humbly request:

  • Let's drop the "otel" from the naming we use when we refer to macOS or Windows -- let's just call it "process context" and "thread context"
  • In any files that include macOS or Windows support, let's explicitly document that macOS and Windows support is not part of the spec and is an experimental datadog extension

My intent here is to give us fully the space to experiment and validate -- if this goes really well it will be great to fold it into the spec; but not confuse outside-Datadog folks, since the specs explicitly call out process context and thread context as being Linux-specific mechanisms for the time being.

@ivoanjo

ivoanjo commented Jul 13, 2026

Copy link
Copy Markdown
Member

Furthermore (forgot to add to the above) -- not being yet tied to the spec gives us flexibility to go "Oh actually we want to do it another different way on Windows" (or macOS) while we're experimenting, whereas if we were folding this right away into the spec it'd be weird to flip-flop as we experiment.

@cataphract cataphract changed the title feat(library-config): support OTel process contexts on macOS and Windows feat(library-config): support process contexts on macOS and Windows Jul 13, 2026
@cataphract

Copy link
Copy Markdown
Contributor Author

OK, I've renamed the PR and added a few comments. Note that this follows the specification to the extent that it can be followed. I's only the part that can only be implemented on Linux, the discovery mechanism, that differs. All the rest -- the data itself, how it's laid out, the update protocol, follows the spec.

@ivoanjo

ivoanjo commented Jul 14, 2026

Copy link
Copy Markdown
Member

At this point, this PR is too big -- I humbly request that we break it down into smaller steps. E.g. first one with all changes for Linux, and then separate windows and macOS support.

With so many changes it'll be a lot of work to figure out what's only moving and what's actually changing.

@cataphract
cataphract force-pushed the glopes/otel-process-ctx-macos-win branch from 9bd8dd1 to 011c154 Compare July 14, 2026 14:35
@cataphract

Copy link
Copy Markdown
Contributor Author

@ivoanjo I've split it into 3 commits. The first is just moves code around and has no change in the linux functionality. The second contains the updates I did to the linux implementation. Finally, the third adds windows and mac os. Hope this helps.

@ivoanjo

ivoanjo commented Jul 14, 2026

Copy link
Copy Markdown
Member

I'd really like separate PRs -- I feel like we're creating a huge PR that will take a bunch of time to deliver, whereas small PRs have small steps that we can agree on and move quickly on.

@ivoanjo

ivoanjo commented Jul 14, 2026

Copy link
Copy Markdown
Member

To be clear, I am aware that split PRs are a pain for the submitter -- yet that's exactly what I'm asking for here, I'm asking you to help us ship this fast by making it easier on the reviewers to move fast on it.

@cataphract

Copy link
Copy Markdown
Contributor Author

As a reviewer, I'd rather have the three commits in one PR, since the reorganization and abstractions make more sense in light in the mac os/windows implementations in the later commits. But to each his own

@cataphract
cataphract force-pushed the glopes/otel-process-ctx-macos-win branch from 011c154 to 1a04bb0 Compare July 14, 2026 17:56
@github-actions github-actions Bot removed the ci-build label Jul 14, 2026
@cataphract cataphract changed the title feat(library-config): support process contexts on macOS and Windows refactor(library-config): reorganize Linux process context Jul 14, 2026
@cataphract
cataphract force-pushed the glopes/otel-process-ctx-macos-win branch from 1a04bb0 to e6af895 Compare July 14, 2026 18:20
@cataphract
cataphract force-pushed the glopes/otel-process-ctx-macos-win branch from e6af895 to 89a636d Compare July 14, 2026 23:03
@@ -0,0 +1,240 @@
// Copyright 2026-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

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.

Nit: add a short module description

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.

Added module documentation explaining the fault-safe Unix pipe mechanism and how inaccessible source memory becomes WouldBlock.

})
}

fn copy(&self, addr: *const u8, len: usize) -> Result<Vec<u8>, PipeCopyError> {

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.

I think this contract (copy won't fail even for unmapped addresses or unitialized memory) should be specified at the trait level, in the documentation of copy. It's not obvious for anyone who hasn't sufficient context (as demonstrated by Daniel's question)

}

#[cfg(target_os = "linux")]
fn create_pipe() -> io::Result<(OwnedFd, OwnedFd, usize)> {

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.

Should the return type be a proper struct instead of a triplet?

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.

Yes. create_pipe() now constructs and returns the existing CopyPipe type directly instead of returning a positional triplet.


// Re-exported for backwards compatibility.
#[cfg(feature = "process-context-writer")]
#[deprecated(note = "use libdd_library_config::otel_process_ctx directly")]

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.

I would be inclined to ignore backward compatibility and just get rid of those. The process context implementation is rather recent, there's little to no code that uses the ProcessContext API directly outside of libadatadog to my knowledge, and even then, we control that code. Any opinion @ivoanjo ?

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.

Yeap +1 on this; I believe most writers are going through libdd-library-config, except maybe node, and we can update that one as well, so I think it's fine to clean these up.

Double so for the reader -- since it's basically new I'm pretty sure nobody's using it yet

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.

I removed the deprecated Linux compatibility module and re-exports. MAPPING_NAME remains because the Linux implementation still uses it.


pub(super) trait ProcessMemoryCopy: Sized {
fn new() -> io::Result<Self>;
fn copy(&self, addr: *const u8, len: usize) -> Result<Vec<u8>, PipeCopyError>;

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.

Re copy: the contract should be clearly specified here.

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.

Done. The trait now documents the caller’s lack of pointer-validity obligations, the exact WouldBlock error for inaccessible memory, the all-or-error result, and the lack of snapshot atomicity.

}

#[derive(Debug)]
pub(super) struct PipeCopyError {

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.

Isn't this a bit Linux-specific (the pipe implementation)? Maybe it's just a minor naming issue, and this should be MemoryCopyError or sth. But it feels like Linux implementation is leaking a bit here.

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.

All three supported implementations—Linux, macOS, and Windows—use a pipe, so the pipe terminology is intentional rather than Linux-specific. I kept PipeCopyError.

pub struct ProcessContextSelfReader {
pid: u32,
pub(in crate::otel_process_ctx) header_ptr: NonNull<u8>,
pipe: Cell<Option<PlatformCopyPipe>>,

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.

Isn't this linux-specific as well? I think PlatformCopyPipe is only defined on Linux, right? In general I would expect this module to have a very simple copy interface -- basically ProcessMemoryCopy that guarantees fault-free copy, without talking about pipes or whatnot, which is an implementation detail.

Then either ProcessContextSelfReader should be parametrized by a Copier: ProcessMemoryCopy, or if that's too heavy, define a ProcessMemoryCopyImpl for each platform in a dedicated module, and import it unconditionally here.

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.

macOS and Windows also use pipes, so PlatformCopyPipe describes the common mechanism across all supported platforms. I kept the current non-generic reader structure.

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.

TBF, I still find that at this level of abstraction on the reader code, whether we use a pipe or process_read_vm or whatnot is an implementation detail at this level of abstraction, and that what matters is that it's a fault-less copy. It could be switched for a different mechanism in the future, for example. But it's really not important


// SAFETY: the reader owns its copy pipe and only stores a process-global address. Cell keeps the
// type !Sync, so the cached pipe cannot be used concurrently through shared references.
unsafe impl Send for ProcessContextSelfReader {}

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.

Same, for modularity purpose, we should probably require ProcessMemoryCopy to be Send, moving the burden to the platform-specific implementation. Maybe the implementation on Windows wouldn't use pipe but a totally different mechanism; it's hard for ProcessContextSelfReader to justify in advance that all implementations for ProcessMemoryCopy in the future will be Send.

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.

ProcessMemoryCopy now requires Send, making each platform pipe prove that property. I also updated the reader’s unsafe Send justification so it only needs to account for the process-global NonNull address.


[features]
otel-thread-ctx = []
default = ["process-context-reader", "process-context-writer"]

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.

I would not make process-context-reader the default. For now it seems quite specific to the PHP profiler.

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.

The reader intentionally remains enabled by default in this first PR because this commit promises to preserve main’s behavior. It is removed from the defaults in the next stacked PR.

@@ -0,0 +1,268 @@
// Copyright 2026-Present Datadog, Inc. https://www.datadoghq.com/

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.

I got quite confused by the fact that there's a top-level reader.rs file, but also submodules in a different reader/xxx subdirectory. Same for writer. Would you have anything against moving reader.s and writer.rs to respective mod.rs files in the corresponding subdirectories? We would have everything writer-related and reader-related at the same place.

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.

I kept the current layout. reader.rs with children under reader/ is the modern idiomatic Rust layout; reader/mod.rs is the older, still-supported form.

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.

Do you have reference for this being the recommended way? I'm genuinely curious. Though maybe I'm just... old 😅 (

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.

Nevermind I found it in the doc. TIL.

@cataphract cataphract Jul 16, 2026

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.

The Rust Book's "Alternate File Paths" section explicitly calls foo/mod.rs the older style and presents foo.rs with foo/bar.rs as the idiomatic layout. The Rust 2018 Edition Guide also explains the move away from mod.rs -- to avoid having many indistinguishable mod.rs tabs open.

@yannham yannham 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.

(Sorry, I meant to approve - none of the comments above are blocking)

@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.

👍 LGTM -- I +1 Yann's notes and I've left some tiny notes of my own but this is basically good to go!

);
}

/// The only end-to-end test with `ProcessContextSelfReader`

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.

Minor: The "The only" part of this comment looks like it can be made out-of-date very easily the next time someone adds another test; maybe simply to only "End to end test"?

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.

Done. The comment now describes it simply as an end-to-end test using ProcessContextSelfReader, without making a uniqueness claim.

fn publish_read_update_read_and_unpublish() {
fn context(service_name: &str) -> ProcessContext {
ProcessContext {
resource: None,

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.

Minor: Maybe put a few things in the resource? An empty resource is actually something we don't expect will happen very often, and thus putting stuff here would make the end-to-end test a bit more realistic

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.

Done. The fixture now includes service.name and telemetry.sdk.language resource attributes, plus datadog.process_tags as an extra attribute.

"mmap failed"
)?;

// We (implicitly) close the file descriptor right away, but this ok

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.

Minor: I know this comment already existed before, yet on a second read I think we can make it stronger even -- the spec does say that the fd is to be closed, it's not a "happy accident" that we do so here.

Suggested change
// We (implicitly) close the file descriptor right away, but this ok
// We (implicitly) close the file descriptor right away on purpose

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.

Updated. The comment now says that the descriptor is closed immediately on purpose and explicitly notes that this does not invalidate the mapping.


// Re-exported for backwards compatibility.
#[cfg(feature = "process-context-writer")]
#[deprecated(note = "use libdd_library_config::otel_process_ctx directly")]

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.

Yeap +1 on this; I believe most writers are going through libdd-library-config, except maybe node, and we can update that one as well, so I think it's fine to clean these up.

Double so for the reader -- since it's basically new I'm pretty sure nobody's using it yet

Comment on lines -6 to -15
//! By design, this reader emulates an out-of-process reader: it does not take the publisher's
//! mutex or hold any guard that prevents the mapping from being unpublished, the header from being
//! rewritten, or the payload allocation from being replaced while it copies memory. The
//! seqlock-style timestamp checks and fences only let it detect concurrent publication or update;
//! unpublish can make the memory disappear entirely.
//!
//! To handle this safely, instead of dereferencing process memory directly, we ask the kernel to
//! copy those bytes into a pipe and then read the pipe back. This gives us the same failure mode an
//! out-of-process reader has: if memory is unmapped or invalid, the copy fails instead of
//! segfaulting the process.

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.

Minor: This comment was kinda nice -- it explained why the reader looks like it does (it's weird), and what constraints we were working with. I think it's worth finding it a new home.

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.

Restored at the top of reader.rs, adjusted to distinguish the shared mapping-lifetime constraints from the platform-specific same-process copy mechanism. The Unix pipe details are documented separately in copy_pipe_unix.rs.

@cataphract

Copy link
Copy Markdown
Contributor Author

/merge

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351 Bot commented Jul 16, 2026

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-07-16 15:47:59 UTC ℹ️ Start processing command /merge


2026-07-16 15:48:07 UTC ℹ️ MergeQueue: waiting for PR to be ready

This pull request is not mergeable according to GitHub. Common reasons include pending required checks, missing approvals, or merge conflicts — but it could also be blocked by other repository rules or settings.
It will be added to the queue as soon as checks pass and/or get approvals. View in MergeQueue UI.
Note: if you pushed new commits since the last approval, you may need additional approval.
You can remove it from the waiting list with /remove command.


2026-07-16 15:59:10 UTC ℹ️ MergeQueue: merge request added to the queue

The expected merge time in main is approximately 1h (p90).


2026-07-16 16:46:35 UTC ℹ️ MergeQueue: This merge request was merged

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.

4 participants