diff --git a/Cargo.lock b/Cargo.lock index c013e8b642f..9e1d81df7a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -400,9 +400,9 @@ dependencies = [ [[package]] name = "bolero" -version = "0.13.0" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeae9bae5224be9a368c3b4f8cc83451473d55bcc1aa522cf56a48828dcf7f6e" +checksum = "0ff44d278fc0062c95327087ed96b3d256906d1d8f579e534a3de8d6b386913a" dependencies = [ "bolero-afl", "bolero-engine", @@ -426,12 +426,11 @@ dependencies = [ [[package]] name = "bolero-engine" -version = "0.13.0" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b2496696794ca673fd085c7237d2b64b825bfe0dedbd5e947ca633532d8132b" +checksum = "dca199170a7c92c669c1019f9219a316b66bcdcfa4b36cac5a460a4c1a851aba" dependencies = [ "anyhow", - "backtrace", "bolero-generator", "lazy_static", "pretty-hex", @@ -441,9 +440,9 @@ dependencies = [ [[package]] name = "bolero-generator" -version = "0.13.1" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06b0c9bd47ec1d25ce698a2b4a0c3d8e527d0046919a04c800035e30bf4ea6d1" +checksum = "98a5782f2650f80d533f58ec339c6dce4cc5428f9c2755894f98156f52af81f2" dependencies = [ "bolero-generator-derive", "either", @@ -454,14 +453,14 @@ dependencies = [ [[package]] name = "bolero-generator-derive" -version = "0.13.0" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385f38498675c06532bed10cd40a4313691a8fb7d9b698fcf096739d422e1764" +checksum = "9a21a3b022507b9edd2050caf370d945e398c1a7c8455531220fa3968c45d29e" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.96", ] [[package]] diff --git a/appsec/tests/integration/src/test/www/base/public/endpoint_collection.php b/appsec/tests/integration/src/test/www/base/public/endpoint_collection.php index 5ce975ebef1..09bf8f05f65 100644 --- a/appsec/tests/integration/src/test/www/base/public/endpoint_collection.php +++ b/appsec/tests/integration/src/test/www/base/public/endpoint_collection.php @@ -3,6 +3,7 @@ echo "are_endpoints_collected before: " . (\DDTrace\are_endpoints_collected() ? 'true' : 'false') . "\n"; \DDTrace\add_endpoint('/test_random_endpoint', 'http.request', 'GET /test_random_endpoint', 'GET'); +\DDTrace\flush_endpoints(); sleep(1); diff --git a/components-rs/common.h b/components-rs/common.h index 004e00962ea..4ff8d1b9603 100644 --- a/components-rs/common.h +++ b/components-rs/common.h @@ -313,6 +313,19 @@ typedef enum ddog_Log { DDOG_LOG_HOOK_TRACE = (5 | (4 << 4)), } ddog_Log; +typedef enum ddog_Method { + DDOG_METHOD_GET = 0, + DDOG_METHOD_POST = 1, + DDOG_METHOD_PUT = 2, + DDOG_METHOD_DELETE = 3, + DDOG_METHOD_PATCH = 4, + DDOG_METHOD_HEAD = 5, + DDOG_METHOD_OPTIONS = 6, + DDOG_METHOD_TRACE = 7, + DDOG_METHOD_CONNECT = 8, + DDOG_METHOD_OTHER = 9, +} ddog_Method; + typedef enum ddog_MetricKind { DDOG_METRIC_KIND_COUNT, DDOG_METRIC_KIND_GAUGE, @@ -1007,19 +1020,6 @@ typedef enum ddog_DynamicInstrumentationConfigState { DDOG_DYNAMIC_INSTRUMENTATION_CONFIG_STATE_NOT_SET, } ddog_DynamicInstrumentationConfigState; -typedef enum ddog_Method { - DDOG_METHOD_GET = 0, - DDOG_METHOD_POST = 1, - DDOG_METHOD_PUT = 2, - DDOG_METHOD_DELETE = 3, - DDOG_METHOD_PATCH = 4, - DDOG_METHOD_HEAD = 5, - DDOG_METHOD_OPTIONS = 6, - DDOG_METHOD_TRACE = 7, - DDOG_METHOD_CONNECT = 8, - DDOG_METHOD_OTHER = 9, -} ddog_Method; - typedef struct ddog_AgentInfoReader ddog_AgentInfoReader; typedef struct ddog_AgentRemoteConfigReader ddog_AgentRemoteConfigReader; diff --git a/components-rs/ddtrace.h b/components-rs/ddtrace.h index 391bf048b55..d939818b087 100644 --- a/components-rs/ddtrace.h +++ b/components-rs/ddtrace.h @@ -19,8 +19,6 @@ extern ddog_VecRemoteConfigCapabilities DDTRACE_REMOTE_CONFIG_CAPABILITIES; extern const uint8_t *DDOG_PHP_FUNCTION; -extern struct ddog_SidecarTransport *ddtrace_sidecar; - /** * # Safety * Must be called from a single-threaded context, such as MINIT. @@ -153,6 +151,15 @@ void ddog_sidecar_telemetry_addDependency_buffer(struct ddog_SidecarActionsBuffe ddog_CharSlice dependency_name, ddog_CharSlice dependency_version); +/** + * Enqueues an endpoint into a telemetry actions buffer (to be sent via ddog_sidecar_telemetry_buffer_flush). + */ +void ddog_sidecar_telemetry_addEndpoint_buffer(struct ddog_SidecarActionsBuffer *buffer, + enum ddog_Method method, + ddog_CharSlice path, + ddog_CharSlice operation_name, + ddog_CharSlice resource_name); + void ddog_sidecar_telemetry_enqueueConfig_buffer(struct ddog_SidecarActionsBuffer *buffer, ddog_CharSlice config_key, ddog_CharSlice config_value, diff --git a/components-rs/sidecar.rs b/components-rs/sidecar.rs index 210f0ad9d15..d7e6a687666 100644 --- a/components-rs/sidecar.rs +++ b/components-rs/sidecar.rs @@ -170,10 +170,6 @@ pub extern "C" fn ddog_sidecar_connect_php( MaybeError::None } -#[no_mangle] -#[allow(non_upper_case_globals)] -pub static mut ddtrace_sidecar: *mut SidecarTransport = std::ptr::null_mut(); - #[no_mangle] pub extern "C" fn ddtrace_sidecar_reconnect( transport: &mut Box, diff --git a/components-rs/telemetry.rs b/components-rs/telemetry.rs index 491e6c97254..8d2f818b940 100644 --- a/components-rs/telemetry.rs +++ b/components-rs/telemetry.rs @@ -1,11 +1,10 @@ use crate::log::Log; -use datadog_sidecar::service::telemetry::path_for_telemetry; +use datadog_sidecar::service::telemetry::{path_for_telemetry, TelemetryCachedClientShmData}; use hashbrown::{Equivalent, HashMap}; -use std::collections::HashSet; use std::ffi::CString; use std::path::PathBuf; -use std::time::{Duration, SystemTime}; +use std::time::Duration; use datadog_ipc::platform::NamedShmHandle; use datadog_sidecar::one_way_shared_memory::{open_named_shm, OneWayShmReader}; @@ -126,6 +125,24 @@ pub unsafe extern "C" fn ddog_sidecar_telemetry_addDependency_buffer( buffer.buffer.push(SidecarAction::Telemetry(action)); } +/// Enqueues an endpoint into a telemetry actions buffer (to be sent via ddog_sidecar_telemetry_buffer_flush). +#[no_mangle] +pub unsafe extern "C" fn ddog_sidecar_telemetry_addEndpoint_buffer( + buffer: &mut SidecarActionsBuffer, + method: data::Method, + path: CharSlice, + operation_name: CharSlice, + resource_name: CharSlice, +) { + let action = TelemetryActions::AddEndpoint(data::Endpoint { + method: Some(method), + path: Some(path.to_utf8_lossy().into_owned()), + operation_name: operation_name.to_utf8_lossy().into_owned(), + resource_name: resource_name.to_utf8_lossy().into_owned(), + }); + buffer.buffer.push(SidecarAction::Telemetry(action)); +} + #[no_mangle] pub unsafe extern "C" fn ddog_sidecar_telemetry_enqueueConfig_buffer( buffer: &mut SidecarActionsBuffer, @@ -237,10 +254,7 @@ pub unsafe extern "C" fn ddog_sidecar_telemetry_add_integration_log_buffer( } pub struct ShmCache { - pub config_sent: bool, - pub integrations: HashSet, - pub composer_paths: HashSet, - pub last_endpoints_push: SystemTime, + pub shared: TelemetryCachedClientShmData, pub reader: OneWayShmReader, } @@ -269,7 +283,7 @@ pub unsafe extern "C" fn ddog_sidecar_telemetry_config_sent( service: CharSlice, env: CharSlice, ) -> bool { - ddog_sidecar_telemetry_cache_get_or_update(cache, service, env).config_sent + ddog_sidecar_telemetry_cache_get_or_update(cache, service, env).shared.config_sent } unsafe fn ddog_sidecar_telemetry_cache_get_or_update<'a>( @@ -287,21 +301,13 @@ unsafe fn ddog_sidecar_telemetry_cache_get_or_update<'a>( if changed { buf = newbuf; } else { - cache.config_sent = false; - cache.integrations.clear(); - cache.composer_paths.clear(); - cache.last_endpoints_push = SystemTime::UNIX_EPOCH; + cache.shared = TelemetryCachedClientShmData::default(); return; } } - if let Ok((config_sent, integrations, composer_paths, last_endpoints_push)) = - bincode::deserialize::<(bool, HashSet, HashSet, SystemTime)>(buf) - { - cache.config_sent = config_sent; - cache.integrations = integrations; - cache.composer_paths = composer_paths; - cache.last_endpoints_push = last_endpoints_push; + if let Ok(shared) = bincode::deserialize::(buf) { + cache.shared = shared; } } } @@ -319,10 +325,7 @@ unsafe fn ddog_sidecar_telemetry_cache_get_or_update<'a>( 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(), - last_endpoints_push: SystemTime::UNIX_EPOCH, + shared: TelemetryCachedClientShmData::default(), }).into_mut(); refresh_cache(cached_entry); @@ -347,10 +350,10 @@ pub unsafe extern "C" fn ddog_sidecar_telemetry_filter_flush( .into_iter() .filter(|action| match action { SidecarAction::Telemetry(TelemetryActions::AddIntegration(integration)) => { - !cache_entry.integrations.contains(&integration.name) + !cache_entry.shared.integrations.contains(integration) } SidecarAction::PhpComposerTelemetryFile(path) => { - !cache_entry.composer_paths.contains(path) + !cache_entry.shared.composer_paths.contains(path) } _ => true, }) @@ -374,6 +377,5 @@ pub unsafe extern "C" fn ddog_sidecar_telemetry_are_endpoints_collected( env: CharSlice, ) -> bool { let cache_entry = ddog_sidecar_telemetry_cache_get_or_update(cache, service, env); - let result = cache_entry.last_endpoints_push.elapsed().map_or(false, |d| d < Duration::from_secs(1800)); // 30 minutes - result + cache_entry.shared.last_endpoints_push.elapsed().map_or(false, |d| d < Duration::from_secs(1800)) // 30 minutes } diff --git a/ext/auto_flush.c b/ext/auto_flush.c index 0da8f6864c4..88cce0a2cac 100644 --- a/ext/auto_flush.c +++ b/ext/auto_flush.c @@ -47,7 +47,7 @@ ZEND_RESULT_CODE ddtrace_flush_tracer(bool force_on_startup, bool collect_cycles char *url = ddtrace_agent_url(); if (get_global_DD_TRACE_SIDECAR_TRACE_SENDER()) { - if (ddtrace_sidecar) { + if (DDTRACE_G(sidecar)) { ddog_SenderParameters parameters = { .tracer_headers_tags = { .container_id = ddtrace_get_container_id(), @@ -59,7 +59,7 @@ ZEND_RESULT_CODE ddtrace_flush_tracer(bool force_on_startup, bool collect_cycles .client_computed_top_level = false, .client_computed_stats = !get_global_DD_APM_TRACING_ENABLED(), }, - .transport = ddtrace_sidecar, + .transport = DDTRACE_G(sidecar), .instance_id = ddtrace_sidecar_instance_id, .limit = limit, .n_requests = get_global_DD_TRACE_AGENT_FLUSH_AFTER_N_REQUESTS(), diff --git a/ext/ddtrace.c b/ext/ddtrace.c index 94d9f0d3d9f..6df5d43a6b6 100644 --- a/ext/ddtrace.c +++ b/ext/ddtrace.c @@ -409,21 +409,21 @@ static inline void dd_alter_prop(size_t prop_offset, zval *old_value, zval *new_ bool ddtrace_alter_dd_service(zval *old_value, zval *new_value, zend_string *new_str) { dd_alter_prop(XtOffsetOf(ddtrace_span_properties, property_service), old_value, new_value, new_str); if (DDTRACE_G(request_initialized)) { - ddtrace_sidecar_submit_root_span_data_direct(&ddtrace_sidecar, NULL, new_str, get_DD_ENV(), get_DD_VERSION()); + ddtrace_sidecar_submit_root_span_data_direct(&DDTRACE_G(sidecar), NULL, new_str, get_DD_ENV(), get_DD_VERSION()); } return true; } bool ddtrace_alter_dd_env(zval *old_value, zval *new_value, zend_string *new_str) { dd_alter_prop(XtOffsetOf(ddtrace_span_properties, property_env), old_value, new_value, new_str); if (DDTRACE_G(request_initialized)) { - ddtrace_sidecar_submit_root_span_data_direct(&ddtrace_sidecar, NULL, get_DD_SERVICE(), new_str, get_DD_VERSION()); + ddtrace_sidecar_submit_root_span_data_direct(&DDTRACE_G(sidecar), NULL, get_DD_SERVICE(), new_str, get_DD_VERSION()); } return true; } bool ddtrace_alter_dd_version(zval *old_value, zval *new_value, zend_string *new_str) { dd_alter_prop(XtOffsetOf(ddtrace_span_properties, property_version), old_value, new_value, new_str); if (DDTRACE_G(request_initialized)) { - ddtrace_sidecar_submit_root_span_data_direct(&ddtrace_sidecar, NULL, get_DD_SERVICE(), get_DD_ENV(), new_str); + ddtrace_sidecar_submit_root_span_data_direct(&DDTRACE_G(sidecar), NULL, get_DD_SERVICE(), get_DD_ENV(), new_str); } return true; } @@ -697,6 +697,9 @@ static PHP_GSHUTDOWN_FUNCTION(ddtrace) { zend_hash_destroy(&ddtrace_globals->git_metadata); + // Drop the per-thread sidecar transport (thread-lifetime, one per thread). + ddtrace_sidecar_gshutdown(); + tsrm_mutex_free(ddtrace_globals->sidecar_universal_service_tags_mutex); #ifdef CXA_THREAD_ATEXIT_WRAPPER @@ -1594,8 +1597,8 @@ static PHP_MSHUTDOWN_FUNCTION(ddtrace) { ddtrace_coms_mshutdown_proxy_env(); } else /* ! part of the if outside the ifdef */ #endif - if (get_global_DD_TRACE_FORCE_FLUSH_ON_SHUTDOWN() && ddtrace_sidecar) { - ddog_sidecar_flush_traces(&ddtrace_sidecar); + if (get_global_DD_TRACE_FORCE_FLUSH_ON_SHUTDOWN() && DDTRACE_G(sidecar)) { + ddog_sidecar_flush_traces(&DDTRACE_G(sidecar)); } ddtrace_log_mshutdown(); @@ -2811,6 +2814,10 @@ PHP_FUNCTION(DDTrace_dogstatsd_set) { PHP_FUNCTION(DDTrace_are_endpoints_collected) { UNUSED(execute_data); + if (!DDTRACE_G(sidecar) || !ddtrace_sidecar_instance_id || !DDTRACE_G(sidecar_queue_id)) { + RETURN_TRUE; // Skip overhead if unnecessary + } + if (!DDTRACE_G(last_service_name) || !DDTRACE_G(last_env_name)) { RETURN_FALSE; } @@ -2863,7 +2870,7 @@ PHP_FUNCTION(DDTrace_add_endpoint) { RETURN_FALSE; } - if (!ddtrace_sidecar || !ddtrace_sidecar_instance_id || !DDTRACE_G(sidecar_queue_id)) { + if (!DDTRACE_G(sidecar) || !ddtrace_sidecar_instance_id || !DDTRACE_G(sidecar_queue_id)) { RETURN_FALSE; } @@ -2872,10 +2879,6 @@ PHP_FUNCTION(DDTrace_add_endpoint) { ddog_CharSlice operation_name_slice = dd_zend_string_to_CharSlice(operation_name); ddog_CharSlice resource_name_slice = dd_zend_string_to_CharSlice(resource_name); - ddog_MaybeError result = ddog_sidecar_telemetry_addEndpoint( - &ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), method_enum, path_slice, operation_name_slice, - resource_name_slice); - LOG_LINE(DEBUG, "Adding endpoint: %.*s (%zu) - operation_name: %.*s (%zu) - resource_name: %.*s (%zu) - method: %.*s (%zu)", (int)path_slice.len, (char *)path_slice.ptr, path_slice.len, @@ -2883,14 +2886,28 @@ PHP_FUNCTION(DDTrace_add_endpoint) { (int)resource_name_slice.len, (char *)resource_name_slice.ptr, resource_name_slice.len, (int)method->len, ZSTR_VAL(method), (size_t)method->len); - if (result.tag == DDOG_OPTION_ERROR_SOME_ERROR) { - ddog_CharSlice message = ddog_Error_message(&result.some); - LOG_LINE(ERROR, "Error submitting endpoint to sidecar: %.*s", (int)message.len, (char *)message.ptr); - ZVAL_FALSE(return_value); - } else { - ZVAL_TRUE(return_value); + ddog_sidecar_telemetry_addEndpoint_buffer(ddtrace_telemetry_buffer(), method_enum, path_slice, operation_name_slice, resource_name_slice); + + RETURN_TRUE; +} + +PHP_FUNCTION(DDTrace_flush_endpoints) { + UNUSED(execute_data); + UNUSED(return_value); + + if (!DDTRACE_G(sidecar) || !ddtrace_sidecar_instance_id || !DDTRACE_G(sidecar_queue_id) || !DDTRACE_G(telemetry_buffer)) { + return; + } + + if (!DDTRACE_G(last_service_name) || !DDTRACE_G(last_env_name)) { + return; } - ddog_MaybeError_drop(result); + + ddog_CharSlice service_name = dd_zend_string_to_CharSlice(DDTRACE_G(last_service_name)); + ddog_CharSlice env_name = dd_zend_string_to_CharSlice(DDTRACE_G(last_env_name)); + + ddtrace_ffi_try("Failed flushing endpoint telemetry buffer", + ddog_sidecar_telemetry_filter_flush(&DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), ddtrace_telemetry_buffer(), ddtrace_telemetry_cache(), service_name, env_name)); } PHP_FUNCTION(dd_trace_send_traces_via_thread) { @@ -2992,7 +3009,7 @@ PHP_FUNCTION(dd_trace_internal_fn) { } } else if (params_count == 1 && FUNCTION_NAME_MATCHES("detect_composer_installed_json")) { ddog_CharSlice path = dd_zend_string_to_CharSlice(Z_STR_P(ZVAL_VARARG_PARAM(params, 0))); - ddtrace_detect_composer_installed_json(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), path); + ddtrace_detect_composer_installed_json(&DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), path); RETVAL_TRUE; } else if (params_count == 2 && FUNCTION_NAME_MATCHES("mark_integration_loaded")) { zval *name = ZVAL_VARARG_PARAM(params, 0); @@ -3020,24 +3037,24 @@ PHP_FUNCTION(dd_trace_internal_fn) { RETVAL_TRUE; } } else if (FUNCTION_NAME_MATCHES("dump_sidecar")) { - if (!ddtrace_sidecar) { + if (!DDTRACE_G(sidecar)) { RETURN_FALSE; } - ddog_CharSlice slice = ddog_sidecar_dump(&ddtrace_sidecar); + ddog_CharSlice slice = ddog_sidecar_dump(&DDTRACE_G(sidecar)); RETVAL_STRINGL(slice.ptr, slice.len); free((void *) slice.ptr); } else if (FUNCTION_NAME_MATCHES("stats_sidecar")) { - if (!ddtrace_sidecar) { + if (!DDTRACE_G(sidecar)) { RETURN_FALSE; } - ddog_CharSlice slice = ddog_sidecar_stats(&ddtrace_sidecar); + ddog_CharSlice slice = ddog_sidecar_stats(&DDTRACE_G(sidecar)); RETVAL_STRINGL(slice.ptr, slice.len); free((void *) slice.ptr); } else if (FUNCTION_NAME_MATCHES("break_sidecar_connection")) { - if (!ddtrace_sidecar) { + if (!DDTRACE_G(sidecar)) { RETURN_FALSE; } - ddog_sidecar_send_garbage(&ddtrace_sidecar); + ddog_sidecar_send_garbage(&DDTRACE_G(sidecar)); ddtrace_generate_runtime_id(); ddtrace_force_new_instance_id(); RETURN_TRUE; @@ -3067,8 +3084,8 @@ PHP_FUNCTION(dd_trace_internal_fn) { } } else #endif - if (ddtrace_sidecar) { - ddtrace_ffi_try("Failed synchronously flushing traces", ddog_sidecar_flush_traces(&ddtrace_sidecar)); + if (DDTRACE_G(sidecar)) { + ddtrace_ffi_try("Failed synchronously flushing traces", ddog_sidecar_flush_traces(&DDTRACE_G(sidecar))); } RETVAL_TRUE; #ifndef _WIN32 @@ -3184,8 +3201,8 @@ PHP_FUNCTION(dd_trace_synchronous_flush) { } } else #endif - if (ddtrace_sidecar) { - ddtrace_ffi_try("Failed synchronously flushing traces", ddog_sidecar_flush_traces(&ddtrace_sidecar)); + if (DDTRACE_G(sidecar)) { + ddtrace_ffi_try("Failed synchronously flushing traces", ddog_sidecar_flush_traces(&DDTRACE_G(sidecar))); } RETURN_NULL(); } diff --git a/ext/ddtrace.h b/ext/ddtrace.h index d0a4a2cd62b..84def7e4f47 100644 --- a/ext/ddtrace.h +++ b/ext/ddtrace.h @@ -149,6 +149,7 @@ ZEND_BEGIN_MODULE_GLOBALS(ddtrace) zend_reference *curl_multi_injecting_spans; char *cgroup_file; + ddog_SidecarTransport *sidecar; ddog_QueueId sidecar_queue_id; MUTEX_T sidecar_universal_service_tags_mutex; ddog_AgentRemoteConfigReader *agent_config_reader; @@ -166,6 +167,7 @@ ZEND_BEGIN_MODULE_GLOBALS(ddtrace) bool request_initialized; HashTable telemetry_spans_created_per_integration; ddog_SidecarActionsBuffer *telemetry_buffer; + ddog_SidecarActionsBuffer *metrics_buffer; bool asm_event_emitted; diff --git a/ext/ddtrace.stub.php b/ext/ddtrace.stub.php index ff371b1d1a5..e4a388e157d 100644 --- a/ext/ddtrace.stub.php +++ b/ext/ddtrace.stub.php @@ -839,6 +839,12 @@ function are_endpoints_collected(): bool {} * @param string $method The method of the endpoint */ function add_endpoint(string $path, string $operation_name, string $resource_name, string $method): bool {} + + /** + * Flush all buffered endpoints to the sidecar immediately. + * Call this once after batching all add_endpoint() calls. + */ + function flush_endpoints(): void {} } namespace DDTrace\System { diff --git a/ext/ddtrace_arginfo.h b/ext/ddtrace_arginfo.h index 14e3f266ab6..a6e618143b5 100644 --- a/ext/ddtrace_arginfo.h +++ b/ext/ddtrace_arginfo.h @@ -1,5 +1,5 @@ -/* This is a generated file, edit the .stub.php file instead. - * Stub hash: 9539356c50d39ee983e578d17d1796e50b761f86 */ +/* This is a generated file, edit ddtrace.stub.php instead. + * Stub hash: b7ca6da3e6dff3aa5fdb4bf9ea0811b3456030fc */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_trace_method, 0, 3, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, className, IS_STRING, 0) @@ -174,6 +174,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_add_endpoint, 0, 4, _IS_ ZEND_ARG_TYPE_INFO(0, method, IS_STRING, 0) ZEND_END_ARG_INFO() +#define arginfo_DDTrace_flush_endpoints arginfo_DDTrace_flush + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_DDTrace_System_container_id, 0, 0, IS_STRING, 1) ZEND_END_ARG_INFO() @@ -391,6 +393,7 @@ ZEND_FUNCTION(DDTrace_resource_weak_store); ZEND_FUNCTION(DDTrace_resource_weak_get); ZEND_FUNCTION(DDTrace_are_endpoints_collected); ZEND_FUNCTION(DDTrace_add_endpoint); +ZEND_FUNCTION(DDTrace_flush_endpoints); ZEND_FUNCTION(DDTrace_System_container_id); ZEND_FUNCTION(DDTrace_System_process_tags_base_hash); ZEND_FUNCTION(DDTrace_Config_integration_analytics_enabled); @@ -485,6 +488,7 @@ static const zend_function_entry ext_functions[] = { ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "resource_weak_get"), zif_DDTrace_resource_weak_get, arginfo_DDTrace_resource_weak_get, 0, NULL, NULL) ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "are_endpoints_collected"), zif_DDTrace_are_endpoints_collected, arginfo_DDTrace_are_endpoints_collected, 0, NULL, NULL) ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "add_endpoint"), zif_DDTrace_add_endpoint, arginfo_DDTrace_add_endpoint, 0, NULL, NULL) + ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace", "flush_endpoints"), zif_DDTrace_flush_endpoints, arginfo_DDTrace_flush_endpoints, 0, NULL, NULL) ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace\\System", "container_id"), zif_DDTrace_System_container_id, arginfo_DDTrace_System_container_id, 0, NULL, NULL) ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace\\System", "process_tags_base_hash"), zif_DDTrace_System_process_tags_base_hash, arginfo_DDTrace_System_process_tags_base_hash, 0, NULL, NULL) ZEND_RAW_FENTRY(ZEND_NS_NAME("DDTrace\\Config", "integration_analytics_enabled"), zif_DDTrace_Config_integration_analytics_enabled, arginfo_DDTrace_Config_integration_analytics_enabled, 0, NULL, NULL) @@ -589,15 +593,15 @@ static zend_class_entry *register_class_DDTrace_SpanEvent(zend_class_entry *clas zval property_attributes_default_value; ZVAL_UNDEF(&property_attributes_default_value); - zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, 1); + zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, true); zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release(property_attributes_name); + zend_string_release_ex(property_attributes_name, true); zval property_timestamp_default_value; ZVAL_UNDEF(&property_timestamp_default_value); - zend_string *property_timestamp_name = zend_string_init("timestamp", sizeof("timestamp") - 1, 1); + zend_string *property_timestamp_name = zend_string_init("timestamp", sizeof("timestamp") - 1, true); zend_declare_typed_property(class_entry, property_timestamp_name, &property_timestamp_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(property_timestamp_name); + zend_string_release_ex(property_timestamp_name, true); return class_entry; } @@ -611,10 +615,10 @@ static zend_class_entry *register_class_DDTrace_ExceptionSpanEvent(zend_class_en zval property_exception_default_value; ZVAL_UNDEF(&property_exception_default_value); - zend_string *property_exception_name = zend_string_init("exception", sizeof("exception") - 1, 1); + zend_string *property_exception_name = zend_string_init("exception", sizeof("exception") - 1, true); zend_string *property_exception_class_Throwable = zend_string_init("Throwable", sizeof("Throwable")-1, 1); zend_declare_typed_property(class_entry, property_exception_name, &property_exception_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_exception_class_Throwable, 0, 0)); - zend_string_release(property_exception_name); + zend_string_release_ex(property_exception_name, true); return class_entry; } @@ -629,33 +633,33 @@ static zend_class_entry *register_class_DDTrace_SpanLink(zend_class_entry *class zval property_traceId_default_value; ZVAL_UNDEF(&property_traceId_default_value); - zend_string *property_traceId_name = zend_string_init("traceId", sizeof("traceId") - 1, 1); + zend_string *property_traceId_name = zend_string_init("traceId", sizeof("traceId") - 1, true); zend_declare_typed_property(class_entry, property_traceId_name, &property_traceId_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_traceId_name); + zend_string_release_ex(property_traceId_name, true); zval property_spanId_default_value; ZVAL_UNDEF(&property_spanId_default_value); - zend_string *property_spanId_name = zend_string_init("spanId", sizeof("spanId") - 1, 1); + zend_string *property_spanId_name = zend_string_init("spanId", sizeof("spanId") - 1, true); zend_declare_typed_property(class_entry, property_spanId_name, &property_spanId_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_spanId_name); + zend_string_release_ex(property_spanId_name, true); zval property_traceState_default_value; ZVAL_UNDEF(&property_traceState_default_value); - zend_string *property_traceState_name = zend_string_init("traceState", sizeof("traceState") - 1, 1); + zend_string *property_traceState_name = zend_string_init("traceState", sizeof("traceState") - 1, true); zend_declare_typed_property(class_entry, property_traceState_name, &property_traceState_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_traceState_name); + zend_string_release_ex(property_traceState_name, true); zval property_attributes_default_value; ZVAL_UNDEF(&property_attributes_default_value); - zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, 1); + zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, true); zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release(property_attributes_name); + zend_string_release_ex(property_attributes_name, true); zval property_droppedAttributesCount_default_value; ZVAL_UNDEF(&property_droppedAttributesCount_default_value); - zend_string *property_droppedAttributesCount_name = zend_string_init("droppedAttributesCount", sizeof("droppedAttributesCount") - 1, 1); + zend_string *property_droppedAttributesCount_name = zend_string_init("droppedAttributesCount", sizeof("droppedAttributesCount") - 1, true); zend_declare_typed_property(class_entry, property_droppedAttributesCount_name, &property_droppedAttributesCount_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(property_droppedAttributesCount_name); + zend_string_release_ex(property_droppedAttributesCount_name, true); return class_entry; } @@ -669,15 +673,15 @@ static zend_class_entry *register_class_DDTrace_GitMetadata(void) zval property_commitSha_default_value; ZVAL_EMPTY_STRING(&property_commitSha_default_value); - zend_string *property_commitSha_name = zend_string_init("commitSha", sizeof("commitSha") - 1, 1); + zend_string *property_commitSha_name = zend_string_init("commitSha", sizeof("commitSha") - 1, true); zend_declare_typed_property(class_entry, property_commitSha_name, &property_commitSha_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_commitSha_name); + zend_string_release_ex(property_commitSha_name, true); zval property_repositoryUrl_default_value; ZVAL_EMPTY_STRING(&property_repositoryUrl_default_value); - zend_string *property_repositoryUrl_name = zend_string_init("repositoryUrl", sizeof("repositoryUrl") - 1, 1); + zend_string *property_repositoryUrl_name = zend_string_init("repositoryUrl", sizeof("repositoryUrl") - 1, true); zend_declare_typed_property(class_entry, property_repositoryUrl_name, &property_repositoryUrl_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_repositoryUrl_name); + zend_string_release_ex(property_repositoryUrl_name, true); return class_entry; } @@ -699,27 +703,27 @@ static zend_class_entry *register_class_DDTrace_SpanData(void) zval property_service_default_value; ZVAL_EMPTY_STRING(&property_service_default_value); - zend_string *property_service_name = zend_string_init("service", sizeof("service") - 1, 1); + zend_string *property_service_name = zend_string_init("service", sizeof("service") - 1, true); zend_declare_typed_property(class_entry, property_service_name, &property_service_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_NULL)); - zend_string_release(property_service_name); + zend_string_release_ex(property_service_name, true); zval property_env_default_value; ZVAL_EMPTY_STRING(&property_env_default_value); - zend_string *property_env_name = zend_string_init("env", sizeof("env") - 1, 1); + zend_string *property_env_name = zend_string_init("env", sizeof("env") - 1, true); zend_declare_typed_property(class_entry, property_env_name, &property_env_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_env_name); + zend_string_release_ex(property_env_name, true); zval property_version_default_value; ZVAL_EMPTY_STRING(&property_version_default_value); - zend_string *property_version_name = zend_string_init("version", sizeof("version") - 1, 1); + zend_string *property_version_name = zend_string_init("version", sizeof("version") - 1, true); zend_declare_typed_property(class_entry, property_version_name, &property_version_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_version_name); + zend_string_release_ex(property_version_name, true); zval property_meta_struct_default_value; ZVAL_EMPTY_ARRAY(&property_meta_struct_default_value); - zend_string *property_meta_struct_name = zend_string_init("meta_struct", sizeof("meta_struct") - 1, 1); + zend_string *property_meta_struct_name = zend_string_init("meta_struct", sizeof("meta_struct") - 1, true); zend_declare_typed_property(class_entry, property_meta_struct_name, &property_meta_struct_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release(property_meta_struct_name); + zend_string_release_ex(property_meta_struct_name, true); zval property_type_default_value; ZVAL_EMPTY_STRING(&property_type_default_value); @@ -727,70 +731,70 @@ static zend_class_entry *register_class_DDTrace_SpanData(void) zval property_meta_default_value; ZVAL_EMPTY_ARRAY(&property_meta_default_value); - zend_string *property_meta_name = zend_string_init("meta", sizeof("meta") - 1, 1); + zend_string *property_meta_name = zend_string_init("meta", sizeof("meta") - 1, true); zend_declare_typed_property(class_entry, property_meta_name, &property_meta_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release(property_meta_name); + zend_string_release_ex(property_meta_name, true); zval property_metrics_default_value; ZVAL_EMPTY_ARRAY(&property_metrics_default_value); - zend_string *property_metrics_name = zend_string_init("metrics", sizeof("metrics") - 1, 1); + zend_string *property_metrics_name = zend_string_init("metrics", sizeof("metrics") - 1, true); zend_declare_typed_property(class_entry, property_metrics_name, &property_metrics_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release(property_metrics_name); + zend_string_release_ex(property_metrics_name, true); zval property_exception_default_value; ZVAL_NULL(&property_exception_default_value); - zend_string *property_exception_name = zend_string_init("exception", sizeof("exception") - 1, 1); + zend_string *property_exception_name = zend_string_init("exception", sizeof("exception") - 1, true); zend_string *property_exception_class_Throwable = zend_string_init("Throwable", sizeof("Throwable")-1, 1); zend_declare_typed_property(class_entry, property_exception_name, &property_exception_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_exception_class_Throwable, 0, MAY_BE_NULL)); - zend_string_release(property_exception_name); + zend_string_release_ex(property_exception_name, true); zval property_id_default_value; ZVAL_UNDEF(&property_id_default_value); - zend_string *property_id_name = zend_string_init("id", sizeof("id") - 1, 1); + zend_string *property_id_name = zend_string_init("id", sizeof("id") - 1, true); zend_declare_typed_property(class_entry, property_id_name, &property_id_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_id_name); + zend_string_release_ex(property_id_name, true); zval property_links_default_value; ZVAL_EMPTY_ARRAY(&property_links_default_value); - zend_string *property_links_name = zend_string_init("links", sizeof("links") - 1, 1); + zend_string *property_links_name = zend_string_init("links", sizeof("links") - 1, true); zend_declare_typed_property(class_entry, property_links_name, &property_links_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release(property_links_name); + zend_string_release_ex(property_links_name, true); zval property_events_default_value; ZVAL_EMPTY_ARRAY(&property_events_default_value); - zend_string *property_events_name = zend_string_init("events", sizeof("events") - 1, 1); + zend_string *property_events_name = zend_string_init("events", sizeof("events") - 1, true); zend_declare_typed_property(class_entry, property_events_name, &property_events_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release(property_events_name); + zend_string_release_ex(property_events_name, true); zval property_peerServiceSources_default_value; ZVAL_EMPTY_ARRAY(&property_peerServiceSources_default_value); - zend_string *property_peerServiceSources_name = zend_string_init("peerServiceSources", sizeof("peerServiceSources") - 1, 1); + zend_string *property_peerServiceSources_name = zend_string_init("peerServiceSources", sizeof("peerServiceSources") - 1, true); zend_declare_typed_property(class_entry, property_peerServiceSources_name, &property_peerServiceSources_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release(property_peerServiceSources_name); + zend_string_release_ex(property_peerServiceSources_name, true); zval property_parent_default_value; - ZVAL_NULL(&property_parent_default_value); + ZVAL_UNDEF(&property_parent_default_value); zend_string *property_parent_class_DDTrace_SpanData = zend_string_init("DDTrace\\SpanData", sizeof("DDTrace\\SpanData")-1, 1); zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_PARENT), &property_parent_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parent_class_DDTrace_SpanData, 0, MAY_BE_NULL)); zval property_stack_default_value; ZVAL_UNDEF(&property_stack_default_value); - zend_string *property_stack_name = zend_string_init("stack", sizeof("stack") - 1, 1); + zend_string *property_stack_name = zend_string_init("stack", sizeof("stack") - 1, true); zend_string *property_stack_class_DDTrace_SpanStack = zend_string_init("DDTrace\\SpanStack", sizeof("DDTrace\\SpanStack")-1, 1); zend_declare_typed_property(class_entry, property_stack_name, &property_stack_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_stack_class_DDTrace_SpanStack, 0, 0)); - zend_string_release(property_stack_name); + zend_string_release_ex(property_stack_name, true); zval property_onClose_default_value; ZVAL_EMPTY_ARRAY(&property_onClose_default_value); - zend_string *property_onClose_name = zend_string_init("onClose", sizeof("onClose") - 1, 1); + zend_string *property_onClose_name = zend_string_init("onClose", sizeof("onClose") - 1, true); zend_declare_typed_property(class_entry, property_onClose_name, &property_onClose_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release(property_onClose_name); + zend_string_release_ex(property_onClose_name, true); zval property_baggage_default_value; ZVAL_EMPTY_ARRAY(&property_baggage_default_value); - zend_string *property_baggage_name = zend_string_init("baggage", sizeof("baggage") - 1, 1); + zend_string *property_baggage_name = zend_string_init("baggage", sizeof("baggage") - 1, true); zend_declare_typed_property(class_entry, property_baggage_name, &property_baggage_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release(property_baggage_name); + zend_string_release_ex(property_baggage_name, true); return class_entry; } @@ -814,65 +818,65 @@ static zend_class_entry *register_class_DDTrace_RootSpanData(zend_class_entry *c zval property_origin_default_value; ZVAL_UNDEF(&property_origin_default_value); - zend_string *property_origin_name = zend_string_init("origin", sizeof("origin") - 1, 1); + zend_string *property_origin_name = zend_string_init("origin", sizeof("origin") - 1, true); zend_declare_typed_property(class_entry, property_origin_name, &property_origin_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_origin_name); + zend_string_release_ex(property_origin_name, true); zval property_propagatedTags_default_value; ZVAL_EMPTY_ARRAY(&property_propagatedTags_default_value); - zend_string *property_propagatedTags_name = zend_string_init("propagatedTags", sizeof("propagatedTags") - 1, 1); + zend_string *property_propagatedTags_name = zend_string_init("propagatedTags", sizeof("propagatedTags") - 1, true); zend_declare_typed_property(class_entry, property_propagatedTags_name, &property_propagatedTags_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release(property_propagatedTags_name); + zend_string_release_ex(property_propagatedTags_name, true); zval property_samplingPriority_default_value; ZVAL_LONG(&property_samplingPriority_default_value, DDTRACE_PRIORITY_SAMPLING_UNKNOWN); - zend_string *property_samplingPriority_name = zend_string_init("samplingPriority", sizeof("samplingPriority") - 1, 1); + zend_string *property_samplingPriority_name = zend_string_init("samplingPriority", sizeof("samplingPriority") - 1, true); zend_declare_typed_property(class_entry, property_samplingPriority_name, &property_samplingPriority_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(property_samplingPriority_name); + zend_string_release_ex(property_samplingPriority_name, true); zval property_propagatedSamplingPriority_default_value; ZVAL_UNDEF(&property_propagatedSamplingPriority_default_value); - zend_string *property_propagatedSamplingPriority_name = zend_string_init("propagatedSamplingPriority", sizeof("propagatedSamplingPriority") - 1, 1); + zend_string *property_propagatedSamplingPriority_name = zend_string_init("propagatedSamplingPriority", sizeof("propagatedSamplingPriority") - 1, true); zend_declare_typed_property(class_entry, property_propagatedSamplingPriority_name, &property_propagatedSamplingPriority_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release(property_propagatedSamplingPriority_name); + zend_string_release_ex(property_propagatedSamplingPriority_name, true); zval property_tracestate_default_value; ZVAL_UNDEF(&property_tracestate_default_value); - zend_string *property_tracestate_name = zend_string_init("tracestate", sizeof("tracestate") - 1, 1); + zend_string *property_tracestate_name = zend_string_init("tracestate", sizeof("tracestate") - 1, true); zend_declare_typed_property(class_entry, property_tracestate_name, &property_tracestate_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_tracestate_name); + zend_string_release_ex(property_tracestate_name, true); zval property_tracestateTags_default_value; ZVAL_EMPTY_ARRAY(&property_tracestateTags_default_value); - zend_string *property_tracestateTags_name = zend_string_init("tracestateTags", sizeof("tracestateTags") - 1, 1); + zend_string *property_tracestateTags_name = zend_string_init("tracestateTags", sizeof("tracestateTags") - 1, true); zend_declare_typed_property(class_entry, property_tracestateTags_name, &property_tracestateTags_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release(property_tracestateTags_name); + zend_string_release_ex(property_tracestateTags_name, true); zval property_parentId_default_value; ZVAL_UNDEF(&property_parentId_default_value); - zend_string *property_parentId_name = zend_string_init("parentId", sizeof("parentId") - 1, 1); + zend_string *property_parentId_name = zend_string_init("parentId", sizeof("parentId") - 1, true); zend_declare_typed_property(class_entry, property_parentId_name, &property_parentId_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_parentId_name); + zend_string_release_ex(property_parentId_name, true); zval property_traceId_default_value; ZVAL_EMPTY_STRING(&property_traceId_default_value); - zend_string *property_traceId_name = zend_string_init("traceId", sizeof("traceId") - 1, 1); + zend_string *property_traceId_name = zend_string_init("traceId", sizeof("traceId") - 1, true); zend_declare_typed_property(class_entry, property_traceId_name, &property_traceId_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_traceId_name); + zend_string_release_ex(property_traceId_name, true); zval property_gitMetadata_default_value; ZVAL_NULL(&property_gitMetadata_default_value); - zend_string *property_gitMetadata_name = zend_string_init("gitMetadata", sizeof("gitMetadata") - 1, 1); + zend_string *property_gitMetadata_name = zend_string_init("gitMetadata", sizeof("gitMetadata") - 1, true); zend_string *property_gitMetadata_class_DDTrace_GitMetadata = zend_string_init("DDTrace\\GitMetadata", sizeof("DDTrace\\GitMetadata")-1, 1); zend_declare_typed_property(class_entry, property_gitMetadata_name, &property_gitMetadata_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_gitMetadata_class_DDTrace_GitMetadata, 0, MAY_BE_NULL)); - zend_string_release(property_gitMetadata_name); + zend_string_release_ex(property_gitMetadata_name, true); zval property_inferredSpan_default_value; ZVAL_NULL(&property_inferredSpan_default_value); - zend_string *property_inferredSpan_name = zend_string_init("inferredSpan", sizeof("inferredSpan") - 1, 1); + zend_string *property_inferredSpan_name = zend_string_init("inferredSpan", sizeof("inferredSpan") - 1, true); zend_string *property_inferredSpan_class_DDTrace_InferredSpanData = zend_string_init("DDTrace\\InferredSpanData", sizeof("DDTrace\\InferredSpanData")-1, 1); zend_declare_typed_property(class_entry, property_inferredSpan_name, &property_inferredSpan_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_inferredSpan_class_DDTrace_InferredSpanData, 0, MAY_BE_NULL)); - zend_string_release(property_inferredSpan_name); + zend_string_release_ex(property_inferredSpan_name, true); return class_entry; } @@ -885,22 +889,22 @@ static zend_class_entry *register_class_DDTrace_SpanStack(void) class_entry = zend_register_internal_class_with_flags(&ce, NULL, 0); zval property_parent_default_value; - ZVAL_NULL(&property_parent_default_value); + ZVAL_UNDEF(&property_parent_default_value); zend_string *property_parent_class_DDTrace_SpanStack = zend_string_init("DDTrace\\SpanStack", sizeof("DDTrace\\SpanStack")-1, 1); zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_PARENT), &property_parent_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parent_class_DDTrace_SpanStack, 0, MAY_BE_NULL)); zval property_active_default_value; ZVAL_NULL(&property_active_default_value); - zend_string *property_active_name = zend_string_init("active", sizeof("active") - 1, 1); + zend_string *property_active_name = zend_string_init("active", sizeof("active") - 1, true); zend_string *property_active_class_DDTrace_SpanData = zend_string_init("DDTrace\\SpanData", sizeof("DDTrace\\SpanData")-1, 1); zend_declare_typed_property(class_entry, property_active_name, &property_active_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_active_class_DDTrace_SpanData, 0, MAY_BE_NULL)); - zend_string_release(property_active_name); + zend_string_release_ex(property_active_name, true); zval property_spanCreationObservers_default_value; ZVAL_EMPTY_ARRAY(&property_spanCreationObservers_default_value); - zend_string *property_spanCreationObservers_name = zend_string_init("spanCreationObservers", sizeof("spanCreationObservers") - 1, 1); + zend_string *property_spanCreationObservers_name = zend_string_init("spanCreationObservers", sizeof("spanCreationObservers") - 1, true); zend_declare_typed_property(class_entry, property_spanCreationObservers_name, &property_spanCreationObservers_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release(property_spanCreationObservers_name); + zend_string_release_ex(property_spanCreationObservers_name, true); return class_entry; } @@ -914,21 +918,21 @@ static zend_class_entry *register_class_DDTrace_Integration(void) zval const_NOT_LOADED_value; ZVAL_LONG(&const_NOT_LOADED_value, DD_TRACE_INTEGRATION_NOT_LOADED); - zend_string *const_NOT_LOADED_name = zend_string_init_interned("NOT_LOADED", sizeof("NOT_LOADED") - 1, 1); + zend_string *const_NOT_LOADED_name = zend_string_init_interned("NOT_LOADED", sizeof("NOT_LOADED") - 1, true); zend_declare_class_constant_ex(class_entry, const_NOT_LOADED_name, &const_NOT_LOADED_value, ZEND_ACC_PUBLIC, NULL); - zend_string_release(const_NOT_LOADED_name); + zend_string_release_ex(const_NOT_LOADED_name, true); zval const_LOADED_value; ZVAL_LONG(&const_LOADED_value, DD_TRACE_INTEGRATION_LOADED); - zend_string *const_LOADED_name = zend_string_init_interned("LOADED", sizeof("LOADED") - 1, 1); + zend_string *const_LOADED_name = zend_string_init_interned("LOADED", sizeof("LOADED") - 1, true); zend_declare_class_constant_ex(class_entry, const_LOADED_name, &const_LOADED_value, ZEND_ACC_PUBLIC, NULL); - zend_string_release(const_LOADED_name); + zend_string_release_ex(const_LOADED_name, true); zval const_NOT_AVAILABLE_value; ZVAL_LONG(&const_NOT_AVAILABLE_value, DD_TRACE_INTEGRATION_NOT_AVAILABLE); - zend_string *const_NOT_AVAILABLE_name = zend_string_init_interned("NOT_AVAILABLE", sizeof("NOT_AVAILABLE") - 1, 1); + zend_string *const_NOT_AVAILABLE_name = zend_string_init_interned("NOT_AVAILABLE", sizeof("NOT_AVAILABLE") - 1, true); zend_declare_class_constant_ex(class_entry, const_NOT_AVAILABLE_name, &const_NOT_AVAILABLE_value, ZEND_ACC_PUBLIC, NULL); - zend_string_release(const_NOT_AVAILABLE_name); + zend_string_release_ex(const_NOT_AVAILABLE_name, true); return class_entry; } diff --git a/ext/exception_serialize.c b/ext/exception_serialize.c index c921f091b10..c9e86bbd63a 100644 --- a/ext/exception_serialize.c +++ b/ext/exception_serialize.c @@ -384,7 +384,7 @@ static void ddtrace_collect_exception_debug_data(zend_object *exception, zend_st ddog_add_str_span_meta_str(span, "error.debug_info_captured", "true"); ddog_add_str_span_meta_CharSlice(span, "_dd.debug.error.exception_hash", (ddog_CharSlice){.ptr = exception_hash, .len = hash_len}); - if (!ddog_exception_hash_limiter_inc(ddtrace_sidecar, (uint64_t)exception_long_hash, get_DD_EXCEPTION_REPLAY_CAPTURE_INTERVAL_SECONDS())) { + if (!ddog_exception_hash_limiter_inc(DDTRACE_G(sidecar), (uint64_t)exception_long_hash, get_DD_EXCEPTION_REPLAY_CAPTURE_INTERVAL_SECONDS())) { LOG(TRACE, "Skipping exception replay capture due to hash %.*s already recently hit", hash_len, exception_hash); goto cleanup; } diff --git a/ext/live_debugger.c b/ext/live_debugger.c index 85ec375e076..2ca10da820f 100644 --- a/ext/live_debugger.c +++ b/ext/live_debugger.c @@ -151,7 +151,7 @@ static void dd_probe_resolved(void *data, bool found) { def->probe.status_msg = DDOG_CHARSLICE_C("Method does not exist on the given class"); def->probe.status_exception = DDOG_CHARSLICE_C("METHOD_NOT_FOUND"); } - ddog_send_debugger_diagnostics(DDTRACE_G(remote_config_state), &ddtrace_sidecar, ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), &def->probe, ddtrace_nanoseconds_realtime() / 1000000); + ddog_send_debugger_diagnostics(DDTRACE_G(remote_config_state), &DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), &def->probe, ddtrace_nanoseconds_realtime() / 1000000); } static int64_t dd_init_live_debugger_probe(const ddog_Probe *probe, dd_probe_def *def, zai_hook_begin begin, zai_hook_end end, void (*def_dtor)(void *), size_t dynamic) { @@ -202,14 +202,14 @@ static int64_t dd_init_live_debugger_probe(const ddog_Probe *probe, dd_probe_def def->probe.status_msg = DDOG_CHARSLICE_C("Method does not exist on the given class"); def->probe.status_exception = DDOG_CHARSLICE_C("METHOD_NOT_FOUND"); error: - ddog_send_debugger_diagnostics(DDTRACE_G(remote_config_state), &ddtrace_sidecar, ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), &def->probe, ddtrace_nanoseconds_realtime() / 1000000); + ddog_send_debugger_diagnostics(DDTRACE_G(remote_config_state), &DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), &def->probe, ddtrace_nanoseconds_realtime() / 1000000); def_dtor(def); return -1; } if (def->probe.status != DDOG_PROBE_STATUS_INSTALLED) { def->probe.status = DDOG_PROBE_STATUS_RECEIVED; - ddog_send_debugger_diagnostics(DDTRACE_G(remote_config_state), &ddtrace_sidecar, ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), &def->probe, ddtrace_nanoseconds_realtime() / 1000000); + ddog_send_debugger_diagnostics(DDTRACE_G(remote_config_state), &DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), &def->probe, ddtrace_nanoseconds_realtime() / 1000000); } zend_hash_index_add_new_ptr(&DDTRACE_G(active_rc_hooks), id, def); @@ -219,7 +219,7 @@ static int64_t dd_init_live_debugger_probe(const ddog_Probe *probe, dd_probe_def static void dd_probe_mark_active(dd_probe_def *def) { if (def->probe.status != DDOG_PROBE_STATUS_EMITTING) { def->probe.status = DDOG_PROBE_STATUS_EMITTING; - ddog_send_debugger_diagnostics(DDTRACE_G(remote_config_state), &ddtrace_sidecar, ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), &def->probe, ddtrace_nanoseconds_realtime() / 1000000); + ddog_send_debugger_diagnostics(DDTRACE_G(remote_config_state), &DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), &def->probe, ddtrace_nanoseconds_realtime() / 1000000); } } @@ -1468,8 +1468,8 @@ bool ddtrace_alter_dynamic_instrumentation_config(zval *old_value, zval *new_val return false; } - if (DDTRACE_G(request_initialized) && ddtrace_sidecar) { - ddog_sidecar_set_request_config(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), Z_TYPE_P(new_value) == IS_TRUE ? DDOG_DYNAMIC_INSTRUMENTATION_CONFIG_STATE_ENABLED : DDOG_DYNAMIC_INSTRUMENTATION_CONFIG_STATE_DISABLED); + if (DDTRACE_G(request_initialized) && DDTRACE_G(sidecar)) { + ddog_sidecar_set_request_config(&DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), Z_TYPE_P(new_value) == IS_TRUE ? DDOG_DYNAMIC_INSTRUMENTATION_CONFIG_STATE_ENABLED : DDOG_DYNAMIC_INSTRUMENTATION_CONFIG_STATE_DISABLED); } return true; } diff --git a/ext/otel_config.c b/ext/otel_config.c index 43c08c0b465..c03fdd42904 100644 --- a/ext/otel_config.c +++ b/ext/otel_config.c @@ -9,8 +9,8 @@ ZEND_EXTERN_MODULE_GLOBALS(ddtrace); static void report_otel_cfg_telemetry_invalid(const char *otel_cfg, const char *dd_cfg, bool pre_rinit) { - if (!pre_rinit && ddtrace_sidecar && get_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { - ddog_sidecar_telemetry_register_metric(&ddtrace_sidecar, DDOG_CHARSLICE_C("otel.env.invalid"), DDOG_METRIC_TYPE_COUNT, DDOG_METRIC_NAMESPACE_TRACERS); + if (!pre_rinit && DDTRACE_G(sidecar) && get_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { + ddog_sidecar_telemetry_register_metric(&DDTRACE_G(sidecar), DDOG_CHARSLICE_C("otel.env.invalid"), DDOG_METRIC_TYPE_COUNT, DDOG_METRIC_NAMESPACE_TRACERS); ddog_SidecarActionsBuffer *buffer = ddtrace_telemetry_buffer(); ddog_CharSlice tags; tags.len = asprintf((char **)&tags.ptr, "config_opentelemetry:%s,config_datadog:%s", otel_cfg, dd_cfg); diff --git a/ext/sidecar.c b/ext/sidecar.c index 2246e0c81e7..66fd11ad39a 100644 --- a/ext/sidecar.c +++ b/ext/sidecar.c @@ -29,6 +29,12 @@ ddog_Endpoint *dogstatsd_endpoint; // always set when ddtrace_endpoint is set struct ddog_InstanceId *ddtrace_sidecar_instance_id; static uint8_t dd_sidecar_formatted_session_id[36]; +// Best-effort pointer for the signal handler (SIGTERM/SIGINT). Set to the first +// per-thread connection; never cleared until MSHUTDOWN. Not atomic: concurrent +// shutdown is already a best-effort race for signal handlers, so atomicity of +// the pointer load alone would not prevent the underlying use-after-free. +ddog_SidecarTransport *ddtrace_sidecar_for_signal = NULL; + // Connection mode tracking dd_sidecar_active_mode_t ddtrace_sidecar_active_mode = DD_SIDECAR_CONNECTION_NONE; int32_t ddtrace_sidecar_master_pid = 0; @@ -57,7 +63,7 @@ static void ddtrace_set_non_resettable_sidecar_globals(void) { } } -// Set the globals that must be updated in case of fork +// Build the process-level instance ID (one per PHP process, reset after fork). static void ddtrace_set_resettable_sidecar_globals(void) { uint8_t formatted_run_time_id[36]; ddtrace_format_runtime_id(&formatted_run_time_id); @@ -127,7 +133,7 @@ static void dd_sidecar_post_connect(ddog_SidecarTransport **transport, bool is_f } void ddtrace_sidecar_update_process_tags(void) { - if (!ddtrace_sidecar) { + if (!DDTRACE_G(sidecar)) { return; } @@ -136,7 +142,7 @@ void ddtrace_sidecar_update_process_tags(void) { return; } - ddog_sidecar_session_set_process_tags(&ddtrace_sidecar, process_tags); + ddog_sidecar_session_set_process_tags(&DDTRACE_G(sidecar), process_tags); } static ddog_SidecarTransport *dd_sidecar_connection_factory_ex(bool is_fork); @@ -156,46 +162,18 @@ static void dd_sidecar_on_reconnect(ddog_SidecarTransport *transport) { dd_sidecar_post_connect(&transport, false, logpath); - // update the sidecar connection on all threads on ZTS -#if ZTS - tsrm_mutex_lock(ddtrace_threads_mutex); - - void *TSRMLS_CACHE; // DDTRACE_G() accesses a variable named TSRMLS_CACHE. Make use of variable shadowing in scopes... - ZEND_HASH_FOREACH_PTR(&ddtrace_tls_bases, TSRMLS_CACHE) { -#endif - // We need the lock even on NTS as it might originate from the background sender - tsrm_mutex_lock(DDTRACE_G(sidecar_universal_service_tags_mutex)); - - // when we get disconnected during shutdown - if (DDTRACE_G(sidecar_queue_id) && DDTRACE_G(last_service_name)) { - ddog_CharSlice service_name = dd_zend_string_to_CharSlice(DDTRACE_G(last_service_name)); - ddog_CharSlice env_name = dd_zend_string_to_CharSlice(DDTRACE_G(last_env_name)); - ddog_CharSlice version = dd_zend_string_to_CharSlice(DDTRACE_G(last_version)); - - ddog_DynamicInstrumentationConfigState dynamic_instrumentation_state; -#if ZTS - // With the current architecture of config it's not accessible via the TSRMLS_CACHE and thus may be actually invalid on the current thread. - // This is a known issue and will be fixed with refactor of the module_globals usage of config. The current behaviour is not perfect, but has to be considered acceptable. - if (zai_config_memoized_entries[DDTRACE_CONFIG_DD_DYNAMIC_INSTRUMENTATION_ENABLED].name_index >= 0) { - dynamic_instrumentation_state = get_global_DD_DYNAMIC_INSTRUMENTATION_ENABLED() ? DDOG_DYNAMIC_INSTRUMENTATION_CONFIG_STATE_ENABLED : DDOG_DYNAMIC_INSTRUMENTATION_CONFIG_STATE_DISABLED; - } else { - dynamic_instrumentation_state = DDOG_DYNAMIC_INSTRUMENTATION_CONFIG_STATE_NOT_SET; - } -#else - dynamic_instrumentation_state = ddtrace_dynamic_instrumentation_state(); -#endif - ddtrace_ffi_try("Failed sending config data", - ddog_sidecar_set_universal_service_tags(&transport, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), service_name, - env_name, version, &DDTRACE_G(active_global_tags), dynamic_instrumentation_state)); - } - - tsrm_mutex_unlock(DDTRACE_G(sidecar_universal_service_tags_mutex)); -#if ZTS - } ZEND_HASH_FOREACH_END(); + tsrm_mutex_lock(DDTRACE_G(sidecar_universal_service_tags_mutex)); - tsrm_mutex_unlock(ddtrace_threads_mutex); -#endif + if (DDTRACE_G(sidecar_queue_id) && DDTRACE_G(last_service_name)) { + ddog_CharSlice service_name = dd_zend_string_to_CharSlice(DDTRACE_G(last_service_name)); + ddog_CharSlice env_name = dd_zend_string_to_CharSlice(DDTRACE_G(last_env_name)); + ddog_CharSlice version = dd_zend_string_to_CharSlice(DDTRACE_G(last_version)); + ddtrace_ffi_try("Failed sending config data", + ddog_sidecar_set_universal_service_tags(&transport, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), service_name, + env_name, version, &DDTRACE_G(active_global_tags), ddtrace_dynamic_instrumentation_state())); + } + tsrm_mutex_unlock(DDTRACE_G(sidecar_universal_service_tags_mutex)); } static ddog_SidecarTransport *dd_sidecar_connect(bool as_worker, bool is_fork) { @@ -275,8 +253,8 @@ static void ddtrace_sidecar_setup_thread_mode(bool appsec_activation, bool appse bool listener_available = ddog_sidecar_is_master_listener_active(ddtrace_sidecar_master_pid); if (is_child_process || listener_available) { - ddtrace_sidecar = dd_sidecar_connect(true, false); - if (ddtrace_sidecar) { + DDTRACE_G(sidecar) = dd_sidecar_connect(true, false); + if (DDTRACE_G(sidecar)) { if (is_child_process) { LOG(INFO, "Worker connected to sidecar master listener (worker PID=%d, master PID=%d)", (int32_t)current_pid, ddtrace_sidecar_master_pid); @@ -285,14 +263,10 @@ static void ddtrace_sidecar_setup_thread_mode(bool appsec_activation, bool appse } if (!is_child_process) { - // listener_available was true but connect failed (e.g. race: socket not yet bound) LOG(WARN, "Failed to connect to own master listener (PID=%d)", (int32_t)current_pid); return; } - // Worker processes must not start their own listener thread - the master listener - // must be started in MINIT (in the master process) so it survives forking. - // If we can't connect, run without the sidecar rather than starting a per-worker thread. LOG(WARN, "Cannot connect to master sidecar listener from worker (child PID=%d, master PID=%d)", (int32_t)current_pid, ddtrace_sidecar_master_pid); return; @@ -308,8 +282,8 @@ static void ddtrace_sidecar_setup_thread_mode(bool appsec_activation, bool appse LOG(INFO, "Started sidecar master listener thread (PID=%d)", ddtrace_sidecar_master_pid); - ddtrace_sidecar = dd_sidecar_connect(true, false); - if (!ddtrace_sidecar) { + DDTRACE_G(sidecar) = dd_sidecar_connect(true, false); + if (!DDTRACE_G(sidecar)) { LOG(WARN, "Failed to connect master process to sidecar"); return; } @@ -416,23 +390,24 @@ void ddtrace_sidecar_setup(bool appsec_activation, bool appsec_config) { if (mode == DD_TRACE_SIDECAR_CONNECTION_MODE_THREAD) { ddtrace_sidecar_setup_thread_mode(appsec_activation, appsec_config); - return; - } - - ddtrace_sidecar = dd_sidecar_connect(false, false); + } else { + DDTRACE_G(sidecar) = dd_sidecar_connect(false, false); - if (!ddtrace_sidecar) { - if (mode == DD_TRACE_SIDECAR_CONNECTION_MODE_AUTO && ddtrace_endpoint) { - LOG(WARN, "Subprocess connection failed, falling back to thread mode"); - ddtrace_sidecar_setup_thread_mode(appsec_activation, appsec_config); - return; + if (!DDTRACE_G(sidecar)) { + if (mode == DD_TRACE_SIDECAR_CONNECTION_MODE_AUTO && ddtrace_endpoint) { + LOG(WARN, "Subprocess connection failed, falling back to thread mode"); + ddtrace_sidecar_setup_thread_mode(appsec_activation, appsec_config); + } else if (ddtrace_endpoint) { + dd_free_endpoints(); + } + } else if (get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { + ddtrace_telemetry_first_init(); } + } - if (ddtrace_endpoint) { - dd_free_endpoints(); - } - } else if (get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { - ddtrace_telemetry_first_init(); + // Record the first established connection for best-effort signal-handler use. + if (DDTRACE_G(sidecar) && !ddtrace_sidecar_for_signal) { + ddtrace_sidecar_for_signal = DDTRACE_G(sidecar); } } @@ -463,44 +438,44 @@ void ddtrace_sidecar_handle_fork(void) { ddtrace_force_new_instance_id(); - if (ddtrace_sidecar) { - ddog_sidecar_transport_drop(ddtrace_sidecar); - ddtrace_sidecar = NULL; + // After fork only one thread (the one that called fork) survives, so we only + // need to drop and reconnect the current thread's transport. + if (DDTRACE_G(sidecar)) { + ddog_sidecar_transport_drop(DDTRACE_G(sidecar)); + DDTRACE_G(sidecar) = NULL; } + ddtrace_sidecar_for_signal = NULL; if (ddtrace_sidecar_active_mode == DD_SIDECAR_CONNECTION_THREAD) { ddtrace_ffi_try("Failed clearing inherited listener state", ddog_sidecar_clear_inherited_listener()); - // Try to connect as a worker to parent's listener - ddtrace_sidecar = dd_sidecar_connect(true, true); - if (ddtrace_sidecar) { + DDTRACE_G(sidecar) = dd_sidecar_connect(true, true); + if (DDTRACE_G(sidecar)) { LOG(INFO, "Child process reconnected to parent's sidecar listener after fork (child PID=%d, parent=%d)", (int32_t)getpid(), ddtrace_sidecar_master_pid); - return; - } - - // Parent's listener not available, fall back to starting a new master in this process - LOG(INFO, "Parent's sidecar listener not available after fork (child PID=%d, parent=%d), starting new master", - (int32_t)getpid(), ddtrace_sidecar_master_pid); + } else { + LOG(INFO, "Parent's sidecar listener not available after fork (child PID=%d, parent=%d), starting new master", + (int32_t)getpid(), ddtrace_sidecar_master_pid); - ddtrace_sidecar_master_pid = (int32_t)getpid(); - if (!ddtrace_ffi_try("Failed starting sidecar master listener in child process", - ddog_sidecar_connect_master((int32_t)ddtrace_sidecar_master_pid))) { - if (ddtrace_endpoint) { - dd_free_endpoints(); + ddtrace_sidecar_master_pid = (int32_t)getpid(); + if (!ddtrace_ffi_try("Failed starting sidecar master listener in child process", + ddog_sidecar_connect_master((int32_t)ddtrace_sidecar_master_pid))) { + if (ddtrace_endpoint) { + dd_free_endpoints(); + } + return; } - return; - } - ddtrace_sidecar = dd_sidecar_connect(true, false); - if (!ddtrace_sidecar) { - LOG(WARN, "Failed to connect to new sidecar master in child process (PID=%d)", - (int32_t)getpid()); + DDTRACE_G(sidecar) = dd_sidecar_connect(true, false); + if (!DDTRACE_G(sidecar)) { + LOG(WARN, "Failed to connect to new sidecar master in child process (PID=%d)", + (int32_t)getpid()); + } } } else if (ddtrace_sidecar_active_mode == DD_SIDECAR_CONNECTION_SUBPROCESS) { - ddtrace_sidecar = ddtrace_sidecar_connect(true); - if (!ddtrace_sidecar) { + DDTRACE_G(sidecar) = ddtrace_sidecar_connect(true); + if (!DDTRACE_G(sidecar)) { if (ddtrace_endpoint) { dd_free_endpoints(); } @@ -508,17 +483,28 @@ void ddtrace_sidecar_handle_fork(void) { ddtrace_sidecar_submit_root_span_data(); } } + + if (DDTRACE_G(sidecar)) { + ddtrace_sidecar_for_signal = DDTRACE_G(sidecar); + } #endif } void ddtrace_sidecar_ensure_active(void) { - if (ddtrace_sidecar) { - ddtrace_sidecar_reconnect(&ddtrace_sidecar, ddtrace_sidecar_connect_callback); + if (DDTRACE_G(sidecar)) { + ddtrace_sidecar_reconnect(&DDTRACE_G(sidecar), ddtrace_sidecar_connect_callback); + } else if (ddtrace_endpoint) { + // First RINIT on this thread: the process-level setup already ran (endpoint is + // set), so establish this thread's own connection now. + DDTRACE_G(sidecar) = ddtrace_sidecar_connect(false); + if (DDTRACE_G(sidecar) && !ddtrace_sidecar_for_signal) { + ddtrace_sidecar_for_signal = DDTRACE_G(sidecar); + } } } void ddtrace_sidecar_finalize(bool clear_id) { - if (!ddtrace_sidecar) { + if (!DDTRACE_G(sidecar)) { return; } @@ -533,12 +519,15 @@ void ddtrace_sidecar_finalize(bool clear_id) { if (clear_id) { ddtrace_ffi_try("Failed removing application from sidecar", - ddog_sidecar_application_remove(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &queue_id)); + ddog_sidecar_application_remove(&DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, &queue_id)); } } void ddtrace_sidecar_shutdown(void) { - // Shutdown master listener if this is the master process and thread mode is active + // In thread mode, drop the main thread's connection before shutting down the + // listener to avoid deadlock. GSHUTDOWN owns transport cleanup for all other + // threads; the main thread's GSHUTDOWN runs after MSHUTDOWN on some SAPIs, + // so we handle it here explicitly for the thread-mode case. #ifdef _WIN32 int32_t current_pid = (int32_t)GetCurrentProcessId(); #else @@ -548,18 +537,18 @@ void ddtrace_sidecar_shutdown(void) { ddtrace_sidecar_master_pid != 0 && current_pid == ddtrace_sidecar_master_pid) { - // Close worker connection first to avoid deadlock - if (ddtrace_sidecar) { - ddog_sidecar_transport_drop(ddtrace_sidecar); - ddtrace_sidecar = NULL; + if (DDTRACE_G(sidecar)) { + ddtrace_sidecar_for_signal = NULL; + + ddog_sidecar_transport_drop(DDTRACE_G(sidecar)); + DDTRACE_G(sidecar) = NULL; } - // Then shutdown listener thread ddtrace_ffi_try("Failed shutting down master listener", ddog_sidecar_shutdown_master_listener()); } - // Standard cleanup + // Process-level instance ID (dropped once at MSHUTDOWN, not per-thread). if (ddtrace_sidecar_instance_id) { ddog_sidecar_instanceId_drop(ddtrace_sidecar_instance_id); ddtrace_sidecar_instance_id = NULL; @@ -569,12 +558,6 @@ void ddtrace_sidecar_shutdown(void) { dd_free_endpoints(); } - if (ddtrace_sidecar) { - ddog_sidecar_transport_drop(ddtrace_sidecar); - ddtrace_sidecar = NULL; - } - - // Reset mode ddtrace_sidecar_active_mode = DD_SIDECAR_CONNECTION_NONE; } @@ -669,62 +652,62 @@ void ddtrace_sidecar_push_tags(ddog_Vec_Tag *vec, zval *tags) { } void ddtrace_sidecar_dogstatsd_count(zend_string *metric, zend_long value, zval *tags) { - if (!ddtrace_sidecar || !get_DD_INTEGRATION_METRICS_ENABLED()) { + if (!DDTRACE_G(sidecar) || !get_DD_INTEGRATION_METRICS_ENABLED()) { return; } ddog_Vec_Tag vec = ddog_Vec_Tag_new(); ddtrace_sidecar_push_tags(&vec, tags); ddtrace_ffi_try("Failed sending dogstatsd count metric", - ddog_sidecar_dogstatsd_count(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec)); + ddog_sidecar_dogstatsd_count(&DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec)); ddog_Vec_Tag_drop(vec); } void ddtrace_sidecar_dogstatsd_distribution(zend_string *metric, double value, zval *tags) { - if (!ddtrace_sidecar || !get_DD_INTEGRATION_METRICS_ENABLED()) { + if (!DDTRACE_G(sidecar) || !get_DD_INTEGRATION_METRICS_ENABLED()) { return; } ddog_Vec_Tag vec = ddog_Vec_Tag_new(); ddtrace_sidecar_push_tags(&vec, tags); ddtrace_ffi_try("Failed sending dogstatsd distribution metric", - ddog_sidecar_dogstatsd_distribution(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec)); + ddog_sidecar_dogstatsd_distribution(&DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec)); ddog_Vec_Tag_drop(vec); } void ddtrace_sidecar_dogstatsd_gauge(zend_string *metric, double value, zval *tags) { - if (!ddtrace_sidecar || !get_DD_INTEGRATION_METRICS_ENABLED()) { + if (!DDTRACE_G(sidecar) || !get_DD_INTEGRATION_METRICS_ENABLED()) { return; } ddog_Vec_Tag vec = ddog_Vec_Tag_new(); ddtrace_sidecar_push_tags(&vec, tags); ddtrace_ffi_try("Failed sending dogstatsd gauge metric", - ddog_sidecar_dogstatsd_gauge(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec)); + ddog_sidecar_dogstatsd_gauge(&DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec)); ddog_Vec_Tag_drop(vec); } void ddtrace_sidecar_dogstatsd_histogram(zend_string *metric, double value, zval *tags) { - if (!ddtrace_sidecar || !get_DD_INTEGRATION_METRICS_ENABLED()) { + if (!DDTRACE_G(sidecar) || !get_DD_INTEGRATION_METRICS_ENABLED()) { return; } ddog_Vec_Tag vec = ddog_Vec_Tag_new(); ddtrace_sidecar_push_tags(&vec, tags); ddtrace_ffi_try("Failed sending dogstatsd histogram metric", - ddog_sidecar_dogstatsd_histogram(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec)); + ddog_sidecar_dogstatsd_histogram(&DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec)); ddog_Vec_Tag_drop(vec); } void ddtrace_sidecar_dogstatsd_set(zend_string *metric, zend_long value, zval *tags) { - if (!ddtrace_sidecar || !get_DD_INTEGRATION_METRICS_ENABLED()) { + if (!DDTRACE_G(sidecar) || !get_DD_INTEGRATION_METRICS_ENABLED()) { return; } ddog_Vec_Tag vec = ddog_Vec_Tag_new(); ddtrace_sidecar_push_tags(&vec, tags); ddtrace_ffi_try("Failed sending dogstatsd set metric", - ddog_sidecar_dogstatsd_set(&ddtrace_sidecar, ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec)); + ddog_sidecar_dogstatsd_set(&DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, dd_zend_string_to_CharSlice(metric), value, &vec)); ddog_Vec_Tag_drop(vec); } @@ -799,7 +782,7 @@ void ddtrace_sidecar_submit_root_span_data_direct(ddog_SidecarTransport **transp } // Force resend on reconnect - if (changed || !root || *transport != ddtrace_sidecar) { + if (changed || !root || *transport != DDTRACE_G(sidecar)) { tsrm_mutex_lock(DDTRACE_G(sidecar_universal_service_tags_mutex)); if (DDTRACE_G(last_service_name)) { zend_string_release(DDTRACE_G(last_service_name)); @@ -834,19 +817,19 @@ void ddtrace_sidecar_submit_root_span_data(void) { if (DDTRACE_G(active_stack)) { ddtrace_root_span_data *root = DDTRACE_G(active_stack)->root_span; if (root) { - ddtrace_sidecar_submit_root_span_data_direct_defaults(&ddtrace_sidecar, root); + ddtrace_sidecar_submit_root_span_data_direct_defaults(&DDTRACE_G(sidecar), root); } } } void ddtrace_sidecar_send_debugger_data(ddog_Vec_DebuggerPayload payloads) { LOGEV(DEBUG, UNUSED(log); ddog_log_debugger_data(&payloads);); - ddog_sidecar_send_debugger_data(&ddtrace_sidecar, ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), payloads); + ddog_sidecar_send_debugger_data(&DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), payloads); } void ddtrace_sidecar_send_debugger_datum(ddog_DebuggerPayload *payload) { LOGEV(DEBUG, UNUSED(log); ddog_log_debugger_datum(payload);); - ddog_sidecar_send_debugger_datum(&ddtrace_sidecar, ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), payload); + ddog_sidecar_send_debugger_datum(&DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, DDTRACE_G(sidecar_queue_id), payload); } void ddtrace_sidecar_activate(void) { @@ -879,19 +862,30 @@ void ddtrace_sidecar_rinit(void) { } } - ddtrace_sidecar_submit_root_span_data_direct_defaults(&ddtrace_sidecar, NULL); + ddtrace_sidecar_submit_root_span_data_direct_defaults(&DDTRACE_G(sidecar), NULL); } void ddtrace_sidecar_rshutdown(void) { ddog_Vec_Tag_drop(DDTRACE_G(active_global_tags)); } +void ddtrace_sidecar_gshutdown(void) { + if (DDTRACE_G(sidecar)) { + ddtrace_sidecar_for_signal = NULL; + + // Drain any accumulated background-sender metrics before the transport goes away. + ddtrace_telemetry_flush_bgs_metrics_final(); + ddog_sidecar_transport_drop(DDTRACE_G(sidecar)); + DDTRACE_G(sidecar) = NULL; + } +} + bool ddtrace_alter_test_session_token(zval *old_value, zval *new_value, zend_string *new_str) { UNUSED(old_value, new_str); - if (ddtrace_sidecar) { + if (DDTRACE_G(sidecar)) { ddog_endpoint_set_test_token(ddtrace_endpoint, dd_zend_string_to_CharSlice(Z_STR_P(new_value))); ddtrace_ffi_try("Failed updating test session token", - ddog_sidecar_set_test_session_token(&ddtrace_sidecar, dd_zend_string_to_CharSlice(Z_STR_P(new_value)))); + ddog_sidecar_set_test_session_token(&DDTRACE_G(sidecar), dd_zend_string_to_CharSlice(Z_STR_P(new_value)))); } #ifndef _WIN32 ddtrace_coms_set_test_session_token(Z_STRVAL_P(new_value), Z_STRLEN_P(new_value)); @@ -900,7 +894,7 @@ bool ddtrace_alter_test_session_token(zval *old_value, zval *new_value, zend_str } bool ddtrace_exception_debugging_is_active(void) { - return ddtrace_sidecar && ddtrace_sidecar_instance_id && get_DD_EXCEPTION_REPLAY_ENABLED(); + return DDTRACE_G(sidecar) && ddtrace_sidecar_instance_id && get_DD_EXCEPTION_REPLAY_ENABLED(); } ddog_crasht_Metadata ddtrace_setup_crashtracking_metadata(ddog_Vec_Tag *tags) { diff --git a/ext/sidecar.h b/ext/sidecar.h index b18bea433e2..6281e90f59e 100644 --- a/ext/sidecar.h +++ b/ext/sidecar.h @@ -14,9 +14,14 @@ typedef enum { DD_SIDECAR_CONNECTION_THREAD = 2 } dd_sidecar_active_mode_t; -extern ddog_SidecarTransport *ddtrace_sidecar; -extern ddog_Endpoint *ddtrace_endpoint; +// ddtrace_sidecar lives in DDTRACE_G(sidecar) — one transport per thread. +// ddtrace_sidecar_instance_id is a process global — one identity per PHP process. extern struct ddog_InstanceId *ddtrace_sidecar_instance_id; +// Best-effort pointer used only by the signal handler (SIGTERM/SIGINT), which cannot call +// TSRMLS_FETCH() safely. Set to the first thread's connection; never cleared until MSHUTDOWN. +// Not atomic: concurrent shutdown is a pre-existing best-effort race for signal handlers. +extern ddog_SidecarTransport *ddtrace_sidecar_for_signal; +extern ddog_Endpoint *ddtrace_endpoint; extern dd_sidecar_active_mode_t ddtrace_sidecar_active_mode; extern int32_t ddtrace_sidecar_master_pid; @@ -56,6 +61,7 @@ void ddtrace_sidecar_send_debugger_datum(ddog_DebuggerPayload *payload); void ddtrace_sidecar_activate(void); void ddtrace_sidecar_rinit(void); void ddtrace_sidecar_rshutdown(void); +void ddtrace_sidecar_gshutdown(void); void ddtrace_sidecar_dogstatsd_count(zend_string *metric, zend_long value, zval *tags); void ddtrace_sidecar_dogstatsd_distribution(zend_string *metric, double value, zval *tags); diff --git a/ext/signals.c b/ext/signals.c index 0373a696d56..09cf0430551 100644 --- a/ext/signals.c +++ b/ext/signals.c @@ -363,7 +363,7 @@ static int dd_call_prev_handler(bool flush) { } if (flush) { - ddog_sidecar_flush_traces(&ddtrace_sidecar); + ddog_sidecar_flush_traces(&ddtrace_sidecar_for_signal); } if (prev_handler == SIG_DFL) { @@ -403,7 +403,7 @@ static void dd_sigint_sigterm_handler(int sig, siginfo_t *si, void *uc) { memcpy(&dd_signal_data.si, si, sizeof(*si)); dd_signal_data.uc = uc; - if (ddtrace_sidecar) { + if (ddtrace_sidecar_for_signal) { // Spawn a thread using clone() to perform sidecar cleanup asynchronously to avoid async unsafeness in the signal handler void *stack_top = dd_signal_async_stack + dd_signal_async_stack_size; int flags = CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD; diff --git a/ext/telemetry.c b/ext/telemetry.c index 6a0f40941ad..8a1ac9a9995 100644 --- a/ext/telemetry.c +++ b/ext/telemetry.c @@ -1,5 +1,10 @@ #include "components-rs/sidecar.h" #include "ddtrace.h" +#ifndef _WIN32 +#include +#else +#include +#endif #include "configuration.h" #include "integrations/integrations.h" #include @@ -76,8 +81,8 @@ static bool dd_check_for_composer_autoloader(zend_ulong invocation, zend_execute UNUSED(invocation, auxiliary, dynamic); ddog_CharSlice composer_path = dd_zend_string_to_CharSlice(execute_data->func->op_array.filename); - if (!ddtrace_sidecar // if sidecar connection was broken, let's skip immediately - || ddtrace_detect_composer_installed_json(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), composer_path)) { + if (!DDTRACE_G(sidecar) // if sidecar connection was broken, let's skip immediately + || ddtrace_detect_composer_installed_json(&DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), composer_path)) { zai_hook_remove((zai_str)ZAI_STR_EMPTY, (zai_str)ZAI_STR_EMPTY, dd_composer_hook_id); } return true; @@ -119,12 +124,12 @@ void ddtrace_telemetry_register_services(ddog_SidecarTransport **sidecar) { } void ddtrace_telemetry_lifecycle_end() { - if (!ddtrace_sidecar || !get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { + if (!DDTRACE_G(sidecar) || !get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { return; } ddtrace_ffi_try("Failed ending sidecar lifecycle", - ddog_sidecar_lifecycle_end(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id))); + ddog_sidecar_lifecycle_end(&DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id))); } void ddtrace_telemetry_finalize() { @@ -220,7 +225,7 @@ void ddtrace_telemetry_finalize() { // Telemetry metrics ddog_CharSlice metric_name = DDOG_CHARSLICE_C("spans_created"); - ddog_sidecar_telemetry_register_metric(&ddtrace_sidecar, metric_name, DDOG_METRIC_TYPE_COUNT, DDOG_METRIC_NAMESPACE_TRACERS); + ddog_sidecar_telemetry_register_metric(&DDTRACE_G(sidecar), metric_name, DDOG_METRIC_TYPE_COUNT, DDOG_METRIC_NAMESPACE_TRACERS); zend_string *integration_name; zval *metric_value; ZEND_HASH_FOREACH_STR_KEY_VAL(&DDTRACE_G(telemetry_spans_created_per_integration), integration_name, metric_value) { @@ -229,18 +234,18 @@ void ddtrace_telemetry_finalize() { zai_string_destroy(&tags); } ZEND_HASH_FOREACH_END(); - ddog_sidecar_telemetry_register_metric(&ddtrace_sidecar, DDOG_CHARSLICE_C("context_header_style.extracted"), DDOG_METRIC_TYPE_COUNT, DDOG_METRIC_NAMESPACE_TRACERS); + ddog_sidecar_telemetry_register_metric(&DDTRACE_G(sidecar), DDOG_CHARSLICE_C("context_header_style.extracted"), DDOG_METRIC_TYPE_COUNT, DDOG_METRIC_NAMESPACE_TRACERS); ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("context_header_style.extracted"), DDTRACE_G(baggage_extract_count), DDOG_CHARSLICE_C("header_style:baggage")); - ddog_sidecar_telemetry_register_metric(&ddtrace_sidecar, DDOG_CHARSLICE_C("context_header_style.injected"), DDOG_METRIC_TYPE_COUNT, DDOG_METRIC_NAMESPACE_TRACERS); + ddog_sidecar_telemetry_register_metric(&DDTRACE_G(sidecar), DDOG_CHARSLICE_C("context_header_style.injected"), DDOG_METRIC_TYPE_COUNT, DDOG_METRIC_NAMESPACE_TRACERS); ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("context_header_style.injected"), DDTRACE_G(baggage_inject_count), DDOG_CHARSLICE_C("header_style:baggage")); - ddog_sidecar_telemetry_register_metric(&ddtrace_sidecar, DDOG_CHARSLICE_C("context_header.truncated"), DDOG_METRIC_TYPE_COUNT, DDOG_METRIC_NAMESPACE_TRACERS); + ddog_sidecar_telemetry_register_metric(&DDTRACE_G(sidecar), DDOG_CHARSLICE_C("context_header.truncated"), DDOG_METRIC_TYPE_COUNT, DDOG_METRIC_NAMESPACE_TRACERS); ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("context_header.truncated"), DDTRACE_G(baggage_max_item_count), DDOG_CHARSLICE_C("truncation_reason:baggage_byte_item_exceeded")); ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("context_header.truncated"), DDTRACE_G(baggage_max_byte_count), DDOG_CHARSLICE_C("truncation_reason:baggage_byte_count_exceeded")); - ddog_sidecar_telemetry_register_metric(&ddtrace_sidecar, DDOG_CHARSLICE_C("context_header_style.malformed"), DDOG_METRIC_TYPE_COUNT, DDOG_METRIC_NAMESPACE_TRACERS); + ddog_sidecar_telemetry_register_metric(&DDTRACE_G(sidecar), DDOG_CHARSLICE_C("context_header_style.malformed"), DDOG_METRIC_TYPE_COUNT, DDOG_METRIC_NAMESPACE_TRACERS); ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("context_header_style.malformed"), DDTRACE_G(baggage_malformed_count), DDOG_CHARSLICE_C("header_style:baggage")); metric_name = DDOG_CHARSLICE_C("logs_created"); - ddog_sidecar_telemetry_register_metric(&ddtrace_sidecar, metric_name, DDOG_METRIC_TYPE_COUNT, DDOG_METRIC_NAMESPACE_GENERAL); + ddog_sidecar_telemetry_register_metric(&DDTRACE_G(sidecar), metric_name, DDOG_METRIC_TYPE_COUNT, DDOG_METRIC_NAMESPACE_GENERAL); static struct { ddog_CharSlice level; ddog_CharSlice tags; @@ -261,10 +266,12 @@ void ddtrace_telemetry_finalize() { dd_commit_metrics(); ddtrace_ffi_try("Failed flushing filtered telemetry buffer", - ddog_sidecar_telemetry_filter_flush(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), buffer, ddtrace_telemetry_cache(), service_name, env_name)); + ddog_sidecar_telemetry_filter_flush(&DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), buffer, ddtrace_telemetry_cache(), service_name, env_name)); ddog_sidecar_telemetry_buffer_drop(buffer); + // Flush any accumulated BGS (background sender) metrics if enough time has passed. + ddtrace_telemetry_flush_bgs_metrics_if_due(); } void ddtrace_telemetry_notify_integration(const char *name, size_t name_len) { @@ -272,7 +279,7 @@ void ddtrace_telemetry_notify_integration(const char *name, size_t name_len) { } void ddtrace_telemetry_notify_integration_version(const char *name, size_t name_len, const char *version, size_t version_len) { - if (ddtrace_sidecar && get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { + if (DDTRACE_G(sidecar) && get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { ddog_CharSlice integration = (ddog_CharSlice) {.len = name_len, .ptr = name}; ddog_CharSlice ver = (ddog_CharSlice) {.len = version_len, .ptr = version}; ddog_sidecar_telemetry_addIntegration_buffer(ddtrace_telemetry_buffer(), integration, ver, true); @@ -309,74 +316,126 @@ void ddtrace_telemetry_inc_spans_created(ddtrace_span_data *span) { zend_string_release(integration); } +// Process-global atomic accumulators for background-sender metrics. +// Written by the BGS thread (coms.c) without any lock; drained by a PHP request +// thread in ddtrace_telemetry_flush_bgs_metrics_if_due(). +static _Atomic(int) bgs_metric_requests = 0; +static _Atomic(int) bgs_metric_responses_1xx = 0; +static _Atomic(int) bgs_metric_responses_2xx = 0; +static _Atomic(int) bgs_metric_responses_3xx = 0; +static _Atomic(int) bgs_metric_responses_4xx = 0; +static _Atomic(int) bgs_metric_responses_5xx = 0; +static _Atomic(int) bgs_metric_errors_timeout = 0; +static _Atomic(int) bgs_metric_errors_network = 0; +static _Atomic(int) bgs_metric_errors_status_code = 0; +// Timestamp (nanoseconds) of the last flush; used to rate-limit to one flush per interval. +static _Atomic(uint64_t) bgs_metrics_last_flush_ns = 0; + void ddtrace_telemetry_send_trace_api_metrics(trace_api_metrics metrics) { - if (!ddtrace_sidecar || !get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { + // Pure atomic accumulation — never touches the sidecar. + if (!metrics.requests) { return; } + atomic_fetch_add(&bgs_metric_requests, metrics.requests); + atomic_fetch_add(&bgs_metric_responses_1xx, metrics.responses_1xx); + atomic_fetch_add(&bgs_metric_responses_2xx, metrics.responses_2xx); + atomic_fetch_add(&bgs_metric_responses_3xx, metrics.responses_3xx); + atomic_fetch_add(&bgs_metric_responses_4xx, metrics.responses_4xx); + atomic_fetch_add(&bgs_metric_responses_5xx, metrics.responses_5xx); + atomic_fetch_add(&bgs_metric_errors_timeout, metrics.errors_timeout); + atomic_fetch_add(&bgs_metric_errors_network, metrics.errors_network); + atomic_fetch_add(&bgs_metric_errors_status_code, metrics.errors_status_code); +} - if (!metrics.requests) { +void ddtrace_telemetry_flush_bgs_metrics_if_due(void) { + if (!DDTRACE_G(sidecar) || !get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { + return; + } + + // Rate-limit: flush at most once per agent flush interval. + uint64_t now_ns = ddtrace_nanoseconds_realtime(); + uint64_t last = atomic_load(&bgs_metrics_last_flush_ns); + uint64_t interval_ns = (uint64_t)get_global_DD_TRACE_AGENT_FLUSH_INTERVAL() * 1000000ULL; + if (now_ns - last < interval_ns) { + return; + } + // CAS ensures only one thread flushes per interval. + if (!atomic_compare_exchange_strong(&bgs_metrics_last_flush_ns, &last, now_ns)) { + return; + } + + int requests = atomic_exchange(&bgs_metric_requests, 0); + if (!requests) { return; } ddog_SidecarActionsBuffer *buffer = ddog_sidecar_telemetry_buffer_alloc(); - ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.requests"), (double)metrics.requests, DDOG_CHARSLICE_C("")); + ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.requests"), requests, DDOG_CHARSLICE_C("")); - if (metrics.responses_1xx) { - ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.responses"), (double)metrics.responses_1xx, DDOG_CHARSLICE_C("status_code:1xx")); + int v; + if ((v = atomic_exchange(&bgs_metric_responses_1xx, 0))) { + ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.responses"), v, DDOG_CHARSLICE_C("status_code:1xx")); } - if (metrics.responses_2xx) { - ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.responses"), (double)metrics.responses_2xx, DDOG_CHARSLICE_C("status_code:2xx")); + if ((v = atomic_exchange(&bgs_metric_responses_2xx, 0))) { + ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.responses"), v, DDOG_CHARSLICE_C("status_code:2xx")); } - if (metrics.responses_3xx) { - ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.responses"), (double)metrics.responses_3xx, DDOG_CHARSLICE_C("status_code:3xx")); + if ((v = atomic_exchange(&bgs_metric_responses_3xx, 0))) { + ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.responses"), v, DDOG_CHARSLICE_C("status_code:3xx")); } - if (metrics.responses_4xx) { - ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.responses"), (double)metrics.responses_4xx, DDOG_CHARSLICE_C("status_code:4xx")); + if ((v = atomic_exchange(&bgs_metric_responses_4xx, 0))) { + ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.responses"), v, DDOG_CHARSLICE_C("status_code:4xx")); } - if (metrics.responses_5xx) { - ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.responses"), (double)metrics.responses_5xx, DDOG_CHARSLICE_C("status_code:5xx")); + if ((v = atomic_exchange(&bgs_metric_responses_5xx, 0))) { + ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.responses"), v, DDOG_CHARSLICE_C("status_code:5xx")); } - - if (metrics.errors_timeout) { - ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.errors"), (double)metrics.errors_timeout, DDOG_CHARSLICE_C("type:timeout")); + if ((v = atomic_exchange(&bgs_metric_errors_timeout, 0))) { + ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.errors"), v, DDOG_CHARSLICE_C("type:timeout")); } - if (metrics.errors_network) { - ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.errors"), (double)metrics.errors_network, DDOG_CHARSLICE_C("type:network")); + if ((v = atomic_exchange(&bgs_metric_errors_network, 0))) { + ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.errors"), v, DDOG_CHARSLICE_C("type:network")); } - if (metrics.errors_status_code) { - ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.errors"), (double)metrics.errors_status_code, DDOG_CHARSLICE_C("type:status_code")); + if ((v = atomic_exchange(&bgs_metric_errors_status_code, 0))) { + ddog_sidecar_telemetry_add_span_metric_point_buffer(buffer, DDOG_CHARSLICE_C("trace_api.errors"), v, DDOG_CHARSLICE_C("type:status_code")); } ddtrace_ffi_try("Failed flushing background sender metrics", - ddog_sidecar_telemetry_buffer_flush(&ddtrace_sidecar, ddtrace_sidecar_instance_id, &dd_bgs_queued_id, buffer)); + ddog_sidecar_telemetry_buffer_flush(&DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, &dd_bgs_queued_id, buffer)); } -ZEND_TLS ddog_SidecarActionsBuffer *metrics_buffer; +void ddtrace_telemetry_flush_bgs_metrics_final(void) { + if (!DDTRACE_G(sidecar) || !get_global_DD_INSTRUMENTATION_TELEMETRY_ENABLED()) { + return; + } + // Bypass the time gate so any remaining metrics are sent before the transport + // is dropped in GSHUTDOWN. Setting last_flush_ns to 0 makes the time check in + // _if_due always pass; the CAS inside still prevents a concurrent double-flush. + atomic_store(&bgs_metrics_last_flush_ns, 0); + ddtrace_telemetry_flush_bgs_metrics_if_due(); +} -DDTRACE_PUBLIC void ddtrace_metric_register_buffer(zend_string *name, ddog_MetricType type, ddog_MetricNamespace ns) -{ - if (!ddtrace_sidecar) { +DDTRACE_PUBLIC void ddtrace_metric_register_buffer(zend_string *name, ddog_MetricType type, ddog_MetricNamespace ns) { + if (!DDTRACE_G(sidecar)) { return; } ddog_CharSlice metric_name = dd_zend_string_to_CharSlice(name); - ddog_sidecar_telemetry_register_metric(&ddtrace_sidecar, metric_name, type, ns); + ddog_sidecar_telemetry_register_metric(&DDTRACE_G(sidecar), metric_name, type, ns); } DDTRACE_PUBLIC bool ddtrace_metric_add_point(zend_string *name, double value, zend_string *tags) { - if (!metrics_buffer) { - metrics_buffer = ddog_sidecar_telemetry_buffer_alloc(); + if (!DDTRACE_G(metrics_buffer)) { + DDTRACE_G(metrics_buffer) = ddog_sidecar_telemetry_buffer_alloc(); } ddog_CharSlice metric_name = dd_zend_string_to_CharSlice(name); ddog_CharSlice tags_slice = dd_zend_string_to_CharSlice(tags); - ddog_sidecar_telemetry_add_span_metric_point_buffer(metrics_buffer, metric_name, value, tags_slice); + ddog_sidecar_telemetry_add_span_metric_point_buffer(DDTRACE_G(metrics_buffer), metric_name, value, tags_slice); return true; } static void dd_commit_metrics() { - if (!metrics_buffer) { + if (!DDTRACE_G(metrics_buffer)) { return; } ddog_sidecar_telemetry_buffer_flush( - &ddtrace_sidecar, ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), metrics_buffer); - metrics_buffer = NULL; + &DDTRACE_G(sidecar), ddtrace_sidecar_instance_id, &DDTRACE_G(sidecar_queue_id), DDTRACE_G(metrics_buffer)); + DDTRACE_G(metrics_buffer) = NULL; } diff --git a/ext/telemetry.h b/ext/telemetry.h index 84a7f0c4345..aa891ea32a6 100644 --- a/ext/telemetry.h +++ b/ext/telemetry.h @@ -35,7 +35,15 @@ void ddtrace_telemetry_finalize(); void ddtrace_telemetry_lifecycle_end(void); void ddtrace_telemetry_register_services(ddog_SidecarTransport **sidecar); void ddtrace_telemetry_inc_spans_created(ddtrace_span_data *span); +// Called by the background sender thread (coms.c) to accumulate metrics atomically. +// Never touches the sidecar; the request thread flushes via ddtrace_telemetry_flush_bgs_metrics_if_due(). void ddtrace_telemetry_send_trace_api_metrics(trace_api_metrics metrics); +// Called from ddtrace_telemetry_finalize() to flush accumulated BGS metrics through +// the current thread's sidecar connection, at most once per flush interval. +void ddtrace_telemetry_flush_bgs_metrics_if_due(void); +// Force-flush accumulated BGS metrics regardless of the time gate. Call immediately +// before dropping the per-thread transport in GSHUTDOWN so no data is lost. +void ddtrace_telemetry_flush_bgs_metrics_final(void); // public API DDTRACE_PUBLIC void ddtrace_metric_register_buffer(zend_string *name, ddog_MetricType type, ddog_MetricNamespace ns); diff --git a/libdatadog b/libdatadog index 561f772f872..7351018ee54 160000 --- a/libdatadog +++ b/libdatadog @@ -1 +1 @@ -Subproject commit 561f772f872ed2edc523ffbd6a2f65e7172ab6dd +Subproject commit 7351018ee5402f0f59adccad4cc1de26598879e6 diff --git a/src/DDTrace/Integrations/Laravel/LaravelIntegration.php b/src/DDTrace/Integrations/Laravel/LaravelIntegration.php index 3319bcb33f0..aa17f5c563e 100644 --- a/src/DDTrace/Integrations/Laravel/LaravelIntegration.php +++ b/src/DDTrace/Integrations/Laravel/LaravelIntegration.php @@ -152,6 +152,7 @@ static function ($This, $scope, $args, $route) { $resourceName = $method . ' ' . $path; \DDTrace\add_endpoint($path, 'http.request', $resourceName, $method); } + \DDTrace\flush_endpoints(); } } ); diff --git a/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php b/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php index d660b2deb55..f6b4c68fff2 100644 --- a/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php +++ b/src/DDTrace/Integrations/Symfony/SymfonyIntegration.php @@ -580,9 +580,10 @@ function_exists('datadog\appsec\push_addresses')) { * Since the arguments passed to the tracing closure on PHP 7 are mutable, * the closure must be run _before_ the original call via 'prehook'. */ + $endpoints_collected = false; $eventDispatcherTracer = [ 'recurse' => true, - 'prehook' => static function(SpanData $span, $args) use (&$injectedActionInfo) { + 'prehook' => static function(SpanData $span, $args) use (&$injectedActionInfo, &$endpoints_collected) { if (\DDTrace\root_span() === $span) { return false; // e.g., lone symfony.console.terminate } @@ -657,17 +658,21 @@ static function(HookData $hook) use ($controllerName) { } } - if (self::$kernel !== null + // This hook may be called multiple times, so we need to make sure we only collect endpoints once + if (!$endpoints_collected + && self::$kernel !== null && \defined(\get_class(self::$kernel) . '::VERSION') && \strpos(self::$kernel::VERSION, '4.') !== 0 - && self::$frameworkPrefix === SymfonyIntegration::NAME - && !\DDTrace\are_endpoints_collected()) - { - /** @var ContainerInterface $container */ - $container = self::$kernel->getContainer(); - $endpoints = EndpointCatalog::generate($container); - foreach ($endpoints as $endpoint) { - \DDTrace\add_endpoint($endpoint['path'], 'http.request', $endpoint['resourceName'], $endpoint['method']); + && self::$frameworkPrefix === SymfonyIntegration::NAME) { + $endpoints_collected = true; + if (!\DDTrace\are_endpoints_collected()) { + /** @var ContainerInterface $container */ + $container = self::$kernel->getContainer(); + $endpoints = EndpointCatalog::generate($container); + foreach ($endpoints as $endpoint) { + \DDTrace\add_endpoint($endpoint['path'], 'http.request', $endpoint['resourceName'], $endpoint['method']); + } + \DDTrace\flush_endpoints(); } } } diff --git a/src/DDTrace/Integrations/WordPress/WordPressIntegration.php b/src/DDTrace/Integrations/WordPress/WordPressIntegration.php index 40d7d3cc814..73085a47543 100644 --- a/src/DDTrace/Integrations/WordPress/WordPressIntegration.php +++ b/src/DDTrace/Integrations/WordPress/WordPressIntegration.php @@ -35,23 +35,6 @@ public static function init(): int }); \DDTrace\hook_method('WP', 'main', null, function ($This, $scope, $args) { - if (class_exists('WP_Query') && !\DDTrace\are_endpoints_collected()) { - $args = array('post_type' => 'any', 'posts_per_page' => -1); - $query = new \WP_Query($args); - foreach ($query->posts as $post) { - $path = property_exists($post, 'guid') ? $post->guid : ''; - $parsed = parse_url($path); - if (isset($parsed['path'])) { - $path = $parsed['path']; - } - if (isset($parsed['query'])) { - $path .= '?' . $parsed['query']; - } - $method = 'GET'; - $resourceName = $method . ' ' . $path; - \DDTrace\add_endpoint($path, 'http.request', $resourceName, $method); - } - } if (\property_exists($This, 'did_permalink') && $This->did_permalink === true) { if ( function_exists('\datadog\appsec\push_addresses') && diff --git a/tests/Integrations/WordPress/TelemetryTestSuite.php b/tests/Integrations/WordPress/TelemetryTestSuite.php deleted file mode 100644 index 9f3bf3577e5..00000000000 --- a/tests/Integrations/WordPress/TelemetryTestSuite.php +++ /dev/null @@ -1,96 +0,0 @@ - 'wordpress_test_app', - 'DD_TRACE_WORDPRESS_CALLBACKS' => '0', - 'DD_TRACE_MYSQLI_ENABLED' => '0', - 'DD_TRACE_AGENT_PORT' => 80, - 'DD_AGENT_HOST' => 'request-replayer', - 'DD_INSTRUMENTATION_TELEMETRY_ENABLED' => 1, - 'DD_LOGS_INJECTION' => 'false', - 'DD_TELEMETRY_HEARTBEAT_INTERVAL' => 10, - ]); - } - - protected function readEndpointsTelemetry($response) - { - $telemetryPayloads = []; - foreach ($response as $request) { - if (strpos($request["uri"], "/telemetry/") === 0) { - $json = json_decode($request["body"], true); - $batch = $json["request_type"] == "message-batch" ? $json["payload"] : [$json]; - foreach ($batch as $innerJson) { - if (isset($innerJson["request_type"]) && $innerJson["request_type"] == "app-endpoints") { - $telemetryPayloads[] = $innerJson["payload"]["endpoints"]; - } - } - } - } - return $telemetryPayloads; - } - - protected function expectedEndpoints() { - return [ - [ - "method" => "GET", - "operation_name" => "http.request", - "path" => "/?page_id=2", - "resource_name" => "GET /?page_id=2", - ], - [ - "method" => "GET", - "operation_name" => "http.request", - "path" => "/?p=1", - "resource_name" => "GET /?p=1", - ], - [ - "method" => "GET", - "operation_name" => "http.request", - "path" => "/?p=5", - "resource_name" => "GET /?p=5", - ] - ]; - } - - public function testAppEndpointsAreSent() - { - $this->call( - GetSpec::create( - 'A simple GET request returning a string', - '/simple?key=value&pwd=should_redact' - ) - ); - - $found_app_endpoints = false; - $until = function ($request) use (&$found_app_endpoints) { - if (strpos($request["body"] ?? "", "app-endpoints") !== false) { - $found_app_endpoints = true; - } - return $found_app_endpoints; - }; - $response = $this->retrieveDumpedData($until); - - //Let's wait for the telemetry data to be sent to the agent - sleep(3); - $endpoints = $this->readEndpointsTelemetry($response); - $endpoints = isset($endpoints[0]) ? $endpoints[0] : []; - - $expected_endpoints = $this->expectedEndpoints(); - - $this->assertCount(count($expected_endpoints), $endpoints); - - foreach ($expected_endpoints as $expected_endpoint) { - $this->assertContains($expected_endpoint, $endpoints); - } - } -} diff --git a/tests/Integrations/WordPress/V4_8/TelemetryTest.php b/tests/Integrations/WordPress/V4_8/TelemetryTest.php deleted file mode 100644 index bf40bea842f..00000000000 --- a/tests/Integrations/WordPress/V4_8/TelemetryTest.php +++ /dev/null @@ -1,40 +0,0 @@ - "GET", - "operation_name" => "http.request", - "path" => "/?p=1", - "resource_name" => "GET /?p=1", - ], - [ - "method" => "GET", - "operation_name" => "http.request", - "path" => "/?page_id=2", - "resource_name" => "GET /?page_id=2", - ] - ]; - } -} diff --git a/tests/Integrations/WordPress/V5_5/TelemetryTest.php b/tests/Integrations/WordPress/V5_5/TelemetryTest.php deleted file mode 100644 index 763cb6639f4..00000000000 --- a/tests/Integrations/WordPress/V5_5/TelemetryTest.php +++ /dev/null @@ -1,23 +0,0 @@ - "GET", - "operation_name" => "http.request", - "path" => "/?page_id=2", - "resource_name" => "GET /?page_id=2", - ], - [ - "method" => "GET", - "operation_name" => "http.request", - "path" => "/?p=1", - "resource_name" => "GET /?p=1", - ] - ]; - } -}