Skip to content

Commit ce1550f

Browse files
committed
Avoid a dedicated socket for crashtracker
Update for libdatadog changes. Signed-off-by: Bob Weinand <bob.weinand@datadoghq.com> Help distinguishing debug logs by url for parallel requests Signed-off-by: Bob Weinand <bob.weinand@datadoghq.com>
1 parent 095f78a commit ce1550f

13 files changed

Lines changed: 304 additions & 158 deletions

File tree

Cargo.lock

Lines changed: 82 additions & 45 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cbindgen.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ rename_types = "PascalCase"
2424
"ZendString" = "_zend_string"
2525
"FILE" = "FILE"
2626
"EndpointConfig" = "ddog_crasht_EndpointConfig"
27+
"Metadata" = "ddog_crasht_Metadata"
2728
"MaybeOwnedZendString" = "_zend_string *"
2829

2930
[enum]

components-rs/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ libdd-tinybytes = { path = "../libdatadog/libdd-tinybytes" }
2424
libdd-trace-utils = { path = "../libdatadog/libdd-trace-utils" }
2525
libdd-trace-stats = { path = "../libdatadog/libdd-trace-stats" }
2626
libdd-trace-protobuf = { path = "../libdatadog/libdd-trace-protobuf" }
27+
libdd-crashtracker = { path = "../libdatadog/libdd-crashtracker", default-features = false, features = ["collector"] }
2728
libdd-crashtracker-ffi = { path = "../libdatadog/libdd-crashtracker-ffi", default-features = false, features = ["collector"] }
2829
libdd-library-config-ffi = { path = "../libdatadog/libdd-library-config-ffi", default-features = false }
2930
spawn_worker = { path = "../libdatadog/spawn_worker" }

components-rs/common.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@
5252

5353
typedef struct ddog_Endpoint ddog_Endpoint;
5454

55-
typedef struct ddog_Tag ddog_Tag;
56-
5755
/**
5856
* Holds the raw parts of a Rust Vec; it should only be created from Rust,
5957
* never from C.
@@ -412,8 +410,11 @@ typedef enum ddog_RemoteConfigCapabilities {
412410
DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_ENABLE_LIVE_DEBUGGING = 41,
413411
DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_DD_MULTICONFIG = 42,
414412
DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_TRACE_TAGGING_RULES = 43,
413+
DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_EXTENDED_DATA_COLLECTION = 44,
415414
DDOG_REMOTE_CONFIG_CAPABILITIES_APM_TRACING_MULTICONFIG = 45,
416415
DDOG_REMOTE_CONFIG_CAPABILITIES_FFE_FLAG_CONFIGURATION_RULES = 46,
416+
DDOG_REMOTE_CONFIG_CAPABILITIES_DD_DATA_STREAMS_TRANSACTION_EXTRACTORS = 47,
417+
DDOG_REMOTE_CONFIG_CAPABILITIES_LLM_OBS_ACTIVATION = 48,
417418
} ddog_RemoteConfigCapabilities;
418419

419420
typedef enum ddog_RemoteConfigProduct {
@@ -426,6 +427,7 @@ typedef enum ddog_RemoteConfigProduct {
426427
DDOG_REMOTE_CONFIG_PRODUCT_ASM_FEATURES,
427428
DDOG_REMOTE_CONFIG_PRODUCT_FFE_FLAGS,
428429
DDOG_REMOTE_CONFIG_PRODUCT_LIVE_DEBUGGER,
430+
DDOG_REMOTE_CONFIG_PRODUCT_LIVE_DEBUGGER_SYMBOL_DB,
429431
} ddog_RemoteConfigProduct;
430432

431433
typedef enum ddog_SpanProbeTarget {
@@ -1110,9 +1112,10 @@ typedef struct ddog_TelemetryWorkerBuilder ddog_TelemetryWorkerBuilder;
11101112
* The worker won't send data to the agent until you call `TelemetryWorkerHandle::send_start`
11111113
*
11121114
* To stop the worker, call `TelemetryWorkerHandle::send_stop` which trigger flush asynchronously
1113-
* then `TelemetryWorkerHandle::wait_for_shutdown`
1115+
* then `TelemetryWorkerHandle::wait_for_shutdown` (native only — wasm callers rely on the
1116+
* SharedRuntime worker JoinHandle instead).
11141117
*/
1115-
typedef struct ddog_TelemetryWorkerHandle ddog_TelemetryWorkerHandle;
1118+
typedef struct ddog_TelemetryWorkerHandle_NativeCapabilities ddog_TelemetryWorkerHandle_NativeCapabilities;
11161119

11171120
typedef enum ddog_Option_U64_Tag {
11181121
DDOG_OPTION_U64_SOME_U64,
@@ -1128,6 +1131,12 @@ typedef struct ddog_Option_U64 {
11281131
};
11291132
} ddog_Option_U64;
11301133

