Skip to main content

dbg_cli/session_db/canonicalizer/
mod.rs

1//! Per-language symbol canonicalization.
2//!
3//! Each language adapter produces a `CanonicalSymbol` from whatever raw
4//! form the underlying tool emitted (demangled C++, .NET FQN, Python
5//! `module.qualname`, etc.). The cross-language join key is
6//! `(lang, fqn)` — that's the column agents join on for cross-session
7//! diffs and cross-track correlation.
8
9pub mod cuda;
10pub mod cxx;
11pub mod dotnet;
12pub mod go;
13pub mod python;
14
15/// A language-agnostic symbol identity. Two sessions' `CanonicalSymbol`
16/// values compare equal iff `(lang, fqn)` match — `file`/`line`/etc. are
17/// metadata, not part of identity.
18#[derive(Clone, Debug, Eq, PartialEq, Hash)]
19pub struct CanonicalSymbol {
20    pub lang: &'static str,
21    pub fqn: String,
22    pub file: Option<String>,
23    pub line: Option<u32>,
24    pub demangled: Option<String>,
25    pub raw: String,
26    pub is_synthetic: bool,
27}
28
29impl CanonicalSymbol {
30    /// The (lang, fqn) join key used for cross-session correlation.
31    pub fn key(&self) -> (&'static str, &str) {
32        (self.lang, &self.fqn)
33    }
34}
35
36/// A per-language canonicalizer. Impls are in the submodules.
37pub trait Canonicalizer: Send + Sync {
38    fn lang(&self) -> &'static str;
39
40    /// Convert a raw (possibly mangled) symbol name into a canonical form.
41    fn canonicalize(&self, raw: &str) -> CanonicalSymbol;
42
43    /// Convert a structured symbol emitted by the tool (e.g., dotnet-trace
44    /// yields separate module/class/method/sig). Default: join with `.`
45    /// and fall through to `canonicalize`. Adapters override when they can
46    /// do something smarter than the default join.
47    fn canonicalize_structured(
48        &self,
49        module: &str,
50        class: &str,
51        method: &str,
52        _sig: &str,
53    ) -> CanonicalSymbol {
54        let parts: Vec<&str> = [module, class, method]
55            .into_iter()
56            .filter(|s| !s.is_empty())
57            .collect();
58        self.canonicalize(&parts.join("."))
59    }
60
61    /// Best-effort unwrap of async/state-machine frames back to the
62    /// user-visible method name. Returns `Some(user_method)` only when
63    /// the raw frame is recognized as a compiler-generated wrapper.
64    fn resolve_async_frame(&self, _raw: &str) -> Option<String> {
65        None
66    }
67}
68
69/// Return the canonicalizer for a language tag or `None` if unknown.
70pub fn for_lang(lang: &str) -> Option<Box<dyn Canonicalizer>> {
71    Some(match lang {
72        "cpp" | "rust" | "c" | "zig" | "d" | "nim" => Box::new(cxx::CxxCanonicalizer::new(lang)),
73        "dotnet" => Box::new(dotnet::DotnetCanonicalizer),
74        "python" => Box::new(python::PythonCanonicalizer),
75        "go" => Box::new(go::GoCanonicalizer),
76        "cuda" => Box::new(cuda::CudaCanonicalizer),
77        _ => return None,
78    })
79}