Skip to content

Commit ad5a7e6

Browse files
authored
Handle APM_MULTI_CONFIG, fixup missing exception replay features (#3791)
* Handle APM_MULTI_CONFIG, fixup missing exception replay features Signed-off-by: Bob Weinand <bob.weinand@datadoghq.com> # Conflicts: # libdatadog * Update to newest libdatadog Signed-off-by: Bob Weinand <bob.weinand@datadoghq.com> --------- Signed-off-by: Bob Weinand <bob.weinand@datadoghq.com>
1 parent 6cee576 commit ad5a7e6

21 files changed

Lines changed: 401 additions & 109 deletions

Cargo.lock

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

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ RUN_TESTS_CMD := DD_SERVICE= DD_ENV= REPORT_EXIT_STATUS=1 TEST_PHP_SRCDIR=$(PROJ
4848

4949
C_FILES = $(shell find components components-rs ext src/dogstatsd zend_abstract_interface -name '*.c' -o -name '*.h' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' )
5050
TEST_FILES = $(shell find tests/ext -name '*.php*' -o -name '*.inc' -o -name '*.json' -o -name '*.yaml' -o -name 'CONFLICTS' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' )
51-
RUST_FILES = $(BUILD_DIR)/Cargo.toml $(BUILD_DIR)/Cargo.lock $(shell find components-rs -name '*.c' -o -name '*.rs' -o -name 'Cargo.toml' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) $(shell find libdatadog/{build-common,datadog-ffe,datadog-ipc,datadog-ipc-macros,datadog-live-debugger,datadog-live-debugger-ffi,datadog-remote-config,datadog-sidecar,datadog-sidecar-ffi,datadog-sidecar-macros,libdd-alloc,libdd-capabilities,libdd-capabilities-impl,libdd-common,libdd-common-ffi,libdd-crashtracker,libdd-crashtracker-ffi,libdd-data-pipeline,libdd-ddsketch,libdd-dogstatsd-client,libdd-library-config,libdd-library-config-ffi,libdd-log,libdd-libunwind-sys,libdd-shared-runtime,libdd-telemetry,libdd-telemetry-ffi,libdd-tinybytes,libdd-trace-*,spawn_worker,tools/{cc_utils,sidecar_mockgen},libdd-trace-*,Cargo.toml} \( -type l -o -type f \) \( -path "*/src*" -o -path "*/examples*" -o -path "*/libdd-libunwind-sys*" -o -path "*Cargo.toml" -o -path "*/build.rs" -o -path "*/tests/dataservice.rs" -o -path "*/tests/service_functional.rs" \) -not -path "*/datadog-ipc/build.rs" -not -path "*/datadog-sidecar-ffi/build.rs")
51+
RUST_FILES = $(BUILD_DIR)/Cargo.toml $(BUILD_DIR)/Cargo.lock $(shell find components-rs -name '*.c' -o -name '*.rs' -o -name 'Cargo.toml' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' ) $(shell find libdatadog/{build-common,datadog-ffe,datadog-ipc,datadog-ipc-macros,datadog-live-debugger,datadog-live-debugger-ffi,datadog-remote-config,datadog-sidecar,datadog-sidecar-ffi,datadog-sidecar-macros,libdd-alloc,libdd-capabilities,libdd-capabilities-impl,libdd-common,libdd-common-ffi,libdd-crashtracker,libdd-crashtracker-ffi,libdd-data-pipeline,libdd-ddsketch,libdd-dogstatsd-client,libdd-library-config,libdd-library-config-ffi,libdd-log,libdd-shared-runtime,libdd-telemetry,libdd-telemetry-ffi,libdd-tinybytes,libdd-trace-*,spawn_worker,tools/{cc_utils,sidecar_mockgen},libdd-trace-*,Cargo.toml} \( -type l -o -type f \) \( -path "*/src*" -o -path "*/examples*" -o -path "*Cargo.toml" -o -path "*/build.rs" -o -path "*/tests/dataservice.rs" -o -path "*/tests/service_functional.rs" \) -not -path "*/datadog-ipc/build.rs" -not -path "*/datadog-sidecar-ffi/build.rs")
5252
ALL_OBJECT_FILES = $(C_FILES) $(RUST_FILES) $(BUILD_DIR)/Makefile
5353
TEST_OPCACHE_FILES = $(shell find tests/opcache -name '*.php*' -o -name '.gitkeep' | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' )
5454
TEST_STUB_FILES = $(shell find tests/ext -type d -name 'stubs' -exec find '{}' -type f \; | awk '{ printf "$(BUILD_DIR)/%s\n", $$1 }' )

components-rs/common.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,12 +588,20 @@ typedef struct ddog_CaptureConfiguration {
588588
uint32_t max_field_count;
589589
} ddog_CaptureConfiguration;
590590

591+
typedef struct ddog_CaptureExpression {
592+
ddog_CharSlice name;
593+
const struct ddog_ProbeValue *expr;
594+
const struct ddog_CaptureConfiguration *capture;
595+
} ddog_CaptureExpression;
596+
591597
typedef struct ddog_LogProbe {
592598
const struct ddog_DslString *segments;
593599
const struct ddog_ProbeCondition *when;
594600
const struct ddog_CaptureConfiguration *capture;
595601
bool capture_snapshot;
596602
uint32_t sampling_snapshots_per_second;
603+
const struct ddog_CaptureExpression *capture_expressions;
604+
uintptr_t capture_expressions_num;
597605
} ddog_LogProbe;
598606

599607
typedef struct ddog_SpanProbeTag {

components-rs/ddtrace.h

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,6 @@ void ddog_apply_agent_info(struct ddog_AgentInfoReader *reader,
8585
*/
8686
void ddog_apply_agent_info_concentrator_config(struct ddog_AgentInfoReader *reader);
8787

88-
/**
89-
* Returns true once the sidecar has received and applied the agent /info response.
90-
* Used by `dd_trace_internal_fn('await_agent_info')` to block until the concentrator
91-
* peer-tag keys and span kinds are initialised.
92-
*/
93-
bool ddog_is_agent_info_ready(void);
94-
9588
bool ddog_shall_log(enum ddog_Log category);
9689

9790
void ddog_set_error_log_level(bool once);
@@ -108,7 +101,8 @@ void ddog_init_remote_config(bool live_debugging_enabled,
108101
bool appsec_activation,
109102
bool appsec_config);
110103

111-
struct ddog_RemoteConfigState *ddog_init_remote_config_state(const struct ddog_Endpoint *endpoint);
104+
struct ddog_RemoteConfigState *ddog_init_remote_config_state(const struct ddog_Endpoint *endpoint,
105+
bool di_enabled);
112106

113107
const char *ddog_remote_config_get_path(const struct ddog_RemoteConfigState *remote_config);
114108

@@ -135,6 +129,14 @@ bool ddog_remote_config_alter_dynamic_config(struct ddog_RemoteConfigState *remo
135129
void ddog_setup_remote_config(ddog_DynamicConfigUpdate update_config,
136130
const struct ddog_LiveDebuggerSetup *setup);
137131

132+
/**
133+
* Enable or disable dynamic instrumentation.
134+
* When disabling: all installed probe hooks are removed (but kept in `active` for reinstallation).
135+
* When enabling: all probes in `active` that have no installed hook are (re-)installed.
136+
*/
137+
void ddog_set_dynamic_instrumentation_enabled(struct ddog_RemoteConfigState *remote_config,
138+
bool enabled);
139+
138140
void ddog_rshutdown_remote_config(struct ddog_RemoteConfigState *remote_config);
139141

140142
void ddog_shutdown_remote_config(struct ddog_RemoteConfigState*);
@@ -174,6 +176,13 @@ bool ddog_exception_hash_limiter_inc(struct ddog_SidecarTransport *connection,
174176
uint64_t hash,
175177
uint32_t granularity_seconds);
176178

179+
/**
180+
* Returns true once the agent /info has been received and applied.
181+
* Used by the PHP extension to skip stats computation until the concentrator
182+
* has been properly initialised with peer-tag keys and span kinds.
183+
*/
184+
bool ddog_is_agent_info_ready(void);
185+
177186
/**
178187
* Look up (or lazily create) the concentrator for `(env, version, service)` and invoke
179188
* `callback` with a shared reference to it while holding the global read lock.

components-rs/live-debugger.h

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
#include <stdio.h>
1111
#include "common.h"
1212

13-
void drop_span_decoration_probe(struct ddog_SpanDecorationProbe);
13+
void ddog_drop_log_probe_capture_expressions(struct ddog_LogProbe);
14+
15+
void ddog_drop_span_decoration_probe(struct ddog_SpanDecorationProbe);
1416

1517
struct ddog_CaptureConfiguration ddog_capture_defaults(void);
1618

@@ -54,6 +56,12 @@ ddog_DebuggerCapture *ddog_create_exception_snapshot(struct ddog_Vec_DebuggerPay
5456
ddog_CharSlice method_name,
5557
uint64_t timestamp);
5658

59+
/**
60+
* Returns a mutable pointer to the last DebuggerPayload in the Vec.
61+
* Used to push stack frames to exception replay snapshots after creation.
62+
*/
63+
struct ddog_DebuggerPayload *ddog_vec_last_debugger_payload(struct ddog_Vec_DebuggerPayload *buffer);
64+
5765
struct ddog_DebuggerPayload *ddog_create_log_probe_snapshot(const struct ddog_Probe *probe,
5866
const ddog_CharSlice *message,
5967
ddog_CharSlice service,
@@ -73,10 +81,25 @@ bool ddog_snapshot_redacted_name(ddog_CharSlice name);
7381

7482
void ddog_snapshot_add_redacted_name(ddog_CharSlice name);
7583

84+
void ddog_snapshot_add_excluded_name(ddog_CharSlice name);
85+
7686
bool ddog_snapshot_redacted_type(ddog_CharSlice name);
7787

7888
void ddog_snapshot_add_redacted_type(ddog_CharSlice name);
7989

90+
void ddog_snapshot_set_throwable(ddog_DebuggerCapture *capture,
91+
ddog_CharSlice throwable_type,
92+
ddog_CharSlice message);
93+
94+
void ddog_snapshot_throwable_add_frame(ddog_DebuggerCapture *capture,
95+
ddog_CharSlice file,
96+
ddog_CharSlice function,
97+
int64_t line);
98+
99+
void ddog_snapshot_add_capture_fields(ddog_DebuggerCapture *capture,
100+
ddog_CharSlice name,
101+
struct ddog_CaptureValue value);
102+
80103
void ddog_snapshot_add_field(ddog_DebuggerCapture *capture,
81104
enum ddog_FieldType type,
82105
ddog_CharSlice name,

components-rs/remote_config.rs

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ pub struct LiveDebuggerState {
113113
pub config_id: String,
114114
pub allow_dfa: Option<Regex>,
115115
pub deny_dfa: Option<Regex>,
116+
pub di_enabled: bool,
116117
}
117118

118119
#[no_mangle]
@@ -177,14 +178,18 @@ pub unsafe extern "C" fn ddog_init_remote_config(
177178
#[no_mangle]
178179
pub unsafe extern "C" fn ddog_init_remote_config_state(
179180
endpoint: &Endpoint,
181+
di_enabled: bool,
180182
) -> Box<RemoteConfigState> {
181183
Box::new(RemoteConfigState {
182184
manager: RemoteConfigManager::new(ConfigInvariants {
183185
language: "php".to_string(),
184186
tracer_version: include_str!("../VERSION").trim().into(),
185187
endpoint: endpoint.clone(),
186188
}),
187-
live_debugger: LiveDebuggerState::default(),
189+
live_debugger: LiveDebuggerState {
190+
di_enabled,
191+
..Default::default()
192+
},
188193
dynamic_config: Default::default(),
189194
})
190195
}
@@ -414,13 +419,17 @@ fn apply_config(
414419
match debugger {
415420
LiveDebuggingData::Probe(probe) => {
416421
debug!("Applying live debugger probe {probe:?}");
417-
let hook_id = (callbacks.set_probe)(probe.into(), limiter);
418-
if hook_id >= 0 {
419-
remote_config
420-
.live_debugger
421-
.spans_map
422-
.insert(probe.id.clone(), hook_id);
422+
if remote_config.live_debugger.di_enabled {
423+
let hook_id = (callbacks.set_probe)(probe.into(), limiter);
424+
if hook_id >= 0 {
425+
remote_config
426+
.live_debugger
427+
.spans_map
428+
.insert(probe.id.clone(), hook_id);
429+
}
423430
}
431+
// If di_enabled is false, probe is stored in `active` but hook is not installed.
432+
// It will be installed when di_enabled transitions to true.
424433
}
425434
LiveDebuggingData::ServiceConfiguration(config) => {
426435
debug!("Applying live debugger service config {config:?}");
@@ -588,6 +597,42 @@ pub unsafe extern "C" fn ddog_setup_remote_config(
588597
LIVE_DEBUGGER_CALLBACKS = Some(setup.callbacks.clone());
589598
}
590599

600+
/// Enable or disable dynamic instrumentation.
601+
/// When disabling: all installed probe hooks are removed (but kept in `active` for reinstallation).
602+
/// When enabling: all probes in `active` that have no installed hook are (re-)installed.
603+
#[no_mangle]
604+
pub extern "C" fn ddog_set_dynamic_instrumentation_enabled(
605+
remote_config: &mut RemoteConfigState,
606+
enabled: bool,
607+
) {
608+
if remote_config.live_debugger.di_enabled == enabled {
609+
return;
610+
}
611+
remote_config.live_debugger.di_enabled = enabled;
612+
613+
if let Some(callbacks) = unsafe { &LIVE_DEBUGGER_CALLBACKS } {
614+
if !enabled {
615+
// Remove all installed probe hooks; keep `active` intact for reinstallation.
616+
for (_, hook_id) in remote_config.live_debugger.spans_map.drain() {
617+
(callbacks.remove_probe)(hook_id);
618+
}
619+
} else {
620+
// Reinstall all probes currently stored in `active`.
621+
for (probe_id, boxed) in remote_config.live_debugger.active.iter() {
622+
if let (LiveDebuggingData::Probe(probe), limiter) = &**boxed {
623+
let hook_id = (callbacks.set_probe)(probe.into(), limiter);
624+
if hook_id >= 0 {
625+
remote_config
626+
.live_debugger
627+
.spans_map
628+
.insert(probe_id.clone(), hook_id);
629+
}
630+
}
631+
}
632+
}
633+
}
634+
}
635+
591636
#[no_mangle]
592637
pub extern "C" fn ddog_rshutdown_remote_config(remote_config: &mut RemoteConfigState) {
593638
remote_config.live_debugger.spans_map.clear();

config.m4

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,6 @@ if test "$PHP_DDTRACE" != "no"; then
6565
PHP_SUBST(DDTRACE_CARGO)
6666
fi
6767

68-
dnl As required by libdd-libunwind-sys
69-
if test "$PHP_DDTRACE_RUST_LIBRARY" = "-"; then
70-
AC_CHECK_TOOL(DDTRACE_AUTOMAKE, automake, [:])
71-
AS_IF([test "$DDTRACE_AUTOMAKE" = ":"], [AC_MSG_ERROR([Please install automake before configuring])])
72-
AC_CHECK_TOOL(DDTRACE_LIBTOOL, libtool, [:])
73-
AS_IF([test "$DDTRACE_LIBTOOL" = ":"], [
74-
AC_CHECK_TOOL(DDTRACE_LIBTOOLIZE, libtoolize, [:])
75-
AS_IF([test "$DDTRACE_LIBTOOLIZE" = ":"], [
76-
AC_MSG_ERROR([Please install libtool before configuring])
77-
])
78-
])
79-
fi
80-
8168
if test "$PHP_DDTRACE_SANITIZE" != "no"; then
8269
dnl gcc needs -lasan, clang needs -shared-libsan
8370
ac_cflags=$LDFLAGS

ext/configuration.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ enum ddtrace_sidecar_connection_mode {
264264
CONFIG(SET, DD_DYNAMIC_INSTRUMENTATION_REDACTED_IDENTIFIERS, "", .ini_change = zai_config_system_ini_change) \
265265
CONFIG(BOOL, DD_APM_TRACING_ENABLED, "true") \
266266
CONFIG(SET, DD_DYNAMIC_INSTRUMENTATION_REDACTED_TYPES, "", .ini_change = zai_config_system_ini_change) \
267+
CONFIG(SET, DD_DYNAMIC_INSTRUMENTATION_REDACTION_EXCLUDED_IDENTIFIERS, "", .ini_change = zai_config_system_ini_change) \
267268
CONFIG(INT, DD_TRACE_BAGGAGE_MAX_ITEMS, "64") \
268269
CONFIG(INT, DD_TRACE_BAGGAGE_MAX_BYTES, "8192") \
269270
CONFIG(BOOL, DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED, "false") \

ext/ddtrace.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1675,7 +1675,7 @@ static void dd_initialize_request(void) {
16751675
DDTRACE_G(request_initialized) = true;
16761676

16771677
if (!DDTRACE_G(remote_config_state) && ddtrace_endpoint) {
1678-
DDTRACE_G(remote_config_state) = ddog_init_remote_config_state(ddtrace_endpoint);
1678+
DDTRACE_G(remote_config_state) = ddog_init_remote_config_state(ddtrace_endpoint, get_DD_DYNAMIC_INSTRUMENTATION_ENABLED());
16791679
}
16801680

16811681
// We need to init RC for the sidecar to write to it immediately

ext/exception_serialize.c

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ static zend_ulong ddtrace_compute_exception_hash(zend_object *exception) {
357357
return hash;
358358
}
359359

360-
static void ddtrace_collect_exception_debug_data(zend_object *exception, zend_string *service_name, uint64_t time, ddog_SpanBytes *span) {
360+
static void ddtrace_collect_exception_debug_data(zend_object *exception, zend_object *throwable, zend_string *service_name, uint64_t time, ddog_SpanBytes *span) {
361361
if (!ddtrace_exception_debugging_is_active()) {
362362
return;
363363
}
@@ -392,14 +392,14 @@ static void ddtrace_collect_exception_debug_data(zend_object *exception, zend_st
392392
char *exception_id = zend_arena_alloc(&DDTRACE_G(debugger_capture_arena).arena, uuid_len);
393393
ddog_snapshot_format_new_uuid((uint8_t(*)[uuid_len])exception_id);
394394

395-
ddog_add_str_span_meta_CharSlice(span, "_dd.debug.error.exception_capture_id", (ddog_CharSlice){.ptr = exception_id, .len = uuid_len});
395+
ddog_add_str_span_meta_CharSlice(span, "_dd.debug.error.exception_id", (ddog_CharSlice){.ptr = exception_id, .len = uuid_len});
396396

397397
memset(&DDTRACE_G(exception_debugger_buffer), 0, sizeof(DDTRACE_G(exception_debugger_buffer)));
398398

399399
zval *frame;
400400
int frame_num = 0;
401401
ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARR_P(trace), frame_num, frame) {
402-
if (get_DD_EXCEPTION_REPLAY_CAPTURE_MAX_FRAMES() >= 0 && get_DD_EXCEPTION_REPLAY_CAPTURE_MAX_FRAMES() < frame_num) {
402+
if (get_DD_EXCEPTION_REPLAY_CAPTURE_MAX_FRAMES() >= 0 && get_DD_EXCEPTION_REPLAY_CAPTURE_MAX_FRAMES() <= frame_num) {
403403
break;
404404
}
405405

@@ -423,6 +423,10 @@ static void ddtrace_collect_exception_debug_data(zend_object *exception, zend_st
423423
}
424424

425425
ddog_DebuggerCapture *capture = dd_create_frame_and_collect_locals(exception_id, exception_hash, frame_num, class_slice, func_slice, locals, service_name, &capture_config, time, span);
426+
zend_string *ex_msg = zai_exception_message(throwable);
427+
ddog_snapshot_set_throwable(capture,
428+
(ddog_CharSlice){ .ptr = ZSTR_VAL(throwable->ce->name), .len = ZSTR_LEN(throwable->ce->name) },
429+
dd_zend_string_to_CharSlice(ex_msg));
426430
locals = zend_hash_find(Z_ARR_P(frame), key_locals);
427431

428432
zend_string *key;
@@ -431,7 +435,7 @@ static void ddtrace_collect_exception_debug_data(zend_object *exception, zend_st
431435
if (args && Z_TYPE_P(args) == IS_ARRAY) {
432436
zend_long idx;
433437
ZEND_HASH_FOREACH_KEY_VAL(Z_ARR_P(args), idx, key, val) {
434-
struct ddog_CaptureValue capture_value = {0};
438+
ddog_CaptureValue capture_value = {0};
435439
ddtrace_create_capture_value(val, &capture_value, &capture_config, capture_config.max_reference_depth);
436440
ddog_CharSlice arg_name;
437441
if (key) {
@@ -455,15 +459,19 @@ static void ddtrace_collect_exception_debug_data(zend_object *exception, zend_st
455459

456460
zval *obj = zend_hash_find(Z_ARR_P(frame), ZSTR_KNOWN(ZEND_STR_OBJECT));
457461
if (obj) {
458-
struct ddog_CaptureValue capture_value = {0};
462+
ddog_CaptureValue capture_value = {0};
459463
ddtrace_create_capture_value(obj, &capture_value, &capture_config, capture_config.max_reference_depth);
460464
ddog_snapshot_add_field(capture, DDOG_FIELD_TYPE_ARG, DDOG_CHARSLICE_C("this"), capture_value);
461465
}
462466
} ZEND_HASH_FOREACH_END();
463467

464-
if (get_DD_EXCEPTION_REPLAY_CAPTURE_MAX_FRAMES() < 0 || get_DD_EXCEPTION_REPLAY_CAPTURE_MAX_FRAMES() > frame_num) {
468+
if (get_DD_EXCEPTION_REPLAY_CAPTURE_MAX_FRAMES() < 0 || get_DD_EXCEPTION_REPLAY_CAPTURE_MAX_FRAMES() > frame_num + 1) {
465469
if (locals && Z_TYPE_P(locals) == IS_ARRAY) {
466-
dd_create_frame_and_collect_locals(exception_id, exception_hash, frame_num + 1, DDOG_CHARSLICE_C(""), DDOG_CHARSLICE_C(""), locals, service_name, &capture_config, time, span);
470+
ddog_DebuggerCapture *last_capture = dd_create_frame_and_collect_locals(exception_id, exception_hash, frame_num + 1, DDOG_CHARSLICE_C(""), DDOG_CHARSLICE_C(""), locals, service_name, &capture_config, time, span);
471+
zend_string *last_ex_msg = zai_exception_message(throwable);
472+
ddog_snapshot_set_throwable(last_capture,
473+
(ddog_CharSlice){ .ptr = ZSTR_VAL(throwable->ce->name), .len = ZSTR_LEN(throwable->ce->name) },
474+
dd_zend_string_to_CharSlice(last_ex_msg));
467475
}
468476
}
469477

@@ -508,8 +516,9 @@ void ddtrace_exception_to_meta(zend_object *exception, zend_string *service_name
508516
previous = zai_exception_read_property(exception, ZSTR_KNOWN(ZEND_STR_PREVIOUS));
509517
}
510518

511-
// exception is now the innermost exception, i.e. what we need
512-
ddtrace_collect_exception_debug_data(exception, service_name, time / 1000000, span);
519+
// Capture frames from the innermost (root cause) exception so the stack trace reflects
520+
// where the error originated. Use the outermost exception for the throwable type/message so it matches the span error and the exception replay filter.
521+
ddtrace_collect_exception_debug_data(exception, exception_root, service_name, time / 1000000, span);
513522

514523
previous = zai_exception_read_property(exception_root, ZSTR_KNOWN(ZEND_STR_PREVIOUS));
515524
while (Z_TYPE_P(previous) == IS_OBJECT && !Z_IS_RECURSIVE_P(previous) &&

0 commit comments

Comments
 (0)