use std::{
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher},
};
use kithara_events::{AbrMode, CancelReason, RequestId, RequestPriority};
use kithara_platform::time::Duration;
use url::Url;
pub trait Probe {
fn record_probe(&self, name: &'static str);
}
pub trait IntoProbeArg: Copy {
#[must_use]
fn from_probe_arg(packed: u64) -> Self {
let _ = packed;
unimplemented!(
"{} did not implement IntoProbeArg::from_probe_arg — \
override the trait method on the type whose packed `u64` \
you are trying to decode (or the test reads the wrong field)",
std::any::type_name::<Self>(),
)
}
fn into_probe_arg(self) -> u64;
}
macro_rules! impl_int_probe_arg {
($($ty:ty),* $(,)?) => {
$(
impl IntoProbeArg for $ty {
fn into_probe_arg(self) -> u64 {
num_traits::AsPrimitive::<u64>::as_(self)
}
fn from_probe_arg(packed: u64) -> Self {
num_traits::AsPrimitive::<Self>::as_(packed)
}
}
)*
};
}
impl_int_probe_arg!(u64, i64, u32, i32, usize);
impl IntoProbeArg for bool {
fn from_probe_arg(packed: u64) -> Self {
packed != 0
}
fn into_probe_arg(self) -> u64 {
u64::from(self)
}
}
impl IntoProbeArg for Duration {
fn from_probe_arg(packed: u64) -> Self {
Self::from_micros(packed)
}
fn into_probe_arg(self) -> u64 {
u64::try_from(self.as_micros()).unwrap_or(u64::MAX)
}
}
impl IntoProbeArg for &Url {
fn into_probe_arg(self) -> u64 {
let mut hasher = DefaultHasher::new();
self.as_str().hash(&mut hasher);
hasher.finish()
}
}
impl IntoProbeArg for RequestId {
fn into_probe_arg(self) -> u64 {
self.get()
}
}
fn request_priority_wire(p: RequestPriority) -> u64 {
match p {
RequestPriority::High => 0,
RequestPriority::Low => 1,
}
}
impl IntoProbeArg for RequestPriority {
fn into_probe_arg(self) -> u64 {
request_priority_wire(self)
}
}
fn cancel_reason_wire(r: CancelReason) -> u64 {
const EPOCH_CANCEL: u64 = 0;
const PEER_CANCEL: u64 = 1;
const DOWNLOADER_SHUTDOWN: u64 = 2;
const BEFORE_START: u64 = 3;
match r {
CancelReason::EpochCancel => EPOCH_CANCEL,
CancelReason::PeerCancel => PEER_CANCEL,
CancelReason::DownloaderShutdown => DOWNLOADER_SHUTDOWN,
CancelReason::BeforeStart => BEFORE_START,
}
}
impl IntoProbeArg for CancelReason {
fn into_probe_arg(self) -> u64 {
cancel_reason_wire(self)
}
}
impl IntoProbeArg for AbrMode {
fn into_probe_arg(self) -> u64 {
num_traits::AsPrimitive::<u64>::as_(usize::from(self))
}
}
impl<T: IntoProbeArg> IntoProbeArg for Option<T> {
fn into_probe_arg(self) -> u64 {
self.map_or(u64::MAX, |value| {
let raw = value.into_probe_arg();
debug_assert!(
raw != u64::MAX,
"Option<T>::None sentinel collides with Some(value) producing u64::MAX"
);
raw
})
}
}
pub fn register_probes() {
imp::register();
}
#[cfg(not(target_arch = "wasm32"))]
#[must_use]
pub fn caller_fn_above(probe_fn_name: &str) -> Option<String> {
let mut found_self = false;
let mut result: Option<String> = None;
backtrace::trace(|frame| {
if result.is_some() {
return false;
}
let mut symbol_seen = false;
backtrace::resolve_frame(frame, |symbol| {
if result.is_some() || symbol_seen {
return;
}
symbol_seen = true;
let Some(raw_name) = symbol.name() else {
return;
};
let demangled = format!("{raw_name}");
let trimmed = demangled
.rsplit_once("::h")
.map_or(demangled.as_str(), |(head, _)| head);
if trimmed.starts_with("kithara_test_utils::probe::")
|| trimmed.starts_with("backtrace::")
|| trimmed.starts_with("std::backtrace::")
{
return;
}
if !found_self {
if trimmed.contains(probe_fn_name) {
found_self = true;
}
return;
}
result = Some(trimmed.to_string());
});
result.is_none()
});
result
}
#[cfg(target_arch = "wasm32")]
pub fn caller_fn_above(_probe_fn_name: &str) -> Option<String> {
None
}
pub fn next_probe_seq() -> u64 {
use std::sync::atomic::{AtomicU64, Ordering};
static SEQ: AtomicU64 = AtomicU64::new(0);
SEQ.fetch_add(1, Ordering::Relaxed)
}
#[must_use]
pub fn next_thread_probe_seq() -> u64 {
use std::cell::Cell;
thread_local! {
static SEQ: Cell<u64> = const { Cell::new(0) };
}
SEQ.with(|cell| {
let v = cell.get();
cell.set(v.wrapping_add(1));
v
})
}
use std::sync::atomic::{AtomicU64, Ordering as AtomicOrdering};
static INSTALL_ID: AtomicU64 = AtomicU64::new(0);
#[cfg(not(target_arch = "wasm32"))]
kithara_platform::tokio::task_local! {
pub static OWNED_INSTALL_ID: u64;
}
#[must_use]
pub fn current_install_id() -> u64 {
#[cfg(not(target_arch = "wasm32"))]
{
if let Ok(id) = OWNED_INSTALL_ID.try_with(|id| *id) {
return id;
}
}
INSTALL_ID.load(AtomicOrdering::Relaxed)
}
#[must_use]
pub fn bump_install_id() -> u64 {
INSTALL_ID.fetch_add(1, AtomicOrdering::Relaxed) + 1
}
#[must_use]
pub fn current_thread_u64() -> u64 {
let mut hasher = DefaultHasher::new();
std::thread::current().id().hash(&mut hasher);
hasher.finish()
}
#[cfg(all(not(target_arch = "wasm32"), feature = "probe"))]
mod imp {
use std::sync::OnceLock;
static REGISTERED: OnceLock<()> = OnceLock::new();
pub(super) fn register() {
REGISTERED.get_or_init(|| {
let _ = usdt::register_probes();
});
}
}
#[cfg(any(target_arch = "wasm32", not(feature = "probe")))]
mod imp {
pub(super) fn register() {}
}