use thread_local::ThreadLocal;
use std::{
cell::RefCell,
ffi::{CStr, CString},
fmt::Debug,
};
use android_trace::AndroidTrace;
use tracing::span::{self, Id};
use tracing_subscriber::{
fmt::{
format::{DefaultFields, Writer},
FormatFields,
},
registry::LookupSpan,
};
#[derive(Debug)]
pub struct AndroidTraceLayer {
trace: AndroidTrace,
fmt_fields: DefaultFields,
current_actual_stack: ThreadLocal<RefCell<ThreadLocalData>>,
}
#[derive(Debug, Default)]
struct ThreadLocalData {
stack: Vec<Option<Id>>,
extra_unclosed_values: u32,
}
impl AndroidTraceLayer {
pub fn new() -> Self {
let trace = AndroidTrace::new_downlevel();
Self::with_trace(trace)
}
pub fn with_trace(trace: AndroidTrace) -> Self {
AndroidTraceLayer {
trace,
fmt_fields: DefaultFields::new(),
current_actual_stack: ThreadLocal::new(),
}
}
}
impl Default for AndroidTraceLayer {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
struct ATraceExtension {
name: CString,
}
impl<S> tracing_subscriber::Layer<S> for AndroidTraceLayer
where
S: tracing::Subscriber + for<'a> LookupSpan<'a> + Debug,
for<'a> <S as LookupSpan<'a>>::Data: Debug,
{
fn on_new_span(
&self,
attrs: &span::Attributes<'_>,
id: &span::Id,
ctx: tracing_subscriber::layer::Context<'_, S>,
) {
if self.trace.is_enabled().unwrap_or(false) {
let span = ctx.span(id).expect("Span not found, this is a bug");
let mut extensions = span.extensions_mut();
let mut name = String::from(attrs.metadata().name());
name.push_str(": ");
if self
.fmt_fields
.format_fields(Writer::new(&mut name), attrs)
.is_ok()
{
let name = CString::new(name);
match name {
Ok(name) => extensions.insert::<ATraceExtension>(ATraceExtension { name }),
Err(e) => eprintln!(
concat!(
"[tracing_android_trace] Unable to format the following ",
"span due to a null byte ({:?}), ignoring: {:?}",
),
e, attrs
),
}
} else {
eprintln!(
"[tracing_android_trace] Unable to format the following event, ignoring: {:?}",
attrs
);
}
}
}
fn on_record(
&self,
_span: &span::Id,
_values: &span::Record<'_>,
_ctx: tracing_subscriber::layer::Context<'_, S>,
) {
}
fn on_follows_from(
&self,
_span: &span::Id,
_follows: &span::Id,
_ctx: tracing_subscriber::layer::Context<'_, S>,
) {
}
fn on_event(&self, _event: &tracing::Event<'_>, _: tracing_subscriber::layer::Context<'_, S>) {
}
fn on_enter(&self, id: &span::Id, ctx: tracing_subscriber::layer::Context<'_, S>) {
let span = ctx.span(id).expect("Span not found, this is a bug");
let extensions = span.extensions();
if let Some(ext) = extensions.get::<ATraceExtension>() {
self.trace.begin_section(&ext.name);
let stack = self.current_actual_stack.get_or_default();
stack.borrow_mut().stack.push(Some(id.clone()));
}
}
fn on_exit(&self, id: &span::Id, ctx: tracing_subscriber::layer::Context<'_, S>) {
let this_span = ctx.span(id).expect("Span not found, this is a bug");
let Some(stack) = self.current_actual_stack.get() else {
return;
};
let mut data = stack.borrow_mut();
let stack = &mut data.stack;
if stack.is_empty() {
let extensions = this_span.extensions();
if extensions.get::<ATraceExtension>().is_some() {
if data.extra_unclosed_values == 0 {
panic!("Internal error: The same span was exited twice?");
} else {
data.extra_unclosed_values -= 1;
}
}
return;
}
let last = stack.last().unwrap().as_ref();
debug_assert!(last.is_some());
if last == Some(id) {
stack.pop();
self.trace.end_section();
#[cfg(debug_assertions)]
{
let extensions = this_span.extensions();
debug_assert!(extensions.get::<ATraceExtension>().is_some());
}
while let Some(None) = stack.last() {
stack.pop();
self.trace.end_section();
}
} else {
const EXTRA_STR: &CStr = c"_";
let mut index_of_this = None;
for (idx, item) in stack.iter_mut().enumerate().rev() {
self.trace.end_section();
if item.as_ref() == Some(id) {
index_of_this = Some(idx);
*item = None;
break;
}
}
let Some(index_of_this) = index_of_this else {
let extra_values = stack
.len()
.try_into()
.expect("Shouldn't have more than u32::MAX depth of stack");
stack.clear();
data.extra_unclosed_values = extra_values;
let extensions = this_span.extensions();
debug_assert!(extensions.get::<ATraceExtension>().is_none());
return;
};
for id in stack[index_of_this..].iter() {
if let Some(id) = id {
let span = ctx.span(id).expect("Span not found, this is a bug");
let extensions = span.extensions();
if let Some(ext) = extensions.get::<ATraceExtension>() {
self.trace.begin_section(&ext.name);
} else {
eprintln!("Unexpectedly had item in stack without ATraceExtension");
}
} else {
self.trace.begin_section(EXTRA_STR);
}
}
}
}
}