From 58d1deb3edac9f4452ea4c57da3f44c7c767db3e Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Mon, 23 Oct 2023 21:15:44 +0200 Subject: [PATCH] add allocation and exception count to `event.json` --- Cargo.lock | 1 + profiling/Cargo.toml | 1 + profiling/src/allocation.rs | 17 +++++++++++++++++ profiling/src/exception.rs | 9 ++++++++- profiling/src/profiling/uploader.rs | 27 +++++++++++++++++++++++++-- 5 files changed, 52 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 982fe7ed45..8b35ccc140 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -702,6 +702,7 @@ dependencies = [ "perfcnt", "rand 0.8.5", "rand_distr", + "serde_json", "uuid", ] diff --git a/profiling/Cargo.toml b/profiling/Cargo.toml index 90e806c3bc..24881ad25e 100644 --- a/profiling/Cargo.toml +++ b/profiling/Cargo.toml @@ -28,6 +28,7 @@ libc = "0.2" log = { version = "0.4", features = ["max_level_trace", "release_max_level_trace"]} once_cell = { version = "1.12" } ouroboros = { version = "0.17.0" } +serde_json = {version = "1.0"} rand = { version = "0.8.5" } rand_distr = { version = "0.4.3" } uuid = { version = "1.0", features = ["v4"] } diff --git a/profiling/src/allocation.rs b/profiling/src/allocation.rs index 4e1ec8eacb..4813cc385e 100644 --- a/profiling/src/allocation.rs +++ b/profiling/src/allocation.rs @@ -8,6 +8,8 @@ use log::{debug, error, trace, warn}; use rand::rngs::ThreadRng; use std::cell::RefCell; use std::ffi::CStr; +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering::SeqCst; use rand_distr::{Distribution, Poisson}; @@ -36,6 +38,15 @@ pub fn allocation_profiling_minit() { /// take a sample every 4096 KiB pub const ALLOCATION_PROFILING_INTERVAL: f64 = 1024.0 * 4096.0; +/// this will store the count of allocations (including reallocations) during a profiling period. +/// This will overflow when doing more then u64::MAX allocations, which seems big enough to ignore. +pub static ALLOCATION_PROFILING_COUNT: AtomicU64 = AtomicU64::new(0); + +/// this will store the accumulated size of all allocations in bytes during the profiling period. +/// This will overflow when allocating more then 18 exabyte of memory (u64::MAX) which might not +/// happen, so we can ignore this +pub static ALLOCATION_PROFILING_SIZE: AtomicU64 = AtomicU64::new(0); + pub struct AllocationProfilingStats { /// number of bytes until next sample collection next_sample: i64, @@ -326,6 +337,9 @@ unsafe extern "C" fn alloc_profiling_gc_mem_caches( } unsafe extern "C" fn alloc_profiling_malloc(len: size_t) -> *mut c_void { + ALLOCATION_PROFILING_COUNT.fetch_add(1, SeqCst); + ALLOCATION_PROFILING_SIZE.fetch_add(len as u64, SeqCst); + let ptr: *mut c_void = ALLOCATION_PROFILING_ALLOC(len); // during startup, minit, rinit, ... current_execute_data is null @@ -379,6 +393,9 @@ unsafe fn allocation_profiling_orig_free(ptr: *mut c_void) { } unsafe extern "C" fn alloc_profiling_realloc(prev_ptr: *mut c_void, len: size_t) -> *mut c_void { + ALLOCATION_PROFILING_COUNT.fetch_add(1, SeqCst); + ALLOCATION_PROFILING_SIZE.fetch_add(len as u64, SeqCst); + let ptr: *mut c_void = ALLOCATION_PROFILING_REALLOC(prev_ptr, len); // during startup, minit, rinit, ... current_execute_data is null diff --git a/profiling/src/exception.rs b/profiling/src/exception.rs index 17124ee559..69420f5003 100644 --- a/profiling/src/exception.rs +++ b/profiling/src/exception.rs @@ -12,9 +12,14 @@ use rand_distr::{Distribution, Poisson}; /// The engine's previous throw exception hook static mut PREV_ZEND_THROW_EXCEPTION_HOOK: Option = None; -/// take a sample every 100 exceptions +/// Take a sample every 100 exceptions pub static EXCEPTION_PROFILING_INTERVAL: AtomicU32 = AtomicU32::new(100); +/// This will store the number of exceptions thrown during a profiling period. It will overflow +/// when throwing more then 4_294_967_295 exceptions during this period which we currently +/// believe will bring down your application anyway, so accurate numbers are not a problem. +pub static EXCEPTION_PROFILING_EXCEPTION_COUNT: AtomicU32 = AtomicU32::new(0); + pub struct ExceptionProfilingStats { /// number of exceptions until next sample collection next_sample: u32, @@ -109,6 +114,8 @@ unsafe extern "C" fn exception_profiling_throw_exception_hook( #[cfg(php7)] exception: *mut zend::zval, #[cfg(php8)] exception: *mut zend::zend_object, ) { + EXCEPTION_PROFILING_EXCEPTION_COUNT.fetch_add(1, Ordering::SeqCst); + let exception_profiling = REQUEST_LOCALS.with(|cell| { cell.try_borrow() .map(|locals| locals.profiling_experimental_exception_enabled) diff --git a/profiling/src/profiling/uploader.rs b/profiling/src/profiling/uploader.rs index 8e70b852de..c109021a8a 100644 --- a/profiling/src/profiling/uploader.rs +++ b/profiling/src/profiling/uploader.rs @@ -1,10 +1,14 @@ +use crate::allocation::{ALLOCATION_PROFILING_COUNT, ALLOCATION_PROFILING_SIZE}; +use crate::exception::EXCEPTION_PROFILING_EXCEPTION_COUNT; use crate::profiling::{UploadMessage, UploadRequest}; use crate::{PROFILER_NAME_STR, PROFILER_VERSION_STR}; use crossbeam_channel::{select, Receiver}; use datadog_profiling::exporter::{Endpoint, File}; use log::{debug, info, warn}; +use serde_json::json; use std::borrow::Cow; use std::str; +use std::sync::atomic::Ordering; use std::sync::{Arc, Barrier}; use std::time::Duration; @@ -27,6 +31,17 @@ impl Uploader { } } + /// This function will not only create the internal metadata JSON representation, but is also + /// in charge to reset all those counters back to 0. + fn create_internal_metadata() -> Option { + let metadata = json!({ + "exceptions_count": EXCEPTION_PROFILING_EXCEPTION_COUNT.swap(0, Ordering::SeqCst), + "allocations_count": ALLOCATION_PROFILING_COUNT.swap(0, Ordering::SeqCst), + "allocations_size": ALLOCATION_PROFILING_SIZE.swap(0, Ordering::SeqCst), + }); + Some(metadata) + } + fn upload(message: UploadRequest) -> anyhow::Result { let index = message.index; let profile = message.profile; @@ -55,8 +70,16 @@ impl Uploader { bytes: serialized.buffer.as_slice(), }]; let timeout = Duration::from_secs(10); - let request = - exporter.build(start, end, &[], files, None, endpoint_counts, None, timeout)?; + let request = exporter.build( + start, + end, + &[], + files, + None, + endpoint_counts, + Self::create_internal_metadata(), + timeout, + )?; debug!("Sending profile to: {}", index.endpoint); let result = exporter.send(request, None)?; Ok(result.status().as_u16())