Skip to content

Add FrankenPHP to the list of recognised/supported SAPIs #2523

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components/sapi/sapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ sapi_t datadog_php_sapi_from_name(string_view_t module) {
{SV("cli-server"), DATADOG_PHP_SAPI_CLI_SERVER},
{SV("embed"), DATADOG_PHP_SAPI_EMBED},
{SV("fpm-fcgi"), DATADOG_PHP_SAPI_FPM_FCGI},
{SV("frankenphp"), DATADOG_PHP_SAPI_FRANKENPHP},
{SV("litespeed"), DATADOG_PHP_SAPI_LITESPEED},
{SV("phpdbg"), DATADOG_PHP_SAPI_PHPDBG},
{SV("tea"), DATADOG_PHP_SAPI_TEA},
Expand Down
1 change: 1 addition & 0 deletions components/sapi/sapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ typedef enum {
DATADOG_PHP_SAPI_EMBED,
DATADOG_PHP_SAPI_LITESPEED,
DATADOG_PHP_SAPI_FPM_FCGI,
DATADOG_PHP_SAPI_FRANKENPHP,
DATADOG_PHP_SAPI_PHPDBG,
DATADOG_PHP_SAPI_TEA,
} datadog_php_sapi;
Expand Down
1 change: 1 addition & 0 deletions components/sapi/tests/sapi.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ TEST_CASE("recognize real sapis", "[sapi]") {
{"cli-server", DATADOG_PHP_SAPI_CLI_SERVER},
{"embed", DATADOG_PHP_SAPI_EMBED},
{"fpm-fcgi", DATADOG_PHP_SAPI_FPM_FCGI},
{"frankenphp", DATADOG_PHP_SAPI_FRANKENPHP},
{"litespeed", DATADOG_PHP_SAPI_LITESPEED},
{"phpdbg", DATADOG_PHP_SAPI_PHPDBG},
{"tea", DATADOG_PHP_SAPI_TEA},
Expand Down
24 changes: 16 additions & 8 deletions ext/ddtrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ static bool dd_has_other_observers;
static int dd_observer_extension_backup = -1;
#endif

static datadog_php_sapi ddtrace_active_sapi = DATADOG_PHP_SAPI_UNKNOWN;

_Atomic(int64_t) ddtrace_warn_legacy_api;

ZEND_DECLARE_MODULE_GLOBALS(ddtrace)
Expand Down Expand Up @@ -571,7 +573,8 @@ static PHP_GSHUTDOWN_FUNCTION(ddtrace) {
zai_hook_gshutdown();

#ifdef CXA_THREAD_ATEXIT_WRAPPER
if (!dd_is_main_thread) {
// FrankenPHP calls `ts_free_thread()` in rshutdown
if (!dd_is_main_thread && ddtrace_active_sapi != DATADOG_PHP_SAPI_FRANKENPHP) {
dd_run_rust_thread_destructors(NULL);
}
#endif
Expand Down Expand Up @@ -983,13 +986,14 @@ static void dd_register_fatal_error_ce(void) {
ddtrace_ce_fatal_error = zend_register_internal_class_ex(&ce, zend_ce_exception);
}

static bool dd_is_compatible_sapi(datadog_php_string_view module_name) {
switch (datadog_php_sapi_from_name(module_name)) {
static bool dd_is_compatible_sapi() {
switch (ddtrace_active_sapi) {
case DATADOG_PHP_SAPI_APACHE2HANDLER:
case DATADOG_PHP_SAPI_CGI_FCGI:
case DATADOG_PHP_SAPI_CLI:
case DATADOG_PHP_SAPI_CLI_SERVER:
case DATADOG_PHP_SAPI_FPM_FCGI:
case DATADOG_PHP_SAPI_FRANKENPHP:
case DATADOG_PHP_SAPI_TEA:
return true;

Expand All @@ -999,8 +1003,7 @@ static bool dd_is_compatible_sapi(datadog_php_string_view module_name) {
}

static void dd_disable_if_incompatible_sapi_detected(void) {
datadog_php_string_view module_name = datadog_php_string_view_from_cstr(sapi_module.name);
if (UNEXPECTED(!dd_is_compatible_sapi(module_name))) {
if (UNEXPECTED(!dd_is_compatible_sapi())) {
LOG(WARN, "Incompatible SAPI detected '%s'; disabling ddtrace", sapi_module.name);
DDTRACE_G(disable) = 1;
}
Expand All @@ -1009,10 +1012,15 @@ static void dd_disable_if_incompatible_sapi_detected(void) {
static PHP_MINIT_FUNCTION(ddtrace) {
UNUSED(type);

ddtrace_active_sapi = datadog_php_sapi_from_name(datadog_php_string_view_from_cstr(sapi_module.name));

#ifdef CXA_THREAD_ATEXIT_WRAPPER
dd_is_main_thread = true;
glibc__cxa_thread_atexit_impl = CXA_THREAD_ATEXIT_PHP;
atexit(dd_clean_main_thread_locals);
// FrankenPHP calls `ts_free_thread()` in rshutdown
if (ddtrace_active_sapi != DATADOG_PHP_SAPI_FRANKENPHP) {
dd_is_main_thread = true;
glibc__cxa_thread_atexit_impl = CXA_THREAD_ATEXIT_PHP;
atexit(dd_clean_main_thread_locals);
}
#endif

// Reset on every minit for `apachectl graceful`.
Expand Down
4 changes: 4 additions & 0 deletions profiling/src/sapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub enum Sapi {
Cli,
CliServer,
Embed,
FrankenPHP,
FpmFcgi,
Litespeed,
PhpDbg,
Expand All @@ -34,6 +35,7 @@ impl Sapi {
("cli", Sapi::Cli),
("cli-server", Sapi::CliServer),
("embed", Sapi::Embed),
("frankenphp", Sapi::FrankenPHP),
("fpm-fcgi", Sapi::FpmFcgi),
("litespeed", Sapi::Litespeed),
("phpdbg", Sapi::PhpDbg),
Expand Down Expand Up @@ -99,6 +101,7 @@ impl AsRef<str> for Sapi {
Sapi::Cli => "cli",
Sapi::CliServer => "cli-server",
Sapi::Embed => "embed",
Sapi::FrankenPHP => "frankenphp",
Sapi::FpmFcgi => "fpm-fcgi",
Sapi::Litespeed => "litespeed",
Sapi::PhpDbg => "phpdbg",
Expand All @@ -125,6 +128,7 @@ mod tests {
("cli", Sapi::Cli),
("cli-server", Sapi::CliServer),
("embed", Sapi::Embed),
("frankenphp", Sapi::FrankenPHP),
("fpm-fcgi", Sapi::FpmFcgi),
("litespeed", Sapi::Litespeed),
("phpdbg", Sapi::PhpDbg),
Expand Down
47 changes: 40 additions & 7 deletions profiling/src/timeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,32 @@ static mut SLEEP_HANDLER: InternalFunctionHandler = None;
static mut USLEEP_HANDLER: InternalFunctionHandler = None;
static mut TIME_NANOSLEEP_HANDLER: InternalFunctionHandler = None;
static mut TIME_SLEEP_UNTIL_HANDLER: InternalFunctionHandler = None;
static mut FRANKENPHP_HANDLE_REQUEST_HANDLER: InternalFunctionHandler = None;

thread_local! {
static IDLE_SINCE: RefCell<Instant> = RefCell::new(Instant::now());
}

enum State {
Idle,
Sleeping,
}

impl State {
fn as_str(&self) -> &'static str {
match self {
State::Idle => "idle",
State::Sleeping => "sleeping",
}
}
}

#[inline]
fn try_sleeping_fn(
func: unsafe extern "C" fn(execute_data: *mut zend_execute_data, return_value: *mut zval),
execute_data: *mut zend_execute_data,
return_value: *mut zval,
state: State,
) -> anyhow::Result<()> {
let timeline_enabled = REQUEST_LOCALS.with(|cell| {
cell.try_borrow()
Expand Down Expand Up @@ -71,7 +87,7 @@ fn try_sleeping_fn(
if let Some(profiler) = guard.as_ref() {
let now = now.as_nanos() as i64;
let duration = duration.as_nanos() as i64;
profiler.collect_idle(now, duration, "sleeping")
profiler.collect_idle(now, duration, state.as_str())
}
}
Err(err) => anyhow::bail!("profiler mutex: {err:#}"),
Expand All @@ -83,8 +99,9 @@ fn sleeping_fn(
func: unsafe extern "C" fn(execute_data: *mut zend_execute_data, return_value: *mut zval),
execute_data: *mut zend_execute_data,
return_value: *mut zval,
state: State,
) {
if let Err(err) = try_sleeping_fn(func, execute_data, return_value) {
if let Err(err) = try_sleeping_fn(func, execute_data, return_value, state) {
warn!("error creating profiling timeline sample for an internal function: {err:#}");
}
}
Expand All @@ -96,7 +113,7 @@ unsafe extern "C" fn ddog_php_prof_sleep(
return_value: *mut zval,
) {
if let Some(func) = SLEEP_HANDLER {
sleeping_fn(func, execute_data, return_value)
sleeping_fn(func, execute_data, return_value, State::Sleeping)
}
}

Expand All @@ -107,7 +124,7 @@ unsafe extern "C" fn ddog_php_prof_usleep(
return_value: *mut zval,
) {
if let Some(func) = USLEEP_HANDLER {
sleeping_fn(func, execute_data, return_value)
sleeping_fn(func, execute_data, return_value, State::Sleeping)
}
}

Expand All @@ -118,7 +135,7 @@ unsafe extern "C" fn ddog_php_prof_time_nanosleep(
return_value: *mut zval,
) {
if let Some(func) = TIME_NANOSLEEP_HANDLER {
sleeping_fn(func, execute_data, return_value)
sleeping_fn(func, execute_data, return_value, State::Sleeping)
}
}

Expand All @@ -129,7 +146,18 @@ unsafe extern "C" fn ddog_php_prof_time_sleep_until(
return_value: *mut zval,
) {
if let Some(func) = TIME_SLEEP_UNTIL_HANDLER {
sleeping_fn(func, execute_data, return_value)
sleeping_fn(func, execute_data, return_value, State::Sleeping)
}
}

/// Wrapping the FrankenPHP `frankenphp_handle_request()` function to take the time it is blocking the current thread
#[no_mangle]
unsafe extern "C" fn ddog_php_prof_frankenphp_handle_request(
execute_data: *mut zend_execute_data,
return_value: *mut zval,
) {
if let Some(func) = FRANKENPHP_HANDLE_REQUEST_HANDLER {
sleeping_fn(func, execute_data, return_value, State::Idle)
}
}

Expand Down Expand Up @@ -174,6 +202,11 @@ pub unsafe fn timeline_startup() {
&mut TIME_SLEEP_UNTIL_HANDLER,
Some(ddog_php_prof_time_sleep_until),
),
zend::datadog_php_zif_handler::new(
CStr::from_bytes_with_nul_unchecked(b"frankenphp_handle_request\0"),
&mut FRANKENPHP_HANDLE_REQUEST_HANDLER,
Some(ddog_php_prof_frankenphp_handle_request),
),
];

for handler in handlers.into_iter() {
Expand Down Expand Up @@ -210,7 +243,7 @@ pub unsafe fn timeline_rinit() {
.unwrap()
.as_nanos() as i64,
idle_since.elapsed().as_nanos() as i64,
"idle",
State::Idle.as_str(),
);
}
});
Expand Down