diff --git a/components-rs/common.h b/components-rs/common.h index bfe1502bce3..38703355a30 100644 --- a/components-rs/common.h +++ b/components-rs/common.h @@ -264,15 +264,19 @@ typedef struct _zend_string _zend_string; #define ddog_MultiTargetFetcher_DEFAULT_CLIENTS_LIMIT 100 -typedef enum ddog_ConfigurationOrigin { - DDOG_CONFIGURATION_ORIGIN_ENV_VAR, - DDOG_CONFIGURATION_ORIGIN_CODE, - DDOG_CONFIGURATION_ORIGIN_DD_CONFIG, - DDOG_CONFIGURATION_ORIGIN_REMOTE_CONFIG, - DDOG_CONFIGURATION_ORIGIN_DEFAULT, - DDOG_CONFIGURATION_ORIGIN_LOCAL_STABLE_CONFIG, - DDOG_CONFIGURATION_ORIGIN_FLEET_STABLE_CONFIG, -} ddog_ConfigurationOrigin; +typedef enum ddog_Log { + DDOG_LOG_ERROR = 1, + DDOG_LOG_WARN = 2, + DDOG_LOG_INFO = 3, + DDOG_LOG_DEBUG = 4, + DDOG_LOG_TRACE = 5, + DDOG_LOG_DEPRECATED = (3 | ddog_LOG_ONCE), + DDOG_LOG_STARTUP = (3 | (2 << 4)), + DDOG_LOG_STARTUP_WARN = (1 | (2 << 4)), + DDOG_LOG_SPAN = (4 | (3 << 4)), + DDOG_LOG_SPAN_TRACE = (5 | (3 << 4)), + DDOG_LOG_HOOK_TRACE = (5 | (4 << 4)), +} ddog_Log; typedef enum ddog_DynamicConfigUpdateMode { DDOG_DYNAMIC_CONFIG_UPDATE_MODE_READ, @@ -281,30 +285,16 @@ typedef enum ddog_DynamicConfigUpdateMode { DDOG_DYNAMIC_CONFIG_UPDATE_MODE_RESTORE, } ddog_DynamicConfigUpdateMode; -typedef enum ddog_EvaluateAt { - DDOG_EVALUATE_AT_ENTRY, - DDOG_EVALUATE_AT_EXIT, -} ddog_EvaluateAt; - typedef enum ddog_InBodyLocation { DDOG_IN_BODY_LOCATION_NONE, DDOG_IN_BODY_LOCATION_START, DDOG_IN_BODY_LOCATION_END, } ddog_InBodyLocation; -typedef enum ddog_Log { - DDOG_LOG_ERROR = 1, - DDOG_LOG_WARN = 2, - DDOG_LOG_INFO = 3, - DDOG_LOG_DEBUG = 4, - DDOG_LOG_TRACE = 5, - DDOG_LOG_DEPRECATED = (3 | ddog_LOG_ONCE), - DDOG_LOG_STARTUP = (3 | (2 << 4)), - DDOG_LOG_STARTUP_WARN = (1 | (2 << 4)), - DDOG_LOG_SPAN = (4 | (3 << 4)), - DDOG_LOG_SPAN_TRACE = (5 | (3 << 4)), - DDOG_LOG_HOOK_TRACE = (5 | (4 << 4)), -} ddog_Log; +typedef enum ddog_EvaluateAt { + DDOG_EVALUATE_AT_ENTRY, + DDOG_EVALUATE_AT_EXIT, +} ddog_EvaluateAt; typedef enum ddog_MetricKind { DDOG_METRIC_KIND_COUNT, @@ -313,6 +303,36 @@ typedef enum ddog_MetricKind { DDOG_METRIC_KIND_DISTRIBUTION, } ddog_MetricKind; +typedef enum ddog_SpanProbeTarget { + DDOG_SPAN_PROBE_TARGET_ACTIVE, + DDOG_SPAN_PROBE_TARGET_ROOT, +} ddog_SpanProbeTarget; + +typedef enum ddog_ProbeStatus { + DDOG_PROBE_STATUS_RECEIVED, + DDOG_PROBE_STATUS_INSTALLED, + DDOG_PROBE_STATUS_EMITTING, + DDOG_PROBE_STATUS_ERROR, + DDOG_PROBE_STATUS_BLOCKED, + DDOG_PROBE_STATUS_WARNING, +} ddog_ProbeStatus; + +typedef enum ddog_ConfigurationOrigin { + DDOG_CONFIGURATION_ORIGIN_ENV_VAR, + DDOG_CONFIGURATION_ORIGIN_CODE, + DDOG_CONFIGURATION_ORIGIN_DD_CONFIG, + DDOG_CONFIGURATION_ORIGIN_REMOTE_CONFIG, + DDOG_CONFIGURATION_ORIGIN_DEFAULT, + DDOG_CONFIGURATION_ORIGIN_LOCAL_STABLE_CONFIG, + DDOG_CONFIGURATION_ORIGIN_FLEET_STABLE_CONFIG, +} ddog_ConfigurationOrigin; + +typedef enum ddog_MetricType { + DDOG_METRIC_TYPE_GAUGE, + DDOG_METRIC_TYPE_COUNT, + DDOG_METRIC_TYPE_DISTRIBUTION, +} ddog_MetricType; + typedef enum ddog_MetricNamespace { DDOG_METRIC_NAMESPACE_TRACERS, DDOG_METRIC_NAMESPACE_PROFILERS, @@ -327,20 +347,16 @@ typedef enum ddog_MetricNamespace { DDOG_METRIC_NAMESPACE_SIDECAR, } ddog_MetricNamespace; -typedef enum ddog_MetricType { - DDOG_METRIC_TYPE_GAUGE, - DDOG_METRIC_TYPE_COUNT, - DDOG_METRIC_TYPE_DISTRIBUTION, -} ddog_MetricType; - -typedef enum ddog_ProbeStatus { - DDOG_PROBE_STATUS_RECEIVED, - DDOG_PROBE_STATUS_INSTALLED, - DDOG_PROBE_STATUS_EMITTING, - DDOG_PROBE_STATUS_ERROR, - DDOG_PROBE_STATUS_BLOCKED, - DDOG_PROBE_STATUS_WARNING, -} ddog_ProbeStatus; +typedef enum ddog_RemoteConfigProduct { + DDOG_REMOTE_CONFIG_PRODUCT_AGENT_CONFIG, + DDOG_REMOTE_CONFIG_PRODUCT_AGENT_TASK, + DDOG_REMOTE_CONFIG_PRODUCT_APM_TRACING, + DDOG_REMOTE_CONFIG_PRODUCT_ASM, + DDOG_REMOTE_CONFIG_PRODUCT_ASM_DATA, + DDOG_REMOTE_CONFIG_PRODUCT_ASM_DD, + DDOG_REMOTE_CONFIG_PRODUCT_ASM_FEATURES, + DDOG_REMOTE_CONFIG_PRODUCT_LIVE_DEBUGGER, +} ddog_RemoteConfigProduct; typedef enum ddog_RemoteConfigCapabilities { DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_ACTIVATION = 1, @@ -388,22 +404,6 @@ typedef enum ddog_RemoteConfigCapabilities { DDOG_REMOTE_CONFIG_CAPABILITIES_ASM_TRACE_TAGGING_RULES = 43, } ddog_RemoteConfigCapabilities; -typedef enum ddog_RemoteConfigProduct { - DDOG_REMOTE_CONFIG_PRODUCT_AGENT_CONFIG, - DDOG_REMOTE_CONFIG_PRODUCT_AGENT_TASK, - DDOG_REMOTE_CONFIG_PRODUCT_APM_TRACING, - DDOG_REMOTE_CONFIG_PRODUCT_ASM, - DDOG_REMOTE_CONFIG_PRODUCT_ASM_DATA, - DDOG_REMOTE_CONFIG_PRODUCT_ASM_DD, - DDOG_REMOTE_CONFIG_PRODUCT_ASM_FEATURES, - DDOG_REMOTE_CONFIG_PRODUCT_LIVE_DEBUGGER, -} ddog_RemoteConfigProduct; - -typedef enum ddog_SpanProbeTarget { - DDOG_SPAN_PROBE_TARGET_ACTIVE, - DDOG_SPAN_PROBE_TARGET_ROOT, -} ddog_SpanProbeTarget; - typedef struct ddog_DebuggerPayload ddog_DebuggerPayload; typedef struct ddog_DslString ddog_DslString; @@ -436,6 +436,8 @@ typedef struct ddog_SidecarActionsBuffer ddog_SidecarActionsBuffer; */ typedef struct ddog_SidecarTransport ddog_SidecarTransport; +typedef struct _zend_string *ddog_OwnedZendString; + /** * Holds the raw parts of a Rust Vec; it should only be created from Rust, * never from C. @@ -451,8 +453,6 @@ typedef struct ddog_Tag { const struct ddog_DslString *value; } ddog_Tag; -typedef struct _zend_string *ddog_OwnedZendString; - typedef struct _zend_string *(*ddog_DynamicConfigUpdate)(ddog_CharSlice config, ddog_OwnedZendString value, enum ddog_DynamicConfigUpdateMode mode); @@ -774,17 +774,18 @@ typedef struct ddog_DebuggerValue ddog_DebuggerValue; #define ddog_EVALUATOR_RESULT_REDACTED (const void*)-2 -typedef enum ddog_DebuggerType { - DDOG_DEBUGGER_TYPE_DIAGNOSTICS, - DDOG_DEBUGGER_TYPE_LOGS, -} ddog_DebuggerType; - typedef enum ddog_FieldType { DDOG_FIELD_TYPE_STATIC, DDOG_FIELD_TYPE_ARG, DDOG_FIELD_TYPE_LOCAL, } ddog_FieldType; +typedef enum ddog_DebuggerType { + DDOG_DEBUGGER_TYPE_DIAGNOSTICS, + DDOG_DEBUGGER_TYPE_SNAPSHOTS, + DDOG_DEBUGGER_TYPE_LOGS, +} ddog_DebuggerType; + typedef struct ddog_Entry ddog_Entry; typedef struct ddog_HashMap_CowStr__Value ddog_HashMap_CowStr__Value; @@ -913,16 +914,6 @@ typedef struct ddog_OwnedCharSlice { void (*free)(ddog_CharSlice); } ddog_OwnedCharSlice; -typedef enum ddog_LogLevel { - DDOG_LOG_LEVEL_ERROR, - DDOG_LOG_LEVEL_WARN, - DDOG_LOG_LEVEL_DEBUG, -} ddog_LogLevel; - -typedef enum ddog_TelemetryWorkerBuilderBoolProperty { - DDOG_TELEMETRY_WORKER_BUILDER_BOOL_PROPERTY_CONFIG_TELEMETRY_DEBUG_LOGGING_ENABLED, -} ddog_TelemetryWorkerBuilderBoolProperty; - typedef enum ddog_TelemetryWorkerBuilderEndpointProperty { DDOG_TELEMETRY_WORKER_BUILDER_ENDPOINT_PROPERTY_CONFIG_ENDPOINT, } ddog_TelemetryWorkerBuilderEndpointProperty; @@ -941,6 +932,16 @@ typedef enum ddog_TelemetryWorkerBuilderStrProperty { DDOG_TELEMETRY_WORKER_BUILDER_STR_PROPERTY_RUNTIME_ID, } ddog_TelemetryWorkerBuilderStrProperty; +typedef enum ddog_TelemetryWorkerBuilderBoolProperty { + DDOG_TELEMETRY_WORKER_BUILDER_BOOL_PROPERTY_CONFIG_TELEMETRY_DEBUG_LOGGING_ENABLED, +} ddog_TelemetryWorkerBuilderBoolProperty; + +typedef enum ddog_LogLevel { + DDOG_LOG_LEVEL_ERROR, + DDOG_LOG_LEVEL_WARN, + DDOG_LOG_LEVEL_DEBUG, +} ddog_LogLevel; + typedef struct ddog_TelemetryWorkerBuilder ddog_TelemetryWorkerBuilder; /** @@ -1065,37 +1066,28 @@ typedef struct ddog_SenderParameters { ddog_CharSlice url; } ddog_SenderParameters; -typedef enum ddog_crasht_BuildIdType { - DDOG_CRASHT_BUILD_ID_TYPE_GNU, - DDOG_CRASHT_BUILD_ID_TYPE_GO, - DDOG_CRASHT_BUILD_ID_TYPE_PDB, - DDOG_CRASHT_BUILD_ID_TYPE_SHA1, -} ddog_crasht_BuildIdType; - /** - * Result type for runtime callback registration + * Stacktrace collection occurs in the context of a crashing process. + * If the stack is sufficiently corruputed, it is possible (but unlikely), + * for stack trace collection itself to crash. + * We recommend fully enabling stacktrace collection, but having an environment + * variable to allow downgrading the collector. */ -typedef enum ddog_crasht_CallbackResult { - DDOG_CRASHT_CALLBACK_RESULT_OK, - DDOG_CRASHT_CALLBACK_RESULT_ERROR, -} ddog_crasht_CallbackResult; - -typedef enum ddog_crasht_DemangleOptions { - DDOG_CRASHT_DEMANGLE_OPTIONS_COMPLETE, - DDOG_CRASHT_DEMANGLE_OPTIONS_NAME_ONLY, -} ddog_crasht_DemangleOptions; - -typedef enum ddog_crasht_ErrorKind { - DDOG_CRASHT_ERROR_KIND_PANIC, - DDOG_CRASHT_ERROR_KIND_UNHANDLED_EXCEPTION, - DDOG_CRASHT_ERROR_KIND_UNIX_SIGNAL, -} ddog_crasht_ErrorKind; - -typedef enum ddog_crasht_FileType { - DDOG_CRASHT_FILE_TYPE_APK, - DDOG_CRASHT_FILE_TYPE_ELF, - DDOG_CRASHT_FILE_TYPE_PE, -} ddog_crasht_FileType; +typedef enum ddog_crasht_StacktraceCollection { + /** + * Stacktrace collection occurs in the + */ + DDOG_CRASHT_STACKTRACE_COLLECTION_DISABLED, + DDOG_CRASHT_STACKTRACE_COLLECTION_WITHOUT_SYMBOLS, + /** + * This option uses `backtrace::resolve_frame_unsynchronized()` to gather symbol information + * and also unwind inlined functions. Enabling this feature will not only provide symbolic + * details, but may also yield additional or less stack frames compared to other + * configurations. + */ + DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_INPROCESS_SYMBOLS, + DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_SYMBOLS_IN_RECEIVER, +} ddog_crasht_StacktraceCollection; /** * This enum represents operations a the tracked library might be engaged in. @@ -1120,6 +1112,12 @@ typedef enum ddog_crasht_OpTypes { DDOG_CRASHT_OP_TYPES_SIZE, } ddog_crasht_OpTypes; +typedef enum ddog_crasht_ErrorKind { + DDOG_CRASHT_ERROR_KIND_PANIC, + DDOG_CRASHT_ERROR_KIND_UNHANDLED_EXCEPTION, + DDOG_CRASHT_ERROR_KIND_UNIX_SIGNAL, +} ddog_crasht_ErrorKind; + /** * See https://man7.org/linux/man-pages/man2/sigaction.2.html * MUST REMAIN IN SYNC WITH THE ENUM IN emit_sigcodes.c @@ -1192,28 +1190,31 @@ typedef enum ddog_crasht_SignalNames { DDOG_CRASHT_SIGNAL_NAMES_UNKNOWN, } ddog_crasht_SignalNames; +typedef enum ddog_crasht_BuildIdType { + DDOG_CRASHT_BUILD_ID_TYPE_GNU, + DDOG_CRASHT_BUILD_ID_TYPE_GO, + DDOG_CRASHT_BUILD_ID_TYPE_PDB, + DDOG_CRASHT_BUILD_ID_TYPE_SHA1, +} ddog_crasht_BuildIdType; + +typedef enum ddog_crasht_FileType { + DDOG_CRASHT_FILE_TYPE_APK, + DDOG_CRASHT_FILE_TYPE_ELF, + DDOG_CRASHT_FILE_TYPE_PE, +} ddog_crasht_FileType; + +typedef enum ddog_crasht_DemangleOptions { + DDOG_CRASHT_DEMANGLE_OPTIONS_COMPLETE, + DDOG_CRASHT_DEMANGLE_OPTIONS_NAME_ONLY, +} ddog_crasht_DemangleOptions; + /** - * Stacktrace collection occurs in the context of a crashing process. - * If the stack is sufficiently corruputed, it is possible (but unlikely), - * for stack trace collection itself to crash. - * We recommend fully enabling stacktrace collection, but having an environment - * variable to allow downgrading the collector. + * Result type for runtime callback registration */ -typedef enum ddog_crasht_StacktraceCollection { - /** - * Stacktrace collection occurs in the - */ - DDOG_CRASHT_STACKTRACE_COLLECTION_DISABLED, - DDOG_CRASHT_STACKTRACE_COLLECTION_WITHOUT_SYMBOLS, - /** - * This option uses `backtrace::resolve_frame_unsynchronized()` to gather symbol information - * and also unwind inlined functions. Enabling this feature will not only provide symbolic - * details, but may also yield additional or less stack frames compared to other - * configurations. - */ - DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_INPROCESS_SYMBOLS, - DDOG_CRASHT_STACKTRACE_COLLECTION_ENABLED_WITH_SYMBOLS_IN_RECEIVER, -} ddog_crasht_StacktraceCollection; +typedef enum ddog_crasht_CallbackResult { + DDOG_CRASHT_CALLBACK_RESULT_OK, + DDOG_CRASHT_CALLBACK_RESULT_ERROR, +} ddog_crasht_CallbackResult; typedef struct ddog_crasht_CrashInfo ddog_crasht_CrashInfo; @@ -1291,7 +1292,7 @@ typedef struct ddog_crasht_Config { /** * Timeout in milliseconds before the signal handler starts tearing things down to return. * If 0, uses the default timeout as specified in - * `datadog_crashtracker::shared::constants::DD_CRASHTRACK_DEFAULT_TIMEOUT`. Otherwise, uses + * `libdd_crashtracker::shared::constants::DD_CRASHTRACK_DEFAULT_TIMEOUT`. Otherwise, uses * the specified timeout value. * This is given as a uint32_t, but the actual timeout needs to fit inside of an i32 (max * 2^31-1). This is a limitation of the various interfaces used to guarantee the timeout. diff --git a/components-rs/crashtracker.h b/components-rs/crashtracker.h index 42209ff18cc..43c14991ba7 100644 --- a/components-rs/crashtracker.h +++ b/components-rs/crashtracker.h @@ -854,7 +854,7 @@ struct ddog_StringWrapperResult ddog_crasht_demangle(ddog_CharSlice name, * signal handler is dangerous, so we fork a sidecar to do the stuff we aren't * allowed to do in the handler. * - * See comments in [datadog-crashtracker/lib.rs] for a full architecture description. + * See comments in [libdd-crashtracker/lib.rs] for a full architecture description. * # Safety * No safety concerns */ @@ -868,7 +868,7 @@ DDOG_CHECK_RETURN struct ddog_VoidResult ddog_crasht_receiver_entry_point_stdin( * signal handler is dangerous, so we fork a sidecar to do the stuff we aren't * allowed to do in the handler. * - * See comments in [datadog-crashtracker/lib.rs] for a full architecture + * See comments in [libdd-crashtracker/lib.rs] for a full architecture * description. * # Safety * No safety concerns diff --git a/components-rs/ddtrace.h b/components-rs/ddtrace.h index 35d06a61820..89039bc88db 100644 --- a/components-rs/ddtrace.h +++ b/components-rs/ddtrace.h @@ -43,6 +43,64 @@ ddog_Configurator *ddog_library_configurator_new_dummy(bool debug_logs, ddog_Cha int posix_spawn_file_actions_addchdir_np(void *file_actions, const char *path); +void ddog_init_span_func(void (*free_func)(ddog_OwnedZendString), + void (*addref_func)(struct _zend_string*), + ddog_OwnedZendString (*init_func)(ddog_CharSlice)); + +void ddog_set_span_service_zstr(ddog_SpanBytes *ptr, struct _zend_string *str); + +void ddog_set_span_name_zstr(ddog_SpanBytes *ptr, struct _zend_string *str); + +void ddog_set_span_resource_zstr(ddog_SpanBytes *ptr, struct _zend_string *str); + +void ddog_set_span_type_zstr(ddog_SpanBytes *ptr, struct _zend_string *str); + +void ddog_add_span_meta_zstr(ddog_SpanBytes *ptr, + struct _zend_string *key, + struct _zend_string *val); + +void ddog_add_CharSlice_span_meta_zstr(ddog_SpanBytes *ptr, + ddog_CharSlice key, + struct _zend_string *val); + +void ddog_add_zstr_span_meta_str(ddog_SpanBytes *ptr, struct _zend_string *key, const char *val); + +void ddog_add_str_span_meta_str(ddog_SpanBytes *ptr, const char *key, const char *val); + +void ddog_add_str_span_meta_zstr(ddog_SpanBytes *ptr, const char *key, struct _zend_string *val); + +void ddog_add_str_span_meta_CharSlice(ddog_SpanBytes *ptr, const char *key, ddog_CharSlice val); + +void ddog_del_span_meta_zstr(ddog_SpanBytes *ptr, struct _zend_string *key); + +void ddog_del_span_meta_str(ddog_SpanBytes *ptr, const char *key); + +bool ddog_has_span_meta_zstr(ddog_SpanBytes *ptr, struct _zend_string *key); + +bool ddog_has_span_meta_str(ddog_SpanBytes *ptr, const char *key); + +ddog_CharSlice ddog_get_span_meta_str(ddog_SpanBytes *span, const char *key); + +void ddog_add_span_metrics_zstr(ddog_SpanBytes *ptr, struct _zend_string *key, double val); + +bool ddog_has_span_metrics_zstr(ddog_SpanBytes *ptr, struct _zend_string *key); + +void ddog_del_span_metrics_zstr(ddog_SpanBytes *ptr, struct _zend_string *key); + +void ddog_add_span_metrics_str(ddog_SpanBytes *ptr, const char *key, double val); + +bool ddog_get_span_metrics_str(ddog_SpanBytes *ptr, const char *key, double *result); + +void ddog_del_span_metrics_str(ddog_SpanBytes *ptr, const char *key); + +void ddog_add_span_meta_struct_zstr(ddog_SpanBytes *ptr, + struct _zend_string *key, + struct _zend_string *val); + +void ddog_add_zstr_span_meta_struct_CharSlice(ddog_SpanBytes *ptr, + struct _zend_string *key, + ddog_CharSlice val); + bool ddog_shall_log(enum ddog_Log category); void ddog_set_error_log_level(bool once); @@ -181,62 +239,4 @@ ddog_MaybeError ddog_sidecar_telemetry_filter_flush(struct ddog_SidecarTransport ddog_CharSlice service, ddog_CharSlice env); -void ddog_init_span_func(void (*free_func)(ddog_OwnedZendString), - void (*addref_func)(struct _zend_string*), - ddog_OwnedZendString (*init_func)(ddog_CharSlice)); - -void ddog_set_span_service_zstr(ddog_SpanBytes *ptr, struct _zend_string *str); - -void ddog_set_span_name_zstr(ddog_SpanBytes *ptr, struct _zend_string *str); - -void ddog_set_span_resource_zstr(ddog_SpanBytes *ptr, struct _zend_string *str); - -void ddog_set_span_type_zstr(ddog_SpanBytes *ptr, struct _zend_string *str); - -void ddog_add_span_meta_zstr(ddog_SpanBytes *ptr, - struct _zend_string *key, - struct _zend_string *val); - -void ddog_add_CharSlice_span_meta_zstr(ddog_SpanBytes *ptr, - ddog_CharSlice key, - struct _zend_string *val); - -void ddog_add_zstr_span_meta_str(ddog_SpanBytes *ptr, struct _zend_string *key, const char *val); - -void ddog_add_str_span_meta_str(ddog_SpanBytes *ptr, const char *key, const char *val); - -void ddog_add_str_span_meta_zstr(ddog_SpanBytes *ptr, const char *key, struct _zend_string *val); - -void ddog_add_str_span_meta_CharSlice(ddog_SpanBytes *ptr, const char *key, ddog_CharSlice val); - -void ddog_del_span_meta_zstr(ddog_SpanBytes *ptr, struct _zend_string *key); - -void ddog_del_span_meta_str(ddog_SpanBytes *ptr, const char *key); - -bool ddog_has_span_meta_zstr(ddog_SpanBytes *ptr, struct _zend_string *key); - -bool ddog_has_span_meta_str(ddog_SpanBytes *ptr, const char *key); - -ddog_CharSlice ddog_get_span_meta_str(ddog_SpanBytes *span, const char *key); - -void ddog_add_span_metrics_zstr(ddog_SpanBytes *ptr, struct _zend_string *key, double val); - -bool ddog_has_span_metrics_zstr(ddog_SpanBytes *ptr, struct _zend_string *key); - -void ddog_del_span_metrics_zstr(ddog_SpanBytes *ptr, struct _zend_string *key); - -void ddog_add_span_metrics_str(ddog_SpanBytes *ptr, const char *key, double val); - -bool ddog_get_span_metrics_str(ddog_SpanBytes *ptr, const char *key, double *result); - -void ddog_del_span_metrics_str(ddog_SpanBytes *ptr, const char *key); - -void ddog_add_span_meta_struct_zstr(ddog_SpanBytes *ptr, - struct _zend_string *key, - struct _zend_string *val); - -void ddog_add_zstr_span_meta_struct_CharSlice(ddog_SpanBytes *ptr, - struct _zend_string *key, - ddog_CharSlice val); - #endif /* DDTRACE_PHP_H */ diff --git a/components-rs/lib.rs b/components-rs/lib.rs index 5cbfc845312..2884f7d07d9 100644 --- a/components-rs/lib.rs +++ b/components-rs/lib.rs @@ -3,11 +3,11 @@ #![feature(linkage)] #![allow(static_mut_refs)] // remove with move to Rust 2024 edition +pub mod bytes; pub mod log; pub mod remote_config; pub mod sidecar; pub mod telemetry; -pub mod bytes; use libdd_common::entity_id::{get_container_id, set_cgroup_file}; use http::uri::{PathAndQuery, Scheme}; diff --git a/components-rs/log.rs b/components-rs/log.rs index 0954aab626d..c9be5722df8 100644 --- a/components-rs/log.rs +++ b/components-rs/log.rs @@ -1,3 +1,5 @@ +use libdd_common_ffi::slice::AsBytes; +use libdd_common_ffi::CharSlice; use std::cell::RefCell; use std::collections::{BTreeSet, HashMap}; use std::ffi::c_char; @@ -5,13 +7,11 @@ use std::fmt::Debug; use std::str::FromStr; use tracing::Level; use tracing_core::{Event, Field, LevelFilter, Subscriber}; -use tracing_subscriber::EnvFilter; -use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields}; use tracing_subscriber::fmt::format::Writer; +use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields}; use tracing_subscriber::registry::LookupSpan; use tracing_subscriber::util::SubscriberInitExt; -use libdd_common_ffi::CharSlice; -use libdd_common_ffi::slice::AsBytes; +use tracing_subscriber::EnvFilter; pub const LOG_ONCE: isize = 1 << 3; @@ -66,7 +66,10 @@ pub extern "C" fn ddog_shall_log(category: Log) -> bool { with_target!(category, tracing::event_enabled!()) } -pub fn log(category: Log, msg: S) where S: AsRef + tracing::Value { +pub fn log(category: Log, msg: S) +where + S: AsRef + tracing::Value, +{ let once = (category as isize & LOG_ONCE) != 0; if once { with_target!(category, tracing::event!(once = true, msg)); @@ -99,16 +102,20 @@ impl tracing_core::field::Visit for LogVisitor { } impl FormatEvent for LogFormatter - where - S: Subscriber + for<'a> LookupSpan<'a>, - N: for<'a> FormatFields<'a> + 'static { +where + S: Subscriber + for<'a> LookupSpan<'a>, + N: for<'a> FormatFields<'a> + 'static, +{ fn format_event( &self, _ctx: &FmtContext<'_, S, N>, _writer: Writer<'_>, - event: &Event<'_> + event: &Event<'_>, ) -> core::fmt::Result { - let mut visitor = LogVisitor { msg: None, once: false }; + let mut visitor = LogVisitor { + msg: None, + once: false, + }; event.record(&mut visitor); fn fmt_msg(event: &Event<'_>, msg: &str, suffix: &str) -> String { @@ -149,10 +156,14 @@ impl FormatEvent for LogFormatter COUNTERS.with(|counter| { let mut counter = counter.borrow_mut(); - *counter.entry(event.metadata().level().to_owned()).or_default() += 1; + *counter + .entry(event.metadata().level().to_owned()) + .or_default() += 1; }); - cb(unsafe { CharSlice::from_raw_parts(msg.as_ptr() as *const c_char, msg.len() - 1) }); + cb(unsafe { + CharSlice::from_raw_parts(msg.as_ptr() as *const c_char, msg.len() - 1) + }); } } Ok(()) @@ -175,7 +186,10 @@ pub unsafe extern "C" fn ddog_set_log_level(level: CharSlice, once: bool) { set_log_subscriber(subscriber) } -fn set_log_subscriber(subscriber: S) where S: SubscriberInitExt { +fn set_log_subscriber(subscriber: S) +where + S: SubscriberInitExt, +{ TRACING_GUARDS.replace(None); // drop first to avoid a prior guard to reset the thread local subscriber it upon replace() TRACING_GUARDS.replace(Some(subscriber.set_default())); } @@ -183,7 +197,10 @@ fn set_log_subscriber(subscriber: S) where S: SubscriberInitExt { #[no_mangle] pub unsafe extern "C" fn ddog_log(category: Log, once: bool, msg: CharSlice) { if once { - with_target!(category, tracing::event!(once = true, "{}", msg.to_utf8_lossy())); + with_target!( + category, + tracing::event!(once = true, "{}", msg.to_utf8_lossy()) + ); } else { with_target!(category, tracing::event!("{}", msg.to_utf8_lossy())); } diff --git a/components-rs/php_sidecar_mockgen/src/bin/php_sidecar_mockgen.rs b/components-rs/php_sidecar_mockgen/src/bin/php_sidecar_mockgen.rs index a31c034e07e..b02332de7df 100644 --- a/components-rs/php_sidecar_mockgen/src/bin/php_sidecar_mockgen.rs +++ b/components-rs/php_sidecar_mockgen/src/bin/php_sidecar_mockgen.rs @@ -41,7 +41,8 @@ fn main() { } let source_modified = fs::metadata("mock_php_syms.c").unwrap().modified().unwrap(); - if fs::metadata("mock_php.shared_lib").map_or(true, |m| m.modified().unwrap() < source_modified) { + if fs::metadata("mock_php.shared_lib").map_or(true, |m| m.modified().unwrap() < source_modified) + { env::set_var("OPT_LEVEL", "2"); let mut cc_build = cc::Build::new(); @@ -76,11 +77,17 @@ fn main() { } }; - let comma_separated = bin.iter().map(|byte| format!("{byte:#X}")).collect::>().join(","); - let out = format!(r#" + let comma_separated = bin + .iter() + .map(|byte| format!("{byte:#X}")) + .collect::>() + .join(","); + let out = format!( + r#" const unsigned char DDTRACE_MOCK_PHP[] = {{{comma_separated}}}; const void *DDTRACE_MOCK_PHP_SIZE = (void *) sizeof(DDTRACE_MOCK_PHP); - "#); + "# + ); if let Err(err) = fs::write(output_path, out) { eprintln!("Failed generating {:?}: {}", output_path, err); diff --git a/components-rs/remote_config.rs b/components-rs/remote_config.rs index 4693d2209c6..e3b671c5efa 100644 --- a/components-rs/remote_config.rs +++ b/components-rs/remote_config.rs @@ -6,11 +6,11 @@ use datadog_live_debugger_ffi::evaluator::{ddog_register_expr_evaluator, Evaluat use datadog_live_debugger_ffi::send_data::{ ddog_debugger_diagnostics_create_unboxed, ddog_snapshot_redacted_type, }; +use datadog_remote_config::config::dynamic::{Configs, TracingSamplingRuleProvenance}; use datadog_remote_config::fetch::ConfigInvariants; use datadog_remote_config::{ RemoteConfigCapabilities, RemoteConfigData, RemoteConfigProduct, Target, }; -use datadog_remote_config::config::dynamic::{Configs, TracingSamplingRuleProvenance}; use datadog_sidecar::service::blocking::SidecarTransport; use datadog_sidecar::service::{InstanceId, QueueId}; use datadog_sidecar::shm_remote_config::{RemoteConfigManager, RemoteConfigUpdate}; diff --git a/components-rs/sidecar.h b/components-rs/sidecar.h index 4746e0d2163..5aea6f0fafe 100644 --- a/components-rs/sidecar.h +++ b/components-rs/sidecar.h @@ -86,12 +86,17 @@ void ddog_remote_config_reader_drop(struct ddog_RemoteConfigReader*); void ddog_sidecar_transport_drop(struct ddog_SidecarTransport*); -/** - * # Safety - * Caller must ensure the process is safe to fork, at the time when this method is called - */ ddog_MaybeError ddog_sidecar_connect(struct ddog_SidecarTransport **connection); +ddog_MaybeError ddog_sidecar_connect_master(int32_t master_pid); + +ddog_MaybeError ddog_sidecar_connect_worker(int32_t master_pid, + struct ddog_SidecarTransport **connection); + +ddog_MaybeError ddog_sidecar_shutdown_master_listener(void); + +ddog_MaybeError ddog_sidecar_clear_inherited_listener(void); + ddog_MaybeError ddog_sidecar_ping(struct ddog_SidecarTransport **transport); ddog_MaybeError ddog_sidecar_flush_traces(struct ddog_SidecarTransport **transport); diff --git a/components-rs/sidecar.rs b/components-rs/sidecar.rs index 0b94deeeab9..ba434a87370 100644 --- a/components-rs/sidecar.rs +++ b/components-rs/sidecar.rs @@ -1,34 +1,34 @@ -use std::ffi::{c_char, CStr, OsStr}; -use std::ops::DerefMut; -#[cfg(unix)] -use std::os::unix::ffi::OsStrExt; -use lazy_static::{lazy_static, LazyStatic}; -use tracing::warn; -#[cfg(windows)] -use std::os::windows::ffi::OsStrExt; -use std::sync::Mutex; -use std::time::Duration; +use datadog_ipc::rate_limiter::{AnyLimiter, ShmLimiterMemory}; use datadog_sidecar::config::{self, AppSecConfig, LogMethod}; use datadog_sidecar::service::blocking::{acquire_exception_hash_rate_limiter, SidecarTransport}; -use libdd_common::rate_limiter::{Limiter, LocalLimiter}; -use datadog_ipc::rate_limiter::{AnyLimiter, ShmLimiterMemory}; use datadog_sidecar::service::exception_hash_rate_limiter::ExceptionHashRateLimiter; use datadog_sidecar::tracer::shm_limiter_path; +use lazy_static::{lazy_static, LazyStatic}; +use libdd_common::rate_limiter::{Limiter, LocalLimiter}; use libdd_common::Endpoint; use libdd_common_ffi::slice::AsBytes; -use libdd_common_ffi::{CharSlice, self as ffi, MaybeError}; +use libdd_common_ffi::{self as ffi, CharSlice, MaybeError}; use libdd_telemetry_ffi::try_c; #[cfg(any(windows, php_shared_build))] use spawn_worker::LibDependency; #[cfg(windows)] use spawn_worker::get_trampoline_target_data; +use std::ffi::{c_char, CStr, OsStr}; +use std::ops::DerefMut; +#[cfg(unix)] +use std::os::unix::ffi::OsStrExt; +#[cfg(windows)] +use std::os::windows::ffi::OsStrExt; +use std::sync::Mutex; +use std::time::Duration; +use tracing::warn; #[cfg(php_shared_build)] extern "C" { - #[linkage="extern_weak"] + #[linkage = "extern_weak"] static DDTRACE_MOCK_PHP: *mut u8; - #[linkage="extern_weak"] + #[linkage = "extern_weak"] static DDTRACE_MOCK_PHP_SIZE: *mut usize; } @@ -36,8 +36,7 @@ extern "C" { fn run_sidecar(mut cfg: config::Config) -> anyhow::Result { if !unsafe { DDTRACE_MOCK_PHP_SIZE }.is_null() { let mock = unsafe { std::slice::from_raw_parts(DDTRACE_MOCK_PHP, *DDTRACE_MOCK_PHP_SIZE) }; - cfg.library_dependencies - .push(LibDependency::Binary(mock)); + cfg.library_dependencies.push(LibDependency::Binary(mock)); } datadog_sidecar::start_or_connect_to_sidecar(cfg) } @@ -54,7 +53,8 @@ pub static mut DDOG_PHP_FUNCTION: *const u8 = std::ptr::null(); #[cfg(windows)] fn run_sidecar(mut cfg: config::Config) -> anyhow::Result { let php_dll = get_trampoline_target_data(unsafe { DDOG_PHP_FUNCTION })?; - cfg.library_dependencies.push(LibDependency::Path(php_dll.into())); + cfg.library_dependencies + .push(LibDependency::Path(php_dll.into())); datadog_sidecar::start_or_connect_to_sidecar(cfg) } @@ -145,12 +145,13 @@ pub extern "C" fn ddog_sidecar_connect_php( } } #[cfg(windows)] - let log_level = log_level.to_utf8_lossy().as_ref().into(); + let log_level = log_level.to_utf8_lossy().as_ref().into(); #[cfg(not(windows))] - let log_level = OsStr::from_bytes(log_level.as_bytes()).into(); - cfg.child_env.insert(OsStr::new("DD_TRACE_LOG_LEVEL").into(), log_level); + let log_level = OsStr::from_bytes(log_level.as_bytes()).into(); + cfg.child_env + .insert(OsStr::new("DD_TRACE_LOG_LEVEL").into(), log_level); } - + let reconnect_fn = on_reconnect.map(|on_reconnect| { let cfg = cfg.clone(); Box::new(move || { @@ -159,7 +160,7 @@ pub extern "C" fn ddog_sidecar_connect_php( Some(transport) }) as Box _> }); - + let mut stream = try_c!(sidecar_connect(cfg)); stream.reconnect_fn = reconnect_fn; *connection = Box::into_raw(stream); @@ -185,17 +186,23 @@ pub extern "C" fn ddtrace_sidecar_reconnect( }); } - lazy_static! { - pub static ref SHM_LIMITER: Option> = ShmLimiterMemory::open(&shm_limiter_path()).map_or_else(|e| { - warn!("Attempt to use the SHM_LIMITER failed: {e:?}"); - None - }, Some); - - pub static ref EXCEPTION_HASH_LIMITER: Option = ExceptionHashRateLimiter::open().map_or_else(|e| { - warn!("Attempt to use the EXCEPTION_HASH_LIMITER failed: {e:?}"); - None - }, Some); + pub static ref SHM_LIMITER: Option> = + ShmLimiterMemory::open(&shm_limiter_path()).map_or_else( + |e| { + warn!("Attempt to use the SHM_LIMITER failed: {e:?}"); + None + }, + Some + ); + pub static ref EXCEPTION_HASH_LIMITER: Option = + ExceptionHashRateLimiter::open().map_or_else( + |e| { + warn!("Attempt to use the EXCEPTION_HASH_LIMITER failed: {e:?}"); + None + }, + Some + ); } pub struct MaybeShmLimiter(Option); @@ -227,12 +234,20 @@ pub extern "C" fn ddog_shm_limiter_inc(limiter: &MaybeShmLimiter, limit: u32) -> } #[no_mangle] -pub extern "C" fn ddog_exception_hash_limiter_inc(connection: &mut SidecarTransport, hash: u64, granularity_seconds: u32) -> bool { +pub extern "C" fn ddog_exception_hash_limiter_inc( + connection: &mut SidecarTransport, + hash: u64, + granularity_seconds: u32, +) -> bool { if let Some(limiter) = &*EXCEPTION_HASH_LIMITER { if let Some(limiter) = limiter.find(hash) { return limiter.inc(); } } - let _ = acquire_exception_hash_rate_limiter(connection, hash, Duration::from_secs(granularity_seconds as u64)); + let _ = acquire_exception_hash_rate_limiter( + connection, + hash, + Duration::from_secs(granularity_seconds as u64), + ); true } diff --git a/components-rs/telemetry.rs b/components-rs/telemetry.rs index c572bceea84..512e92fe614 100644 --- a/components-rs/telemetry.rs +++ b/components-rs/telemetry.rs @@ -303,19 +303,25 @@ unsafe fn ddog_sidecar_telemetry_cache_get_or_update<'a>( let env_str = env.to_utf8_lossy(); // I hate you, borrow checker, you get an unsafe from me! - if let Some(cached_entry) = (&mut *(cache as *mut ShmCacheMap)).get_mut(&(service_str.as_ref(), env_str.as_ref())) { + if let Some(cached_entry) = + (&mut *(cache as *mut ShmCacheMap)).get_mut(&(service_str.as_ref(), env_str.as_ref())) + { refresh_cache(cached_entry); return cached_entry; } let shm_path = path_for_telemetry(&service_str, &env_str); - let reader = OneWayShmReader::::new(open_named_shm(&shm_path).ok(), shm_path); - let cached_entry = cache.entry(ShmCacheKey(service_str.into(), env_str.into())).insert(ShmCache { - reader, - config_sent: false, - integrations: HashSet::new(), - composer_paths: HashSet::new(), - }).into_mut(); + let reader = + OneWayShmReader::::new(open_named_shm(&shm_path).ok(), shm_path); + let cached_entry = cache + .entry(ShmCacheKey(service_str.into(), env_str.into())) + .insert(ShmCache { + reader, + config_sent: false, + integrations: HashSet::new(), + composer_paths: HashSet::new(), + }) + .into_mut(); refresh_cache(cached_entry); cached_entry diff --git a/dockerfiles/ci/bookworm/.env b/dockerfiles/ci/bookworm/.env index 1a92ca601b5..654839246c9 100644 --- a/dockerfiles/ci/bookworm/.env +++ b/dockerfiles/ci/bookworm/.env @@ -1,2 +1,2 @@ -BOOKWORM_CURRENT_VERSION=6 -BOOKWORM_NEXT_VERSION=7 +BOOKWORM_CURRENT_VERSION=5 +BOOKWORM_NEXT_VERSION=6 diff --git a/dockerfiles/ci/bookworm/php-8.3/suppr.txt b/dockerfiles/ci/bookworm/php-8.3/suppr.txt index 741c68ed250..dc215f490a6 100644 --- a/dockerfiles/ci/bookworm/php-8.3/suppr.txt +++ b/dockerfiles/ci/bookworm/php-8.3/suppr.txt @@ -5,3 +5,5 @@ leak:_dl_map_object_deps leak:__res_context_send leak:_dl_make_tlsdesc_dynamic leak:_dl_catch_exception +leak:__cxa_thread_atexit +leak:CURRENT_PARKER diff --git a/dockerfiles/ci/bookworm/php-8.4/suppr.txt b/dockerfiles/ci/bookworm/php-8.4/suppr.txt index 39c643abb53..8f09d3ddad7 100644 --- a/dockerfiles/ci/bookworm/php-8.4/suppr.txt +++ b/dockerfiles/ci/bookworm/php-8.4/suppr.txt @@ -1 +1,3 @@ leak:timer_create +leak:__cxa_thread_atexit +leak:CURRENT_PARKER diff --git a/dockerfiles/ci/bookworm/php-8.5/suppr.txt b/dockerfiles/ci/bookworm/php-8.5/suppr.txt index 39c643abb53..8f09d3ddad7 100644 --- a/dockerfiles/ci/bookworm/php-8.5/suppr.txt +++ b/dockerfiles/ci/bookworm/php-8.5/suppr.txt @@ -1 +1,3 @@ leak:timer_create +leak:__cxa_thread_atexit +leak:CURRENT_PARKER diff --git a/ext/ddtrace.c b/ext/ddtrace.c index ab3077d8432..987384f9065 100644 --- a/ext/ddtrace.c +++ b/ext/ddtrace.c @@ -433,9 +433,6 @@ static void dd_activate_once(void) { // must run before the first zai_hook_activate as ddtrace_telemetry_setup installs a global hook if (!ddtrace_disable) { - bool appsec_activation = false; - bool appsec_config = false; - #ifndef _WIN32 // Only disable sidecar sender when explicitly disabled bool bgs_fallback = DD_SIDECAR_TRACE_SENDER_DEFAULT && get_global_DD_TRACE_SIDECAR_TRACE_SENDER() && zai_config_memoized_entries[DDTRACE_CONFIG_DD_TRACE_SIDECAR_TRACE_SENDER].name_index == ZAI_CONFIG_ORIGIN_DEFAULT && !get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED(); @@ -451,16 +448,17 @@ static void dd_activate_once(void) { bgs_service = ddtrace_default_service_name(); } } +#endif - // if we're to enable appsec, we need to enable sidecar + // If we're to enable appsec, we need to enable sidecar + bool appsec_activation = false; + bool appsec_config = false; bool enable_sidecar = ddtrace_sidecar_maybe_enable_appsec(&appsec_activation, &appsec_config); if (!enable_sidecar) { enable_sidecar = get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED() || get_global_DD_TRACE_SIDECAR_TRACE_SENDER(); } - if (enable_sidecar) -#endif - { + if (enable_sidecar) { bool request_startup = PG(during_request_startup); PG(during_request_startup) = false; ddtrace_sidecar_setup(appsec_activation, appsec_config); @@ -1552,6 +1550,8 @@ static PHP_MINIT_FUNCTION(ddtrace) { ddtrace_signals_minit(); #endif + ddtrace_sidecar_minit(false, false); + return SUCCESS; } @@ -1605,7 +1605,11 @@ static PHP_MSHUTDOWN_FUNCTION(ddtrace) { ddtrace_user_req_shutdown(); - ddtrace_sidecar_shutdown(); + // Only shutdown sidecar in MSHUTDOWN for non-CLI SAPIs. + // CLI SAPI shuts down in RSHUTDOWN to allow thread joins before ASAN checks. + if (strcmp(sapi_module.name, "cli") != 0) { + ddtrace_sidecar_shutdown(); + } ddtrace_live_debugger_mshutdown(); @@ -2617,6 +2621,9 @@ PHP_FUNCTION(DDTrace_Internal_add_span_flag) { void dd_internal_handle_fork(void) { // CHILD PROCESS #ifndef _WIN32 + ddtrace_pid_t current_pid = getpid(); + bool is_child_process = (ddtrace_master_pid != 0 && current_pid != ddtrace_master_pid); + if (!get_global_DD_TRACE_SIDECAR_TRACE_SENDER()) { ddtrace_coms_curl_shutdown(); ddtrace_coms_clean_background_sender_after_fork(); @@ -2636,7 +2643,33 @@ void dd_internal_handle_fork(void) { } ddtrace_seed_prng(); ddtrace_generate_runtime_id(); - ddtrace_reset_sidecar(); + +#ifndef _WIN32 + if (get_global_DD_TRACE_SIDECAR_TRACE_SENDER() && ddtrace_sidecar) { + if (is_child_process) { + // Clear inherited listener state - child doesn't own the master listener thread + ddtrace_ffi_try("Failed clearing inherited listener state", ddog_sidecar_clear_inherited_listener()); + + ddtrace_force_new_instance_id(); + + if (ddtrace_sidecar_connect_worker_after_fork()) { + LOG(DEBUG, "Child process connected to master sidecar as worker (child_pid=%d, master_pid=%d)", + current_pid, ddtrace_master_pid); + + if (ddtrace_sidecar) { + ddtrace_sidecar_submit_root_span_data(); + } + } else { + LOG(WARN, "Failed to connect child process as worker to master sidecar (child_pid=%d, master_pid=%d)", + current_pid, ddtrace_master_pid); + ddtrace_reset_sidecar(); + } + } + } else +#endif + { + ddtrace_reset_sidecar(); + } if (!get_DD_TRACE_FORKED_PROCESS()) { ddtrace_disable_tracing_in_current_request(); } diff --git a/ext/handlers_pcntl.c b/ext/handlers_pcntl.c index acff140cfe4..6353d58d156 100644 --- a/ext/handlers_pcntl.c +++ b/ext/handlers_pcntl.c @@ -27,18 +27,92 @@ static zif_handler dd_pcntl_forkx_handler = NULL; #define JOIN_BGS_BEFORE_FORK 1 #endif +static bool dd_master_listener_was_active = false; + static void dd_prefork() { #if JOIN_BGS_BEFORE_FORK if (!get_global_DD_TRACE_SIDECAR_TRACE_SENDER()) { ddtrace_coms_flush_shutdown_writer_synchronous(); } #endif + +#ifndef _WIN32 + // Check if master listener is active before fork + dd_master_listener_was_active = (ddtrace_master_pid != 0 && getpid() == ddtrace_master_pid); + + if (dd_master_listener_was_active) { + // Shutdown master listener before fork to avoid Tokio runtime corruption. + // Tokio's async runtime and fork() are fundamentally incompatible - forking + // while Tokio threads are active causes state corruption in the parent process. + // See: https://github.com/tokio-rs/tokio/issues/4301 + + // First close parent's worker connection to prevent handler thread deadlock + if (ddtrace_sidecar) { + ddog_sidecar_transport_drop(ddtrace_sidecar); + ddtrace_sidecar = NULL; + } + + // Then shutdown the master listener and its Tokio runtime + ddog_sidecar_shutdown_master_listener(); + } +#endif } +static void dd_postfork_parent() { +#ifndef _WIN32 + // Restart master listener in parent if it was active before fork + if (dd_master_listener_was_active) { + // Reinitialize master listener with fresh Tokio runtime. + // This recreates the async runtime that was shut down before fork. + + // First, restart the master listener thread + if (!ddtrace_ffi_try("Failed restarting sidecar master listener after fork", + ddog_sidecar_connect_master((int32_t)ddtrace_master_pid))) { + LOG(WARN, "Failed to restart sidecar master listener after fork"); + dd_master_listener_was_active = false; + return; + } + + // Then reconnect to it as a worker + ddog_SidecarTransport *sidecar_transport = NULL; + if (!ddtrace_ffi_try("Failed reconnecting master to sidecar after fork", + ddog_sidecar_connect_worker((int32_t)ddtrace_master_pid, &sidecar_transport))) { + LOG(WARN, "Failed to reconnect master process to sidecar after fork"); + dd_master_listener_was_active = false; + return; + } + + ddtrace_sidecar = sidecar_transport; + dd_master_listener_was_active = false; + } +#endif +} + +// Declare LSAN runtime interface functions +#if defined(__SANITIZE_ADDRESS__) && !defined(_WIN32) +void __lsan_disable(void); +void __lsan_enable(void); +#endif + static void dd_handle_fork(zval *return_value) { if (Z_LVAL_P(return_value) == 0) { + // CHILD PROCESS + // Disable ASAN leak detection in child to avoid false positives from inherited + // Tokio TLS and runtime state that can't be properly cleaned up after fork +#if defined(__SANITIZE_ADDRESS__) && !defined(_WIN32) + // The child inherits Tokio runtime thread-local storage from the parent's + // master listener and connection handlers. These TLS destructors reference + // threads that don't exist in the child, causing spurious leak reports. + // We disable leak detection in the child since the inherited memory will + // be reclaimed when the child process exits. + __lsan_disable(); +#endif dd_internal_handle_fork(); } else { + // PARENT PROCESS + // Restart master listener if it was shut down before fork + dd_postfork_parent(); + #if JOIN_BGS_BEFORE_FORK if (!get_global_DD_TRACE_SIDECAR_TRACE_SENDER()) { ddtrace_coms_restart_writer(); diff --git a/ext/sidecar.c b/ext/sidecar.c index 4ae29295b64..7ad27863e84 100644 --- a/ext/sidecar.c +++ b/ext/sidecar.c @@ -9,6 +9,7 @@ #include #include #include +#include
#include "sidecar.h" #include "live_debugger.h" #include "telemetry.h" @@ -23,6 +24,7 @@ ZEND_EXTERN_MODULE_GLOBALS(ddtrace); ddog_Endpoint *ddtrace_endpoint; ddog_Endpoint *dogstatsd_endpoint; // always set when ddtrace_endpoint is set struct ddog_InstanceId *ddtrace_sidecar_instance_id; +ddtrace_pid_t ddtrace_master_pid = 0; static uint8_t dd_sidecar_formatted_session_id[36]; static inline void dd_set_endpoint_test_token(ddog_Endpoint *endpoint) { @@ -152,6 +154,9 @@ static void dd_sidecar_on_reconnect(ddog_SidecarTransport *transport) { } +// Forward declaration (non-static for export to handlers_pcntl.c) +ddog_SidecarTransport *dd_sidecar_connection_factory(void); + static ddog_SidecarTransport *dd_sidecar_connection_factory_ex(bool is_fork) { // Should not happen, unless the agent url is malformed if (!ddtrace_endpoint) { @@ -172,13 +177,16 @@ static ddog_SidecarTransport *dd_sidecar_connection_factory_ex(bool is_fork) { } ddog_SidecarTransport *sidecar_transport; - if (!ddtrace_ffi_try("Failed connecting to the sidecar", ddog_sidecar_connect_php(&sidecar_transport, logpath, dd_zend_string_to_CharSlice(get_global_DD_TRACE_LOG_LEVEL()), get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED(), dd_sidecar_on_reconnect, ddtrace_endpoint))) { + if (!ddtrace_ffi_try("Failed connecting to sidecar as worker", + ddog_sidecar_connect_worker((int32_t)ddtrace_master_pid, &sidecar_transport))) { dd_free_endpoints(); return NULL; } dd_sidecar_post_connect(&sidecar_transport, is_fork, logpath); + ddtrace_sidecar_reconnect(&sidecar_transport, dd_sidecar_connection_factory); + return sidecar_transport; } @@ -210,24 +218,117 @@ bool ddtrace_sidecar_maybe_enable_appsec(bool *appsec_activation, bool *appsec_c #endif } -void ddtrace_sidecar_setup(bool appsec_activation, bool appsec_config) { +void ddtrace_sidecar_minit(bool appsec_activation, bool appsec_config) { + UNUSED(appsec_activation, appsec_config); + + if (ddtrace_master_pid == 0) { +#ifdef _WIN32 + ddtrace_master_pid = _getpid(); +#else + ddtrace_master_pid = getpid(); +#endif + } +} + +static void ddtrace_sidecar_setup_master(bool appsec_activation, bool appsec_config) { +#ifndef _WIN32 + ddtrace_pid_t current_pid = getpid(); + bool is_child_process = (ddtrace_master_pid != 0 && current_pid != ddtrace_master_pid); + + if (is_child_process) { + return; + } +#endif + ddtrace_set_non_resettable_sidecar_globals(); ddtrace_set_resettable_sidecar_globals(); ddog_init_remote_config(get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED(), appsec_activation, appsec_config); - ddtrace_sidecar = dd_sidecar_connection_factory(); - if (!ddtrace_sidecar) { // Something went wrong + if (!ddtrace_ffi_try("Failed starting sidecar master listener", ddog_sidecar_connect_master((int32_t)ddtrace_master_pid))) { + LOG(WARN, "Failed to start sidecar master listener"); + if (ddtrace_endpoint) { + dd_free_endpoints(); + } + return; + } + + ddog_SidecarTransport *sidecar_transport = NULL; + if (!ddtrace_ffi_try("Failed connecting master to sidecar", ddog_sidecar_connect_worker((int32_t)ddtrace_master_pid, &sidecar_transport))) { + LOG(WARN, "Failed to connect master process to sidecar"); if (ddtrace_endpoint) { dd_free_endpoints(); } + return; } + char logpath[MAXPATHLEN]; + int error_fd = atomic_load(&ddtrace_error_log_fd); + if (error_fd == -1 || ddtrace_get_fd_path(error_fd, logpath) < 0) { + *logpath = 0; + } + + dd_sidecar_post_connect(&sidecar_transport, false, logpath); + ddtrace_sidecar = sidecar_transport; + if (get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { ddtrace_telemetry_first_init(); } } +void ddtrace_sidecar_rinit(void) { + if (get_DD_TRACE_GIT_METADATA_ENABLED()) { + zval git_object; + ZVAL_UNDEF(&git_object); + ddtrace_inject_git_metadata(&git_object); + if (Z_TYPE(git_object) == IS_OBJECT) { + ddtrace_git_metadata *git_metadata = (ddtrace_git_metadata *) Z_OBJ(git_object); + if (Z_TYPE(git_metadata->property_commit) == IS_STRING) { + UNUSED(ddog_Vec_Tag_push(&DDTRACE_G(active_global_tags), DDOG_CHARSLICE_C("git.commit.sha"), + dd_zend_string_to_CharSlice(Z_STR(git_metadata->property_commit)))); + } + if (Z_TYPE(git_metadata->property_repository) == IS_STRING) { + UNUSED(ddog_Vec_Tag_push(&DDTRACE_G(active_global_tags), DDOG_CHARSLICE_C("git.repository_url"), + dd_zend_string_to_CharSlice(Z_STR(git_metadata->property_repository)))); + } + OBJ_RELEASE(&git_metadata->std); + } + } + + ddtrace_sidecar_submit_root_span_data_direct_defaults(&ddtrace_sidecar, NULL); +} + +void ddtrace_sidecar_setup(bool appsec_activation, bool appsec_config) { + ddtrace_sidecar_setup_master(appsec_activation, appsec_config); +} + +bool ddtrace_sidecar_connect_worker_after_fork(void) { + if (!ddtrace_endpoint || ddtrace_master_pid == 0) { + return false; + } + + ddog_SidecarTransport *sidecar_transport = NULL; + if (!ddtrace_ffi_try("Failed connecting worker to sidecar after fork", + ddog_sidecar_connect_worker((int32_t)ddtrace_master_pid, &sidecar_transport))) { + return false; + } + + char logpath[MAXPATHLEN]; + int error_fd = atomic_load(&ddtrace_error_log_fd); + if (error_fd == -1 || ddtrace_get_fd_path(error_fd, logpath) < 0) { + *logpath = 0; + } + + dd_sidecar_post_connect(&sidecar_transport, true, logpath); + + if (ddtrace_sidecar) { + ddog_sidecar_transport_drop(ddtrace_sidecar); + } + ddtrace_sidecar = sidecar_transport; + + return true; +} + void ddtrace_sidecar_ensure_active(void) { if (ddtrace_sidecar) { ddtrace_sidecar_reconnect(&ddtrace_sidecar, dd_sidecar_connection_factory); @@ -255,17 +356,31 @@ void ddtrace_sidecar_finalize(bool clear_id) { } void ddtrace_sidecar_shutdown(void) { - if (ddtrace_sidecar_instance_id) { - ddog_sidecar_instanceId_drop(ddtrace_sidecar_instance_id); + // Shutdown master listener thread FIRST if this is the master process + // This must happen before dropping the sidecar transport to ensure clean shutdown +#ifndef _WIN32 + ddtrace_pid_t current_pid = getpid(); + if (ddtrace_master_pid != 0 && current_pid == ddtrace_master_pid) { + ddtrace_ffi_try("Failed shutting down master listener", ddog_sidecar_shutdown_master_listener()); + } +#else + if (ddtrace_master_pid != 0 && _getpid() == ddtrace_master_pid) { + ddtrace_ffi_try("Failed shutting down master listener", ddog_sidecar_shutdown_master_listener()); + } +#endif + + if (ddtrace_sidecar) { + ddog_sidecar_transport_drop(ddtrace_sidecar); } if (ddtrace_endpoint) { dd_free_endpoints(); } - if (ddtrace_sidecar) { - ddog_sidecar_transport_drop(ddtrace_sidecar); + if (ddtrace_sidecar_instance_id) { + ddog_sidecar_instanceId_drop(ddtrace_sidecar_instance_id); } + } void ddtrace_force_new_instance_id(void) { @@ -557,30 +672,30 @@ void ddtrace_sidecar_activate(void) { } ZEND_HASH_FOREACH_END(); } -void ddtrace_sidecar_rinit(void) { - if (get_DD_TRACE_GIT_METADATA_ENABLED()) { - zval git_object; - ZVAL_UNDEF(&git_object); - ddtrace_inject_git_metadata(&git_object); - if (Z_TYPE(git_object) == IS_OBJECT) { - ddtrace_git_metadata *git_metadata = (ddtrace_git_metadata *) Z_OBJ(git_object); - if (Z_TYPE(git_metadata->property_commit) == IS_STRING) { - UNUSED(ddog_Vec_Tag_push(&DDTRACE_G(active_global_tags), DDOG_CHARSLICE_C("git.commit.sha"), - dd_zend_string_to_CharSlice(Z_STR(git_metadata->property_commit)))); - } - if (Z_TYPE(git_metadata->property_repository) == IS_STRING) { - UNUSED(ddog_Vec_Tag_push(&DDTRACE_G(active_global_tags), DDOG_CHARSLICE_C("git.repository_url"), - dd_zend_string_to_CharSlice(Z_STR(git_metadata->property_repository)))); +void ddtrace_sidecar_rshutdown(void) { + ddog_Vec_Tag_drop(DDTRACE_G(active_global_tags)); + + // For CLI SAPI, shut down the master listener thread here in RSHUTDOWN + // since the process will exit after this request. For other SAPIs, + // the master listener persists across requests and shuts down in MSHUTDOWN. + if (strcmp(sapi_module.name, "cli") == 0) { +#ifndef _WIN32 + ddtrace_pid_t current_pid = getpid(); + if (ddtrace_master_pid != 0 && current_pid == ddtrace_master_pid) { + // CRITICAL: Close the worker connection BEFORE shutting down the master listener. + // The master process has its own worker connection to the master listener. + // If we don't close it first, the handler thread waiting on this connection + // will never exit, causing the listener thread join to timeout. + if (ddtrace_sidecar) { + ddog_sidecar_transport_drop(ddtrace_sidecar); + ddtrace_sidecar = NULL; } - OBJ_RELEASE(&git_metadata->std); + + ddtrace_ffi_try("Failed shutting down master listener in RSHUTDOWN", + ddog_sidecar_shutdown_master_listener()); } +#endif } - - ddtrace_sidecar_submit_root_span_data_direct_defaults(&ddtrace_sidecar, NULL); -} - -void ddtrace_sidecar_rshutdown(void) { - ddog_Vec_Tag_drop(DDTRACE_G(active_global_tags)); } bool ddtrace_alter_test_session_token(zval *old_value, zval *new_value, zend_string *new_str) { diff --git a/ext/sidecar.h b/ext/sidecar.h index b3593ceaf1c..6c82f3081a3 100644 --- a/ext/sidecar.h +++ b/ext/sidecar.h @@ -6,10 +6,18 @@ #include "ddtrace_export.h" #include "ddtrace.h" #include "zend_string.h" +#ifndef _WIN32 +#include +typedef pid_t ddtrace_pid_t; +#else +#include +typedef int ddtrace_pid_t; +#endif extern ddog_SidecarTransport *ddtrace_sidecar; extern ddog_Endpoint *ddtrace_endpoint; extern struct ddog_InstanceId *ddtrace_sidecar_instance_id; +extern ddtrace_pid_t ddtrace_master_pid; DDTRACE_PUBLIC const uint8_t *ddtrace_get_formatted_session_id(void); struct telemetry_rc_info { @@ -20,7 +28,9 @@ struct telemetry_rc_info { }; DDTRACE_PUBLIC struct telemetry_rc_info ddtrace_get_telemetry_rc_info(void); +void ddtrace_sidecar_minit(bool appsec_activation, bool appsec_config); void ddtrace_sidecar_setup(bool appsec_activation, bool appsec_config); +bool ddtrace_sidecar_connect_worker_after_fork(void); bool ddtrace_sidecar_maybe_enable_appsec(bool *appsec_activation, bool *appsec_config); void ddtrace_sidecar_ensure_active(void); void ddtrace_sidecar_finalize(bool clear_id); @@ -34,6 +44,8 @@ ddog_Endpoint *ddtrace_sidecar_agent_endpoint(void); void ddtrace_sidecar_submit_root_span_data_direct_defaults(ddog_SidecarTransport **transport, ddtrace_root_span_data *root); void ddtrace_sidecar_submit_root_span_data_direct(ddog_SidecarTransport **transport, ddtrace_root_span_data *root, zend_string *cfg_service, zend_string *cfg_env, zend_string *cfg_version); +ddog_SidecarTransport *dd_sidecar_connection_factory(void); + void ddtrace_sidecar_send_debugger_data(ddog_Vec_DebuggerPayload payloads); void ddtrace_sidecar_send_debugger_datum(ddog_DebuggerPayload *payload); diff --git a/libdatadog b/libdatadog index 629bce09547..a0ffd63bbcb 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit 629bce09547abc77d7bbda623921f97eb5611949 +Subproject commit a0ffd63bbcbee90b7afaf545ecc16ed895427a55