@@ -16,6 +16,7 @@ mod exception;
16
16
17
17
#[ cfg( feature = "timeline" ) ]
18
18
mod timeline;
19
+ mod wall_time;
19
20
20
21
use bindings as zend;
21
22
use bindings:: { ddog_php_prof_php_version_id, ZendExtension , ZendResult } ;
@@ -32,8 +33,6 @@ use sapi::Sapi;
32
33
use std:: borrow:: Cow ;
33
34
use std:: cell:: RefCell ;
34
35
use std:: ffi:: CStr ;
35
- use std:: mem:: MaybeUninit ;
36
- use std:: ops:: Deref ;
37
36
use std:: os:: raw:: c_int;
38
37
use std:: path:: PathBuf ;
39
38
use std:: str:: FromStr ;
@@ -170,19 +169,6 @@ pub extern "C" fn get_module() -> &'static mut zend::ModuleEntry {
170
169
unsafe { & mut * MODULE }
171
170
}
172
171
173
- /// The engine's previous `zend_interrupt_function` value, if there is one.
174
- /// Note that because of things like Apache reload which call minit more than
175
- /// once per process, this cannot be made into a OnceCell nor lazy_static.
176
- static mut PREV_INTERRUPT_FUNCTION : MaybeUninit < Option < zend:: VmInterruptFn > > =
177
- MaybeUninit :: uninit ( ) ;
178
-
179
- /// The engine's previous `zend::zend_execute_internal` value, or
180
- /// `zend::execute_internal` if none. This is a highly active path, so although
181
- /// it could be made safe with Mutex, the cost is too high.
182
- static mut PREV_EXECUTE_INTERNAL : MaybeUninit <
183
- unsafe extern "C" fn ( execute_data : * mut zend:: zend_execute_data , return_value : * mut zend:: zval ) ,
184
- > = MaybeUninit :: uninit ( ) ;
185
-
186
172
/* Important note on the PHP lifecycle:
187
173
* Based on how some SAPIs work and the documentation, one might expect that
188
174
* MINIT is called once per process, but this is only sort-of true. Some SAPIs
@@ -287,18 +273,7 @@ extern "C" fn minit(_type: c_int, module_number: c_int) -> ZendResult {
287
273
} ;
288
274
289
275
// Safety: during minit there shouldn't be any threads to race against these writes.
290
- unsafe {
291
- PREV_INTERRUPT_FUNCTION . write ( zend:: zend_interrupt_function) ;
292
- PREV_EXECUTE_INTERNAL . write ( zend:: zend_execute_internal. unwrap_or ( zend:: execute_internal) ) ;
293
-
294
- zend:: zend_interrupt_function = Some ( if zend:: zend_interrupt_function. is_some ( ) {
295
- interrupt_function_wrapper
296
- } else {
297
- capi:: datadog_profiling_interrupt_function
298
- } ) ;
299
-
300
- zend:: zend_execute_internal = Some ( execute_internal) ;
301
- } ;
276
+ unsafe { wall_time:: minit ( ) } ;
302
277
303
278
/* Safety: all arguments are valid for this C call.
304
279
* Note that on PHP 7 this never fails, and on PHP 8 it returns void.
@@ -1000,104 +975,6 @@ fn notify_trace_finished(local_root_span_id: u64, span_type: Cow<str>, resource:
1000
975
} ) ;
1001
976
}
1002
977
1003
- /// Gathers a time sample if the configured period has elapsed and resets the
1004
- /// interrupt_count.
1005
- fn interrupt_function ( execute_data : * mut zend:: zend_execute_data ) {
1006
- REQUEST_LOCALS . with ( |cell| {
1007
- // try to borrow and bail out if not successful
1008
- let locals = match cell. try_borrow ( ) {
1009
- Ok ( locals) => locals,
1010
- Err ( _) => {
1011
- return ;
1012
- }
1013
- } ;
1014
-
1015
- if !locals. profiling_enabled {
1016
- return ;
1017
- }
1018
-
1019
- /* Other extensions/modules or the engine itself may trigger an
1020
- * interrupt, but given how expensive it is to gather a stack trace,
1021
- * it should only be done if we triggered it ourselves. So
1022
- * interrupt_count serves dual purposes:
1023
- * 1. Track how many interrupts there were.
1024
- * 2. Ensure we don't collect on someone else's interrupt.
1025
- */
1026
- let interrupt_count = locals. interrupt_count . swap ( 0 , Ordering :: SeqCst ) ;
1027
- if interrupt_count == 0 {
1028
- return ;
1029
- }
1030
-
1031
- if let Some ( profiler) = PROFILER . lock ( ) . unwrap ( ) . as_ref ( ) {
1032
- // Safety: execute_data was provided by the engine, and the profiler doesn't mutate it.
1033
- profiler. collect_time ( execute_data, interrupt_count, locals. deref ( ) ) ;
1034
- }
1035
- } ) ;
1036
- }
1037
-
1038
- /// A wrapper for the `datadog_profiling_interrupt_function` to call the
1039
- /// previous interrupt handler, if there was one.
1040
- extern "C" fn interrupt_function_wrapper ( execute_data : * mut zend:: zend_execute_data ) {
1041
- interrupt_function ( execute_data) ;
1042
-
1043
- // Safety: PREV_INTERRUPT_FUNCTION was written during minit, doesn't change during runtime.
1044
- unsafe {
1045
- if let Some ( prev_interrupt) = * PREV_INTERRUPT_FUNCTION . as_mut_ptr ( ) {
1046
- prev_interrupt ( execute_data) ;
1047
- }
1048
- }
1049
- }
1050
-
1051
- /// Returns true if the func tied to the execute_data is a trampoline.
1052
- /// # Safety
1053
- /// This is only safe to execute _before_ executing the trampoline, because the trampoline may
1054
- /// free the `execute_data.func` _without_ setting it to NULL:
1055
- /// https://heap.space/xref/PHP-8.2/Zend/zend_closures.c?r=af2110e6#60-63
1056
- /// So no code can inspect the func after the call has been made, which is why you would call this function: find out before you
1057
- /// call the function if indeed you need to skip certain code after it has been executed.
1058
- unsafe fn execute_data_func_is_trampoline ( execute_data : * const zend:: zend_execute_data ) -> bool {
1059
- if execute_data. is_null ( ) {
1060
- return false ;
1061
- }
1062
-
1063
- if ( * execute_data) . func . is_null ( ) {
1064
- return false ;
1065
- }
1066
- return ( ( * ( * execute_data) . func ) . common . fn_flags & zend:: ZEND_ACC_CALL_VIA_TRAMPOLINE ) != 0 ;
1067
- }
1068
-
1069
- /// Overrides the engine's zend_execute_internal hook in order to process pending VM interrupts
1070
- /// while the internal function is still on top of the call stack. The VM does not process the
1071
- /// interrupt until the call returns so that it could theoretically jump to a different opcode,
1072
- /// like a fiber scheduler.
1073
- /// For our particular case this is problematic. For example, when the user does something like
1074
- /// `sleep(seconds: 10)`, the normal interrupt handling will not trigger until sleep returns, so
1075
- /// we'd then attribute all that time spent sleeping to whatever runs next. This is why we intercept
1076
- /// `zend_execute_internal` and process our own VM interrupts, but it doesn't delegate to the
1077
- /// previous VM interrupt hook, as it's not expecting to be called from this state.
1078
- extern "C" fn execute_internal (
1079
- execute_data : * mut zend:: zend_execute_data ,
1080
- return_value : * mut zend:: zval ,
1081
- ) {
1082
- // SAFETY: called before executing the trampoline.
1083
- let leaf_frame = if unsafe { execute_data_func_is_trampoline ( execute_data) } {
1084
- // SAFETY: if is_trampoline is set, then there must be a valid execute_data.
1085
- unsafe { * execute_data } . prev_execute_data
1086
- } else {
1087
- execute_data
1088
- } ;
1089
-
1090
- // SAFETY: PREV_EXECUTE_INTERNAL was written during minit, doesn't change during runtime.
1091
- let prev_execute_internal = unsafe { * PREV_EXECUTE_INTERNAL . as_mut_ptr ( ) } ;
1092
-
1093
- // SAFETY: calling prev_execute without modification will be safe.
1094
- unsafe { prev_execute_internal ( execute_data, return_value) } ;
1095
-
1096
- // See safety section of `execute_data_func_is_trampoline` docs for why the leaf frame is used
1097
- // instead of the execute_data ptr.
1098
- interrupt_function ( leaf_frame) ;
1099
- }
1100
-
1101
978
#[ cfg( test) ]
1102
979
mod tests {
1103
980
use super :: * ;
0 commit comments