Skip to content

Commit a3e585a

Browse files
committed
Make use of fast_shutdown to avoid freeing overhead
Signed-off-by: Bob Weinand <[email protected]>
1 parent c70ff24 commit a3e585a

9 files changed

Lines changed: 62 additions & 35 deletions

File tree

ext/auto_flush.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@
1919

2020
ZEND_EXTERN_MODULE_GLOBALS(ddtrace);
2121

22-
ZEND_RESULT_CODE ddtrace_flush_tracer(bool force_on_startup, bool collect_cycles) {
22+
ZEND_RESULT_CODE ddtrace_flush_tracer(bool force_on_startup, bool collect_cycles, bool fast_shutdown) {
2323
bool success = true;
2424

2525
ddog_TracesBytes *traces = ddog_get_traces();
2626
if (collect_cycles) {
27-
ddtrace_serialize_closed_spans_with_cycle(traces);
27+
ddtrace_serialize_closed_spans_with_cycle(traces, fast_shutdown);
2828
} else {
29-
ddtrace_serialize_closed_spans(traces);
29+
ddtrace_serialize_closed_spans(traces, fast_shutdown);
3030
}
3131

3232
// Prevent traces from requests not executing any PHP code:
@@ -113,7 +113,7 @@ ZEND_RESULT_CODE ddtrace_flush_tracer(bool force_on_startup, bool collect_cycles
113113
DDTRACE_PUBLIC void ddtrace_close_all_spans_and_flush()
114114
{
115115
ddtrace_close_all_open_spans(true);
116-
ddtrace_flush_tracer(true, true);
116+
ddtrace_flush_tracer(true, true, false);
117117
}
118118

119119
#define DEFAULT_UDS_PATH "/var/run/datadog/apm.socket"

ext/auto_flush.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include <php.h>
66
#include <stdbool.h>
77

8-
ZEND_RESULT_CODE ddtrace_flush_tracer(bool force_on_startup, bool collect_cycles);
8+
ZEND_RESULT_CODE ddtrace_flush_tracer(bool force_on_startup, bool collect_cycles, bool fast_shutdown);
99

1010
// This function is exported and used by appsec
1111
DDTRACE_PUBLIC void ddtrace_close_all_spans_and_flush(void);

ext/ddtrace.c

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1775,8 +1775,8 @@ static void dd_clean_globals(void) {
17751775
#endif
17761776
}
17771777

1778-
static void dd_shutdown_hooks_and_observer(void) {
1779-
zai_hook_clean();
1778+
static void dd_shutdown_hooks_and_observer(bool fast_shutdown) {
1779+
zai_hook_clean(fast_shutdown);
17801780

17811781
#if PHP_VERSION_ID >= 80000 && PHP_VERSION_ID < 80200
17821782
#if PHP_VERSION_ID < 80100
@@ -1797,7 +1797,7 @@ static void dd_shutdown_hooks_and_observer(void) {
17971797
#endif
17981798
}
17991799

1800-
void dd_force_shutdown_tracing(void) {
1800+
void dd_force_shutdown_tracing(bool fast_shutdown) {
18011801
DDTRACE_G(in_shutdown) = true;
18021802

18031803
zend_try {
@@ -1807,7 +1807,7 @@ void dd_force_shutdown_tracing(void) {
18071807
} zend_end_try();
18081808

18091809
zend_try {
1810-
if (ddtrace_flush_tracer(false, true) == FAILURE) {
1810+
if (ddtrace_flush_tracer(false, true, fast_shutdown) == FAILURE) {
18111811
LOG(WARN, "Unable to flush the tracer");
18121812
}
18131813
} zend_catch {
@@ -1818,7 +1818,7 @@ void dd_force_shutdown_tracing(void) {
18181818
ddtrace_disable_tracing_in_current_request(); // implicitly calling dd_clean_globals
18191819

18201820
// The hooks shall not be reset, just disabled at runtime.
1821-
dd_shutdown_hooks_and_observer();
1821+
dd_shutdown_hooks_and_observer(fast_shutdown);
18221822

18231823
DDTRACE_G(in_shutdown) = false;
18241824
}
@@ -1832,16 +1832,31 @@ static void dd_finalize_sidecar_lifecycle(bool clear_id) {
18321832
static PHP_RSHUTDOWN_FUNCTION(ddtrace) {
18331833
UNUSED(module_number, type);
18341834

1835+
// We deliberately select to not free some data structures, as to avoid the overhead of freeing them.
1836+
// Just proper destruction can have significant and easily measurable overhead on applications.
1837+
// Prior to PHP 7.2 fast shutdown was an opcache only feature
1838+
#if ZEND_DEBUG || PHP_VERSION_ID < 70200
1839+
bool fast_shutdown = 0;
1840+
#elif defined(__SANITIZE_ADDRESS__)
1841+
char *force_fast_shutdown = getenv("ZEND_ASAN_FORCE_FAST_SHUTDOWN");
1842+
bool fast_shutdown = (
1843+
is_zend_mm()
1844+
|| (force_fast_shutdown && ZEND_ATOL(force_fast_shutdown))
1845+
) && !EG(full_tables_cleanup);
1846+
#else
1847+
bool fast_shutdown = is_zend_mm() && !EG(full_tables_cleanup);
1848+
#endif
1849+
18351850
zend_hash_destroy(&DDTRACE_G(traced_spans));
18361851

18371852
// this needs to be done before dropping the spans
18381853
// run unconditionally because ddtrace may've been disabled mid-request
18391854
ddtrace_exec_handlers_rshutdown();
18401855

18411856
if (get_DD_TRACE_ENABLED()) {
1842-
dd_force_shutdown_tracing();
1857+
dd_force_shutdown_tracing(fast_shutdown);
18431858
} else if (!ddtrace_disable) {
1844-
dd_shutdown_hooks_and_observer();
1859+
dd_shutdown_hooks_and_observer(fast_shutdown);
18451860
}
18461861

18471862
if (DDTRACE_G(remote_config_state)) {
@@ -1851,7 +1866,9 @@ static PHP_RSHUTDOWN_FUNCTION(ddtrace) {
18511866
if (!ddtrace_disable) {
18521867
ddtrace_autoload_rshutdown();
18531868

1854-
OBJ_RELEASE(&DDTRACE_G(active_stack)->std);
1869+
if (!fast_shutdown) {
1870+
OBJ_RELEASE(&DDTRACE_G(active_stack)->std);
1871+
}
18551872
DDTRACE_G(active_stack) = NULL;
18561873
}
18571874

@@ -2344,7 +2361,7 @@ PHP_FUNCTION(dd_trace_serialize_closed_spans) {
23442361
ddtrace_mark_all_span_stacks_flushable();
23452362

23462363
ddog_TracesBytes *traces = ddog_get_traces();
2347-
ddtrace_serialize_closed_spans_with_cycle(traces);
2364+
ddtrace_serialize_closed_spans_with_cycle(traces, false);
23482365

23492366
zval traces_zv = dd_serialize_rust_traces_to_zval(traces);
23502367

@@ -3221,7 +3238,7 @@ PHP_FUNCTION(DDTrace_flush) {
32213238
if (get_DD_AUTOFINISH_SPANS()) {
32223239
ddtrace_close_userland_spans_until(NULL);
32233240
}
3224-
if (ddtrace_flush_tracer(false, get_DD_TRACE_FLUSH_COLLECT_CYCLES()) == FAILURE) {
3241+
if (ddtrace_flush_tracer(false, get_DD_TRACE_FLUSH_COLLECT_CYCLES(), false) == FAILURE) {
32253242
LOG_LINE(WARN, "Unable to flush the tracer");
32263243
}
32273244
RETURN_NULL();

ext/ddtrace.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ bool ddtrace_alter_default_propagation_style(zval *old_value, zval *new_value, z
7171
bool ddtrace_alter_dd_service(zval *old_value, zval *new_value, zend_string *new_str);
7272
bool ddtrace_alter_dd_env(zval *old_value, zval *new_value, zend_string *new_str);
7373
bool ddtrace_alter_dd_version(zval *old_value, zval *new_value, zend_string *new_str);
74-
void dd_force_shutdown_tracing(void);
74+
void dd_force_shutdown_tracing(bool fast_shutdown);
7575
void dd_internal_handle_fork(void);
7676
#ifdef CXA_THREAD_ATEXIT_WRAPPER
7777
void dd_run_rust_thread_destructors(void *unused);

ext/handlers_exception.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ void dd_exception_handler_freed(zend_object *object) {
380380
// Here we are at the very last chance before objects are unconditionally freed.
381381
// Let's force-disable the tracing in case it wasn't yet
382382
// Typically RSHUTDOWN would handle that, but since 8.1.0 opcache will free our objects before module_shutdown during preloading
383-
dd_force_shutdown_tracing();
383+
dd_force_shutdown_tracing(false);
384384
}
385385
}
386386
#endif

ext/span.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -836,7 +836,7 @@ static void dd_close_entry_span_of_stack(ddtrace_span_stack *stack) {
836836
ddtrace_switch_span_stack(stack->parent_stack);
837837
}
838838

839-
if (get_DD_TRACE_AUTO_FLUSH_ENABLED() && ddtrace_flush_tracer(false, get_DD_TRACE_FLUSH_COLLECT_CYCLES()) == FAILURE) {
839+
if (get_DD_TRACE_AUTO_FLUSH_ENABLED() && ddtrace_flush_tracer(false, get_DD_TRACE_FLUSH_COLLECT_CYCLES(), false) == FAILURE) {
840840
// In case we have root spans enabled, we need to always flush if we close that one (RSHUTDOWN)
841841
LOG(WARN, "Unable to auto flush the tracer");
842842
}
@@ -1080,7 +1080,7 @@ void ddtrace_drop_span(ddtrace_span_data *span) {
10801080
dd_drop_span(span, false);
10811081
}
10821082

1083-
void ddtrace_serialize_closed_spans(ddog_TracesBytes *traces) {
1083+
void ddtrace_serialize_closed_spans(ddog_TracesBytes *traces, bool fast_shutdown) {
10841084
if (DDTRACE_G(top_closed_stack)) {
10851085
ddtrace_span_stack *rootstack = DDTRACE_G(top_closed_stack);
10861086
DDTRACE_G(top_closed_stack) = NULL;
@@ -1108,7 +1108,9 @@ void ddtrace_serialize_closed_spans(ddog_TracesBytes *traces) {
11081108
// remove the artificially increased RC while closing again
11091109
GC_SET_REFCOUNT(&tmp->std, GC_REFCOUNT(&tmp->std) - DD_RC_CLOSED_MARKER);
11101110
#endif
1111-
OBJ_RELEASE(&tmp->std);
1111+
if (!fast_shutdown) {
1112+
OBJ_RELEASE(&tmp->std);
1113+
}
11121114
} while (span != end);
11131115
// We hold a reference to stacks with flushable spans
11141116
OBJ_RELEASE(&stack->std);
@@ -1128,10 +1130,10 @@ void ddtrace_serialize_closed_spans(ddog_TracesBytes *traces) {
11281130
DDTRACE_G(dropped_spans_count) = 0;
11291131
}
11301132

1131-
void ddtrace_serialize_closed_spans_with_cycle(ddog_TracesBytes *traces) {
1133+
void ddtrace_serialize_closed_spans_with_cycle(ddog_TracesBytes *traces, bool fast_shutdown) {
11321134
// We need to loop here, as closing the last span root stack could add other spans here
11331135
while (DDTRACE_G(top_closed_stack)) {
1134-
ddtrace_serialize_closed_spans(traces);
1136+
ddtrace_serialize_closed_spans(traces, fast_shutdown);
11351137
if (DDTRACE_G(open_spans_count)) {
11361138
// Also flush possible cycles here, if there are remaining open spans
11371139
gc_collect_cycles();

ext/span.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,8 @@ void ddtrace_close_top_span_without_stack_swap(ddtrace_span_data *span);
259259
void ddtrace_close_all_open_spans(bool force_close_root_span);
260260
void ddtrace_drop_span(ddtrace_span_data *span);
261261
void ddtrace_mark_all_span_stacks_flushable(void);
262-
void ddtrace_serialize_closed_spans(ddog_TracesBytes *traces);
263-
void ddtrace_serialize_closed_spans_with_cycle(ddog_TracesBytes *traces);
262+
void ddtrace_serialize_closed_spans(ddog_TracesBytes *traces, bool fast_shutdown);
263+
void ddtrace_serialize_closed_spans_with_cycle(ddog_TracesBytes *traces, bool fast_shutdown);
264264
zend_string *ddtrace_span_id_as_string(uint64_t id);
265265
zend_string *ddtrace_trace_id_as_string(ddtrace_trace_id id);
266266
zend_string *ddtrace_span_id_as_hex_string(uint64_t id);

zend_abstract_interface/hook/hook.c

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,12 +1178,16 @@ bool zai_hook_ginit(void) {
11781178
return true;
11791179
}
11801180

1181-
bool zai_hook_rinit(void) {
1181+
static void zai_hook_init_hashtables() {
11821182
zend_hash_init(&zai_hook_tls->inheritors, 8, NULL, zai_hook_inheritors_destroy, 0);
11831183
zend_hash_init(&zai_hook_tls->request_files.hooks, 8, NULL, zai_hook_destroy, 0);
11841184
zend_hash_init(&zai_hook_tls->request_functions, 8, NULL, zai_hook_hash_destroy, 0);
11851185
zend_hash_init(&zai_hook_tls->request_classes, 8, NULL, zai_hook_hash_destroy, 0);
11861186
zend_hash_init(&zai_hook_resolved, 8, NULL, NULL, 0);
1187+
}
1188+
1189+
bool zai_hook_rinit(void) {
1190+
zai_hook_init_hashtables();
11871191
zend_hash_init(&zai_function_location_map, 8, NULL, zai_function_location_destroy, 0);
11881192

11891193
// reserve low hook ids for static hooks
@@ -1488,17 +1492,21 @@ bool zai_hook_remove(zai_str scope, zai_str function, zend_long index) {
14881492
return false;
14891493
}
14901494

1491-
void zai_hook_clean(void) {
1492-
// graceful clean: ensure that destructors executing during cleanup may still access zai_hook_resolved
1493-
zend_hash_apply(&zai_hook_resolved, zai_hook_clean_graceful_del);
1494-
zend_hash_clean(&zai_hook_tls->request_functions);
1495-
zend_hash_clean(&zai_hook_tls->request_classes);
1495+
void zai_hook_clean(bool fast_shutdown) {
1496+
if (fast_shutdown) {
1497+
// We just reinitialize the hashtables to empty, so that no other hooks are executed.
1498+
// In the possible case where more code gets executed after - that's fine; the actual destroy comes later.
1499+
zai_hook_init_hashtables();
1500+
} else {
1501+
// graceful clean: ensure that destructors executing during cleanup may still access zai_hook_resolved
1502+
zend_hash_apply(&zai_hook_resolved, zai_hook_clean_graceful_del);
1503+
zend_hash_clean(&zai_hook_tls->request_functions);
1504+
zend_hash_clean(&zai_hook_tls->request_classes);
14961505

1497-
zend_hash_iterators_remove(&zai_hook_tls->request_files.hooks);
1498-
zend_hash_clean(&zai_hook_tls->request_files.hooks);
1506+
zend_hash_iterators_remove(&zai_hook_tls->request_files.hooks);
1507+
zend_hash_clean(&zai_hook_tls->request_files.hooks);
1508+
}
14991509
zai_hook_tls->request_files.dynamic = 0;
1500-
1501-
zend_hash_clean(&zai_function_location_map);
15021510
}
15031511

15041512
static void zai_hook_iterator_set_current_and_advance(zai_hook_iterator *it) {

zend_abstract_interface/hook/hook.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ bool zai_hook_ginit(void);
1414
bool zai_hook_rinit(void);
1515
void zai_hook_post_startup(void);
1616
void zai_hook_activate(void);
17-
void zai_hook_clean(void);
17+
void zai_hook_clean(bool fast_shutdown);
1818
void zai_hook_rshutdown(void);
1919
void zai_hook_gshutdown(void);
2020
void zai_hook_mshutdown(void); /* }}} */

0 commit comments

Comments
 (0)