Skip to content

Commit

Permalink
Merge branch 'master' into florian/opcache-restarts
Browse files Browse the repository at this point in the history
  • Loading branch information
realFlowControl committed Nov 14, 2024
2 parents 0975799 + ba07209 commit 7d15bec
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 75 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1269,7 +1269,9 @@ test_integrations_frankenphp: global_test_run_dependencies
test_integrations_roadrunner: global_test_run_dependencies tests/Frameworks/Roadrunner/Version_2/composer.lock-php$(PHP_MAJOR_MINOR)
$(call run_tests_debug,tests/Integrations/Roadrunner/V2)
test_integrations_googlespanner: global_test_run_dependencies tests/Integrations/GoogleSpanner/composer.lock-php$(PHP_MAJOR_MINOR)
$(eval TEST_EXTRA_INI=-d extension=grpc.so)
$(call run_tests_debug,tests/Integrations/GoogleSpanner)
$(eval TEST_EXTRA_INI=)
test_integrations_sqlsrv: global_test_run_dependencies
$(eval TEST_EXTRA_INI=-d extension=sqlsrv.so)
$(call run_tests_debug,tests/Integrations/SQLSRV)
Expand Down
6 changes: 6 additions & 0 deletions dockerfiles/ci/buster/build-extensions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ else
popd
fi

# ext-grpc is needed for google spanner
if [[ $PHP_VERSION_ID -ge 80 ]]; then
pecl install grpc;
# avoid installing it by default, it seems to stall some testsuites.
fi

# We don't install any redis.so to inis, but allow selection at runtime.
if [[ $PHP_VERSION_ID -lt 80 ]]; then
pecl install redis-3.1.6
Expand Down
9 changes: 9 additions & 0 deletions ext/exception_serialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,8 @@ static void ddtrace_collect_exception_debug_data(zend_object *exception, zend_st

add_meta(context, DDTRACE_STRING_LITERAL("_dd.debug.error.exception_capture_id"), (ddtrace_string){exception_id, uuid_len});

memset(&DDTRACE_G(exception_debugger_buffer), 0, sizeof(DDTRACE_G(exception_debugger_buffer)));

zval *frame;
int frame_num = 0;
ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARR_P(trace), frame_num, frame) {
Expand Down Expand Up @@ -470,6 +472,13 @@ static void ddtrace_collect_exception_debug_data(zend_object *exception, zend_st
}
}

// Note: We MUST immediately send this, and not defer, as stuff may be freed during span processing. Including stuff potentially contained within the exception debugger payload.
ddtrace_sidecar_send_debugger_data(DDTRACE_G(exception_debugger_buffer));
if (DDTRACE_G(debugger_capture_arena)) {
zend_arena_destroy(DDTRACE_G(debugger_capture_arena));
DDTRACE_G(debugger_capture_arena) = NULL;
}

zend_string_release(key_locals);
}

Expand Down
10 changes: 0 additions & 10 deletions ext/span.c
Original file line number Diff line number Diff line change
Expand Up @@ -725,8 +725,6 @@ void ddtrace_drop_span(ddtrace_span_data *span) {

void ddtrace_serialize_closed_spans(zval *serialized) {
if (DDTRACE_G(top_closed_stack)) {
memset(&DDTRACE_G(exception_debugger_buffer), 0, sizeof(DDTRACE_G(exception_debugger_buffer)));

ddtrace_span_stack *rootstack = DDTRACE_G(top_closed_stack);
DDTRACE_G(top_closed_stack) = NULL;
do {
Expand Down Expand Up @@ -759,14 +757,6 @@ void ddtrace_serialize_closed_spans(zval *serialized) {
}
} while (stack);
} while (rootstack);

if (ddtrace_exception_debugging_is_active()) {
ddtrace_sidecar_send_debugger_data(DDTRACE_G(exception_debugger_buffer));
if (DDTRACE_G(debugger_capture_arena)) {
zend_arena_destroy(DDTRACE_G(debugger_capture_arena));
DDTRACE_G(debugger_capture_arena) = NULL;
}
}
}

// Reset closed span counter for limit-refresh, don't touch open spans
Expand Down
8 changes: 7 additions & 1 deletion profiling/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,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(),
];

Expand Down
170 changes: 106 additions & 64 deletions profiling/src/timeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -30,12 +30,6 @@ static mut PREV_ZEND_COMPILE_FILE: Option<zend::VmZendCompileFile> = None;
static mut PREV_ZEND_ACCEL_SCHEDULE_RESTART_HOOK: Option<zend::VmZendAccelScheduleRestartHook> =
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<Instant> = RefCell::new(Instant::now());
#[cfg(php_zts)]
Expand All @@ -45,6 +39,7 @@ thread_local! {
enum State {
Idle,
Sleeping,
Select,
#[cfg(php_zts)]
ThreadStart,
#[cfg(php_zts)]
Expand All @@ -56,6 +51,7 @@ impl State {
match self {
State::Idle => "idle",
State::Sleeping => "sleeping",
State::Select => "select",
#[cfg(php_zts)]
State::ThreadStart => "thread start",
#[cfg(php_zts)]
Expand Down Expand Up @@ -107,60 +103,40 @@ 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, $state:expr) => {
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)
}
}
};
}

/// 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)
}
}
// 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_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);
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)]
Expand All @@ -177,6 +153,16 @@ unsafe extern "C" fn ddog_php_prof_zend_error_observer(
return;
}

let timeline_enabled = REQUEST_LOCALS.with(|cell| {
cell.try_borrow()
.map(|locals| locals.system_settings().profiling_timeline_enabled)
.unwrap_or(false)
});

if !timeline_enabled {
return;
}

#[cfg(zend_error_observer_80)]
let file = unsafe {
let mut len = 0;
Expand Down Expand Up @@ -274,36 +260,92 @@ 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!("stream_select"),
ptr::addr_of_mut!(STREAM_SELECT_HANDLER),
Some(ddog_php_prof_stream_select),
),
zend::datadog_php_zif_handler::new(
cstr!("socket_select"),
ptr::addr_of_mut!(SOCKET_SELECT_HANDLER),
Some(ddog_php_prof_socket_select),
),
zend::datadog_php_zif_handler::new(
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!("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!("event_base_loop"),
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!("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!("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!("parallel\\events"),
cstr!("poll"),
ptr::addr_of_mut!(PARALLEL_EVENTS_POLL_HANDLER),
Some(ddog_php_prof_parallel_events_poll),
),
];

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
Expand Down

0 comments on commit 7d15bec

Please sign in to comment.