Skip to content

Commit efe4fa8

Browse files
khanayan123claude
andcommitted
Propagate root session ID via cross-platform SHM carrier
Replaces exclusive env var propagation with a shared memory carrier: - Linux: memfd_create("dd-session-ids") without MFD_CLOEXEC; child discovers via /proc/self/fd/ scan - macOS: shm_open("/dd-session-ids-<pid>"); child opens by getppid() - Windows: CreateFileMapping("Local\dd-session-ids-<pid>"); child opens by Toolhelp32-resolved parent PID Tracer creates the carrier on startup and keeps it alive as a member. finalize_config() prefers SHM on read, falls back to the env var for interop with SDKs that still use _DD_ROOT_CPP_SESSION_ID. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0e65a27 commit efe4fa8

7 files changed

Lines changed: 240 additions & 2 deletions

File tree

include/datadog/tracer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class TraceSampler;
3333
class SpanSampler;
3434
class IDGenerator;
3535
class InMemoryFile;
36+
class SessionCarrier;
3637

3738
class Tracer {
3839
std::shared_ptr<Logger> logger_;
@@ -51,6 +52,7 @@ class Tracer {
5152
// read to determine if the process is instrumented with a tracer and to
5253
// retrieve relevant tracing information.
5354
std::shared_ptr<InMemoryFile> metadata_file_;
55+
std::shared_ptr<SessionCarrier> session_carrier_;
5456
Baggage::Options baggage_opts_;
5557
bool baggage_injection_enabled_;
5658
bool baggage_extraction_enabled_;

src/datadog/platform_util.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,30 @@
2424
namespace datadog {
2525
namespace tracing {
2626

27+
// SessionCarrier: cross-platform inheritable shared memory segment for
28+
// propagating the root session ID to exec'd child processes.
29+
//
30+
// The parent creates a carrier before exec()'ing a child. The child calls
31+
// read_inherited_session_id() at startup to recover the root session ID.
32+
class SessionCarrier final {
33+
void* handle_ = nullptr;
34+
35+
explicit SessionCarrier(void* handle);
36+
37+
public:
38+
SessionCarrier(const SessionCarrier&) = delete;
39+
SessionCarrier& operator=(const SessionCarrier&) = delete;
40+
SessionCarrier(SessionCarrier&&) noexcept;
41+
SessionCarrier& operator=(SessionCarrier&&) noexcept;
42+
~SessionCarrier();
43+
44+
static Expected<SessionCarrier> create(StringView root_session_id);
45+
};
46+
47+
// Returns the root session ID inherited from a parent tracer via shared
48+
// memory. Returns nullopt if no carrier is found.
49+
Optional<std::string> read_inherited_session_id();
50+
2751
// A wrapper around an in-memory file descriptor.
2852
//
2953
// This class provides a simple interface to create an in-memory file, write

src/datadog/platform_util_darwin.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
#include <fcntl.h>
12
#include <libproc.h>
23
#include <pthread.h>
4+
#include <sys/mman.h>
35
#include <sys/sysctl.h>
46
#include <sys/types.h>
57
#include <sys/utsname.h>
@@ -9,6 +11,7 @@
911
#include <cstdint>
1012
#include <fstream>
1113
#include <regex>
14+
#include <string>
1215

1316
#include "platform_util.h"
1417

@@ -79,6 +82,56 @@ int at_fork_in_child(void (*on_fork)()) {
7982
/*in child*/ on_fork);
8083
}
8184

85+
SessionCarrier::SessionCarrier(void* handle) : handle_(handle) {}
86+
87+
SessionCarrier::SessionCarrier(SessionCarrier&& rhs) noexcept {
88+
std::swap(handle_, rhs.handle_);
89+
}
90+
91+
SessionCarrier& SessionCarrier::operator=(SessionCarrier&& rhs) noexcept {
92+
std::swap(handle_, rhs.handle_);
93+
return *this;
94+
}
95+
96+
SessionCarrier::~SessionCarrier() {
97+
if (!handle_) return;
98+
auto* name = static_cast<std::string*>(handle_);
99+
shm_unlink(name->c_str());
100+
delete name;
101+
}
102+
103+
Expected<SessionCarrier> SessionCarrier::create(StringView root_session_id) {
104+
std::string name = "/dd-session-ids-" + std::to_string(::getpid());
105+
shm_unlink(name.c_str()); // remove any stale segment
106+
int fd = shm_open(name.c_str(), O_CREAT | O_RDWR, 0600);
107+
if (fd == -1) {
108+
return Error{Error::Code::OTHER, "shm_open failed"};
109+
}
110+
const std::string data{root_session_id};
111+
ftruncate(fd, static_cast<off_t>(data.size()));
112+
::write(fd, data.data(), data.size());
113+
close(fd);
114+
return SessionCarrier(new std::string{name});
115+
}
116+
117+
Optional<std::string> read_inherited_session_id() {
118+
std::string name = "/dd-session-ids-" + std::to_string(::getppid());
119+
int fd = shm_open(name.c_str(), O_RDONLY, 0);
120+
if (fd == -1) return nullopt;
121+
struct stat st;
122+
if (fstat(fd, &st) != 0 || st.st_size == 0) {
123+
close(fd);
124+
return nullopt;
125+
}
126+
void* mem = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
127+
close(fd);
128+
if (mem == MAP_FAILED) return nullopt;
129+
std::string result(static_cast<const char*>(mem),
130+
static_cast<size_t>(st.st_size));
131+
munmap(mem, st.st_size);
132+
return result;
133+
}
134+
82135
InMemoryFile::InMemoryFile(void* handle) : handle_(handle) {}
83136

84137
InMemoryFile::~InMemoryFile() {}

src/datadog/platform_util_unix.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <dirent.h>
12
#include <errno.h>
23
#include <fcntl.h>
34
#include <pthread.h>
@@ -9,7 +10,10 @@
910
#include <unistd.h>
1011

1112
#include <cassert>
13+
#include <cstdio>
1214
#include <cstdint>
15+
#include <cstdlib>
16+
#include <cstring>
1317
#include <fstream>
1418
#include <regex>
1519
#include <string>
@@ -93,6 +97,70 @@ int at_fork_in_child(void (*on_fork)()) {
9397
/*in child*/ on_fork);
9498
}
9599

100+
SessionCarrier::SessionCarrier(void* handle) : handle_(handle) {}
101+
102+
SessionCarrier::SessionCarrier(SessionCarrier&& rhs) noexcept {
103+
std::swap(handle_, rhs.handle_);
104+
}
105+
106+
SessionCarrier& SessionCarrier::operator=(SessionCarrier&& rhs) noexcept {
107+
std::swap(handle_, rhs.handle_);
108+
return *this;
109+
}
110+
111+
SessionCarrier::~SessionCarrier() {
112+
if (!handle_) return;
113+
int* fd_ptr = static_cast<int*>(handle_);
114+
close(*fd_ptr);
115+
delete fd_ptr;
116+
}
117+
118+
Expected<SessionCarrier> SessionCarrier::create(StringView root_session_id) {
119+
// Omit MFD_CLOEXEC so the fd survives exec() and is visible to children.
120+
int fd = memfd_create("dd-session-ids", MFD_ALLOW_SEALING);
121+
if (fd == -1) {
122+
return Error{Error::Code::OTHER,
123+
"memfd_create failed: " + std::to_string(errno)};
124+
}
125+
const std::string data{root_session_id};
126+
if (::write(fd, data.data(), data.size()) !=
127+
static_cast<ssize_t>(data.size())) {
128+
close(fd);
129+
return Error{Error::Code::OTHER,
130+
"SessionCarrier write failed: " + std::to_string(errno)};
131+
}
132+
fcntl(fd, F_ADD_SEALS,
133+
F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL);
134+
return SessionCarrier(new int{fd});
135+
}
136+
137+
Optional<std::string> read_inherited_session_id() {
138+
DIR* dir = opendir("/proc/self/fd");
139+
if (!dir) return nullopt;
140+
struct dirent* entry;
141+
while ((entry = readdir(dir)) != nullptr) {
142+
int fd = std::atoi(entry->d_name);
143+
if (fd <= 2) continue;
144+
char link_path[64];
145+
std::snprintf(link_path, sizeof(link_path), "/proc/self/fd/%d", fd);
146+
char target[256];
147+
ssize_t len = readlink(link_path, target, sizeof(target) - 1);
148+
if (len <= 0) continue;
149+
target[len] = '\0';
150+
if (std::strncmp(target, "memfd:dd-session-ids", 20) != 0) continue;
151+
struct stat st;
152+
if (fstat(fd, &st) != 0 || st.st_size == 0) continue;
153+
void* mem = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
154+
if (mem == MAP_FAILED) continue;
155+
std::string result(static_cast<const char*>(mem), st.st_size);
156+
munmap(mem, st.st_size);
157+
closedir(dir);
158+
return result;
159+
}
160+
closedir(dir);
161+
return nullopt;
162+
}
163+
96164
InMemoryFile::InMemoryFile(void* handle) : handle_(handle) {}
97165

98166
InMemoryFile::InMemoryFile(InMemoryFile&& rhs) {

src/datadog/platform_util_windows.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <windows.h>
33
// clang-format on
44
#include <processthreadsapi.h>
5+
#include <tlhelp32.h>
56
#include <winsock.h>
67

78
#include <cassert>
@@ -141,6 +142,86 @@ int at_fork_in_child(void (*on_fork)()) {
141142
return 0;
142143
}
143144

145+
namespace {
146+
147+
DWORD get_parent_process_id() {
148+
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
149+
if (snapshot == INVALID_HANDLE_VALUE) return 0;
150+
PROCESSENTRY32 pe;
151+
pe.dwSize = sizeof(pe);
152+
DWORD own_pid = GetCurrentProcessId();
153+
DWORD ppid = 0;
154+
if (Process32First(snapshot, &pe)) {
155+
do {
156+
if (pe.th32ProcessID == own_pid) {
157+
ppid = pe.th32ParentProcessID;
158+
break;
159+
}
160+
} while (Process32Next(snapshot, &pe));
161+
}
162+
CloseHandle(snapshot);
163+
return ppid;
164+
}
165+
166+
} // namespace
167+
168+
SessionCarrier::SessionCarrier(void* handle) : handle_(handle) {}
169+
170+
SessionCarrier::SessionCarrier(SessionCarrier&& rhs) noexcept {
171+
std::swap(handle_, rhs.handle_);
172+
}
173+
174+
SessionCarrier& SessionCarrier::operator=(SessionCarrier&& rhs) noexcept {
175+
std::swap(handle_, rhs.handle_);
176+
return *this;
177+
}
178+
179+
SessionCarrier::~SessionCarrier() {
180+
if (!handle_) return;
181+
auto* h = static_cast<HANDLE*>(handle_);
182+
CloseHandle(*h);
183+
delete h;
184+
}
185+
186+
Expected<SessionCarrier> SessionCarrier::create(StringView root_session_id) {
187+
std::string name =
188+
"Local\\dd-session-ids-" + std::to_string(GetCurrentProcessId());
189+
const std::string data{root_session_id};
190+
DWORD size = static_cast<DWORD>(data.size());
191+
HANDLE h = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE,
192+
0, size, name.c_str());
193+
if (!h) {
194+
return Error{Error::Code::OTHER, "CreateFileMapping failed"};
195+
}
196+
void* view = MapViewOfFile(h, FILE_MAP_WRITE, 0, 0, size);
197+
if (view) {
198+
std::memcpy(view, data.data(), data.size());
199+
UnmapViewOfFile(view);
200+
}
201+
return SessionCarrier(new HANDLE{h});
202+
}
203+
204+
Optional<std::string> read_inherited_session_id() {
205+
DWORD ppid = get_parent_process_id();
206+
if (!ppid) return nullopt;
207+
std::string name = "Local\\dd-session-ids-" + std::to_string(ppid);
208+
HANDLE h = OpenFileMappingA(FILE_MAP_READ, FALSE, name.c_str());
209+
if (!h) return nullopt;
210+
void* view = MapViewOfFile(h, FILE_MAP_READ, 0, 0, 0);
211+
if (!view) {
212+
CloseHandle(h);
213+
return nullopt;
214+
}
215+
MEMORY_BASIC_INFORMATION mbi;
216+
VirtualQuery(view, &mbi, sizeof(mbi));
217+
const char* data = static_cast<const char*>(view);
218+
std::string result(data, strnlen(data, mbi.RegionSize));
219+
UnmapViewOfFile(view);
220+
CloseHandle(h);
221+
if (result.empty()) return nullopt;
222+
return result;
223+
}
224+
144225
InMemoryFile::InMemoryFile(void* handle) : handle_(handle) {}
145226

146227
InMemoryFile::InMemoryFile(InMemoryFile&& rhs) {

src/datadog/tracer.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ Tracer::Tracer(const FinalizedTracerConfig& config,
6666
baggage_extraction_enabled_(false),
6767
tracing_enabled_(config.tracing_enabled),
6868
resource_renaming_mode_(config.resource_renaming_mode) {
69+
// Create a shared memory carrier so exec'd children can inherit the root
70+
// session ID without relying solely on environment variables.
71+
if (auto carrier = SessionCarrier::create(signature_.root_session_id)) {
72+
session_carrier_ = std::make_shared<SessionCarrier>(std::move(*carrier));
73+
}
74+
// Also set the env var for interop with SDKs that still use it.
6975
environment::set(environment::_DD_ROOT_CPP_SESSION_ID,
7076
signature_.root_session_id);
7177

src/datadog/tracer_config.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,12 @@ Expected<FinalizedTracerConfig> finalize_config(const TracerConfig &user_config,
407407
final_config.runtime_id = user_config.runtime_id;
408408
}
409409

410-
if (auto val = lookup(environment::_DD_ROOT_CPP_SESSION_ID)) {
411-
final_config.root_session_id = std::string{*val};
410+
// Prefer shared memory carrier (set by a C++ parent); fall back to the
411+
// environment variable for interop with SDKs that still use env vars.
412+
if (auto shm = read_inherited_session_id()) {
413+
final_config.root_session_id = std::move(*shm);
414+
} else if (auto env = lookup(environment::_DD_ROOT_CPP_SESSION_ID)) {
415+
final_config.root_session_id = std::string{*env};
412416
}
413417

414418
final_config.process_tags = user_config.process_tags;

0 commit comments

Comments
 (0)