Skip to content
Merged
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
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion profiling/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ lazy_static = { version = "1.4" }
libc = "0.2"
# TRACE set to max to support runtime configuration.
log = { version = "0.4", features = ["max_level_trace", "release_max_level_trace"]}
once_cell = { version = "1.12" }
serde_json = {version = "1.0"}
rand = { version = "0.8.5" }
rand_distr = { version = "0.4.3" }
Expand Down
17 changes: 9 additions & 8 deletions profiling/src/bindings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,13 +246,16 @@ pub struct ZendExtension {

pub use ZendExtension as zend_extension;

impl Default for ModuleEntry {
fn default() -> Self {
impl ModuleEntry {
/// Creates a new ModuleEntry with default values for const-compatible fields.
/// Non-const fields (functions, build_id) are set to null and should be initialized separately.
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
Self {
size: std::mem::size_of::<Self>() as c_ushort,
size: core::mem::size_of::<Self>() as c_ushort,
zend_api: ZEND_MODULE_API_NO,
zend_debug: ZEND_DEBUG as u8,
zts: USING_ZTS as u8,
zend_debug: ZEND_DEBUG as c_uchar,
zts: USING_ZTS as c_uchar,
ini_entry: ptr::null(),
deps: ptr::null(),
name: c"".as_ptr(),
Expand All @@ -264,20 +267,18 @@ impl Default for ModuleEntry {
info_func: None,
version: ptr::null(),
globals_size: 0,

#[cfg(php_zts)]
globals_id_ptr: ptr::null_mut(),
#[cfg(not(php_zts))]
globals_ptr: ptr::null_mut(),

globals_ctor: None,
globals_dtor: None,
post_deactivate_func: None,
module_started: 0,
type_: MODULE_PERSISTENT as c_uchar,
handle: ptr::null_mut(),
module_number: -1,
build_id: unsafe { datadog_module_build_id() },
build_id: ptr::null(),
}
}
}
Expand Down
106 changes: 56 additions & 50 deletions profiling/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,13 @@ use core::ptr;
use lazy_static::lazy_static;
use libdd_common::{cstr, tag, tag::Tag};
use log::{debug, error, info, trace, warn};
use once_cell::sync::{Lazy, OnceCell};
use profiling::{LocalRootSpanResourceMessage, Profiler, VmInterrupt};
use sapi::Sapi;
use std::borrow::Cow;
use std::cell::{BorrowError, BorrowMutError, RefCell};
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::{Arc, Once};
use std::sync::{Arc, Once, OnceLock};
use std::thread::{AccessError, LocalKey};
use std::time::{Duration, Instant};
use uuid::Uuid;
Expand Down Expand Up @@ -146,7 +145,7 @@ lazy_static! {
/// Additionally, the tracer is going to ask for this in its ACTIVATE handler,
/// so whatever it is replaced with needs to also follow the
/// initialize-on-first-use pattern.
static RUNTIME_ID: OnceCell<Uuid> = OnceCell::new();
static RUNTIME_ID: OnceLock<Uuid> = OnceLock::new();
// If ddtrace is loaded, we fetch the uuid from there instead
extern "C" {
pub static ddtrace_runtime_id: *const Uuid;
Expand All @@ -159,55 +158,62 @@ extern "C" {
#[cfg(php_zts)]
static mut GLOBALS_ID_PTR: i32 = 0;

/// The function `get_module` is what makes this a PHP module. Please do not
/// call this directly; only let it be called by the engine. Generally it is
/// only called once, but if someone accidentally loads the module twice then
/// it might get called more than once, though it will warn and not use the
/// consecutive return value.
/// Module dependencies for the profiler extension.
static MODULE_DEPS: [zend::ModuleDep; 8] = [
zend::ModuleDep::required(cstr!("standard")),
zend::ModuleDep::required(cstr!("json")),
zend::ModuleDep::optional(cstr!("ddtrace")),
// Optionally, be dependent on these event extensions so that the functions they provide
// are registered in the function table and we can hook into them.
zend::ModuleDep::optional(cstr!("ev")),
zend::ModuleDep::optional(cstr!("event")),
zend::ModuleDep::optional(cstr!("libevent")),
zend::ModuleDep::optional(cstr!("uv")),
zend::ModuleDep::end(),
];

/// The module entry for the profiler extension. Fields that aren't
/// const-compatible are set in get_module().
static mut MODULE: zend::ModuleEntry = zend::ModuleEntry {
deps: MODULE_DEPS.as_ptr(),
name: PROFILER_NAME.as_ptr(),
functions: ptr::null(), // Will be set in get_module()
module_startup_func: Some(minit),
module_shutdown_func: Some(mshutdown),
request_startup_func: Some(rinit),
request_shutdown_func: Some(rshutdown),
info_func: Some(minfo),
version: PROFILER_VERSION.as_ptr(),
globals_size: 1,
#[cfg(php_zts)]
globals_id_ptr: ptr::addr_of_mut!(GLOBALS_ID_PTR),
#[cfg(not(php_zts))]
globals_ptr: ptr::null_mut(),
globals_ctor: Some(ginit),
globals_dtor: Some(gshutdown),
post_deactivate_func: Some(prshutdown),
build_id: ptr::null(), // Will be set in get_module()
..zend::ModuleEntry::new()
};

/// The function `get_module` is what makes this a PHP module.
///
/// # Safety
///
/// Do not call this function manually; it will be called by the engine.
/// Generally it is only called once, but if someone accidentally loads the
/// module twice then it might get called more than once, though it will warn
/// and not use the consecutive return value.
#[no_mangle]
pub extern "C" fn get_module() -> &'static mut zend::ModuleEntry {
static DEPS: [zend::ModuleDep; 8] = [
zend::ModuleDep::required(cstr!("standard")),
zend::ModuleDep::required(cstr!("json")),
zend::ModuleDep::optional(cstr!("ddtrace")),
// Optionally, be dependent on these event extensions so that the functions they provide
// are registered in the function table and we can hook into them.
zend::ModuleDep::optional(cstr!("ev")),
zend::ModuleDep::optional(cstr!("event")),
zend::ModuleDep::optional(cstr!("libevent")),
zend::ModuleDep::optional(cstr!("uv")),
zend::ModuleDep::end(),
];

// In PHP modules written in C, this just returns the address of a global,
// mutable variable. In Rust, you cannot initialize such a complicated
// global variable because of initialization order issues that have been
// found through decades of C++ experience.
// There are a variety of ways to deal with this; this is just one way.
static mut MODULE: Lazy<zend::ModuleEntry> = Lazy::new(|| zend::ModuleEntry {
name: PROFILER_NAME.as_ptr(),
// SAFETY: php_ffi.c defines this correctly
functions: unsafe { bindings::ddog_php_prof_functions },
module_startup_func: Some(minit),
module_shutdown_func: Some(mshutdown),
request_startup_func: Some(rinit),
request_shutdown_func: Some(rshutdown),
info_func: Some(minfo),
version: PROFILER_VERSION.as_ptr(),
post_deactivate_func: Some(prshutdown),
deps: DEPS.as_ptr(),
globals_ctor: Some(ginit),
globals_dtor: Some(gshutdown),
globals_size: 1,
#[cfg(php_zts)]
globals_id_ptr: ptr::addr_of_mut!(GLOBALS_ID_PTR),
#[cfg(not(php_zts))]
globals_ptr: ptr::null_mut(),
..Default::default()
});
pub unsafe extern "C" fn get_module() -> *mut zend::ModuleEntry {
let module = ptr::addr_of_mut!(MODULE);

// SAFETY: well, it's at least as safe as what every single C extension does.
unsafe { &mut *ptr::addr_of_mut!(MODULE) }
// Set fields that aren't const-compatible.
unsafe {
ptr::addr_of_mut!((*module).functions).write(bindings::ddog_php_prof_functions);
ptr::addr_of_mut!((*module).build_id).write(bindings::datadog_module_build_id());
}
module
}

unsafe extern "C" fn ginit(_globals_ptr: *mut c_void) {
Expand Down
7 changes: 3 additions & 4 deletions profiling/src/profiling/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,12 @@ use libdd_profiling::api::{
use libdd_profiling::exporter::Tag;
use libdd_profiling::internal::Profile as InternalProfile;
use log::{debug, info, trace, warn};
use once_cell::sync::OnceCell;
use std::borrow::Cow;
use std::collections::HashMap;
use std::hash::Hash;
use std::num::NonZeroI64;
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicU32, Ordering};
use std::sync::{Arc, Barrier};
use std::sync::{Arc, Barrier, OnceLock};
use std::thread::JoinHandle;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};

Expand All @@ -58,7 +57,7 @@ const UPLOAD_CHANNEL_CAPACITY: usize = 8;

/// The global profiler. Profiler gets made during the first rinit after an
/// minit, and is destroyed on mshutdown.
static mut PROFILER: OnceCell<Profiler> = OnceCell::new();
static mut PROFILER: OnceLock<Profiler> = OnceLock::new();

/// Order this array this way:
/// 1. Always enabled types.
Expand Down Expand Up @@ -657,7 +656,7 @@ const DDPROF_TIME: &str = "ddprof_time";
const DDPROF_UPLOAD: &str = "ddprof_upload";

impl Profiler {
/// Will initialize the `PROFILER` OnceCell and makes sure that only one thread will do so.
/// Will initialize the `PROFILER` OnceLock and makes sure that only one thread will do so.
pub fn init(system_settings: &SystemSettings) {
// SAFETY: the `get_or_init` access is a thread-safe API, and the
// PROFILER is only being mutated in single-threaded phases such as
Expand Down
2 changes: 1 addition & 1 deletion profiling/src/profiling/thread_utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::SAPI;
use libc::sched_yield;
use once_cell::sync::OnceCell;
use std::cell::OnceCell;
use std::mem::MaybeUninit;
use std::thread::JoinHandle;
use std::time::{Duration, Instant};
Expand Down
4 changes: 2 additions & 2 deletions profiling/src/sapi.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::zend::sapi_request_info;
use log::warn;
use once_cell::sync::OnceCell;
use std::borrow::Cow;
use std::collections::HashMap;
use std::ffi::{CStr, OsStr};
use std::fmt::{Display, Formatter};
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::sync::OnceLock;

// todo: unify with ../component/sapi
#[derive(Copy, Clone, Eq, PartialEq)]
Expand All @@ -27,7 +27,7 @@ pub enum Sapi {

impl Sapi {
pub fn from_name(name: &str) -> Sapi {
static SAPIS: OnceCell<HashMap<&str, Sapi>> = OnceCell::new();
static SAPIS: OnceLock<HashMap<&str, Sapi>> = OnceLock::new();
let sapis = SAPIS.get_or_init(|| {
HashMap::from_iter([
("apache2handler", Sapi::Apache2Handler),
Expand Down
Loading