use std::{
collections::HashMap,
fmt,
sync::{
atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicUsize, Ordering},
Arc, OnceLock, RwLock,
},
time::Instant,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct BacktraceOptions {
pub max_captures_per_second: u32,
pub max_resolutions_per_second: u32,
}
impl BacktraceOptions {
const SAFE_CAPTURES_PER_SECOND: u32 = 1_000;
const SAFE_RESOLUTIONS_PER_SECOND: u32 = 5;
}
impl Default for BacktraceOptions {
fn default() -> Self {
if rust_backtrace_enabled() {
Self {
max_captures_per_second: Self::SAFE_CAPTURES_PER_SECOND,
max_resolutions_per_second: Self::SAFE_RESOLUTIONS_PER_SECOND,
}
} else {
Self {
max_captures_per_second: 0,
max_resolutions_per_second: 0,
}
}
}
}
pub fn set_backtrace_options(options: BacktraceOptions) {
PROGRAMMATIC_OVERRIDE.store(true, Ordering::Release);
global_capture_throttle().set_capacity(options.max_captures_per_second);
global_resolution_limiter().set_capacity(options.max_resolutions_per_second);
}
pub(crate) fn ensure_initialized() {
ENV_INIT_DONE.get_or_init(|| {
if !PROGRAMMATIC_OVERRIDE.load(Ordering::Acquire) {
let options = resolve_from_env();
global_capture_throttle().set_capacity(options.max_captures_per_second);
global_resolution_limiter().set_capacity(options.max_resolutions_per_second);
}
});
}
fn resolve_from_env() -> BacktraceOptions {
let defaults = BacktraceOptions::default();
BacktraceOptions {
max_captures_per_second: env_u32(
"AZURE_COSMOS_BACKTRACE_CAPTURES_PER_SECOND",
defaults.max_captures_per_second,
),
max_resolutions_per_second: env_u32(
"AZURE_COSMOS_BACKTRACE_RESOLUTIONS_PER_SECOND",
defaults.max_resolutions_per_second,
),
}
}
fn env_u32(name: &str, default: u32) -> u32 {
parse_env_u32(std::env::var(name).ok().as_deref(), default)
}
fn parse_env_u32(raw: Option<&str>, default: u32) -> u32 {
raw.and_then(|s| s.trim().parse::<u32>().ok())
.unwrap_or(default)
}
static PROGRAMMATIC_OVERRIDE: AtomicBool = AtomicBool::new(false);
static ENV_INIT_DONE: OnceLock<()> = OnceLock::new();
pub(crate) fn rust_backtrace_enabled() -> bool {
static ENABLED: OnceLock<bool> = OnceLock::new();
*ENABLED.get_or_init(|| {
fn var_is_on(name: &str) -> Option<bool> {
match std::env::var(name) {
Ok(value) => Some(!value.is_empty() && value != "0"),
Err(_) => None,
}
}
var_is_on("RUST_LIB_BACKTRACE")
.or_else(|| var_is_on("RUST_BACKTRACE"))
.unwrap_or(false)
})
}
const WINDOW_SECS: u64 = 1;
const DEFAULT_FRAME_CACHE_SOFT_CAP: usize = 100_000;
static FRAME_CACHE_SOFT_CAP: AtomicUsize = AtomicUsize::new(DEFAULT_FRAME_CACHE_SOFT_CAP);
#[derive(Clone)]
pub struct Backtrace {
inner: Arc<BacktraceInner>,
}
struct BacktraceInner {
ips: Vec<usize>,
rendered: OnceLock<Option<Arc<str>>>,
}
#[derive(Clone, Debug)]
struct ResolvedFrame {
ip: usize,
symbol: Option<String>,
filename: Option<String>,
lineno: Option<u32>,
}
impl Backtrace {
pub(crate) fn capture() -> Option<Self> {
ensure_initialized();
if !global_capture_throttle().try_acquire() {
return None;
}
let mut ips: Vec<usize> = Vec::with_capacity(64);
backtrace::trace(|frame| {
ips.push(frame.ip() as usize);
true
});
if ips.is_empty() {
return None;
}
Some(Self {
inner: Arc::new(BacktraceInner {
ips,
rendered: OnceLock::new(),
}),
})
}
pub(crate) fn rendered(&self) -> Option<&Arc<str>> {
self.inner
.rendered
.get_or_init(|| try_render(&self.inner.ips).map(Arc::<str>::from))
.as_ref()
}
}
impl fmt::Debug for Backtrace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Backtrace")
.field("frame_count", &self.inner.ips.len())
.field("rendered", &self.inner.rendered.get().map(Option::is_some))
.finish()
}
}
fn try_render(ips: &[usize]) -> Option<String> {
let frames = try_resolve_frames(ips)?;
let mut out = String::with_capacity(frames.len() * 64);
for (i, frame) in frames.iter().enumerate() {
use fmt::Write;
let _ = write!(out, "{i:4}: ");
match frame.symbol.as_deref() {
Some(sym) => out.push_str(sym),
None => {
let _ = write!(out, "<unknown> @ 0x{:x}", frame.ip);
}
}
if let Some(file) = frame.filename.as_deref() {
let _ = write!(out, "\n at {file}");
if let Some(line) = frame.lineno {
let _ = write!(out, ":{line}");
}
}
out.push('\n');
}
Some(out)
}
fn try_resolve_frames(ips: &[usize]) -> Option<Vec<ResolvedFrame>> {
ensure_initialized();
let mut out: Vec<Option<ResolvedFrame>> = Vec::with_capacity(ips.len());
let mut missing: Vec<(usize, usize)> = Vec::new();
{
let cache = frame_cache().read().unwrap();
for (idx, &ip) in ips.iter().enumerate() {
match cache.get(&ip) {
Some(frame) => out.push(Some((**frame).clone())),
None => {
out.push(None);
missing.push((idx, ip));
}
}
}
}
if !missing.is_empty() {
if !global_resolution_limiter().try_acquire() {
return None;
}
let mut resolved: Vec<(usize, Arc<ResolvedFrame>)> = Vec::with_capacity(missing.len());
for (idx, ip) in &missing {
resolved.push((*idx, Arc::new(resolve_single(*ip))));
}
let evicted = {
let mut cache = frame_cache().write().unwrap();
let evicted = if cache.len() >= FRAME_CACHE_SOFT_CAP.load(Ordering::Relaxed) {
Some(std::mem::take(&mut *cache))
} else {
None
};
for (idx, frame) in resolved {
let cached = cache
.entry(frame.ip)
.or_insert_with(|| frame.clone())
.clone();
out[idx] = Some((*cached).clone());
}
evicted
};
if let Some(evicted) = evicted {
std::thread::Builder::new()
.name("cosmos-backtrace-cache-evict".into())
.spawn(move || drop(evicted))
.map(drop)
.unwrap_or_else(|_| {
});
}
}
Some(
out.into_iter()
.map(|f| {
debug_assert!(f.is_some(), "all frame slots must be filled");
f.unwrap_or(ResolvedFrame {
ip: 0,
symbol: None,
filename: None,
lineno: None,
})
})
.collect(),
)
}
fn resolve_single(ip: usize) -> ResolvedFrame {
let mut frame = ResolvedFrame {
ip,
symbol: None,
filename: None,
lineno: None,
};
backtrace::resolve(ip as *mut std::ffi::c_void, |sym| {
if frame.symbol.is_none() {
frame.symbol = sym.name().map(|n| n.to_string());
}
if frame.filename.is_none() {
frame.filename = sym
.filename()
.and_then(|p| p.to_str().map(|s| s.to_owned()));
}
if frame.lineno.is_none() {
frame.lineno = sym.lineno();
}
});
frame
}
fn frame_cache() -> &'static RwLock<HashMap<usize, Arc<ResolvedFrame>>> {
static CACHE: OnceLock<RwLock<HashMap<usize, Arc<ResolvedFrame>>>> = OnceLock::new();
CACHE.get_or_init(|| RwLock::new(HashMap::new()))
}
#[cfg(test)]
pub(crate) fn clear_frame_cache_for_tests() {
frame_cache().write().unwrap().clear();
}
#[cfg(test)]
pub(crate) fn frame_cache_contains_for_tests(ip: usize) -> bool {
frame_cache().read().unwrap().contains_key(&ip)
}
#[cfg(test)]
pub(crate) fn frame_cache_len_for_tests() -> usize {
frame_cache().read().unwrap().len()
}
#[cfg(test)]
pub(crate) fn set_frame_cache_soft_cap_for_tests(cap: usize) -> usize {
FRAME_CACHE_SOFT_CAP.swap(cap, Ordering::Relaxed)
}
pub struct BacktraceCaptureLimiter {
capacity: AtomicU32,
state: AtomicU64,
}
impl BacktraceCaptureLimiter {
const fn new_disabled() -> Self {
Self {
capacity: AtomicU32::new(0),
state: AtomicU64::new(0),
}
}
#[cfg(any(test, feature = "__internal_backtrace_bench"))]
pub fn capacity(&self) -> u32 {
self.capacity.load(Ordering::Relaxed)
}
pub fn set_capacity(&self, capacity: u32) {
self.capacity.store(capacity, Ordering::Relaxed);
}
pub fn try_acquire(&self) -> bool {
let capacity = self.capacity.load(Ordering::Relaxed);
if capacity == 0 {
return false;
}
let now_secs = now_monotonic_secs();
loop {
let raw = self.state.load(Ordering::Acquire);
let window_start = raw >> 32;
let count = (raw & 0xFFFF_FFFF) as u32;
let (new_window, new_count) = if now_secs.saturating_sub(window_start) >= WINDOW_SECS {
(now_secs, 1u32)
} else if count < capacity {
(window_start, count + 1)
} else {
return false;
};
let new_raw = (new_window << 32) | (new_count as u64);
if self
.state
.compare_exchange_weak(raw, new_raw, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
{
return true;
}
}
}
#[cfg(any(test, feature = "__internal_backtrace_bench"))]
fn reset_for_tests(&self) {
self.state.store(0, Ordering::Release);
}
}
fn now_monotonic_secs() -> u64 {
static ANCHOR: OnceLock<Instant> = OnceLock::new();
let anchor = ANCHOR.get_or_init(Instant::now);
Instant::now().saturating_duration_since(*anchor).as_secs()
}
pub(crate) fn global_resolution_limiter() -> &'static BacktraceCaptureLimiter {
static LIMITER: BacktraceCaptureLimiter = BacktraceCaptureLimiter::new_disabled();
&LIMITER
}
pub(crate) fn global_capture_throttle() -> &'static BacktraceCaptureLimiter {
static LIMITER: BacktraceCaptureLimiter = BacktraceCaptureLimiter::new_disabled();
&LIMITER
}
#[cfg(feature = "__internal_backtrace_bench")]
#[doc(hidden)]
pub mod __bench {
use super::{
global_capture_throttle as inner_capture_throttle,
global_resolution_limiter as inner_resolution_limiter, Backtrace, BacktraceCaptureLimiter,
};
use std::sync::Arc;
pub fn capture() -> Option<Backtrace> {
Backtrace::capture()
}
pub fn render(bt: &Backtrace) -> Option<Arc<str>> {
bt.rendered().cloned()
}
pub fn capture_throttle() -> &'static BacktraceCaptureLimiter {
inner_capture_throttle()
}
pub fn resolution_limiter() -> &'static BacktraceCaptureLimiter {
inner_resolution_limiter()
}
pub fn reset_limiter(limiter: &BacktraceCaptureLimiter) {
limiter.reset_for_tests();
}
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use std::sync::Mutex;
pub(crate) fn backtrace_inner_arc_identity(bt: &Backtrace) -> usize {
Arc::as_ptr(&bt.inner) as usize
}
pub(crate) static TEST_LOCK: Mutex<()> = Mutex::new(());
fn with_limiter_capacity<R>(capacity: u32, f: impl FnOnce() -> R) -> R {
let _guard = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
ensure_initialized();
let prev = global_resolution_limiter().capacity();
global_resolution_limiter().set_capacity(capacity);
global_resolution_limiter().reset_for_tests();
let prev_throttle = global_capture_throttle().capacity();
global_capture_throttle().set_capacity(10_000);
global_capture_throttle().reset_for_tests();
let r = f();
global_resolution_limiter().set_capacity(prev);
global_resolution_limiter().reset_for_tests();
global_capture_throttle().set_capacity(prev_throttle);
global_capture_throttle().reset_for_tests();
r
}
#[test]
fn capture_succeeds_under_resolution_pressure() {
with_limiter_capacity(0, || {
assert!(Backtrace::capture().is_some());
});
}
#[test]
fn capture_throttle_caps_per_second_captures() {
with_limiter_capacity(5, || {
let capacity = 5;
global_capture_throttle().set_capacity(capacity);
global_capture_throttle().reset_for_tests();
for _ in 0..(capacity * 2) {
let _ = Backtrace::capture();
}
assert!(
Backtrace::capture().is_none(),
"after draining {capacity} tokens, captures in the same window must be throttled"
);
});
}
#[test]
fn rendering_returns_none_when_budget_exhausted_for_cache_misses() {
with_limiter_capacity(0, || {
clear_frame_cache_for_tests();
let bt = Backtrace::capture().expect("capture always succeeds");
assert!(
bt.rendered().is_none(),
"expected None when budget=0 and cache is empty"
);
});
}
#[test]
fn cache_hits_do_not_consume_budget() {
with_limiter_capacity(1, || {
clear_frame_cache_for_tests();
let bt1 = Backtrace::capture().expect("capture");
let s1 = bt1.rendered().expect("first render succeeds");
assert!(!s1.is_empty());
assert!(frame_cache_len_for_tests() > 0);
let bt2 = Backtrace::capture().expect("capture");
if let Some(s2) = bt2.rendered() {
assert!(
!s2.contains("<unknown>"),
"successful render must not contain placeholders: {s2}"
);
}
});
}
#[test]
fn rendered_is_cached_per_backtrace() {
with_limiter_capacity(5, || {
let bt = Backtrace::capture().expect("capture");
let s1 = bt.rendered().expect("render");
let s2 = bt.rendered().expect("render");
assert!(std::ptr::eq(s1.as_ptr(), s2.as_ptr()));
});
}
#[test]
fn none_render_is_also_cached_per_backtrace() {
with_limiter_capacity(0, || {
clear_frame_cache_for_tests();
let bt = Backtrace::capture().expect("capture");
assert!(bt.rendered().is_none());
global_resolution_limiter().set_capacity(1_000);
global_resolution_limiter().reset_for_tests();
assert!(
bt.rendered().is_none(),
"rendered() must be deterministic per-Backtrace; None must stay None"
);
});
}
#[test]
fn frame_cache_evicts_when_soft_cap_reached() {
with_limiter_capacity(100, || {
clear_frame_cache_for_tests();
let prev_cap = set_frame_cache_soft_cap_for_tests(10);
let first: Vec<usize> = (1..=12).collect();
assert!(
try_resolve_frames(&first).is_some(),
"first resolve_frames call must succeed (budget acquired once)"
);
for ip in &first {
assert!(
frame_cache_contains_for_tests(*ip),
"expected IP {ip} in cache before eviction trips"
);
}
let second: Vec<usize> = (13..=15).collect();
assert!(try_resolve_frames(&second).is_some());
for ip in &first {
assert!(
!frame_cache_contains_for_tests(*ip),
"pre-eviction IP {ip} must be gone after swap"
);
}
for ip in &second {
assert!(
frame_cache_contains_for_tests(*ip),
"post-eviction IP {ip} must be present in fresh cache"
);
}
set_frame_cache_soft_cap_for_tests(prev_cap);
});
}
#[test]
fn capacity_zero_disables_capture() {
let _guard = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
ensure_initialized();
let prev = global_capture_throttle().capacity();
global_capture_throttle().set_capacity(0);
global_capture_throttle().reset_for_tests();
assert!(
Backtrace::capture().is_none(),
"capacity=0 must disable capture entirely"
);
global_capture_throttle().set_capacity(prev);
global_capture_throttle().reset_for_tests();
}
#[test]
fn capacity_nonzero_enables_capture() {
let _guard = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
ensure_initialized();
let prev = global_capture_throttle().capacity();
global_capture_throttle().set_capacity(8);
global_capture_throttle().reset_for_tests();
assert!(
Backtrace::capture().is_some(),
"capacity>0 must allow capture within the fresh window"
);
global_capture_throttle().set_capacity(prev);
global_capture_throttle().reset_for_tests();
}
#[test]
fn rust_backtrace_enabled_is_stable() {
let first = rust_backtrace_enabled();
let prev = std::env::var("RUST_BACKTRACE").ok();
unsafe {
std::env::set_var("RUST_BACKTRACE", if first { "0" } else { "1" });
}
assert_eq!(
rust_backtrace_enabled(),
first,
"rust_backtrace_enabled must be cached (OnceLock) and ignore later env mutations"
);
unsafe {
match prev {
Some(v) => std::env::set_var("RUST_BACKTRACE", v),
None => std::env::remove_var("RUST_BACKTRACE"),
}
}
}
#[test]
fn set_backtrace_options_writes_both_limiter_capacities() {
let _guard = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let prev_cap = global_capture_throttle().capacity();
let prev_res = global_resolution_limiter().capacity();
set_backtrace_options(BacktraceOptions {
max_captures_per_second: 42,
max_resolutions_per_second: 7,
});
assert_eq!(global_capture_throttle().capacity(), 42);
assert_eq!(global_resolution_limiter().capacity(), 7);
global_capture_throttle().set_capacity(prev_cap);
global_resolution_limiter().set_capacity(prev_res);
global_capture_throttle().reset_for_tests();
global_resolution_limiter().reset_for_tests();
}
#[test]
fn set_backtrace_options_overrides_disabled_baseline() {
let _guard = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let prev_cap = global_capture_throttle().capacity();
let prev_res = global_resolution_limiter().capacity();
set_backtrace_options(BacktraceOptions {
max_captures_per_second: 0,
max_resolutions_per_second: 0,
});
global_capture_throttle().reset_for_tests();
assert!(
Backtrace::capture().is_none(),
"with both caps at 0 capture must be disabled"
);
set_backtrace_options(BacktraceOptions {
max_captures_per_second: 100,
max_resolutions_per_second: 0,
});
global_capture_throttle().reset_for_tests();
assert!(
Backtrace::capture().is_some(),
"programmatic override of a disabled baseline must re-enable capture"
);
global_capture_throttle().set_capacity(prev_cap);
global_resolution_limiter().set_capacity(prev_res);
global_capture_throttle().reset_for_tests();
global_resolution_limiter().reset_for_tests();
}
#[test]
fn set_backtrace_options_overrides_enabled_baseline() {
let _guard = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let prev_cap = global_capture_throttle().capacity();
let prev_res = global_resolution_limiter().capacity();
set_backtrace_options(BacktraceOptions {
max_captures_per_second: 1_000,
max_resolutions_per_second: 5,
});
global_capture_throttle().reset_for_tests();
assert!(Backtrace::capture().is_some());
set_backtrace_options(BacktraceOptions {
max_captures_per_second: 0,
max_resolutions_per_second: 0,
});
global_capture_throttle().reset_for_tests();
assert!(
Backtrace::capture().is_none(),
"programmatic override to 0 must disable capture regardless of prior state"
);
global_capture_throttle().set_capacity(prev_cap);
global_resolution_limiter().set_capacity(prev_res);
global_capture_throttle().reset_for_tests();
global_resolution_limiter().reset_for_tests();
}
#[test]
fn parse_env_u32_precedence() {
assert_eq!(parse_env_u32(None, 99), 99);
assert_eq!(parse_env_u32(Some("7"), 99), 7);
assert_eq!(parse_env_u32(Some(" 7 "), 99), 7);
assert_eq!(parse_env_u32(Some("not-a-number"), 99), 99);
assert_eq!(parse_env_u32(Some(""), 99), 99);
assert_eq!(parse_env_u32(Some("0"), 99), 0);
}
#[test]
fn set_backtrace_options_wins_against_subsequent_ensure_initialized() {
let _guard = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let throttle = global_capture_throttle();
let resolution = global_resolution_limiter();
let prev_cap = throttle.capacity();
let prev_res = resolution.capacity();
let prev_override = PROGRAMMATIC_OVERRIDE.swap(false, Ordering::AcqRel);
set_backtrace_options(BacktraceOptions {
max_captures_per_second: 12_345,
max_resolutions_per_second: 67,
});
assert_eq!(throttle.capacity(), 12_345);
assert_eq!(resolution.capacity(), 67);
ensure_initialized();
assert_eq!(
throttle.capacity(),
12_345,
"ensure_initialized() must not clobber a prior set_backtrace_options() capture capacity",
);
assert_eq!(
resolution.capacity(),
67,
"ensure_initialized() must not clobber a prior set_backtrace_options() resolution capacity",
);
throttle.set_capacity(prev_cap);
resolution.set_capacity(prev_res);
PROGRAMMATIC_OVERRIDE.store(prev_override, Ordering::Release);
}
}