Skip to content

Commit

Permalink
add allocation and exception count to event.json
Browse files Browse the repository at this point in the history
  • Loading branch information
realFlowControl committed Nov 27, 2023
1 parent 3b04614 commit 58d1deb
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions profiling/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
17 changes: 17 additions & 0 deletions profiling/src/allocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion profiling/src/exception.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@ use rand_distr::{Distribution, Poisson};
/// The engine's previous throw exception hook
static mut PREV_ZEND_THROW_EXCEPTION_HOOK: Option<zend::VmZendThrowExceptionHook> = 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,
Expand Down Expand Up @@ -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)
Expand Down
27 changes: 25 additions & 2 deletions profiling/src/profiling/uploader.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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<serde_json::Value> {
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<u16> {
let index = message.index;
let profile = message.profile;
Expand Down Expand Up @@ -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())
Expand Down

0 comments on commit 58d1deb

Please sign in to comment.