use std::fmt::{Debug, Write};
use std::sync::atomic::{AtomicU64, Ordering};
use indextree::{Arena, NodeId};
use itertools::Itertools;
use parking_lot::{Mutex, MutexGuard};
use crate::root::current_context;
use crate::Span;
#[derive(Debug, Clone)]
struct SpanNode {
span: Span,
start_time: coarsetime::Instant,
}
impl SpanNode {
fn new(span: Span) -> Self {
Self {
span,
start_time: coarsetime::Instant::now(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct ContextId(pub(crate) u64);
#[derive(Debug, Clone)]
pub struct Tree {
arena: Arena<SpanNode>,
root: NodeId,
current: NodeId,
}
#[cfg(feature = "serde")]
mod serde_impl {
use serde::ser::SerializeStruct as _;
use serde::Serialize;
use super::*;
struct SpanNodeSer<'a> {
arena: &'a Arena<SpanNode>,
node: NodeId,
}
impl Serialize for SpanNodeSer<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let inner = self.arena[self.node].get();
let mut s = serializer.serialize_struct("Span", 4)?;
let id: usize = self.node.into();
s.serialize_field("id", &id)?;
s.serialize_field("span", &inner.span)?;
s.serialize_field("elapsed_ns", &inner.start_time.elapsed().as_nanos())?;
let children = (self.node.children(self.arena))
.map(|node| SpanNodeSer {
arena: self.arena,
node,
})
.sorted_by_key(|child| {
let inner = self.arena[child.node].get();
inner.start_time
})
.collect_vec();
s.serialize_field("children", &children)?;
s.end()
}
}
impl Serialize for Tree {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut s = serializer.serialize_struct("Tree", 3)?;
let current_id: usize = self.current.into();
s.serialize_field("current", ¤t_id)?;
s.serialize_field(
"tree",
&SpanNodeSer {
arena: &self.arena,
node: self.root,
},
)?;
let detached = self
.detached_roots()
.map(|node| SpanNodeSer {
arena: &self.arena,
node,
})
.collect_vec();
s.serialize_field("detached", &detached)?;
s.end()
}
}
}
impl std::fmt::Display for Tree {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt_node(
f: &mut std::fmt::Formatter<'_>,
arena: &Arena<SpanNode>,
node: NodeId,
depth: usize,
current: NodeId,
) -> std::fmt::Result {
f.write_str(&" ".repeat(depth * 2))?;
let inner = arena[node].get();
f.write_str(&inner.span.name)?;
let elapsed: std::time::Duration = inner.start_time.elapsed().into();
write!(
f,
" [{}{:.3?}]",
if !inner.span.is_long_running && elapsed.as_secs() >= 10 {
"!!! "
} else {
""
},
elapsed
)?;
if depth > 0 && node == current {
f.write_str(" <== current")?;
}
f.write_char('\n')?;
for child in node
.children(arena)
.sorted_by_key(|&id| arena[id].get().start_time)
{
fmt_node(f, arena, child, depth + 1, current)?;
}
Ok(())
}
fmt_node(f, &self.arena, self.root, 0, self.current)?;
for node in self.detached_roots() {
writeln!(f, "[Detached {node}]")?;
fmt_node(f, &self.arena, node, 1, self.current)?;
}
Ok(())
}
}
impl Tree {
#[cfg(test)]
pub(crate) fn active_node_count(&self) -> usize {
self.arena.iter().filter(|n| !n.is_removed()).count()
}
#[cfg(test)]
pub(crate) fn detached_node_count(&self) -> usize {
self.arena
.iter()
.filter(|n| {
!n.is_removed()
&& n.parent().is_none()
&& self.arena.get_node_id(n).unwrap() != self.root
})
.count()
}
fn detached_roots(&self) -> impl Iterator<Item = NodeId> + '_ {
self.arena
.iter()
.filter(|n| !n.is_removed()) .map(|node| self.arena.get_node_id(node).unwrap()) .filter(|&id| id != self.root && self.arena[id].parent().is_none()) }
pub(crate) fn push(&mut self, span: Span) -> NodeId {
let child = self.arena.new_node(SpanNode::new(span));
self.current.prepend(child, &mut self.arena);
self.current = child;
child
}
pub(crate) fn step_in(&mut self, child: NodeId) {
if !self.current.children(&self.arena).contains(&child) {
self.current.prepend(child, &mut self.arena);
}
self.current = child;
}
pub(crate) fn pop(&mut self) {
let parent = self.arena[self.current]
.parent()
.expect("the root node should not be popped");
self.remove_and_detach(self.current);
self.current = parent;
}
pub(crate) fn step_out(&mut self) {
let parent = self.arena[self.current]
.parent()
.expect("the root node should not be stepped out");
self.current = parent;
}
pub(crate) fn remove_and_detach(&mut self, node: NodeId) {
node.detach(&mut self.arena);
node.remove(&mut self.arena);
}
pub(crate) fn current(&self) -> NodeId {
self.current
}
}
#[derive(Debug)]
pub(crate) struct TreeContext {
id: ContextId,
verbose: bool,
tree: Mutex<Tree>,
}
impl TreeContext {
pub(crate) fn new(root_span: Span, verbose: bool) -> Self {
static ID: AtomicU64 = AtomicU64::new(0);
let id = ID.fetch_add(1, Ordering::Relaxed);
let root_span = root_span.long_running();
let mut arena = Arena::new();
let root = arena.new_node(SpanNode::new(root_span));
Self {
id: ContextId(id),
verbose,
tree: Tree {
arena,
root,
current: root,
}
.into(),
}
}
pub(crate) fn id(&self) -> ContextId {
self.id
}
pub(crate) fn tree(&self) -> MutexGuard<'_, Tree> {
self.tree.lock()
}
pub(crate) fn verbose(&self) -> bool {
self.verbose
}
}
pub fn current_tree() -> Option<Tree> {
current_context().map(|c| c.tree().clone())
}