diff --git a/libdd-common/Cargo.toml b/libdd-common/Cargo.toml index e2e5520c36..056d30374c 100644 --- a/libdd-common/Cargo.toml +++ b/libdd-common/Cargo.toml @@ -47,7 +47,7 @@ const_format = "0.2.34" nix = { version = "0.29", features = ["process"] } [target.'cfg(windows)'.dependencies.windows-sys] version = "0.52" -features = ["Win32_Foundation", "Win32_System_Performance"] +features = ["Win32_Foundation", "Win32_System_Performance", "Win32_System_Threading"] [target.'cfg(unix)'.dependencies] rustls = { version = "0.23", default-features = false, optional = true, features = ["aws-lc-rs"] } diff --git a/libdd-common/src/lib.rs b/libdd-common/src/lib.rs index b7e69af08e..f5e2af179d 100644 --- a/libdd-common/src/lib.rs +++ b/libdd-common/src/lib.rs @@ -28,6 +28,7 @@ pub mod rate_limiter; pub mod tag; #[cfg(any(test, feature = "test-utils"))] pub mod test_utils; +pub mod threading; pub mod timeout; pub mod unix_utils; pub mod worker; diff --git a/libdd-common/src/threading.rs b/libdd-common/src/threading.rs new file mode 100644 index 0000000000..c2ae43e468 --- /dev/null +++ b/libdd-common/src/threading.rs @@ -0,0 +1,36 @@ +// Copyright 2026-Present Datadog, Inc. https://www.datadoghq.com/ +// SPDX-License-Identifier: Apache-2.0 + +/// Returns a numeric identifier for the current OS thread. +#[cfg(target_os = "linux")] +pub fn get_current_thread_id() -> i64 { + // SAFETY: syscall(SYS_gettid) has no preconditions for current thread. + unsafe { libc::syscall(libc::SYS_gettid) as i64 } +} + +/// Returns a numeric identifier for the current OS thread. +#[cfg(target_os = "macos")] +pub fn get_current_thread_id() -> i64 { + let mut tid: u64 = 0; + // SAFETY: `pthread_threadid_np` has no preconditions for current thread + // when pthread_t is 0 and output pointer is valid. + let rc = unsafe { libc::pthread_threadid_np(0, &mut tid) }; + debug_assert_eq!( + rc, + 0, + "pthread_threadid_np failed: {rc} ({})", + std::io::Error::from_raw_os_error(rc) + ); + tid as i64 +} + +/// Returns a numeric identifier for the current OS thread. +#[cfg(target_os = "windows")] +pub fn get_current_thread_id() -> i64 { + // SAFETY: GetCurrentThreadId has no preconditions. + unsafe { windows_sys::Win32::System::Threading::GetCurrentThreadId() as i64 } +} + +/// Returns a numeric identifier for the current OS thread. +#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))] +compile_error!("libdd_common::threading::get_current_thread_id is unsupported on this platform"); diff --git a/libdd-profiling/benches/add_samples.rs b/libdd-profiling/benches/add_samples.rs index 6f43e0f828..fc27d886b3 100644 --- a/libdd-profiling/benches/add_samples.rs +++ b/libdd-profiling/benches/add_samples.rs @@ -2,8 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use criterion::*; +use libdd_common::threading::get_current_thread_id; use libdd_profiling::api2::Location2; -use libdd_profiling::profiles::datatypes::{Function, FunctionId2, MappingId2}; +use libdd_profiling::profiles::datatypes::{Function, FunctionId2, MappingId2, StringId2}; use libdd_profiling::{self as profiling, api, api2}; fn make_sample_types() -> Vec { @@ -103,6 +104,14 @@ pub fn bench_add_sample_vs_add2(c: &mut Criterion) { let strings = dict.strings(); let functions = dict.functions(); + let thread_id = get_current_thread_id(); + let thread_id_key: StringId2 = strings.try_insert("thread id").unwrap().into(); + let labels_api = vec![api::Label { + key: "thread id", + str: "", + num: thread_id, + num_unit: "", + }]; let frames2 = frames.map(|f| { let set_id = functions @@ -127,7 +136,7 @@ pub fn bench_add_sample_vs_add2(c: &mut Criterion) { let sample = api::Sample { locations: locations.clone(), values: &values, - labels: vec![], + labels: labels_api.clone(), }; black_box(profile.try_add_sample(sample, None)).unwrap(); } @@ -145,8 +154,7 @@ pub fn bench_add_sample_vs_add2(c: &mut Criterion) { .unwrap(); let (locations, values) = make_stack_api2(frames2.as_slice()); for _ in 0..1000 { - // Provide an empty iterator for labels conversion path - let labels_iter = std::iter::empty::>(); + let labels_iter = [Ok(api2::Label::num(thread_id_key, thread_id, ""))].into_iter(); // SAFETY: all ids come from the profile's dictionary. black_box(unsafe { profile.try_add_sample2(&locations, &values, labels_iter, None)