@@ -24,6 +24,12 @@ static mut PREV_ZEND_COMPILE_STRING: Option<zend::VmZendCompileString> = None;
24
24
/// The engine's original (or neighbouring extensions) `zend_compile_file()` function
25
25
static mut PREV_ZEND_COMPILE_FILE : Option < zend:: VmZendCompileFile > = None ;
26
26
27
+ /// The engine's original (or neighbouring extensions) `zend_accel_schedule_restart_hook()`
28
+ /// function
29
+ #[ cfg( php_opcache_restart_hook) ]
30
+ static mut PREV_ZEND_ACCEL_SCHEDULE_RESTART_HOOK : Option < zend:: VmZendAccelScheduleRestartHook > =
31
+ None ;
32
+
27
33
static mut SLEEP_HANDLER : InternalFunctionHandler = None ;
28
34
static mut USLEEP_HANDLER : InternalFunctionHandler = None ;
29
35
static mut TIME_NANOSLEEP_HANDLER : InternalFunctionHandler = None ;
@@ -192,12 +198,63 @@ unsafe extern "C" fn ddog_php_prof_zend_error_observer(
192
198
}
193
199
}
194
200
201
+ /// Will be called by the opcache extension when a restart is scheduled. The `reason` is this enum:
202
+ /// ```C
203
+ /// typedef enum _zend_accel_restart_reason {
204
+ /// ACCEL_RESTART_OOM, /* restart because of out of memory */
205
+ /// ACCEL_RESTART_HASH, /* restart because of hash overflow */
206
+ /// ACCEL_RESTART_USER /* restart scheduled by opcache_reset() */
207
+ /// } zend_accel_restart_reason;
208
+ /// ```
209
+ #[ no_mangle]
210
+ #[ cfg( php_opcache_restart_hook) ]
211
+ unsafe extern "C" fn ddog_php_prof_zend_accel_schedule_restart_hook ( reason : i32 ) {
212
+ let timeline_enabled = REQUEST_LOCALS . with ( |cell| {
213
+ cell. try_borrow ( )
214
+ . map ( |locals| locals. system_settings ( ) . profiling_timeline_enabled )
215
+ . unwrap_or ( false )
216
+ } ) ;
217
+
218
+ if timeline_enabled {
219
+ let now = SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . unwrap ( ) ;
220
+ if let Some ( profiler) = Profiler :: get ( ) {
221
+ let now = now. as_nanos ( ) as i64 ;
222
+ let file = unsafe {
223
+ zend:: zai_str_from_zstr ( zend:: zend_get_executed_filename_ex ( ) . as_mut ( ) )
224
+ . into_string ( )
225
+ } ;
226
+ profiler. collect_opcache_restart (
227
+ now,
228
+ file,
229
+ zend:: zend_get_executed_lineno ( ) ,
230
+ match reason {
231
+ 0 => "out of memory" ,
232
+ 1 => "hash overflow" ,
233
+ 2 => "`opcache_restart()` called" ,
234
+ _ => "unknown" ,
235
+ } ,
236
+ ) ;
237
+ }
238
+ }
239
+
240
+ if let Some ( prev) = PREV_ZEND_ACCEL_SCHEDULE_RESTART_HOOK {
241
+ prev ( reason) ;
242
+ }
243
+ }
244
+
195
245
/// This functions needs to be called in MINIT of the module
196
246
pub fn timeline_minit ( ) {
197
247
unsafe {
198
248
#[ cfg( zend_error_observer) ]
199
249
zend:: zend_observer_error_register ( Some ( ddog_php_prof_zend_error_observer) ) ;
200
250
251
+ #[ cfg( php_opcache_restart_hook) ]
252
+ {
253
+ PREV_ZEND_ACCEL_SCHEDULE_RESTART_HOOK = zend:: zend_accel_schedule_restart_hook;
254
+ zend:: zend_accel_schedule_restart_hook =
255
+ Some ( ddog_php_prof_zend_accel_schedule_restart_hook) ;
256
+ }
257
+
201
258
// register our function in the `gc_collect_cycles` pointer
202
259
PREV_GC_COLLECT_CYCLES = zend:: gc_collect_cycles;
203
260
zend:: gc_collect_cycles = Some ( ddog_php_prof_gc_collect_cycles) ;
0 commit comments