1134+
/**
1135+
* FFI-facing alias: the C ABI surface is native-only, so the worker handle is
1136+
* always pinned to [`NativeCapabilities`].
1137+
*/
1138+
typedef struct ddog_TelemetryWorkerHandle_NativeCapabilities ddog_TelemetryWorkerHandle;
1139+
11311140
typedef enum ddog_Option_Bool_Tag {
11321141
DDOG_OPTION_BOOL_SOME_BOOL,
11331142
DDOG_OPTION_BOOL_NONE_BOOL,

components-rs/datadog.h

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,26 @@ struct ddog_Endpoint *datadog_otel_metrics_endpoint_from_url(ddog_CharSlice url)
5555

5656
struct ddog_Endpoint *datadog_otel_metrics_endpoint_from_agent_url(ddog_CharSlice url);
5757

58-
void datadog_endpoint_as_crashtracker_config(const struct ddog_Endpoint *endpoint,
59-
void (*callback)(ddog_crasht_EndpointConfig, void*),
60-
void *userdata);
58+
/**
59+
* Initialize crashtracking, selecting the receiver strategy for this process:
60+
* - Linux, sidecar host (`master_pid == getpid()`): the in-process thread-mode sidecar can't
61+
* serve its own crash, so spawn a fork+exec subprocess receiver (like the standalone daemon),
62+
* resolving frames there since a crashing process can't reliably symbolize itself.
63+
* - Linux, worker/collector: connect to the sidecar IPC socket and upgrade it to a crashtracker
64+
* receiver on crash (`SOCK_SEQPACKET` + `enter_crashtracker_receiver`), streaming the report
65+
* over that single socket and resolving frames in-process.
66+
* - other unix (macOS): no sidecar upgrade; the default connector reaches the socket path.
67+
*
68+
* `master_pid` is the thread-mode master listener PID (0 if none): it keys the IPC socket and, on
69+
* Linux, distinguishes the host from a worker.
70+
*
71+
* # Safety
72+
* `endpoint` must point to a valid `Endpoint`; `metadata`'s borrowed strings/tags must outlive the
73+
* call (they are copied into owned storage before it returns).
74+
*/
75+
ddog_MaybeError datadog_crashtracker_init(const struct ddog_Endpoint *endpoint,
76+
ddog_crasht_Metadata metadata,
77+
int32_t master_pid);
6178

6279
ddog_Configurator *ddog_library_configurator_new_dummy(bool debug_logs, ddog_CharSlice language);
6380

@@ -189,7 +206,12 @@ void ddog_rshutdown_remote_config(struct ddog_RemoteConfigState *remote_config);
189206

190207
void ddog_shutdown_remote_config(struct ddog_RemoteConfigState*);
191208

192-
void ddog_drop_probe(struct ddog_Probe probe);
209+
/**
210+
* Free the FFI-owned allocations in a `Probe` (the `tags` vec and the nested
211+
* span-decoration / log allocations) by consuming it; borrowed `CharSlice`s are
212+
* left untouched. Called from `dd_probe_dtor` when a probe is uninstalled.
213+
*/
214+
void ddog_drop_probe(struct ddog_Probe);
193215

194216
void ddog_log_debugger_data(const struct ddog_Vec_DebuggerPayload *payloads);
195217

components-rs/lib.rs

Lines changed: 136 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use std::ffi::{c_char, OsStr};
2222
use std::path::Path;
2323
use std::ptr::null_mut;
2424
use uuid::Uuid;
25-
25+
use datadog_sidecar::crashtracker::crashtracker_receiver_request_bytes;
2626
pub use libdd_crashtracker_ffi::*;
2727
pub use libdd_library_config_ffi::*;
2828
pub use datadog_sidecar_ffi::*;
@@ -196,26 +196,144 @@ pub unsafe extern "C" fn datadog_otel_metrics_endpoint_from_agent_url(url: CharS
196196
}
197197
}
198198

