diff --git a/profiling/build.rs b/profiling/build.rs index ea22fd0fcb..66c6b63266 100644 --- a/profiling/build.rs +++ b/profiling/build.rs @@ -358,7 +358,7 @@ fn cfg_php_feature_flags(vernum: u64) { if vernum >= 80400 { println!("cargo:rustc-cfg=php_frameless"); println!("cargo:rustc-cfg=php_opcache_restart_hook"); - println!("cargo:rustc-cfg=php_new_zendmm_hooks"); + println!("cargo:rustc-cfg=php_zend_mm_set_custom_handlers_ex"); } } diff --git a/profiling/src/allocation/allocation_ge84.rs b/profiling/src/allocation/allocation_ge84.rs index 6a8dafda87..c3fb11ae52 100644 --- a/profiling/src/allocation/allocation_ge84.rs +++ b/profiling/src/allocation/allocation_ge84.rs @@ -87,12 +87,24 @@ macro_rules! tls_zend_mm_state { }; } -pub fn alloc_prof_rinit() { +#[allow(dead_code)] +pub fn alloc_prof_minit() { + #[cfg(not(php_zts))] + alloc_prof_ginit(); +} + +#[allow(dead_code)] +pub fn alloc_prof_mshutdown() { + #[cfg(not(php_zts))] + alloc_prof_gshutdown(); +} + +pub fn alloc_prof_ginit() { ZEND_MM_STATE.with(|cell| { let zend_mm_state = cell.get(); // Only need to create an observed heap once per thread. When we have it, we can just - // install the observed hook via `zend::zend_mm_set_heap()` + // install the observed heap via `zend::zend_mm_set_heap()` if unsafe { (*zend_mm_state).heap.is_null() } { // Safety: `zend_mm_get_heap()` always returns a non-null pointer to a valid heap structure let prev_heap = unsafe { zend::zend_mm_get_heap() }; @@ -130,7 +142,7 @@ pub fn alloc_prof_rinit() { } } - // Create our observed heap and prepare custom handlers + // Create a new (to be observed) heap and prepare custom handlers let heap = unsafe { zend::zend_mm_startup() }; unsafe { ptr::addr_of_mut!((*zend_mm_state).heap).write(heap) }; @@ -145,10 +157,55 @@ pub fn alloc_prof_rinit() { Some(alloc_prof_shutdown), ); } + debug!("New observed heap created"); } + }); +} - // install the observed heap into ZendMM +pub fn alloc_prof_gshutdown() { + ZEND_MM_STATE.with(|cell| { + let zend_mm_state = cell.get(); + unsafe { + // remove custom handlers to allow for ZendMM internal shutdown + zend::zend_mm_set_custom_handlers_ex( + (*zend_mm_state).heap, + None, + None, + None, + None, + None, + ); + + // reset to defaults + ptr::addr_of_mut!((*zend_mm_state).alloc).write(alloc_prof_orig_alloc); + ptr::addr_of_mut!((*zend_mm_state).free).write(alloc_prof_orig_free); + ptr::addr_of_mut!((*zend_mm_state).realloc).write(alloc_prof_orig_realloc); + ptr::addr_of_mut!((*zend_mm_state).gc).write(alloc_prof_orig_gc); + ptr::addr_of_mut!((*zend_mm_state).shutdown).write(alloc_prof_orig_shutdown); + ptr::addr_of_mut!((*zend_mm_state).prev_custom_mm_alloc).write(None); + ptr::addr_of_mut!((*zend_mm_state).prev_custom_mm_free).write(None); + ptr::addr_of_mut!((*zend_mm_state).prev_custom_mm_realloc).write(None); + ptr::addr_of_mut!((*zend_mm_state).prev_custom_mm_gc).write(None); + ptr::addr_of_mut!((*zend_mm_state).prev_custom_mm_shutdown).write(None); + + // This shutdown will free the observed heap we created in minit + zend::zend_mm_shutdown((*zend_mm_state).heap, true, true); + + ptr::addr_of_mut!((*zend_mm_state).heap).write(ptr::null_mut()); + ptr::addr_of_mut!((*zend_mm_state).prev_heap).write(ptr::null_mut()); + + debug!("Observed heap was freed and `zend_mm_state` reset"); + } + }); +} + +pub fn alloc_prof_rinit() { + ZEND_MM_STATE.with(|cell| { + let zend_mm_state = cell.get(); + // Safety: `zend_mm_state.heap` got initialized in `MINIT` and is guaranteed to + // be a non null pointer to a valid `zend::zend_mm_heap` struct unsafe { + // Install our observed heap into ZendMM zend::zend_mm_set_heap((*zend_mm_state).heap); } }); @@ -181,7 +238,7 @@ pub fn alloc_prof_rshutdown() { let mut custom_mm_gc: Option = None; let mut custom_mm_shutdown: Option = None; - // Safety: `unwrap()` is safe here, as `heap` is initialized in `RINIT` + // Safety: `unwrap()` is safe here, as `heap` is initialized in `MINIT` let heap = unsafe { (*zend_mm_state).heap }; unsafe { zend::zend_mm_get_custom_handlers_ex( diff --git a/profiling/src/allocation/mod.rs b/profiling/src/allocation/mod.rs index f7303d112d..9980db0adf 100644 --- a/profiling/src/allocation/mod.rs +++ b/profiling/src/allocation/mod.rs @@ -8,9 +8,9 @@ use rand_distr::{Distribution, Poisson}; use std::cell::RefCell; use std::sync::atomic::AtomicU64; -#[cfg(php_new_zendmm_hooks)] +#[cfg(php_zend_mm_set_custom_handlers_ex)] mod allocation_ge84; -#[cfg(not(php_new_zendmm_hooks))] +#[cfg(not(php_zend_mm_set_custom_handlers_ex))] pub mod allocation_le83; /// take a sample every 4096 KiB @@ -78,12 +78,35 @@ thread_local! { } pub fn alloc_prof_minit() { - #[cfg(not(php_new_zendmm_hooks))] + #[cfg(not(php_zend_mm_set_custom_handlers_ex))] allocation_le83::alloc_prof_minit(); + #[cfg(php_zend_mm_set_custom_handlers_ex)] + allocation_ge84::alloc_prof_minit(); } +#[allow(dead_code)] +pub fn alloc_prof_mshutdown() { + #[cfg(php_zend_mm_set_custom_handlers_ex)] + allocation_ge84::alloc_prof_mshutdown(); +} + +#[allow(dead_code)] +#[cfg(php_zts)] +pub fn alloc_prof_ginit() { + #[cfg(php_zend_mm_set_custom_handlers_ex)] + allocation_ge84::alloc_prof_ginit(); +} + +#[allow(dead_code)] +#[cfg(php_zts)] +pub fn alloc_prof_gshutdown() { + #[cfg(php_zend_mm_set_custom_handlers_ex)] + allocation_ge84::alloc_prof_gshutdown(); +} + +#[allow(dead_code)] pub fn alloc_prof_startup() { - #[cfg(not(php_new_zendmm_hooks))] + #[cfg(not(php_zend_mm_set_custom_handlers_ex))] allocation_le83::alloc_prof_startup(); } @@ -105,9 +128,9 @@ pub fn alloc_prof_rinit() { return; } - #[cfg(not(php_new_zendmm_hooks))] + #[cfg(not(php_zend_mm_set_custom_handlers_ex))] allocation_le83::alloc_prof_rinit(); - #[cfg(php_new_zendmm_hooks)] + #[cfg(php_zend_mm_set_custom_handlers_ex)] allocation_ge84::alloc_prof_rinit(); trace!("Memory allocation profiling enabled.") @@ -124,8 +147,8 @@ pub fn alloc_prof_rshutdown() { return; } - #[cfg(not(php_new_zendmm_hooks))] + #[cfg(not(php_zend_mm_set_custom_handlers_ex))] allocation_le83::alloc_prof_rshutdown(); - #[cfg(php_new_zendmm_hooks)] + #[cfg(php_zend_mm_set_custom_handlers_ex)] allocation_ge84::alloc_prof_rshutdown(); } diff --git a/profiling/src/bindings/mod.rs b/profiling/src/bindings/mod.rs index bfb178a1af..77cd127071 100644 --- a/profiling/src/bindings/mod.rs +++ b/profiling/src/bindings/mod.rs @@ -46,9 +46,9 @@ pub type VmMmCustomAllocFn = unsafe extern "C" fn(size_t) -> *mut c_void; pub type VmMmCustomReallocFn = unsafe extern "C" fn(*mut c_void, size_t) -> *mut c_void; #[cfg(feature = "allocation_profiling")] pub type VmMmCustomFreeFn = unsafe extern "C" fn(*mut c_void); -#[cfg(all(feature = "allocation_profiling", php_new_zendmm_hooks))] +#[cfg(all(feature = "allocation_profiling", php_zend_mm_set_custom_handlers_ex))] pub type VmMmCustomGcFn = unsafe extern "C" fn() -> size_t; -#[cfg(all(feature = "allocation_profiling", php_new_zendmm_hooks))] +#[cfg(all(feature = "allocation_profiling", php_zend_mm_set_custom_handlers_ex))] pub type VmMmCustomShutdownFn = unsafe extern "C" fn(bool, bool); // todo: this a lie on some PHP versions; is it a problem even though zend_bool diff --git a/profiling/src/config.rs b/profiling/src/config.rs index b84650fbb2..c21185e838 100644 --- a/profiling/src/config.rs +++ b/profiling/src/config.rs @@ -1,4 +1,4 @@ -#[cfg(not(php_new_zendmm_hooks))] +#[cfg(not(php_zend_mm_set_custom_handlers_ex))] use crate::allocation; use crate::bindings::zai_config_type::*; use crate::bindings::{ @@ -102,7 +102,7 @@ impl SystemSettings { } // Work around version-specific issues. - #[cfg(not(php_new_zendmm_hooks))] + #[cfg(not(php_zend_mm_set_custom_handlers_ex))] if allocation::allocation_le83::first_rinit_should_disable_due_to_jit() { system_settings.profiling_allocation_enabled = false; } diff --git a/profiling/src/lib.rs b/profiling/src/lib.rs index 3b3db288a0..e15efb26b1 100644 --- a/profiling/src/lib.rs +++ b/profiling/src/lib.rs @@ -203,12 +203,18 @@ pub extern "C" fn get_module() -> &'static mut zend::ModuleEntry { unsafe extern "C" fn ginit(_globals_ptr: *mut c_void) { #[cfg(all(feature = "timeline", php_zts))] timeline::timeline_ginit(); + + #[cfg(feature = "allocation_profiling")] + allocation::alloc_prof_ginit(); } #[cfg(php_zts)] unsafe extern "C" fn gshutdown(_globals_ptr: *mut c_void) { #[cfg(all(feature = "timeline", php_zts))] timeline::timeline_gshutdown(); + + #[cfg(feature = "allocation_profiling")] + allocation::alloc_prof_gshutdown(); } /* Important note on the PHP lifecycle: @@ -337,15 +343,15 @@ extern "C" fn minit(_type: c_int, module_number: c_int) -> ZendResult { */ unsafe { zend::zend_register_extension(&extension, handle) }; - #[cfg(feature = "allocation_profiling")] - allocation::alloc_prof_minit(); - #[cfg(feature = "timeline")] timeline::timeline_minit(); #[cfg(feature = "exception_profiling")] exception::exception_profiling_minit(); + #[cfg(feature = "allocation_profiling")] + allocation::alloc_prof_minit(); + // There are a few things which need to do something on the first rinit of // each minit/mshutdown cycle. In Apache, when doing `apachectl graceful`, // there can be more than one of these cycles per process. @@ -851,6 +857,9 @@ extern "C" fn mshutdown(_type: c_int, _module_number: c_int) -> ZendResult { #[cfg(feature = "exception_profiling")] exception::exception_profiling_mshutdown(); + #[cfg(feature = "allocation_profiling")] + allocation::alloc_prof_mshutdown(); + Profiler::stop(Duration::from_secs(1)); ZendResult::Success