in eden/scm/lib/tracing-collector/src/model.rs [1363:1554]
fn render_tree_spans(&self, tree_spans: Vec<RawTreeSpan>, opts: &AsciiOptions) -> Rows {
struct Context<'a> {
this: &'a TracingData,
opts: &'a AsciiOptions,
tree_spans: Vec<RawTreeSpan>,
rows: Vec<Row>,
}
/// Extract value from espan.meta.
fn extract<'a>(ctx: &'a Context, espan: &'a Espan, name: &'a str) -> &'a str {
let meta = &espan.meta;
if let Some((key_id, _)) = ctx.this.strings.0.get_full(name) {
let key_id = StringId(key_id as u64);
if let Some(value_id) = meta.get(&key_id) {
return ctx.this.strings.get(*value_id);
}
}
""
}
/// Render RawTreeSpan to rows.
fn render_span(ctx: &mut Context, id: usize, mut indent: usize, first_row_ch: char) {
let tree_span = &ctx.tree_spans[id];
if let Some(espan_id) = tree_span.espan_id {
let this = ctx.this;
let strings = &this.strings;
let espan = match ctx.this.get_espan(espan_id) {
Some(espan) => espan,
None => return,
};
let name = extract(ctx, espan, "name");
let source_location = {
let module_path = extract(ctx, espan, "module_path");
let line = extract(ctx, espan, "line");
if module_path.is_empty() {
let cat = extract(ctx, espan, "cat");
if cat.is_empty() {
String::new()
} else {
format!("({})", cat)
}
} else if line.is_empty() {
module_path.to_string()
} else {
format!("{} line {}", module_path, line)
}
};
let start = tree_span.start_time / 1000;
let duration = if tree_span.is_event {
"0".to_string()
} else if tree_span.is_incomplete() {
"...".to_string()
} else {
// Use milliseconds. This is consistent with traceprof.
format!("+{}", tree_span.duration / 1000)
};
let mut call_count = if tree_span.call_count > 1 {
format!(" ({} times)", tree_span.call_count)
} else {
assert!(tree_span.call_count > 0);
String::new()
};
if espan.ref_count >= ctx.this.max_span_ref_count {
call_count += " (truncated)"
}
let first_row = Row {
columns: vec![
start.to_string(),
duration,
format!(
"{}{} {}{}",
" ".repeat(indent),
first_row_ch,
name,
call_count
),
source_location,
],
};
ctx.rows.push(first_row);
// Extra metadata (other than name, module_path and line)
let extra_meta: Vec<(&str, &str)> = espan
.meta
.iter()
.map(|(&key, &value)| (strings.get(key), strings.get(value)))
.filter(|(key, value)| {
*key != "name" && *key != "module_path" && *key != "line" && *key != "cat"
})
.collect();
if first_row_ch == '\\' {
indent += 1;
}
for (i, (key, value)) in extra_meta.iter().enumerate() {
let value = if value.len() > 32 {
format!("{}...", &value[..30])
} else {
value.to_string()
};
let row = Row {
columns: vec![
String::new(),
String::new(),
format!("{}| - {} = {}", " ".repeat(indent), key, value),
format!(":"),
],
};
ctx.rows.push(row);
}
}
}
/// Visit a span and its children recursively.
fn visit(ctx: &mut Context, id: usize, indent: usize, ch: char) {
// Print out this span.
render_span(ctx, id, indent, ch);
// Figure out children to visit.
let child_ids: Vec<usize> = ctx.tree_spans[id]
.children
.iter()
.cloned()
.filter(|&id| {
ctx.tree_spans[id].is_interesting(ctx.opts, Some(&ctx.tree_spans[id]))
})
.collect();
// Preserve a straight line if there is only one child:
//
// | foo ('bar' is the only child)
// | bar <- case 1
//
// Increase indent if there are multi-children (case 2),
// or it's already not a straight line (case 3):
//
// | foo ('bar1' and 'bar2' are children)
// \ bar1 <- case 2
// | bar1.1 <- case 3
// | bar1.2 <- case 1
// \ bar2 <- case 2
// \ bar2.1 <- case 2
// \ bar2.2 <- case 2
//
let (indent, ch) = if child_ids.len() >= 2 {
// case 2
(indent + 1, '\\')
} else if ch == '\\' {
// case 3
(indent + 1, '|')
} else {
// case 1
(indent, '|')
};
for id in child_ids {
visit(ctx, id, indent, ch)
}
}
let mut context = Context {
this: self,
opts,
tree_spans,
rows: vec![Row {
columns: ["Start", "Dur.ms", "| Name", "Source"]
.iter()
.map(ToString::to_string)
.collect(),
}],
};
// Visit the root RawTreeSpan.
visit(&mut context, 0, 0, '|');
let column_alignments = vec![
Alignment::Right, // start time
Alignment::Right, // duration
Alignment::Left, // graph, name
Alignment::Left, // module, line number
];
let column_min_widths = vec![4, 4, 20, 0];
let column_max_widths = vec![20, 20, 80, 80];
Rows {
rows: context.rows,
column_alignments,
column_min_widths,
column_max_widths,
}
}