Skip to content

Commit ba07209

Browse files
feat(profiling) add stream_select()-type functions to timeline (#2943)
* cleanup * observe `stream_select()`-type functions * add \parallel\Events::poll * remove socket_accept() * cleanup
1 parent 66df35c commit ba07209

File tree

2 files changed

+103
-65
lines changed

2 files changed

+103
-65
lines changed

profiling/src/lib.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,16 @@ static mut GLOBALS_ID_PTR: i32 = 0;
153153
/// consecutive return value.
154154
#[no_mangle]
155155
pub extern "C" fn get_module() -> &'static mut zend::ModuleEntry {
156-
static DEPS: [zend::ModuleDep; 4] = [
156+
static DEPS: [zend::ModuleDep; 8] = [
157157
zend::ModuleDep::required(cstr!("standard")),
158158
zend::ModuleDep::required(cstr!("json")),
159159
zend::ModuleDep::optional(cstr!("ddtrace")),
160+
// Optionally, be dependent on these event extensions so that the functions they provide
161+
// are registered in the function table and we can hook into them.
162+
zend::ModuleDep::optional(cstr!("ev")),
163+
zend::ModuleDep::optional(cstr!("event")),
164+
zend::ModuleDep::optional(cstr!("libevent")),
165+
zend::ModuleDep::optional(cstr!("uv")),
160166
zend::ModuleDep::end(),
161167
];
162168

profiling/src/timeline.rs

Lines changed: 96 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ use crate::zend::{
44
InternalFunctionHandler,
55
};
66
use crate::REQUEST_LOCALS;
7+
use ddcommon::cstr;
78
use libc::c_char;
89
use log::{error, trace};
910
#[cfg(php_zts)]
1011
use std::cell::Cell;
1112
use std::cell::RefCell;
12-
use std::ffi::CStr;
1313
use std::ptr;
1414
use std::time::Instant;
1515
use std::time::SystemTime;
@@ -24,12 +24,6 @@ static mut PREV_ZEND_COMPILE_STRING: Option<zend::VmZendCompileString> = None;
2424
/// The engine's original (or neighbouring extensions) `zend_compile_file()` function
2525
static mut PREV_ZEND_COMPILE_FILE: Option<zend::VmZendCompileFile> = None;
2626

27-
static mut SLEEP_HANDLER: InternalFunctionHandler = None;
28-
static mut USLEEP_HANDLER: InternalFunctionHandler = None;
29-
static mut TIME_NANOSLEEP_HANDLER: InternalFunctionHandler = None;
30-
static mut TIME_SLEEP_UNTIL_HANDLER: InternalFunctionHandler = None;
31-
static mut FRANKENPHP_HANDLE_REQUEST_HANDLER: InternalFunctionHandler = None;
32-
3327
thread_local! {
3428
static IDLE_SINCE: RefCell<Instant> = RefCell::new(Instant::now());
3529
#[cfg(php_zts)]
@@ -39,6 +33,7 @@ thread_local! {
3933
enum State {
4034
Idle,
4135
Sleeping,
36+
Select,
4237
#[cfg(php_zts)]
4338
ThreadStart,
4439
#[cfg(php_zts)]
@@ -50,6 +45,7 @@ impl State {
5045
match self {
5146
State::Idle => "idle",
5247
State::Sleeping => "sleeping",
48+
State::Select => "select",
5349
#[cfg(php_zts)]
5450
State::ThreadStart => "thread start",
5551
#[cfg(php_zts)]
@@ -101,60 +97,40 @@ fn sleeping_fn(
10197
}
10298
}
10399

104-
/// Wrapping the PHP `sleep()` function to take the time it is blocking the current thread
105-
#[no_mangle]
106-
unsafe extern "C" fn ddog_php_prof_sleep(
107-
execute_data: *mut zend_execute_data,
108-
return_value: *mut zval,
109-
) {
110-
if let Some(func) = SLEEP_HANDLER {
111-
sleeping_fn(func, execute_data, return_value, State::Sleeping)
112-
}
113-
}
114-
115-
/// Wrapping the PHP `usleep()` function to take the time it is blocking the current thread
116-
#[no_mangle]
117-
unsafe extern "C" fn ddog_php_prof_usleep(
118-
execute_data: *mut zend_execute_data,
119-
return_value: *mut zval,
120-
) {
121-
if let Some(func) = USLEEP_HANDLER {
122-
sleeping_fn(func, execute_data, return_value, State::Sleeping)
123-
}
124-
}
125-
126-
/// Wrapping the PHP `time_nanosleep()` function to take the time it is blocking the current thread
127-
#[no_mangle]
128-
unsafe extern "C" fn ddog_php_prof_time_nanosleep(
129-
execute_data: *mut zend_execute_data,
130-
return_value: *mut zval,
131-
) {
132-
if let Some(func) = TIME_NANOSLEEP_HANDLER {
133-
sleeping_fn(func, execute_data, return_value, State::Sleeping)
134-
}
135-
}
136-
137-
/// Wrapping the PHP `time_sleep_until()` function to take the time it is blocking the current thread
138-
#[no_mangle]
139-
unsafe extern "C" fn ddog_php_prof_time_sleep_until(
140-
execute_data: *mut zend_execute_data,
141-
return_value: *mut zval,
142-
) {
143-
if let Some(func) = TIME_SLEEP_UNTIL_HANDLER {
144-
sleeping_fn(func, execute_data, return_value, State::Sleeping)
145-
}
100+
macro_rules! create_sleeping_fn {
101+
($fn_name:ident, $handler:ident, $state:expr) => {
102+
static mut $handler: InternalFunctionHandler = None;
103+
104+
#[no_mangle]
105+
unsafe extern "C" fn $fn_name(
106+
execute_data: *mut zend_execute_data,
107+
return_value: *mut zval,
108+
) {
109+
if let Some(func) = $handler {
110+
sleeping_fn(func, execute_data, return_value, $state)
111+
}
112+
}
113+
};
146114
}
147115

148-
/// Wrapping the FrankenPHP `frankenphp_handle_request()` function to take the time it is blocking the current thread
149-
#[no_mangle]
150-
unsafe extern "C" fn ddog_php_prof_frankenphp_handle_request(
151-
execute_data: *mut zend_execute_data,
152-
return_value: *mut zval,
153-
) {
154-
if let Some(func) = FRANKENPHP_HANDLE_REQUEST_HANDLER {
155-
sleeping_fn(func, execute_data, return_value, State::Idle)
156-
}
157-
}
116+
// Functions that are sleeping
117+
create_sleeping_fn!(ddog_php_prof_sleep, SLEEP_HANDLER, State::Sleeping);
118+
create_sleeping_fn!(ddog_php_prof_usleep, USLEEP_HANDLER, State::Sleeping);
119+
create_sleeping_fn!(ddog_php_prof_time_nanosleep, TIME_NANOSLEEP_HANDLER, State::Sleeping);
120+
create_sleeping_fn!(ddog_php_prof_time_sleep_until, TIME_SLEEP_UNTIL_HANDLER, State::Sleeping);
121+
122+
// Idle functions: these are functions which are like RSHUTDOWN -> RINIT
123+
create_sleeping_fn!(ddog_php_prof_frankenphp_handle_request, FRANKENPHP_HANDLE_REQUEST_HANDLER, State::Idle);
124+
125+
// Functions that are blocking on I/O
126+
create_sleeping_fn!(ddog_php_prof_stream_select, STREAM_SELECT_HANDLER, State::Select);
127+
create_sleeping_fn!(ddog_php_prof_socket_select, SOCKET_SELECT_HANDLER, State::Select);
128+
create_sleeping_fn!(ddog_php_prof_curl_multi_select, CURL_MULTI_SELECT_HANDLER, State::Select);
129+
create_sleeping_fn!(ddog_php_prof_uv_run, UV_RUN_HANDLER, State::Select);
130+
create_sleeping_fn!(ddog_php_prof_event_base_loop, EVENT_BASE_LOOP_HANDLER, State::Select);
131+
create_sleeping_fn!(ddog_php_prof_eventbase_loop, EVENTBASE_LOOP_HANDLER, State::Select);
132+
create_sleeping_fn!(ddog_php_prof_ev_loop_run, EV_LOOP_RUN_HANDLER, State::Select);
133+
create_sleeping_fn!(ddog_php_prof_parallel_events_poll, PARALLEL_EVENTS_POLL_HANDLER, State::Select);
158134

159135
/// Will be called by the ZendEngine on all errors happening. This is a PHP 8 API
160136
#[cfg(zend_error_observer)]
@@ -227,36 +203,92 @@ pub fn timeline_minit() {
227203
pub unsafe fn timeline_startup() {
228204
let handlers = [
229205
zend::datadog_php_zif_handler::new(
230-
CStr::from_bytes_with_nul_unchecked(b"sleep\0"),
206+
cstr!("sleep"),
231207
ptr::addr_of_mut!(SLEEP_HANDLER),
232208
Some(ddog_php_prof_sleep),
233209
),
234210
zend::datadog_php_zif_handler::new(
235-
CStr::from_bytes_with_nul_unchecked(b"usleep\0"),
211+
cstr!("usleep"),
236212
ptr::addr_of_mut!(USLEEP_HANDLER),
237213
Some(ddog_php_prof_usleep),
238214
),
239215
zend::datadog_php_zif_handler::new(
240-
CStr::from_bytes_with_nul_unchecked(b"time_nanosleep\0"),
216+
cstr!("time_nanosleep"),
241217
ptr::addr_of_mut!(TIME_NANOSLEEP_HANDLER),
242218
Some(ddog_php_prof_time_nanosleep),
243219
),
244220
zend::datadog_php_zif_handler::new(
245-
CStr::from_bytes_with_nul_unchecked(b"time_sleep_until\0"),
221+
cstr!("time_sleep_until"),
246222
ptr::addr_of_mut!(TIME_SLEEP_UNTIL_HANDLER),
247223
Some(ddog_php_prof_time_sleep_until),
248224
),
249225
zend::datadog_php_zif_handler::new(
250-
CStr::from_bytes_with_nul_unchecked(b"frankenphp_handle_request\0"),
226+
cstr!("frankenphp_handle_request"),
251227
ptr::addr_of_mut!(FRANKENPHP_HANDLE_REQUEST_HANDLER),
252228
Some(ddog_php_prof_frankenphp_handle_request),
253229
),
230+
zend::datadog_php_zif_handler::new(
231+
cstr!("stream_select"),
232+
ptr::addr_of_mut!(STREAM_SELECT_HANDLER),
233+
Some(ddog_php_prof_stream_select),
234+
),
235+
zend::datadog_php_zif_handler::new(
236+
cstr!("socket_select"),
237+
ptr::addr_of_mut!(SOCKET_SELECT_HANDLER),
238+
Some(ddog_php_prof_socket_select),
239+
),
240+
zend::datadog_php_zif_handler::new(
241+
cstr!("curl_multi_select"),
242+
ptr::addr_of_mut!(CURL_MULTI_SELECT_HANDLER),
243+
Some(ddog_php_prof_curl_multi_select),
244+
),
245+
// provided by `ext-uv` from https://pecl.php.net/package/uv
246+
zend::datadog_php_zif_handler::new(
247+
cstr!("uv_run"),
248+
ptr::addr_of_mut!(UV_RUN_HANDLER),
249+
Some(ddog_php_prof_uv_run),
250+
),
251+
// provided by `ext-libevent` from https://pecl.php.net/package/libevent
252+
zend::datadog_php_zif_handler::new(
253+
cstr!("event_base_loop"),
254+
ptr::addr_of_mut!(EVENT_BASE_LOOP_HANDLER),
255+
Some(ddog_php_prof_event_base_loop),
256+
),
254257
];
255258

256259
for handler in handlers.into_iter() {
257260
// Safety: we've set all the parameters correctly for this C call.
258261
zend::datadog_php_install_handler(handler);
259262
}
263+
264+
let handlers = [
265+
// provided by `ext-ev` from https://pecl.php.net/package/ev
266+
zend::datadog_php_zim_handler::new(
267+
cstr!("evloop"),
268+
cstr!("run"),
269+
ptr::addr_of_mut!(EV_LOOP_RUN_HANDLER),
270+
Some(ddog_php_prof_ev_loop_run),
271+
),
272+
// provided by `ext-event` from https://pecl.php.net/package/event
273+
zend::datadog_php_zim_handler::new(
274+
cstr!("eventbase"),
275+
cstr!("loop"),
276+
ptr::addr_of_mut!(EVENTBASE_LOOP_HANDLER),
277+
Some(ddog_php_prof_eventbase_loop),
278+
),
279+
// provided by `ext-parallel` from https://pecl.php.net/package/parallel
280+
zend::datadog_php_zim_handler::new(
281+
cstr!("parallel\\events"),
282+
cstr!("poll"),
283+
ptr::addr_of_mut!(PARALLEL_EVENTS_POLL_HANDLER),
284+
Some(ddog_php_prof_parallel_events_poll),
285+
),
286+
];
287+
288+
for handler in handlers.into_iter() {
289+
// Safety: we've set all the parameters correctly for this C call.
290+
zend::datadog_php_install_method_handler(handler);
291+
}
260292
}
261293

262294
/// This function is run during the RINIT phase and reports any `IDLE_SINCE` duration as an idle

0 commit comments

Comments
 (0)