From acfde3da7a7399ffd35014481908419b33b315b9 Mon Sep 17 00:00:00 2001 From: Alexandre Rulleau Date: Wed, 11 Feb 2026 12:52:34 +0100 Subject: [PATCH 1/2] fix(logging): fsync crash logs before _Exit() to prevent data loss When a SIGSEGV occurs, the signal handler logs "Segmentation fault encountered" and then calls _Exit() which terminates the process immediately. Without fsync(), kernel write buffers may not be flushed to disk before termination, causing a race condition where the error log file is sometimes not created. This fix adds fsync() on Unix/Linux and _commit() on Windows after write() in ddtrace_log_with_time() to ensure crash logs persist to disk before process termination. The issue affects production (rare but possible during power loss, kernel panic, or I/O errors) and causes consistent test failures where tests check for log files immediately after crashes (before kernel writeback completes). Fixes flaky test_metrics SigSegVTest::testGet failures on Kubernetes where dd_php_error.log was not being created consistently. --- ext/logging.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ext/logging.c b/ext/logging.c index c0e2f9fb717..deaa957e96b 100644 --- a/ext/logging.c +++ b/ext/logging.c @@ -160,6 +160,15 @@ int ddtrace_log_with_time(int fd, const char *msg, int msg_len) { int ret = write(fd, msgbuf, p - msgbuf); + // Flush to disk to ensure data persists even if process terminates immediately + if (ret > 0) { +#ifndef _WIN32 + fsync(fd); +#else + _commit(fd); +#endif + } + free(msgbuf); return ret; } From db4f3450d2e09456dc8824fc9dd6bb6978501867 Mon Sep 17 00:00:00 2001 From: Alexandre Rulleau Date: Wed, 11 Feb 2026 14:34:40 +0100 Subject: [PATCH 2/2] fix(signals): move flush in sigsegv handler Signed-off-by: Alexandre Rulleau --- ext/logging.c | 9 --------- ext/signals.c | 9 +++++++++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/logging.c b/ext/logging.c index deaa957e96b..c0e2f9fb717 100644 --- a/ext/logging.c +++ b/ext/logging.c @@ -160,15 +160,6 @@ int ddtrace_log_with_time(int fd, const char *msg, int msg_len) { int ret = write(fd, msgbuf, p - msgbuf); - // Flush to disk to ensure data persists even if process terminates immediately - if (ret > 0) { -#ifndef _WIN32 - fsync(fd); -#else - _commit(fd); -#endif - } - free(msgbuf); return ret; } diff --git a/ext/signals.c b/ext/signals.c index 0c2de33c6a0..096bf28b20b 100644 --- a/ext/signals.c +++ b/ext/signals.c @@ -100,6 +100,15 @@ static void dd_sigsegv_handler(int sig) { #endif } + int error_log_fd = atomic_load(&ddtrace_error_log_fd); + if (error_log_fd != -1) { +#ifndef _WIN32 + fsync(error_log_fd); +#else + _commit(error_log_fd); +#endif + } + // _Exit to avoid atexit() handlers, they may crash in this SIGSEGV signal handler... _Exit(128 + sig); }