Skip to content
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
15 changes: 15 additions & 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 @@ -22,6 +22,7 @@ cfg-if = { version = "1.0" }
cpu-time = { version = "1.0" }
chrono = { version = "0.4" }
crossbeam-channel = { version = "0.5", default-features = false, features = ["std"] }
dashmap = { version = "6.1" }
http = { version = "1.4" }
libdd-alloc = { git = "https://github.com/DataDog/libdatadog", tag = "v27.0.0" }
libdd-profiling = { git = "https://github.com/DataDog/libdatadog", tag = "v27.0.0" }
Expand Down
24 changes: 19 additions & 5 deletions profiling/src/allocation/allocation_ge84.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::allocation::{allocation_profiling_stats_should_collect, collect_allocation};
use crate::allocation::{allocation_profiling_stats_should_collect, collect_allocation, free_allocation};
use crate::bindings as zend;
use crate::PROFILER_NAME;
use core::ptr;
Expand Down Expand Up @@ -286,7 +286,7 @@ unsafe extern "C" fn alloc_prof_malloc(len: size_t) -> *mut c_void {
}

if allocation_profiling_stats_should_collect(len) {
collect_allocation(len);
collect_allocation(ptr, len);
}

ptr
Expand Down Expand Up @@ -316,6 +316,11 @@ unsafe fn alloc_prof_orig_alloc(len: size_t) -> *mut c_void {
/// custom handlers won't be installed. We cannot just point to the original
/// `zend::_zend_mm_free()` as the function definitions differ.
unsafe extern "C" fn alloc_prof_free(ptr: *mut c_void) {
// Check if this was a tracked allocation (before freeing!)
if !ptr.is_null() {
free_allocation(ptr);
}

tls_zend_mm_state_get!(free)(ptr);
}

Expand Down Expand Up @@ -348,12 +353,21 @@ unsafe extern "C" fn alloc_prof_realloc(prev_ptr: *mut c_void, len: size_t) -> *

// during startup, minit, rinit, ... current_execute_data is null
// we are only interested in allocations during userland operations
if zend::ddog_php_prof_get_current_execute_data().is_null() || ptr::eq(ptr, prev_ptr) {
if zend::ddog_php_prof_get_current_execute_data().is_null() {
return ptr;
}

if allocation_profiling_stats_should_collect(len) {
collect_allocation(len);
// If pointer changed, treat as free(old) + alloc(new)
if !ptr::eq(ptr, prev_ptr) {
// Untrack the old allocation if it was tracked
if !prev_ptr.is_null() {
free_allocation(prev_ptr);
}

// Sample the new allocation
if allocation_profiling_stats_should_collect(len) {
collect_allocation(ptr, len);
}
}

ptr
Expand Down
24 changes: 19 additions & 5 deletions profiling/src/allocation/allocation_le83.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::allocation::{allocation_profiling_stats_should_collect, collect_allocation};
use crate::allocation::{allocation_profiling_stats_should_collect, collect_allocation, free_allocation};
use crate::bindings::{
self as zend, datadog_php_install_handler, datadog_php_zif_handler,
ddog_php_prof_copy_long_into_zval,
Expand Down Expand Up @@ -300,7 +300,7 @@ unsafe extern "C" fn alloc_prof_malloc(len: size_t) -> *mut c_void {
}

if allocation_profiling_stats_should_collect(len) {
collect_allocation(len);
collect_allocation(ptr, len);
}

ptr
Expand Down Expand Up @@ -330,6 +330,11 @@ unsafe fn alloc_prof_orig_alloc(len: size_t) -> *mut c_void {
/// custom handlers won't be installed. We cannot just point to the original
/// `zend::_zend_mm_free()` as the function definitions differ.
unsafe extern "C" fn alloc_prof_free(ptr: *mut c_void) {
// Check if this was a tracked allocation (before freeing!)
if !ptr.is_null() {
free_allocation(ptr);
}

tls_zend_mm_state_get!(free)(ptr);
}

Expand Down Expand Up @@ -358,12 +363,21 @@ unsafe extern "C" fn alloc_prof_realloc(prev_ptr: *mut c_void, len: size_t) -> *

// during startup, minit, rinit, ... current_execute_data is null
// we are only interested in allocations during userland operations
if zend::ddog_php_prof_get_current_execute_data().is_null() || ptr::eq(ptr, prev_ptr) {
if zend::ddog_php_prof_get_current_execute_data().is_null() {
return ptr;
}

if allocation_profiling_stats_should_collect(len) {
collect_allocation(len);
// If pointer changed, treat as free(old) + alloc(new)
if !ptr::eq(ptr, prev_ptr) {
// Untrack the old allocation if it was tracked
if !prev_ptr.is_null() {
free_allocation(prev_ptr);
}

// Sample the new allocation
if allocation_profiling_stats_should_collect(len) {
collect_allocation(ptr, len);
}
}

ptr
Expand Down
16 changes: 15 additions & 1 deletion profiling/src/allocation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,13 @@ impl AllocationProfilingStats {
}
}

/// Collect an allocation sample and optionally track it for live heap profiling.
///
/// # Arguments
/// * `ptr` - The pointer returned by the allocator (used for live heap tracking)
/// * `len` - The size of the allocation in bytes
#[cold]
pub fn collect_allocation(len: size_t) {
pub fn collect_allocation(ptr: *mut c_void, len: size_t) {
if let Some(profiler) = Profiler::get() {
// Check if there's a pending time interrupt that we can handle now
// instead of waiting for an interrupt handler. This is slightly more
Expand All @@ -150,6 +155,7 @@ pub fn collect_allocation(len: size_t) {
unsafe {
profiler.collect_allocations(
zend::ddog_php_prof_get_current_execute_data(),
ptr,
1_i64,
len as i64,
(interrupt_count > 0).then_some(interrupt_count),
Expand All @@ -158,6 +164,14 @@ pub fn collect_allocation(len: size_t) {
}
}

/// Called when memory is freed. If this pointer was tracked for live heap,
/// sends the deallocation sample to cancel out the original allocation.
pub fn free_allocation(ptr: *mut c_void) {
if let Some(profiler) = Profiler::get() {
profiler.free_allocation(ptr);
}
}

#[cfg(not(php_zend_mm_set_custom_handlers_ex))]
pub fn alloc_prof_startup() {
allocation_le83::alloc_prof_startup();
Expand Down
29 changes: 29 additions & 0 deletions profiling/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub struct SystemSettings {
pub profiling_experimental_cpu_time_enabled: bool,
pub profiling_allocation_enabled: bool,
pub profiling_allocation_sampling_distance: NonZeroU32,
pub profiling_heap_live_enabled: bool,
pub profiling_timeline_enabled: bool,
pub profiling_exception_enabled: bool,
pub profiling_exception_message_enabled: bool,
Expand All @@ -69,6 +70,7 @@ impl SystemSettings {
profiling_experimental_cpu_time_enabled: false,
profiling_allocation_enabled: false,
profiling_allocation_sampling_distance: NonZeroU32::MAX,
profiling_heap_live_enabled: false,
profiling_timeline_enabled: false,
profiling_exception_enabled: false,
profiling_exception_message_enabled: false,
Expand Down Expand Up @@ -98,6 +100,7 @@ impl SystemSettings {
profiling_experimental_cpu_time_enabled: profiling_experimental_cpu_time_enabled(),
profiling_allocation_enabled: profiling_allocation_enabled(),
profiling_allocation_sampling_distance: profiling_allocation_sampling_distance(),
profiling_heap_live_enabled: profiling_heap_live_enabled(),
profiling_timeline_enabled: profiling_timeline_enabled(),
profiling_exception_enabled: profiling_exception_enabled(),
profiling_exception_message_enabled: profiling_exception_message_enabled(),
Expand Down Expand Up @@ -405,6 +408,7 @@ pub(crate) enum ConfigId {
ProfilingExperimentalCpuTimeEnabled,
ProfilingAllocationEnabled,
ProfilingAllocationSamplingDistance,
ProfilingHeapLiveEnabled,
ProfilingTimelineEnabled,
ProfilingExceptionEnabled,
ProfilingExceptionMessageEnabled,
Expand Down Expand Up @@ -437,6 +441,7 @@ impl ConfigId {
ProfilingExperimentalCpuTimeEnabled => b"DD_PROFILING_EXPERIMENTAL_CPU_TIME_ENABLED\0",
ProfilingAllocationEnabled => b"DD_PROFILING_ALLOCATION_ENABLED\0",
ProfilingAllocationSamplingDistance => b"DD_PROFILING_ALLOCATION_SAMPLING_DISTANCE\0",
ProfilingHeapLiveEnabled => b"DD_PROFILING_HEAP_LIVE_ENABLED\0",
ProfilingTimelineEnabled => b"DD_PROFILING_TIMELINE_ENABLED\0",
ProfilingExceptionEnabled => b"DD_PROFILING_EXCEPTION_ENABLED\0",
ProfilingExceptionMessageEnabled => b"DD_PROFILING_EXCEPTION_MESSAGE_ENABLED\0",
Expand Down Expand Up @@ -475,6 +480,7 @@ static DEFAULT_SYSTEM_SETTINGS: SystemSettings = SystemSettings {
profiling_allocation_enabled: true,
// SAFETY: value is > 0.
profiling_allocation_sampling_distance: unsafe { NonZeroU32::new_unchecked(1024 * 4096) },
profiling_heap_live_enabled: false,
profiling_timeline_enabled: true,
profiling_exception_enabled: true,
profiling_exception_message_enabled: false,
Expand Down Expand Up @@ -553,6 +559,17 @@ unsafe fn profiling_allocation_sampling_distance() -> NonZeroU32 {
unsafe { NonZeroU32::new_unchecked(int) }
}

/// # Safety
/// This function must only be called after config has been initialized in
/// rinit, and before it is uninitialized in mshutdown.
unsafe fn profiling_heap_live_enabled() -> bool {
profiling_allocation_enabled()
&& get_system_bool(
ProfilingHeapLiveEnabled,
DEFAULT_SYSTEM_SETTINGS.profiling_heap_live_enabled,
)
}

/// # Safety
/// This function must only be called after config has been initialized in
/// rinit, and before it is uninitialized in mshutdown.
Expand Down Expand Up @@ -1014,6 +1031,18 @@ pub(crate) fn minit(module_number: libc::c_int) {
displayer: None,
env_config_fallback: None,
},
zai_config_entry {
id: transmute::<ConfigId, u16>(ProfilingHeapLiveEnabled),
name: ProfilingHeapLiveEnabled.env_var_name(),
type_: ZAI_CONFIG_TYPE_BOOL,
default_encoded_value: ZaiStr::literal(b"0\0"),
aliases: ptr::null_mut(),
aliases_count: 0,
ini_change: Some(zai_config_system_ini_change),
parser: None,
displayer: None,
env_config_fallback: None,
},
zai_config_entry {
id: transmute::<ConfigId, u16>(ProfilingTimelineEnabled),
name: ProfilingTimelineEnabled.env_var_name(),
Expand Down
13 changes: 13 additions & 0 deletions profiling/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,19 @@ unsafe extern "C" fn minfo(module_ptr: *mut zend::ModuleEntry) {
no_all
}
);
zend::php_info_print_table_row(
2,
c"Heap Live Profiling Enabled".as_ptr(),
if system_settings.profiling_heap_live_enabled {
yes
} else if !system_settings.profiling_allocation_enabled {
c"false (requires allocation profiling)".as_ptr()
} else if system_settings.profiling_enabled {
no
} else {
no_all
},
);
zend::php_info_print_table_row(
2,
c"Timeline Enabled".as_ptr(),
Expand Down
2 changes: 1 addition & 1 deletion profiling/src/profiling/backtrace.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::profiling::stack_walking::ZendFrame;
use core::ops::Deref;

#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct Backtrace {
frames: Vec<ZendFrame>,
}
Expand Down
Loading
Loading