Skip to content

Commit

Permalink
poc
Browse files Browse the repository at this point in the history
  • Loading branch information
realFlowControl committed Nov 13, 2024
1 parent 49b3dc0 commit 35291b3
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 1 deletion.
1 change: 1 addition & 0 deletions profiling/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,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");
}
}

Expand Down
2 changes: 2 additions & 0 deletions profiling/src/bindings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub type VmGcCollectCyclesFn = unsafe extern "C" fn() -> i32;
#[cfg(feature = "timeline")]
pub type VmZendCompileFile =
unsafe extern "C" fn(*mut zend_file_handle, i32) -> *mut _zend_op_array;
#[cfg(all(feature = "timeline", php_opcache_restart_hook))]
pub type VmZendAccelScheduleRestartHook = unsafe extern "C" fn(i32);
#[cfg(all(feature = "timeline", php_zend_compile_string_has_position))]
pub type VmZendCompileString = unsafe extern "C" fn(
*mut zend_string,
Expand Down
40 changes: 40 additions & 0 deletions profiling/src/profiling/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,46 @@ impl Profiler {
}
}

/// This function can be called to collect an opcache restart
#[cfg(feature = "timeline")]
pub(crate) fn collect_opcache_restart(
&self,
now: i64,
file: String,
line: u32,
reason: &'static str,
) {
let mut labels = Profiler::common_labels(1);

labels.push(Label {
key: "reason",
value: LabelValue::Str(reason.into()),
});

let n_labels = labels.len();

match self.prepare_and_send_message(
vec![ZendFrame {
function: "[opcache restart]".into(),
file: Some(file),
line,
}],
SampleValues {
timeline: 1,
..Default::default()
},
labels,
now,
) {
Ok(_) => {
trace!("Sent event 'idle' with {n_labels} labels to profiler.")
}
Err(err) => {
warn!("Failed to send event 'idle' with {n_labels} labels to profiler: {err}")
}
}
}

/// This function can be called to collect any kind of inactivity that is happening
#[cfg(feature = "timeline")]
pub fn collect_idle(&self, now: i64, duration: i64, reason: &'static str) {
Expand Down
3 changes: 2 additions & 1 deletion profiling/src/profiling/thread_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ pub fn get_current_thread_name() -> String {
}

thread_name
}).clone()
})
.clone()
})
}

Expand Down
56 changes: 56 additions & 0 deletions profiling/src/timeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ static mut PREV_ZEND_COMPILE_STRING: Option<zend::VmZendCompileString> = None;
/// The engine's original (or neighbouring extensions) `zend_compile_file()` function
static mut PREV_ZEND_COMPILE_FILE: Option<zend::VmZendCompileFile> = None;

/// The engine's original (or neighbouring extensions) `zend_accel_schedule_restart_hook()`
/// function
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;
Expand Down Expand Up @@ -192,12 +197,63 @@ unsafe extern "C" fn ddog_php_prof_zend_error_observer(
}
}

/// Will be called by the opcache extension when a restart is scheduled. The `reason` is this enum:
/// ```C
/// typedef enum _zend_accel_restart_reason {
/// ACCEL_RESTART_OOM, /* restart because of out of memory */
/// ACCEL_RESTART_HASH, /* restart because of hash overflow */
/// ACCEL_RESTART_USER /* restart scheduled by opcache_reset() */
/// } zend_accel_restart_reason;
/// ```
#[no_mangle]
#[cfg(php_opcache_restart_hook)]
unsafe extern "C" fn ddog_php_prof_zend_accel_schedule_restart_hook(reason: i32) {
let timeline_enabled = REQUEST_LOCALS.with(|cell| {
cell.try_borrow()
.map(|locals| locals.system_settings().profiling_timeline_enabled)
.unwrap_or(false)
});

if timeline_enabled {
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
if let Some(profiler) = Profiler::get() {
let now = now.as_nanos() as i64;
let file = unsafe {
zend::zai_str_from_zstr(zend::zend_get_executed_filename_ex().as_mut())
.into_string()
};
profiler.collect_opcache_restart(
now,
file,
zend::zend_get_executed_lineno(),
match reason {
0 => "out of memory",
1 => "hash overflow",
2 => "`opcache_restart()` called",
_ => "unknown",
},
);
}
}

if let Some(prev) = PREV_ZEND_ACCEL_SCHEDULE_RESTART_HOOK {
prev(reason);
}
}

/// This functions needs to be called in MINIT of the module
pub fn timeline_minit() {
unsafe {
#[cfg(zend_error_observer)]
zend::zend_observer_error_register(Some(ddog_php_prof_zend_error_observer));

#[cfg(php_opcache_restart_hook)]
{
PREV_ZEND_ACCEL_SCHEDULE_RESTART_HOOK = zend::zend_accel_schedule_restart_hook;
zend::zend_accel_schedule_restart_hook =
Some(ddog_php_prof_zend_accel_schedule_restart_hook);
}

// register our function in the `gc_collect_cycles` pointer
PREV_GC_COLLECT_CYCLES = zend::gc_collect_cycles;
zend::gc_collect_cycles = Some(ddog_php_prof_gc_collect_cycles);
Expand Down

0 comments on commit 35291b3

Please sign in to comment.