Skip to content

Commit 1fd19f1

Browse files
committed
feat: demangle symbols and improve graph output
Add GRAPH_UNMANGLE support to demangle symbol names in dot/d2 output, separate display names from node identity so demangled names render correctly, sort external function nodes for deterministic output, fix double-escaping of newlines in D2 container labels, and wire UNMANGLE and SKIPLANGSTART env vars through Make graph targets.
1 parent 1a368ba commit 1fd19f1

5 files changed

Lines changed: 50 additions & 13 deletions

File tree

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ OUTDIR_SVG=output-svg
8989
OUTDIR_PNG=output-png
9090
OUTDIR_D2=output-d2
9191

92+
# Pass UNMANGLE=1 to demangle symbol names in graph output.
93+
# Pass SKIPLANGSTART=1 to filter out std::rt::lang_start items.
94+
GRAPH_ENV := $(if $(UNMANGLE),GRAPH_UNMANGLE=1) $(if $(SKIPLANGSTART),SKIP_LANG_START=1)
95+
9296
clean-graphs:
9397
@rm -rf $(OUTDIR_DOT) $(OUTDIR_SVG) $(OUTDIR_PNG) $(OUTDIR_D2)
9498

@@ -105,7 +109,7 @@ dot:
105109
@for rs in $(TESTDIR)/*.rs; do \
106110
name=$$(basename $$rs .rs); \
107111
echo "Generating $$name.smir.dot"; \
108-
cargo run --release -- --dot -Zno-codegen $$rs 2>/dev/null; \
112+
$(GRAPH_ENV) cargo run --release -- --dot -Zno-codegen $$rs 2>/dev/null; \
109113
mv $$name.smir.dot $(OUTDIR_DOT)/ 2>/dev/null || true; \
110114
done
111115

@@ -130,6 +134,6 @@ d2:
130134
@for rs in $(TESTDIR)/*.rs; do \
131135
name=$$(basename $$rs .rs); \
132136
echo "Generating $$name.smir.d2"; \
133-
cargo run --release -- --d2 -Zno-codegen $$rs 2>/dev/null; \
137+
$(GRAPH_ENV) cargo run --release -- --d2 -Zno-codegen $$rs 2>/dev/null; \
134138
mv $$name.smir.d2 $(OUTDIR_D2)/ 2>/dev/null || true; \
135139
done

src/compat/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
//! `DefId`, etc.) without requiring them to know which rustc crate the
2626
//! type actually lives in.
2727
28+
pub extern crate rustc_demangle;
2829
pub extern crate rustc_middle;
2930
pub extern crate rustc_monomorphize;
3031
pub extern crate rustc_session;

src/mk_graph/context.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use stable_mir::ty::{ConstantKind, IndexedVal, MirConst, Ty};
1111

1212
use crate::printer::SmirJson;
1313

14+
use crate::compat::rustc_demangle::demangle;
15+
1416
use super::index::{AllocIndex, LayoutInfo, TypeEntry, TypeIndex, TypeKind};
1517
use super::util::{function_string, short_fn_name, GraphLabelString};
1618

@@ -23,25 +25,50 @@ pub struct GraphContext {
2325
pub allocs: AllocIndex,
2426
pub types: TypeIndex,
2527
pub functions: HashMap<Ty, String>,
28+
/// When `GRAPH_UNMANGLE=1`, maps mangled symbol names to demangled
29+
/// display names. Empty otherwise. Renderers should call
30+
/// `display_name()` for labels rather than using `functions` values
31+
/// directly.
32+
display_names: HashMap<String, String>,
2633
}
2734

2835
impl GraphContext {
2936
pub fn from_smir(smir: &SmirJson) -> Self {
3037
let types = TypeIndex::from_types(&smir.types);
3138
let allocs = AllocIndex::from_alloc_infos(&smir.allocs, &types);
39+
let unmangle = std::env::var("GRAPH_UNMANGLE").is_ok();
3240
let functions: HashMap<Ty, String> = smir
3341
.functions
3442
.iter()
3543
.map(|(k, v)| (k.0, function_string(v.clone())))
3644
.collect();
45+
let display_names: HashMap<String, String> = if unmangle {
46+
functions
47+
.values()
48+
.map(|name| (name.clone(), demangle(name).to_string()))
49+
.collect()
50+
} else {
51+
HashMap::new()
52+
};
3753

3854
Self {
3955
allocs,
4056
types,
4157
functions,
58+
display_names,
4259
}
4360
}
4461

62+
/// Return the display-friendly name for a symbol. When
63+
/// `GRAPH_UNMANGLE=1` is set this is the demangled form;
64+
/// otherwise it's the original (mangled) name.
65+
pub fn display_name<'a>(&'a self, name: &'a str) -> &'a str {
66+
self.display_names
67+
.get(name)
68+
.map(|s| s.as_str())
69+
.unwrap_or(name)
70+
}
71+
4572
/// Render a constant operand with alloc information
4673
pub fn render_const(&self, const_: &MirConst) -> String {
4774
let ty = const_.ty();
@@ -78,7 +105,7 @@ impl GraphContext {
78105
// Function pointers, unit type, etc.
79106
if ty.kind().is_fn() {
80107
if let Some(name) = self.functions.get(&ty) {
81-
format!("const fn {}", short_fn_name(name))
108+
format!("const fn {}", short_fn_name(self.display_name(name)))
82109
} else {
83110
format!("const {}", ty_name)
84111
}
@@ -240,7 +267,7 @@ impl GraphContext {
240267
} => {
241268
let fn_name = self
242269
.resolve_call_target(func)
243-
.map(|n| short_fn_name(&n))
270+
.map(|n| short_fn_name(self.display_name(&n)))
244271
.unwrap_or_else(|| "?".to_string());
245272
let arg_str = args
246273
.iter()

src/mk_graph/output/d2.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ fn render_d2_function(
6666
out: &mut String,
6767
) {
6868
let fn_id = short_name(name);
69-
let display_name = escape_d2(&name_lines(name));
69+
let display_name = name_lines(&escape_d2(name));
7070

7171
// Function container
7272
out.push_str(&format!("{}: {{\n", fn_id));
@@ -131,7 +131,8 @@ fn render_d2_call_edges(
131131
}
132132

133133
let target_id = short_name(&callee_name);
134-
out.push_str(&format!("{}: \"{}\"\n", target_id, escape_d2(&callee_name)));
134+
let display = ctx.display_name(&callee_name);
135+
out.push_str(&format!("{}: \"{}\"\n", target_id, escape_d2(display)));
135136
out.push_str(&format!("{}.style.fill: \"#ffe0e0\"\n", target_id));
136137
out.push_str(&format!("{}.bb{} -> {}: call\n", fn_id, idx, target_id));
137138
}

src/mk_graph/output/dot.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,17 @@ impl SmirJson {
5757
}
5858

5959
// first create all nodes for functions not in the items list
60-
for f in ctx.functions.values() {
61-
if !item_names.contains(f) {
62-
graph
63-
.node_named(block_name(f, 0))
64-
.set_label(&name_lines(f))
65-
.set_color(Color::Red);
66-
}
60+
let mut external_fns: Vec<&String> = ctx
61+
.functions
62+
.values()
63+
.filter(|f| !item_names.contains(*f))
64+
.collect();
65+
external_fns.sort();
66+
for f in external_fns {
67+
graph
68+
.node_named(block_name(f, 0))
69+
.set_label(&name_lines(ctx.display_name(f)))
70+
.set_color(Color::Red);
6771
}
6872

6973
for item in self.items {

0 commit comments

Comments
 (0)