axbuild 0.4.15

An OS build lib toolkit used by arceos
Documentation
use std::{fs, fs::File, io::Write, path::Path};

use anyhow::Context;

use super::{
    super::ArgsPerf,
    args_support::{
        effective_callchain, effective_max_depth, flamegraph_min_percent, host_time_enabled,
        perf_needs_debuginfo, perf_needs_frame_pointers,
    },
    harness::QperfTools,
    monitor::PerfWindowReport,
    outputs::{PerfOutputs, count_lines},
    qemu::{DEFAULT_STARRY_SHELL_PREFIX, QPERF_QUEUE_SIZE},
};

pub(super) struct SummaryInputs<'a> {
    pub(super) outputs: &'a PerfOutputs,
    pub(super) tools: &'a QperfTools,
    pub(super) elf: &'a Path,
    pub(super) arch: &'a str,
    pub(super) target: &'a str,
    pub(super) args: &'a ArgsPerf,
    pub(super) flamegraph_generated: bool,
    pub(super) window: &'a PerfWindowReport,
}

pub(super) fn write_summary(input: SummaryInputs<'_>) -> anyhow::Result<()> {
    let outputs = input.outputs;
    let args = input.args;
    let window = input.window;
    let folded_lines = count_lines(&outputs.folded).unwrap_or(0);
    let plugin_summary = outputs.raw.with_extension("summary.txt");
    let plugin_summary_text = fs::read_to_string(plugin_summary).ok();

    let mut file = File::create(&outputs.summary)
        .with_context(|| format!("failed to create {}", outputs.summary.display()))?;
    writeln!(file, "qperf_format_version = 3")?;
    writeln!(file, "arch = {}", input.arch)?;
    writeln!(file, "target = {}", input.target)?;
    writeln!(file, "frequency_hz = {}", args.freq)?;
    writeln!(file, "max_stack_depth = {}", effective_max_depth(args))?;
    writeln!(file, "sampling_mode = {}", args.mode)?;
    writeln!(file, "callchain_mode = {}", effective_callchain(args))?;
    writeln!(file, "full_stack = {}", args.full_stack)?;
    writeln!(file, "perf_debuginfo = {}", perf_needs_debuginfo(args))?;
    writeln!(
        file,
        "perf_force_frame_pointers = {}",
        perf_needs_frame_pointers(args)
    )?;
    writeln!(file, "case = {}", args.case)?;
    writeln!(file, "symbol_style = {}", args.symbol_style)?;
    writeln!(file, "flamegraph_kind = {}", args.flamegraph_kind)?;
    writeln!(
        file,
        "flamegraph_min_percent = {}",
        flamegraph_min_percent(args)
    )?;
    if let Some(focus) = args.focus.as_deref() {
        writeln!(file, "focus = {focus}")?;
    }
    writeln!(file, "kernel_filter = {}", args.kernel_filter)?;
    writeln!(file, "host_time = {}", host_time_enabled(args))?;
    if host_time_enabled(args) {
        writeln!(file, "host_time_output = {}", outputs.host_time.display())?;
    }
    writeln!(file, "host_perf = {}", args.host_perf)?;
    if args.host_perf {
        writeln!(file, "host_perf_events = {}", args.host_perf_events)?;
        writeln!(file, "host_perf_output = {}", outputs.host_perf.display())?;
        writeln!(
            file,
            "host_perf_note = host perf stat measures the QEMU process on the host; it is not a \
             guest PMU cache/cycle counter"
        )?;
    }
    if let Some(shell_init_cmd) = args.shell_init_cmd.as_deref() {
        writeln!(file, "shell_init_cmd = {shell_init_cmd}")?;
        writeln!(
            file,
            "shell_prefix = {}",
            args.shell_prefix
                .as_deref()
                .unwrap_or(DEFAULT_STARRY_SHELL_PREFIX)
        )?;
    }
    if let Some(start_marker) = args.start_marker.as_deref() {
        writeln!(file, "start_marker = {start_marker}")?;
    }
    if let Some(stop_marker) = args.stop_marker.as_deref() {
        writeln!(file, "stop_marker = {stop_marker}")?;
    }
    if let Some(workload_timeout) = args.workload_timeout {
        writeln!(file, "workload_timeout = {workload_timeout}")?;
    }
    writeln!(file, "qperf_metrics = {}", args.qperf_metrics)?;
    writeln!(file, "window_enabled = {}", window.enabled)?;
    if let Some(start_time) = window.start_time {
        writeln!(file, "window_start_time = {start_time:.9}")?;
    }
    if let Some(stop_time) = window.stop_time {
        writeln!(file, "window_stop_time = {stop_time:.9}")?;
    }
    if let Some(duration) = window.duration_sec {
        writeln!(file, "window_duration_sec = {duration:.9}")?;
    }
    writeln!(
        file,
        "window_truncated_by_timeout = {}",
        window.truncated_by_timeout
    )?;
    writeln!(file, "window_report = {}", outputs.window.display())?;
    writeln!(file, "resolve_stats = {}", outputs.resolve_stats.display())?;
    if !args.qemu_args.is_empty() {
        writeln!(file, "extra_qemu_args = {}", args.qemu_args.join(" "))?;
    }
    writeln!(
        file,
        "build_profile = {}",
        if args.debug { "debug" } else { "release" }
    )?;
    writeln!(file, "queue_size = {QPERF_QUEUE_SIZE}")?;
    writeln!(file, "timeout_seconds = {}", args.timeout)?;
    writeln!(file, "kernel_elf = {}", input.elf.display())?;
    writeln!(file, "plugin = {}", input.tools.plugin.display())?;
    writeln!(file, "analyzer = {}", input.tools.analyzer.display())?;
    writeln!(file, "raw_samples = {}", outputs.raw.display())?;
    writeln!(file, "folded_stack = {}", outputs.folded.display())?;
    writeln!(
        file,
        "stack_depth_summary = {}",
        outputs.stack_depth_summary.display()
    )?;
    writeln!(
        file,
        "workload_folded_stack = {}",
        outputs.folded_workload.display()
    )?;
    writeln!(
        file,
        "boot_folded_stack = {}",
        outputs.folded_boot.display()
    )?;
    writeln!(
        file,
        "post_folded_stack = {}",
        outputs.folded_post.display()
    )?;
    writeln!(file, "folded_stack_lines = {folded_lines}")?;
    writeln!(
        file,
        "flamegraph_generated = {}",
        input.flamegraph_generated
    )?;
    if input.flamegraph_generated {
        writeln!(file, "flamegraph = {}", outputs.flamegraph.display())?;
        writeln!(
            file,
            "workload_flamegraph = {}",
            outputs.flamegraph_workload.display()
        )?;
        writeln!(
            file,
            "boot_flamegraph = {}",
            outputs.flamegraph_boot.display()
        )?;
        writeln!(
            file,
            "post_flamegraph = {}",
            outputs.flamegraph_post.display()
        )?;
    }
    if let Some(plugin_summary_text) = plugin_summary_text {
        writeln!(file)?;
        writeln!(file, "[plugin_summary]")?;
        write!(file, "{plugin_summary_text}")?;
    } else {
        writeln!(file, "dropped_samples = unknown")?;
        writeln!(
            file,
            "plugin_summary = unavailable; QEMU may have been stopped by timeout before plugin \
             shutdown"
        )?;
    }
    Ok(())
}

