From 9ff334305632d391a7d4ca17ec2e060acb6ec8ee Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Tue, 12 Nov 2024 10:21:23 +0100 Subject: [PATCH 1/5] cleanup --- profiling/src/timeline.rs | 77 ++++++++++----------------------------- 1 file changed, 19 insertions(+), 58 deletions(-) diff --git a/profiling/src/timeline.rs b/profiling/src/timeline.rs index ab69528ca1..71c0c354ae 100644 --- a/profiling/src/timeline.rs +++ b/profiling/src/timeline.rs @@ -24,12 +24,6 @@ static mut PREV_ZEND_COMPILE_STRING: Option = None; /// The engine's original (or neighbouring extensions) `zend_compile_file()` function static mut PREV_ZEND_COMPILE_FILE: Option = None; -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 = RefCell::new(Instant::now()); #[cfg(php_zts)] @@ -101,60 +95,27 @@ fn sleeping_fn( } } -/// Wrapping the PHP `sleep()` function to take the time it is blocking the current thread -#[no_mangle] -unsafe extern "C" fn ddog_php_prof_sleep( - execute_data: *mut zend_execute_data, - return_value: *mut zval, -) { - if let Some(func) = SLEEP_HANDLER { - sleeping_fn(func, execute_data, return_value, State::Sleeping) - } -} - -/// Wrapping the PHP `usleep()` function to take the time it is blocking the current thread -#[no_mangle] -unsafe extern "C" fn ddog_php_prof_usleep( - execute_data: *mut zend_execute_data, - return_value: *mut zval, -) { - if let Some(func) = USLEEP_HANDLER { - sleeping_fn(func, execute_data, return_value, State::Sleeping) - } -} - -/// Wrapping the PHP `time_nanosleep()` function to take the time it is blocking the current thread -#[no_mangle] -unsafe extern "C" fn ddog_php_prof_time_nanosleep( - execute_data: *mut zend_execute_data, - return_value: *mut zval, -) { - if let Some(func) = TIME_NANOSLEEP_HANDLER { - sleeping_fn(func, execute_data, return_value, State::Sleeping) - } -} - -/// Wrapping the PHP `time_sleep_until()` function to take the time it is blocking the current thread -#[no_mangle] -unsafe extern "C" fn ddog_php_prof_time_sleep_until( - execute_data: *mut zend_execute_data, - return_value: *mut zval, -) { - if let Some(func) = TIME_SLEEP_UNTIL_HANDLER { - sleeping_fn(func, execute_data, return_value, State::Sleeping) - } +macro_rules! create_sleeping_fn { + ($fn_name:ident, $handler:ident) => { + static mut $handler: InternalFunctionHandler = None; + + #[no_mangle] + unsafe extern "C" fn $fn_name( + execute_data: *mut zend_execute_data, + return_value: *mut zval, + ) { + if let Some(func) = $handler { + 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) - } -} +create_sleeping_fn!(ddog_php_prof_sleep, SLEEP_HANDLER); +create_sleeping_fn!(ddog_php_prof_usleep, USLEEP_HANDLER); +create_sleeping_fn!(ddog_php_prof_time_nanosleep, TIME_NANOSLEEP_HANDLER); +create_sleeping_fn!(ddog_php_prof_time_sleep_until, TIME_SLEEP_UNTIL_HANDLER); +create_sleeping_fn!(ddog_php_prof_frankenphp_handle_request, FRANKENPHP_HANDLE_REQUEST_HANDLER); /// Will be called by the ZendEngine on all errors happening. This is a PHP 8 API #[cfg(zend_error_observer)] From 92ee98c8c693fb194e9d18e65303a9cf27c06988 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Tue, 12 Nov 2024 11:00:26 +0100 Subject: [PATCH 2/5] observe `stream_select()`-type functions --- profiling/src/lib.rs | 8 +++- profiling/src/timeline.rs | 83 +++++++++++++++++++++++++++++++++++---- 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/profiling/src/lib.rs b/profiling/src/lib.rs index d3232c2898..2fc9c3902f 100644 --- a/profiling/src/lib.rs +++ b/profiling/src/lib.rs @@ -144,10 +144,16 @@ static mut GLOBALS_ID_PTR: i32 = 0; /// consecutive return value. #[no_mangle] pub extern "C" fn get_module() -> &'static mut zend::ModuleEntry { - static DEPS: [zend::ModuleDep; 4] = [ + static DEPS: [zend::ModuleDep; 8] = [ zend::ModuleDep::required(cstr!("standard")), zend::ModuleDep::required(cstr!("json")), zend::ModuleDep::optional(cstr!("ddtrace")), + // Optionally, be dependent on these event extensions so that the functions they provide + // are registered in the function table and we can hook into them. + zend::ModuleDep::optional(cstr!("ev")), + zend::ModuleDep::optional(cstr!("event")), + zend::ModuleDep::optional(cstr!("libevent")), + zend::ModuleDep::optional(cstr!("uv")), zend::ModuleDep::end(), ]; diff --git a/profiling/src/timeline.rs b/profiling/src/timeline.rs index 71c0c354ae..c8d9cbd6a5 100644 --- a/profiling/src/timeline.rs +++ b/profiling/src/timeline.rs @@ -33,6 +33,7 @@ thread_local! { enum State { Idle, Sleeping, + Select, #[cfg(php_zts)] ThreadStart, #[cfg(php_zts)] @@ -44,6 +45,7 @@ impl State { match self { State::Idle => "idle", State::Sleeping => "sleeping", + State::Select => "select", #[cfg(php_zts)] State::ThreadStart => "thread start", #[cfg(php_zts)] @@ -96,7 +98,7 @@ fn sleeping_fn( } macro_rules! create_sleeping_fn { - ($fn_name:ident, $handler:ident) => { + ($fn_name:ident, $handler:ident, $state:expr) => { static mut $handler: InternalFunctionHandler = None; #[no_mangle] @@ -105,17 +107,30 @@ macro_rules! create_sleeping_fn { return_value: *mut zval, ) { if let Some(func) = $handler { - sleeping_fn(func, execute_data, return_value, State::Sleeping) + sleeping_fn(func, execute_data, return_value, $state) } } }; } -create_sleeping_fn!(ddog_php_prof_sleep, SLEEP_HANDLER); -create_sleeping_fn!(ddog_php_prof_usleep, USLEEP_HANDLER); -create_sleeping_fn!(ddog_php_prof_time_nanosleep, TIME_NANOSLEEP_HANDLER); -create_sleeping_fn!(ddog_php_prof_time_sleep_until, TIME_SLEEP_UNTIL_HANDLER); -create_sleeping_fn!(ddog_php_prof_frankenphp_handle_request, FRANKENPHP_HANDLE_REQUEST_HANDLER); +// Functions that are sleeping +create_sleeping_fn!(ddog_php_prof_sleep, SLEEP_HANDLER, State::Sleeping); +create_sleeping_fn!(ddog_php_prof_usleep, USLEEP_HANDLER, State::Sleeping); +create_sleeping_fn!(ddog_php_prof_time_nanosleep, TIME_NANOSLEEP_HANDLER, State::Sleeping); +create_sleeping_fn!(ddog_php_prof_time_sleep_until, TIME_SLEEP_UNTIL_HANDLER, State::Sleeping); + +// Idle functions: these are functions which are like RSHUTDOWN -> RINIT +create_sleeping_fn!(ddog_php_prof_frankenphp_handle_request, FRANKENPHP_HANDLE_REQUEST_HANDLER, State::Idle); + +// Functions that are blocking on I/O +create_sleeping_fn!(ddog_php_prof_stream_select, STREAM_SELECT_HANDLER, State::Select); +create_sleeping_fn!(ddog_php_prof_socket_select, SOCKET_SELECT_HANDLER, State::Select); +create_sleeping_fn!(ddog_php_prof_socket_accept, SOCKET_ACCEPT_HANDLER, State::Select); +create_sleeping_fn!(ddog_php_prof_curl_multi_select, CURL_MULTI_SELECT_HANDLER, State::Select); +create_sleeping_fn!(ddog_php_prof_uv_run, UV_RUN_HANDLER, State::Select); +create_sleeping_fn!(ddog_php_prof_event_base_loop, EVENT_BASE_LOOP_HANDLER, State::Select); +create_sleeping_fn!(ddog_php_prof_eventbase_loop, EVENTBASE_LOOP_HANDLER, State::Select); +create_sleeping_fn!(ddog_php_prof_ev_loop_run, EV_LOOP_RUN_HANDLER, State::Select); /// Will be called by the ZendEngine on all errors happening. This is a PHP 8 API #[cfg(zend_error_observer)] @@ -202,12 +217,66 @@ pub unsafe fn timeline_startup() { ptr::addr_of_mut!(FRANKENPHP_HANDLE_REQUEST_HANDLER), Some(ddog_php_prof_frankenphp_handle_request), ), + zend::datadog_php_zif_handler::new( + CStr::from_bytes_with_nul_unchecked(b"stream_select\0"), + ptr::addr_of_mut!(STREAM_SELECT_HANDLER), + Some(ddog_php_prof_stream_select), + ), + zend::datadog_php_zif_handler::new( + CStr::from_bytes_with_nul_unchecked(b"socket_select\0"), + ptr::addr_of_mut!(SOCKET_SELECT_HANDLER), + Some(ddog_php_prof_socket_select), + ), + zend::datadog_php_zif_handler::new( + CStr::from_bytes_with_nul_unchecked(b"socket_accept\0"), + ptr::addr_of_mut!(SOCKET_SELECT_HANDLER), + Some(ddog_php_prof_socket_select), + ), + zend::datadog_php_zif_handler::new( + CStr::from_bytes_with_nul_unchecked(b"curl_multi_select\0"), + ptr::addr_of_mut!(CURL_MULTI_SELECT_HANDLER), + Some(ddog_php_prof_curl_multi_select), + ), + // provided by `ext-uv` from https://pecl.php.net/package/uv + zend::datadog_php_zif_handler::new( + CStr::from_bytes_with_nul_unchecked(b"uv_run\0"), + ptr::addr_of_mut!(UV_RUN_HANDLER), + Some(ddog_php_prof_uv_run), + ), + // provided by `ext-libevent` from https://pecl.php.net/package/libevent + zend::datadog_php_zif_handler::new( + CStr::from_bytes_with_nul_unchecked(b"event_base_loop\0"), + ptr::addr_of_mut!(EVENT_BASE_LOOP_HANDLER), + Some(ddog_php_prof_event_base_loop), + ), ]; for handler in handlers.into_iter() { // Safety: we've set all the parameters correctly for this C call. zend::datadog_php_install_handler(handler); } + + let handlers = [ + // provided by `ext-ev` from https://pecl.php.net/package/ev + zend::datadog_php_zim_handler::new( + CStr::from_bytes_with_nul_unchecked(b"evloop\0"), + CStr::from_bytes_with_nul_unchecked(b"run\0"), + ptr::addr_of_mut!(EV_LOOP_RUN_HANDLER), + Some(ddog_php_prof_ev_loop_run), + ), + // provided by `ext-event` from https://pecl.php.net/package/event + zend::datadog_php_zim_handler::new( + CStr::from_bytes_with_nul_unchecked(b"eventbase\0"), + CStr::from_bytes_with_nul_unchecked(b"loop\0"), + ptr::addr_of_mut!(EVENTBASE_LOOP_HANDLER), + Some(ddog_php_prof_eventbase_loop), + ), + ]; + + for handler in handlers.into_iter() { + // Safety: we've set all the parameters correctly for this C call. + zend::datadog_php_install_method_handler(handler); + } } /// This function is run during the RINIT phase and reports any `IDLE_SINCE` duration as an idle From 128d860eb4e0ef0063151625e5f4ce01c384b6e4 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Wed, 13 Nov 2024 10:18:02 +0100 Subject: [PATCH 3/5] add \parallel\Events::poll --- profiling/src/timeline.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/profiling/src/timeline.rs b/profiling/src/timeline.rs index c8d9cbd6a5..a4bebbf87a 100644 --- a/profiling/src/timeline.rs +++ b/profiling/src/timeline.rs @@ -131,6 +131,7 @@ create_sleeping_fn!(ddog_php_prof_uv_run, UV_RUN_HANDLER, State::Select); create_sleeping_fn!(ddog_php_prof_event_base_loop, EVENT_BASE_LOOP_HANDLER, State::Select); create_sleeping_fn!(ddog_php_prof_eventbase_loop, EVENTBASE_LOOP_HANDLER, State::Select); create_sleeping_fn!(ddog_php_prof_ev_loop_run, EV_LOOP_RUN_HANDLER, State::Select); +create_sleeping_fn!(ddog_php_prof_parallel_events_poll, PARALLEL_EVENTS_POLL_HANDLER, State::Select); /// Will be called by the ZendEngine on all errors happening. This is a PHP 8 API #[cfg(zend_error_observer)] @@ -271,6 +272,13 @@ pub unsafe fn timeline_startup() { ptr::addr_of_mut!(EVENTBASE_LOOP_HANDLER), Some(ddog_php_prof_eventbase_loop), ), + // provided by `ext-parallel` from https://pecl.php.net/package/parallel + zend::datadog_php_zim_handler::new( + CStr::from_bytes_with_nul_unchecked(b"parallel\\events\0"), + CStr::from_bytes_with_nul_unchecked(b"poll\0"), + ptr::addr_of_mut!(PARALLEL_EVENTS_POLL_HANDLER), + Some(ddog_php_prof_parallel_events_poll), + ), ]; for handler in handlers.into_iter() { From bc4daad228511ad04c1362cf3d406ddc951df582 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Wed, 13 Nov 2024 15:26:27 +0100 Subject: [PATCH 4/5] remove socket_accept() --- profiling/src/timeline.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/profiling/src/timeline.rs b/profiling/src/timeline.rs index a4bebbf87a..b1337ff52b 100644 --- a/profiling/src/timeline.rs +++ b/profiling/src/timeline.rs @@ -125,7 +125,6 @@ create_sleeping_fn!(ddog_php_prof_frankenphp_handle_request, FRANKENPHP_HANDLE_R // Functions that are blocking on I/O create_sleeping_fn!(ddog_php_prof_stream_select, STREAM_SELECT_HANDLER, State::Select); create_sleeping_fn!(ddog_php_prof_socket_select, SOCKET_SELECT_HANDLER, State::Select); -create_sleeping_fn!(ddog_php_prof_socket_accept, SOCKET_ACCEPT_HANDLER, State::Select); create_sleeping_fn!(ddog_php_prof_curl_multi_select, CURL_MULTI_SELECT_HANDLER, State::Select); create_sleeping_fn!(ddog_php_prof_uv_run, UV_RUN_HANDLER, State::Select); create_sleeping_fn!(ddog_php_prof_event_base_loop, EVENT_BASE_LOOP_HANDLER, State::Select); @@ -228,11 +227,6 @@ pub unsafe fn timeline_startup() { ptr::addr_of_mut!(SOCKET_SELECT_HANDLER), Some(ddog_php_prof_socket_select), ), - zend::datadog_php_zif_handler::new( - CStr::from_bytes_with_nul_unchecked(b"socket_accept\0"), - ptr::addr_of_mut!(SOCKET_SELECT_HANDLER), - Some(ddog_php_prof_socket_select), - ), zend::datadog_php_zif_handler::new( CStr::from_bytes_with_nul_unchecked(b"curl_multi_select\0"), ptr::addr_of_mut!(CURL_MULTI_SELECT_HANDLER), From 31a763db3088f49d30c9d2d7d5d13a35ab82585b Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Wed, 13 Nov 2024 15:30:06 +0100 Subject: [PATCH 5/5] cleanup --- profiling/src/timeline.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/profiling/src/timeline.rs b/profiling/src/timeline.rs index b1337ff52b..d889b02cf2 100644 --- a/profiling/src/timeline.rs +++ b/profiling/src/timeline.rs @@ -4,12 +4,12 @@ use crate::zend::{ InternalFunctionHandler, }; use crate::REQUEST_LOCALS; +use ddcommon::cstr; use libc::c_char; use log::{error, trace}; #[cfg(php_zts)] use std::cell::Cell; use std::cell::RefCell; -use std::ffi::CStr; use std::ptr; use std::time::Instant; use std::time::SystemTime; @@ -193,54 +193,54 @@ pub fn timeline_minit() { pub unsafe fn timeline_startup() { let handlers = [ zend::datadog_php_zif_handler::new( - CStr::from_bytes_with_nul_unchecked(b"sleep\0"), + cstr!("sleep"), ptr::addr_of_mut!(SLEEP_HANDLER), Some(ddog_php_prof_sleep), ), zend::datadog_php_zif_handler::new( - CStr::from_bytes_with_nul_unchecked(b"usleep\0"), + cstr!("usleep"), ptr::addr_of_mut!(USLEEP_HANDLER), Some(ddog_php_prof_usleep), ), zend::datadog_php_zif_handler::new( - CStr::from_bytes_with_nul_unchecked(b"time_nanosleep\0"), + cstr!("time_nanosleep"), ptr::addr_of_mut!(TIME_NANOSLEEP_HANDLER), Some(ddog_php_prof_time_nanosleep), ), zend::datadog_php_zif_handler::new( - CStr::from_bytes_with_nul_unchecked(b"time_sleep_until\0"), + cstr!("time_sleep_until"), ptr::addr_of_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"), + cstr!("frankenphp_handle_request"), ptr::addr_of_mut!(FRANKENPHP_HANDLE_REQUEST_HANDLER), Some(ddog_php_prof_frankenphp_handle_request), ), zend::datadog_php_zif_handler::new( - CStr::from_bytes_with_nul_unchecked(b"stream_select\0"), + cstr!("stream_select"), ptr::addr_of_mut!(STREAM_SELECT_HANDLER), Some(ddog_php_prof_stream_select), ), zend::datadog_php_zif_handler::new( - CStr::from_bytes_with_nul_unchecked(b"socket_select\0"), + cstr!("socket_select"), ptr::addr_of_mut!(SOCKET_SELECT_HANDLER), Some(ddog_php_prof_socket_select), ), zend::datadog_php_zif_handler::new( - CStr::from_bytes_with_nul_unchecked(b"curl_multi_select\0"), + cstr!("curl_multi_select"), ptr::addr_of_mut!(CURL_MULTI_SELECT_HANDLER), Some(ddog_php_prof_curl_multi_select), ), // provided by `ext-uv` from https://pecl.php.net/package/uv zend::datadog_php_zif_handler::new( - CStr::from_bytes_with_nul_unchecked(b"uv_run\0"), + cstr!("uv_run"), ptr::addr_of_mut!(UV_RUN_HANDLER), Some(ddog_php_prof_uv_run), ), // provided by `ext-libevent` from https://pecl.php.net/package/libevent zend::datadog_php_zif_handler::new( - CStr::from_bytes_with_nul_unchecked(b"event_base_loop\0"), + cstr!("event_base_loop"), ptr::addr_of_mut!(EVENT_BASE_LOOP_HANDLER), Some(ddog_php_prof_event_base_loop), ), @@ -254,22 +254,22 @@ pub unsafe fn timeline_startup() { let handlers = [ // provided by `ext-ev` from https://pecl.php.net/package/ev zend::datadog_php_zim_handler::new( - CStr::from_bytes_with_nul_unchecked(b"evloop\0"), - CStr::from_bytes_with_nul_unchecked(b"run\0"), + cstr!("evloop"), + cstr!("run"), ptr::addr_of_mut!(EV_LOOP_RUN_HANDLER), Some(ddog_php_prof_ev_loop_run), ), // provided by `ext-event` from https://pecl.php.net/package/event zend::datadog_php_zim_handler::new( - CStr::from_bytes_with_nul_unchecked(b"eventbase\0"), - CStr::from_bytes_with_nul_unchecked(b"loop\0"), + cstr!("eventbase"), + cstr!("loop"), ptr::addr_of_mut!(EVENTBASE_LOOP_HANDLER), Some(ddog_php_prof_eventbase_loop), ), // provided by `ext-parallel` from https://pecl.php.net/package/parallel zend::datadog_php_zim_handler::new( - CStr::from_bytes_with_nul_unchecked(b"parallel\\events\0"), - CStr::from_bytes_with_nul_unchecked(b"poll\0"), + cstr!("parallel\\events"), + cstr!("poll"), ptr::addr_of_mut!(PARALLEL_EVENTS_POLL_HANDLER), Some(ddog_php_prof_parallel_events_poll), ),