Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(profiling): remove execute_internal hook #2719

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ debug = 2 # full debug info

[profile.release]
codegen-units = 1
debug = "line-tables-only"
debug = 2 #todo: revert back
incremental = false
lto = "fat"

Expand Down
15 changes: 15 additions & 0 deletions profiling/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ fn main() {
let vernum = php_config_vernum();
let post_startup_cb = cfg_post_startup_cb(vernum);
let preload = cfg_preload(vernum);
let good_closure_invoke = cfg_good_closure_invoke(vernum);
let fibers = cfg_fibers(vernum);
let run_time_cache = cfg_run_time_cache(vernum);
let trigger_time_sample = cfg_trigger_time_sample();
Expand All @@ -37,6 +38,7 @@ fn main() {
post_startup_cb,
preload,
run_time_cache,
good_closure_invoke,
fibers,
trigger_time_sample,
vernum,
Expand Down Expand Up @@ -83,11 +85,13 @@ const ZAI_H_FILES: &[&str] = &[
"../zend_abstract_interface/json/json.h",
];

#[allow(clippy::too_many_arguments)]
fn build_zend_php_ffis(
php_config_includes: &str,
post_startup_cb: bool,
preload: bool,
run_time_cache: bool,
good_closure_invoke: bool,
fibers: bool,
trigger_time_sample: bool,
vernum: u64,
Expand Down Expand Up @@ -132,6 +136,7 @@ fn build_zend_php_ffis(
let files = ["src/php_ffi.c", "../ext/handlers_api.c"];
let post_startup_cb = if post_startup_cb { "1" } else { "0" };
let preload = if preload { "1" } else { "0" };
let good_closure_invoke = if good_closure_invoke { "1" } else { "0" };
let fibers = if fibers { "1" } else { "0" };
let run_time_cache = if run_time_cache { "1" } else { "0" };
let trigger_time_sample = if trigger_time_sample { "1" } else { "0" };
Expand All @@ -146,6 +151,7 @@ fn build_zend_php_ffis(
.files(files.into_iter().chain(zai_c_files))
.define("CFG_POST_STARTUP_CB", post_startup_cb)
.define("CFG_PRELOAD", preload)
.define("CFG_GOOD_CLOSURE_INVOKE", good_closure_invoke)
.define("CFG_FIBERS", fibers)
.define("CFG_RUN_TIME_CACHE", run_time_cache)
.define("CFG_STACK_WALKING_TESTS", stack_walking_tests)
Expand Down Expand Up @@ -310,6 +316,15 @@ fn cfg_php_major_version(vernum: u64) {
println!("cargo:rustc-cfg=php{major_version}");
}

fn cfg_good_closure_invoke(vernum: u64) -> bool {
if vernum >= 80300 {
println!("cargo:rustc-cfg=php_good_closure_invoke");
true
} else {
false
}
}

fn cfg_fibers(vernum: u64) -> bool {
if vernum >= 80100 {
println!("cargo:rustc-cfg=php_has_fibers");
Expand Down
9 changes: 5 additions & 4 deletions profiling/src/bindings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use libc::{c_char, c_int, c_uchar, c_uint, c_ushort, c_void, size_t};
use std::borrow::Cow;
use std::ffi::CStr;
use std::marker::PhantomData;
use std::sync::atomic::AtomicBool;
use std::{ptr, str};

extern "C" {
Expand Down Expand Up @@ -127,7 +126,7 @@ impl _zend_function {
/// Returns the module name, if there is one. May return Some(b"\0").
pub fn module_name(&self) -> Option<&[u8]> {
if self.is_internal() {
// Safety: union access is guarded by is_internal(), and assume
// SAFETY: union access is guarded by is_internal(), and assume
// its module is valid.
unsafe { self.internal_function.module.as_ref() }
.filter(|module| !module.name.is_null())
Expand Down Expand Up @@ -288,10 +287,11 @@ impl Default for ZendExtension {
}

extern "C" {
/// Retrieves the VM interrupt address of the calling PHP thread.
/// Retrieves the addresses of various parts of executor_globals.
///
/// # Safety
/// Must be called from a PHP thread during a request.
pub fn datadog_php_profiling_vm_interrupt_addr() -> *const AtomicBool;
pub fn ddog_php_prof_executor_global_addrs() -> ExecutorGlobalAddrs;

/// Registers the extension. Note that it's kept in a zend_llist and gets
/// pemalloc'd + memcpy'd into place. The engine says this is a mutable
Expand Down Expand Up @@ -347,6 +347,7 @@ extern "C" {
}

use crate::config::ConfigId;
use crate::ExecutorGlobalAddrs;
pub use zend_module_dep as ModuleDep;

impl ModuleDep {
Expand Down
15 changes: 10 additions & 5 deletions profiling/src/capi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,16 @@ extern "C" fn ddog_php_prof_trigger_time_sample() {
super::REQUEST_LOCALS.with(|cell| {
if let Ok(locals) = cell.try_borrow() {
if locals.system_settings().profiling_enabled {
// Safety: only vm interrupts are stored there, or possibly null (edges only).
if let Some(vm_interrupt) = unsafe { locals.vm_interrupt_addr.as_ref() } {
locals.interrupt_count.fetch_add(1, Ordering::SeqCst);
vm_interrupt.store(true, Ordering::SeqCst);
}
// Safety: only vm interrupts are stored there.
let vm_interrupt = unsafe { locals.executor_global_addrs.vm_interrupt.as_ref() };
let current_execute_data_cache =
unsafe { *locals.executor_global_addrs.current_execute_data.as_ptr() };

locals.interrupt_count.fetch_add(1, Ordering::SeqCst);
locals
.current_execute_data_cache
.store(current_execute_data_cache, Ordering::SeqCst);
vm_interrupt.store(true, Ordering::SeqCst);
}
}
})
Expand Down
83 changes: 56 additions & 27 deletions profiling/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ mod exception;
#[cfg(feature = "timeline")]
mod timeline;

use crate::bindings::ddog_php_prof_executor_global_addrs;
use crate::config::{SystemSettings, INITIAL_SYSTEM_SETTINGS};
use bindings::{
self as zend, ddog_php_prof_php_version, ddog_php_prof_php_version_id, ZendExtension,
ZendResult,
self as zend, ddog_php_prof_php_version, ddog_php_prof_php_version_id, zend_execute_data, zval,
ZendExtension, ZendResult,
};
use clocks::*;
use core::ptr;
Expand All @@ -33,14 +34,14 @@ use lazy_static::lazy_static;
use libc::c_char;
use log::{debug, error, info, trace, warn};
use once_cell::sync::{Lazy, OnceCell};
use profiling::{LocalRootSpanResourceMessage, Profiler, VmInterrupt};
use profiling::{LocalRootSpanResourceMessage, Profiler};
use sapi::Sapi;
use std::borrow::Cow;
use std::cell::RefCell;
use std::ffi::CStr;
use std::ops::Deref;
use std::os::raw::c_int;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicU32, Ordering};
use std::sync::{Arc, Mutex, Once};
use std::time::{Duration, Instant};
use uuid::Uuid;
Expand Down Expand Up @@ -333,6 +334,23 @@ extern "C" fn prshutdown() -> ZendResult {
ZendResult::Success
}

// Keep in-sync with php_ffi.c.
#[repr(C)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct ExecutorGlobalAddrs {
pub vm_stack_top: ptr::NonNull<*mut zval>,
pub current_execute_data: ptr::NonNull<*mut zend_execute_data>,
pub vm_interrupt: ptr::NonNull<AtomicBool>,
}

impl ExecutorGlobalAddrs {
/// # Safety
/// Must be called on a PHP thread during a request.
unsafe fn new() -> ExecutorGlobalAddrs {
ddog_php_prof_executor_global_addrs()
}
}

pub struct RequestLocals {
pub env: Option<String>,
pub service: Option<String>,
Expand All @@ -347,29 +365,33 @@ pub struct RequestLocals {
pub system_settings: ptr::NonNull<SystemSettings>,

pub interrupt_count: AtomicU32,
pub vm_interrupt_addr: *const AtomicBool,
pub current_execute_data_cache: AtomicPtr<zend_execute_data>,
pub executor_global_addrs: ExecutorGlobalAddrs,
}

impl RequestLocals {
#[track_caller]
pub fn system_settings(&self) -> &SystemSettings {
// SAFETY: it should always be valid, either set to the
// INITIAL_SYSTEM_SETTINGS or to the SYSTEM_SETTINGS.
unsafe { self.system_settings.as_ref() }
}
}

impl Default for RequestLocals {
fn default() -> RequestLocals {
/// # Safety
/// Must be called from a PHP Thread.
unsafe fn new() -> RequestLocals {
RequestLocals {
env: None,
service: None,
version: None,
system_settings: ptr::NonNull::from(INITIAL_SYSTEM_SETTINGS.deref()),
interrupt_count: AtomicU32::new(0),
vm_interrupt_addr: ptr::null_mut(),
current_execute_data_cache: AtomicPtr::new(ptr::null_mut()),

// SAFETY: required by this function's safety conditions.
executor_global_addrs: ExecutorGlobalAddrs::new(),
}
}

#[track_caller]
pub fn system_settings(&self) -> &SystemSettings {
// SAFETY: it should always be valid, either set to the
// INITIAL_SYSTEM_SETTINGS or to the SYSTEM_SETTINGS.
unsafe { self.system_settings.as_ref() }
}
}

thread_local! {
Expand All @@ -378,7 +400,8 @@ thread_local! {
wall_time: Instant::now(),
});

static REQUEST_LOCALS: RefCell<RequestLocals> = RefCell::new(RequestLocals::default());
// These are only meant to be used on a PHP thread.
static REQUEST_LOCALS: RefCell<RequestLocals> = RefCell::new(unsafe { RequestLocals::new() });

/// The tags for this thread/request. These get sent to other threads,
/// which is why they are Arc. However, they are wrapped in a RefCell
Expand Down Expand Up @@ -426,9 +449,9 @@ extern "C" fn rinit(_type: c_int, _module_number: c_int) -> ZendResult {
// initialize the thread local storage and cache some items
REQUEST_LOCALS.with(|cell| {
let mut locals = cell.borrow_mut();
// Safety: we are in rinit on a PHP thread.
locals.vm_interrupt_addr = unsafe { zend::datadog_php_profiling_vm_interrupt_addr() };
locals.interrupt_count.store(0, Ordering::SeqCst);
// Safety: we are in rinit on a PHP thread.
locals.executor_global_addrs = unsafe { ddog_php_prof_executor_global_addrs() };

// Safety: We are after first rinit and before mshutdown.
unsafe {
Expand Down Expand Up @@ -555,11 +578,17 @@ extern "C" fn rinit(_type: c_int, _module_number: c_int) -> ZendResult {
}

if let Some(profiler) = PROFILER.lock().unwrap().as_ref() {
let interrupt = VmInterrupt {
interrupt_count_ptr: &locals.interrupt_count as *const AtomicU32,
engine_ptr: locals.vm_interrupt_addr,
let id = profiling::interrupts::Id {
vm_interrupt_addr: locals.executor_global_addrs.vm_interrupt,
current_execute_data_addr: locals.executor_global_addrs.current_execute_data,
};
let state = profiling::interrupts::State {
interrupt_count_addr: ptr::NonNull::from(&locals.interrupt_count),
current_execute_data_addr: ptr::NonNull::from(
&locals.current_execute_data_cache,
),
};
profiler.add_interrupt(interrupt);
profiler.add_interrupt(id, state);
}
});
} else {
Expand Down Expand Up @@ -611,11 +640,11 @@ extern "C" fn rshutdown(_type: c_int, _module_number: c_int) -> ZendResult {
// and we don't need to optimize for that.
if system_settings.profiling_enabled {
if let Some(profiler) = PROFILER.lock().unwrap().as_ref() {
let interrupt = VmInterrupt {
interrupt_count_ptr: &locals.interrupt_count,
engine_ptr: locals.vm_interrupt_addr,
let id = profiling::interrupts::Id {
current_execute_data_addr: locals.executor_global_addrs.current_execute_data,
vm_interrupt_addr: locals.executor_global_addrs.vm_interrupt,
};
profiler.remove_interrupt(interrupt);
profiler.remove_interrupt(id);
}
}
});
Expand Down
17 changes: 16 additions & 1 deletion profiling/src/php_ffi.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,22 @@ void datadog_php_profiling_startup(zend_extension *extension) {
#endif
}

void *datadog_php_profiling_vm_interrupt_addr(void) { return &EG(vm_interrupt); }
// Keep in-sync with Rust ExecutorGlobalAddrs.
struct executor_global_addrs {
zval **vm_stack_top;
zend_execute_data **current_execute_data;
void *vm_interrupt;
};

// Keep in-sync with Rust version.
struct executor_global_addrs ddog_php_prof_executor_global_addrs(void) {
struct executor_global_addrs addrs = {
&EG(vm_stack_top),
&EG(current_execute_data),
(void*)&EG(vm_interrupt),
};
return addrs;
}

zend_module_entry *datadog_get_module_entry(const char *str, uintptr_t len) {
return zend_hash_str_find_ptr(&module_registry, str, len);
Expand Down
5 changes: 0 additions & 5 deletions profiling/src/php_ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,6 @@ sapi_request_info datadog_sapi_globals_request_info();
*/
zend_module_entry *datadog_get_module_entry(const char *str, uintptr_t len);

/**
* Fetches the VM interrupt address of the calling PHP thread.
*/
void *datadog_php_profiling_vm_interrupt_addr(void);

/**
* For Code Hotspots, we need the tracer's local root span id and the current
* span id. This is a cross-product struct, so keep it in sync with tracer's
Expand Down
Loading
Loading