pub(super) fn print_report(outputs: &PerfOutputs, args: &ArgsPerf, report_harness: &Path) {
    println!("qperf report generated:");
    println!("  report: {}", outputs.report_md.display());
    println!("  flamegraph: {}", outputs.flamegraph.display());
    println!("  folded stack: {}", outputs.folded.display());
    println!(
        "  stack depth summary: {}",
        outputs.stack_depth_summary.display()
    );
    println!("  json: {}", outputs.report_json.display());
    println!("  callchain mode: {}", effective_callchain(args));
    println!("  hotspots: {}", outputs.hotspots_csv.display());
    println!(
        "  hotspot categories: {}",
        outputs.hotspot_categories_csv.display()
    );
    println!("  qperf dir: {}", outputs.dir.display());
    println!("  raw samples: {}", outputs.raw.display());
    if outputs.flamegraph_workload.exists() {
        println!(
            "  workload flamegraph: {}",
            outputs.flamegraph_workload.display()
        );
    }
    if outputs.flamegraph_boot.exists() {
        println!("  boot flamegraph: {}", outputs.flamegraph_boot.display());
    }
    if outputs.flamegraph_post.exists() {
        println!("  post flamegraph: {}", outputs.flamegraph_post.display());
    }
    if outputs.flamegraph_focus.exists() {
        println!(
            "  focused flamegraph: {}",
            outputs.flamegraph_focus.display()
        );
    }
    if outputs.flamegraph_html.exists() {
        println!("  html flamegraph: {}", outputs.flamegraph_html.display());
    }
    println!("  qemu config: {}", outputs.qemu_config.display());
    if outputs.host_time.exists() {
        println!("  host time: {}", outputs.host_time.display());
    }
    if outputs.host_perf.exists() {
        println!("  host perf: {}", outputs.host_perf.display());
    }
    if outputs.window.exists() {
        println!("  window: {}", outputs.window.display());
    }
    println!("  summary: {}", outputs.summary.display());
    println!("  report harness: {}", report_harness.display());
    println!(
        "  compare hint: cargo starry perf-compare is not a cargo subcommand; use python3 {} \
         perf-compare --baseline {} --candidate <other-report.json>",
        report_harness.display(),
        outputs.report_json.display()
    );
}