diff --git a/Cargo.lock b/Cargo.lock index c79063463eb..589e0516690 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1483,6 +1483,7 @@ dependencies = [ "crossbeam-channel", "datadog-alloc", "datadog-profiling", + "datadog-thin-str", "ddcommon 10.0.0", "env_logger 0.11.3", "indexmap 2.2.6", @@ -1653,6 +1654,15 @@ dependencies = [ "syn 2.0.71", ] +[[package]] +name = "datadog-thin-str" +version = "1.0.0" +dependencies = [ + "allocator-api2", + "naughty-strings", + "tagged-pointer", +] + [[package]] name = "datadog-trace-normalization" version = "0.0.1" @@ -3327,6 +3337,15 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "naughty-strings" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77f60b021f91344453a569296aced9b149bdf75ac182cfc4631f6b55a5aa590f" +dependencies = [ + "serde_json", +] + [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -5057,6 +5076,12 @@ dependencies = [ "libc 0.2.159", ] +[[package]] +name = "tagged-pointer" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538c399a85621c9b74ac21001d7e1dd81867ebd076724f047b1227e722de4a7" + [[package]] name = "tarpc" version = "0.31.0" diff --git a/profiling/Cargo.toml b/profiling/Cargo.toml index 2f0794e1864..59b94e06e6c 100644 --- a/profiling/Cargo.toml +++ b/profiling/Cargo.toml @@ -21,6 +21,7 @@ chrono = { version = "0.4" } crossbeam-channel = { version = "0.5", default-features = false, features = ["std"] } datadog-alloc = { git = "https://github.com/DataDog/libdatadog", tag = "v10.0.0" } datadog-profiling = { git = "https://github.com/DataDog/libdatadog", tag = "v10.0.0" } +datadog-thin-str = { version = "1", path = "../thin-str", features = ["std"] } ddcommon = { git = "https://github.com/DataDog/libdatadog", tag = "v10.0.0" } env_logger = { version = "0.11", default-features = false } indexmap = { version = "2.2" } @@ -46,6 +47,10 @@ perfcnt = "0.8.0" name = "stack_walking" harness = false +[[bench]] +name = "concatenation" +harness = false + [features] default = ["allocation_profiling", "timeline", "exception_profiling"] allocation_profiling = [] diff --git a/profiling/benches/concatenation.rs b/profiling/benches/concatenation.rs new file mode 100644 index 00000000000..5d9e4c22da4 --- /dev/null +++ b/profiling/benches/concatenation.rs @@ -0,0 +1,83 @@ +use criterion::{criterion_group, criterion_main, Criterion}; + +#[allow(unused)] +fn extract_function_name_v1(module_name: &[u8], class_name: &[u8], method_name: &[u8]) -> Vec { + let mut buffer = Vec::::new(); + + if !module_name.is_empty() { + buffer.extend_from_slice(module_name); + buffer.push(b'|'); + } + + if !class_name.is_empty() { + buffer.extend_from_slice(class_name); + buffer.extend_from_slice(b"::"); + } + buffer.extend_from_slice(method_name); + buffer +} + +#[allow(unused)] +fn extract_function_name_v2(module_name: &[u8], class_name: &[u8], method_name: &[u8]) -> Vec { + let opt_module_separator: &[u8] = if module_name.is_empty() { b"" } else { b"|" }; + let opt_class_separator: &[u8] = if class_name.is_empty() { b"" } else { b"::" }; + let cap = module_name.len() + + opt_module_separator.len() + + class_name.len() + + opt_class_separator.len() + + method_name.len(); + let mut buffer = Vec::::with_capacity(cap); + + buffer.extend_from_slice(module_name); + buffer.extend_from_slice(opt_module_separator); + buffer.extend_from_slice(class_name); + buffer.extend_from_slice(opt_class_separator); + buffer.extend_from_slice(method_name); + buffer +} + +fn bench_concatenation_userland(c: &mut Criterion) { + c.bench_function("bench_concatenation_userland", |b| { + b.iter(|| { + for _ in 1..=100 { + _ = std::hint::black_box(extract_function_name_v2( + b"", + b"Twig\\Template", + b"displayWithErrorHandling", + )) + } + }); + }); +} + +fn bench_concatenation_internal1(c: &mut Criterion) { + c.bench_function("bench_concatenation_internal1", |b| { + b.iter(|| { + for _ in 1..=100 { + _ = std::hint::black_box(extract_function_name_v2( + b"dom", + b"DOMDocumentFragment", + b"__construct", + )) + } + }); + }); +} + +fn bench_concatenation_internal2(c: &mut Criterion) { + c.bench_function("bench_concatenation_internal2", |b| { + b.iter(|| { + for _ in 1..=100 { + _ = std::hint::black_box(extract_function_name_v2(b"standard", b"", b"file")) + } + }); + }); +} + +criterion_group!( + benches, + bench_concatenation_userland, + bench_concatenation_internal1, + bench_concatenation_internal2, +); +criterion_main!(benches); diff --git a/profiling/src/lib.rs b/profiling/src/lib.rs index 3b3db288a02..b663a670a7b 100644 --- a/profiling/src/lib.rs +++ b/profiling/src/lib.rs @@ -6,8 +6,8 @@ mod logging; mod pcntl; pub mod profiling; mod sapi; -mod thin_str; mod wall_time; +mod well_known; #[cfg(php_run_time_cache)] mod string_set; @@ -313,7 +313,7 @@ extern "C" fn minit(_type: c_int, module_number: c_int) -> ZendResult { }; /* Currently, the engine is always copying this struct. Every time a new - * PHP version is released, we should double check zend_register_extension + * PHP version is released, we should double-check zend_register_extension * to ensure the address is not mutated nor stored. Well, hopefully we * catch it _before_ a release. */ diff --git a/profiling/src/profiling/mod.rs b/profiling/src/profiling/mod.rs index 7685e0cc52f..43d6a424909 100644 --- a/profiling/src/profiling/mod.rs +++ b/profiling/src/profiling/mod.rs @@ -17,6 +17,7 @@ use crate::bindings::ddog_php_prof_get_active_fiber_test as ddog_php_prof_get_ac use crate::bindings::{datadog_php_profiling_get_profiling_context, zend_execute_data}; use crate::config::SystemSettings; +use crate::well_known::WellKnown; use crate::{CLOCKS, TAGS}; use chrono::Utc; use core::{ptr, str}; @@ -26,6 +27,7 @@ use datadog_profiling::api::{ }; use datadog_profiling::exporter::Tag; use datadog_profiling::internal::Profile as InternalProfile; +use datadog_thin_str::ThinString; use log::{debug, info, trace, warn}; use once_cell::sync::OnceCell; use std::borrow::Cow; @@ -49,6 +51,9 @@ use datadog_profiling::api::UpscalingInfo; #[cfg(feature = "exception_profiling")] use crate::exception::EXCEPTION_PROFILING_INTERVAL; +#[cfg(feature = "timeline")] +use crate::timeline::IncludeType; +use crate::timeline::State; const UPLOAD_PERIOD: Duration = Duration::from_secs(67); @@ -514,9 +519,6 @@ pub enum UploadMessage { Upload(Box), } -#[cfg(feature = "timeline")] -const COW_EVAL: Cow = Cow::Borrowed("[eval]"); - const DDPROF_TIME: &str = "ddprof_time"; const DDPROF_UPLOAD: &str = "ddprof_upload"; @@ -901,20 +903,23 @@ impl Profiler { } #[cfg(feature = "timeline")] - const TIMELINE_COMPILE_FILE_LABELS: &'static [Label] = &[Label { - key: "event", - value: LabelValue::Str(Cow::Borrowed("compilation")), - }]; + pub fn timeline_compile_file_labels(extra_labels: usize) -> Vec