199-
#[no_mangle]
199+
/// Initialize crashtracking, selecting the receiver strategy for this process:
200+
/// - Linux, sidecar host (`master_pid == getpid()`): the in-process thread-mode sidecar can't
201+
/// serve its own crash, so spawn a fork+exec subprocess receiver (like the standalone daemon),
202+
/// resolving frames there since a crashing process can't reliably symbolize itself.
203+
/// - Linux, worker/collector: connect to the sidecar IPC socket and upgrade it to a crashtracker
204+
/// receiver on crash (`SOCK_SEQPACKET` + `enter_crashtracker_receiver`), streaming the report
205+
/// over that single socket and resolving frames in-process.
206+
/// - other unix (macOS): no sidecar upgrade; the default connector reaches the socket path.
207+
///
208+
/// `master_pid` is the thread-mode master listener PID (0 if none): it keys the IPC socket and, on
209+
/// Linux, distinguishes the host from a worker.
210+
///
211+
/// # Safety
212+
/// `endpoint` must point to a valid `Endpoint`; `metadata`'s borrowed strings/tags must outlive the
213+
/// call (they are copied into owned storage before it returns).
200214
#[cfg(unix)]
201-
pub unsafe extern "C" fn datadog_endpoint_as_crashtracker_config(
215+
#[no_mangle]
216+
#[allow(clippy::missing_safety_doc)]
217+
pub unsafe extern "C" fn datadog_crashtracker_init(
202218
endpoint: &Endpoint,
203-
callback: unsafe extern "C" fn(EndpointConfig<'_>, *mut std::ffi::c_void),
204-
userdata: *mut std::ffi::c_void,
205-
) {
206-
let url_str = endpoint.url.to_string();
207-
unsafe {
208-
callback(
209-
EndpointConfig {
210-
url: CharSlice::from(url_str.as_str()),
211-
api_key: CharSlice::from(endpoint.api_key.as_deref().unwrap_or("")),
212-
test_token: CharSlice::from(endpoint.test_token.as_deref().unwrap_or("")),
213-
timeout: endpoint.timeout_ms,
214-
use_system_resolver: endpoint.use_system_resolver,
215-
},
216-
userdata,
217-
);
219+
metadata: Metadata,
220+
master_pid: i32,
221+
) -> MaybeError {
222+
use libdd_crashtracker::{CrashtrackerConfiguration, StacktraceCollection};
223+
224+
let result = (|| -> anyhow::Result<()> {
225+
let metadata: libdd_crashtracker::Metadata = metadata.try_into()?;
226+
227+
let mut builder = CrashtrackerConfiguration::builder()
228+
.collect_all_threads(true)
229+
.timeout(std::time::Duration::from_millis(5000))
230+
.endpoint_use_system_resolver(endpoint.use_system_resolver)
231+
.endpoint_url(&endpoint.url.to_string());
232+
if let Some(api_key) = endpoint.api_key.as_deref() {
233+
builder = builder.endpoint_api_key(api_key);
234+
}
235+
if let Some(test_token) = endpoint.test_token.as_deref() {
236+
builder = builder.endpoint_test_token(test_token);
237+
}
238+
if endpoint.timeout_ms != 0 {
239+
builder = builder.endpoint_timeout_ms(endpoint.timeout_ms);
240+
}
241+
242+
#[cfg(target_os = "linux")]
243+
{
244+
// Worker/collector: open a fresh connection to the sidecar IPC socket, upgrade it with
245+
// the SEQPACKET connector, and resolve frames in-process.
246+
if master_pid == 0 || master_pid != std::process::id() as i32 {
247+
let socket_path = datadog_sidecar::crashtracker::crashtracker_ipc_socket_path(
248+
master_pid as u32,
249+
datadog_sidecar::config::FromEnv::ipc_mode(),
250+
);
251+
// Prime the request bytes outside the crash handler so the connector never
252+
// allocates in signal context.
253+
let _ = crashtracker_receiver_request_bytes();
254+
let config = builder
255+
.resolve_frames(StacktraceCollection::EnabledWithInprocessSymbols)
256+
.unix_socket_path(socket_path.to_string_lossy().into_owned())
257+
.unix_socket_connector(
258+
datadog_sidecar::crashtracker::connect_to_sidecar_receiver,
259+
)
260+
.build()?;
261+
return libdd_crashtracker::init(
262+
config,
263+
libdd_crashtracker::CrashtrackerReceiverConfig::default(),
264+
metadata,
265+
);
266+
}
267+
// Thread-mode host: its in-process sidecar can't serve its own crash, so spawn a
268+
// transient fork+exec subprocess receiver and resolve frames there.
269+
let config = builder
270+
.resolve_frames(StacktraceCollection::EnabledWithSymbolsInReceiver)
271+
.build()?;
272+
let receiver_config =
273+
datadog_sidecar::build_crashtracker_receiver_config(None, None)?;
274+
libdd_crashtracker::init(config, receiver_config, metadata)
275+
}
276+
277+
// macOS can't open a fresh SOCK_SEQPACKET connection signal-safely, so reuse the
278+
// already-open sidecar fd and upgrade it at crash time (no-op if there's no connection).
279+
// The path is a placeholder the connector ignores; it just has to be non-empty so the
280+
// crashtracker takes the connector path.
281+
#[cfg(target_os = "macos")]
282+
{
283+
// Prime the request bytes outside the crash handler so the connector never
284+
// allocates in signal context.
285+
let _ = crashtracker_receiver_request_bytes();
286+
let config = builder
287+
.resolve_frames(StacktraceCollection::EnabledWithInprocessSymbols)
288+
.unix_socket_path("datadog-sidecar-crashtracker".to_string())
289+
.unix_socket_connector(reuse_sidecar_fd_connector)
290+
.build()?;
291+
libdd_crashtracker::init(
292+
config,
293+
libdd_crashtracker::CrashtrackerReceiverConfig::default(),
294+
metadata,
295+
)
296+
}
297+
298+
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
299+
{
300+
let _ = (master_pid, builder, metadata);
301+
Ok(())
302+
}
303+
})();
304+
match result {
305+
Ok(()) => MaybeError::None,
306+
Err(e) => {
307+
MaybeError::Some(Error::from(format!("{e:?}")))
308+
}
309+
}
310+
}
311+
312+
/// On macos we cannot easily create a new signal safe connection to the sidecar, so we reuse the
313+
/// already open fd from datadog_sidecar_for_signal.
314+
#[cfg(target_os = "macos")]
315+
fn reuse_sidecar_fd_connector(_unix_socket_path: &str) -> std::os::fd::RawFd {
316+
extern "C" {
317+
// Set by the sidecar connect path (sidecar.c) to the live transport for best-effort
318+
// signal-handler use; null when there is no connection. The transport pointer is stable
319+
// across transparent reconnects (only its inner sender is swapped), so reading the fd
320+
// through it stays current. Typed as an opaque pointer to keep the `extern` block FFI-safe;
321+
// cast to the real type below.
322+
static mut datadog_sidecar_for_signal: *mut std::ffi::c_void;
323+
}
324+
325+
// Best-effort, signal context: read the transport pointer and get its current fd via
326+
// SidecarTransport::signal_fd (which uses get_mut, never locking). Going through the raw
327+
// pointer knowingly bypasses aliasing checks — the crashing thread is the only realistic
328+
// accessor.
329+
let transport = unsafe { datadog_sidecar_for_signal }
330+
as *mut datadog_sidecar::service::blocking::SidecarTransport;
331+
if transport.is_null() {
332+
return -1;
218333
}
334+
let fd = unsafe { (*transport).as_raw_fd() };
335+
let bytes = crashtracker_receiver_request_bytes();
336+
let sent = unsafe { libc::send(dup, bytes.as_ptr() as *const libc::c_void, bytes.len(), 0) };
219337
}
220338

221339
// Hack: Without this, the PECL build of the tracer does not contain the ddog_library_* functions
57.8 KB
Binary file not shown.

components-rs/remote_config.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -650,9 +650,6 @@ pub extern "C" fn ddog_remote_configs_service_env_change(
650650
tags: &libdd_common_ffi::Vec<Tag>,
651651
process_tags: &libdd_common_ffi::Vec<Tag>,
652652
) -> bool {
653-
// libdatadog now hides `Target`'s fields and exposes a constructor. `tags`
654-
// and `process_tags` are stored as already-formatted "key:value" strings,
655-
// so convert the incoming `Tag`s via their `Display` impl.
656653
let new_target = Target::new(
657654
service.to_utf8_lossy().to_string(),
658655
env.to_utf8_lossy().to_string(),

components-rs/sidecar.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -442,11 +442,6 @@ ddog_MaybeError ddog_sidecar_set_test_session_token(struct ddog_SidecarTransport
442442
void ddog_sidecar_reconnect(struct ddog_SidecarTransport **transport,
443443
struct ddog_SidecarTransport *(*factory)(void));
444444

445-
/**
446-
* Return the path of the crashtracker unix domain socket.
447-
*/
448-
ddog_CharSlice ddog_sidecar_get_crashtracker_unix_socket_path(void);
449-
450445
/**
451446
* Gets an agent info reader.
452447
*/

components-rs/stats.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -149,13 +149,7 @@ fn build_fixed_key<'a>(span: &'a PhpSpanStats<'a>) -> FixedAggregationKey<&'a st
149149
http_endpoint: extract_http_endpoint(span),
150150
http_status_code: extract_http_status_code(span),
151151
is_synthetics_request: is_synthetics_request(span),
152-
// `is_trace_root` became a three-valued `Trilean` in libdatadog; the PHP
153-
// side only ever knows true/false, so map the bool onto True/False.
154-
is_trace_root: if span.is_trace_root {
155-
Trilean::True
156-
} else {
157-
Trilean::False
158-
},
152+
is_trace_root: if span.is_trace_root { Trilean::True } else { Trilean::False },
159153
grpc_status_code: extract_grpc_status_code(span),
160154
service_source: char_slice_str(span.service_source),
161155
}

0 commit comments

Comments
 (0)