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}