fn render_tree_spans()

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,
        }
    }