Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/dwarves

dwarves-gui/src/tune_pane.rs

//! **Syscall profiles** — the review step of the Heimdall tuner in the UI.
//!
//! A THIN layer on the `dwarves tune` verbs (owner 2026-10-31: "dwarves verbs
//! first, the UI later as a thin layer on those verbs"). Pick a `<repo>:<row>`
//! and the pane asks `dwarves tune show` for it; the buttons ask `check`,
//! `probe` and `approve`. Every verb runs as a queued jera job
//! ([`dwarves::tune::jobs`]) — never on the frame — and when it lands the pane
//! draws the verb's own `dwarves.tune/1` document: per program × libc the
//! calls (near-miss and bare allows marked), what is not covered off a VM,
//! each reviewed file's approval, the drift the last check found, and the
//! kernel options each call needs when the row has a saved kernel join.
//!
//! Nothing here reads a profile or computes a finding: the document is
//! rendered as it came. The reviewer is `git config user.name` (the verb takes
//! it; an e-mail is refused there).

use std::path::PathBuf;
use std::sync::Arc;

use dwarves::tune::jobs::{JobState, TuneJob};
use dwarves::tune::{TuneArgs, TuneRequest, TuneVerb};
use egui::{Color32, RichText, Ui};
use facett_core::Facet;
use serde_json::{Value, json};

/// The tab's title.
pub const TITLE: &str = "Syscall profiles";

/// **The seam the buttons fire through.** The live one queues the verbs on
/// jera; a test injects a fake that records what was asked.
pub trait TuneRunner: Send + Sync {
    /// The `<repo>:<row>` artifacts to offer.
    fn artifacts(&self) -> Vec<String>;
    /// Queue one verb (`note`: approve's note). `Err`: a refusal, shown verbatim.
    fn submit(&self, verb: TuneVerb, artifact: &str, note: &str) -> Result<TuneJob, String>;
    /// Where the job stands.
    fn state(&self, job: &TuneJob) -> Option<JobState>;
}

/// The live runner: [`dwarves::tune::jobs::TuneJobs`] over the checkouts.
pub struct QueuedTune {
    jobs: dwarves::tune::jobs::TuneJobs,
    checkouts: PathBuf,
}

impl QueuedTune {
    /// Start the queue's worker; `<repo>:<row>` resolves under `checkouts`.
    pub fn start(checkouts: PathBuf) -> Result<Self, String> {
        let jobs = dwarves::tune::jobs::TuneJobs::start(jera_sink()).map_err(|e| format!("{e:#}"))?;
        Ok(Self { jobs, checkouts })
    }
}

/// The tune jobs' ledger rows: the UI process holds no ledger of its own.
fn jera_sink() -> dwarves::tune::jobs::JobSink {
    dwarves::tune::jobs::JobSink::noop()
}

impl TuneRunner for QueuedTune {
    fn artifacts(&self) -> Vec<String> {
        dwarves::tune::artifacts_under(&self.checkouts)
    }
    fn submit(&self, verb: TuneVerb, artifact: &str, note: &str) -> Result<TuneJob, String> {
        let req = TuneRequest {
            verb: Some(verb),
            args: TuneArgs {
                artifact: artifact.into(),
                checkouts: Some(self.checkouts.clone()),
                note: note.into(),
                ..Default::default()
            },
            tool: None,
            cwd: self.checkouts.clone(),
        };
        self.jobs.submit(req).map_err(|e| format!("{e:#}"))
    }
    fn state(&self, job: &TuneJob) -> Option<JobState> {
        self.jobs.state(&job.key)
    }
}

/// A runner that could not start: every ask is refused with its reason.
pub struct NoRunner(pub String);

impl TuneRunner for NoRunner {
    fn artifacts(&self) -> Vec<String> {
        Vec::new()
    }
    fn submit(&self, _: TuneVerb, _: &str, _: &str) -> Result<TuneJob, String> {
        Err(self.0.clone())
    }
    fn state(&self, _: &TuneJob) -> Option<JobState> {
        None
    }
}

/// The checkouts the shipped tab reads: `$DWARVES_CHECKOUTS`, else
/// `$DWARVES_CONSTELLATION`, else the parent of the checkout the UI runs in.
pub fn default_checkouts() -> PathBuf {
    for k in ["DWARVES_CHECKOUTS", "DWARVES_CONSTELLATION"] {
        if let Some(v) = std::env::var_os(k).filter(|v| !v.is_empty()) {
            return PathBuf::from(v);
        }
    }
    let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
    let root = dwarves::tune::checkout_root(&cwd);
    root.parent().map(PathBuf::from).unwrap_or(root)
}

struct Pending {
    verb: TuneVerb,
    job: TuneJob,
    phase: &'static str,
}

/// **The pane.**
pub struct TunePane {
    runner: Arc<dyn TuneRunner>,
    artifacts: Option<Vec<String>>,
    selected: Option<String>,
    /// The last document a verb answered (drawn as it came).
    doc: Option<Value>,
    /// The last `check` document (its drift stays on screen after approve).
    last_check: Option<Value>,
    /// The last refusal's line.
    error: Option<String>,
    pending: Option<Pending>,
    note: String,
    /// Every verb asked, in order (`show`, `check`, …) — the robot's witness.
    asked: Vec<String>,
}

impl TunePane {
    pub fn new(runner: Arc<dyn TuneRunner>) -> Self {
        Self {
            runner,
            artifacts: None,
            selected: None,
            doc: None,
            last_check: None,
            error: None,
            pending: None,
            note: String::new(),
            asked: Vec::new(),
        }
    }

    /// The shipped tab: the jera-queued verbs over [`default_checkouts`].
    pub fn live() -> Self {
        let runner: Arc<dyn TuneRunner> = match QueuedTune::start(default_checkouts()) {
            Ok(q) => Arc::new(q),
            Err(e) => Arc::new(NoRunner(format!("the tune queue did not start: {e}"))),
        };
        Self::new(runner)
    }

    fn ask(&mut self, verb: TuneVerb) {
        let Some(a) = self.selected.clone() else { return };
        if self.pending.is_some() {
            return;
        }
        let note = if verb == TuneVerb::Approve { self.note.trim().to_string() } else { String::new() };
        self.asked.push(verb.word().to_string());
        match self.runner.submit(verb, &a, &note) {
            Ok(job) => {
                self.error = None;
                self.pending = Some(Pending { verb, job, phase: "queued" });
            }
            Err(e) => self.error = Some(e),
        }
    }

    fn select(&mut self, a: String) {
        if self.selected.as_deref() == Some(a.as_str()) || self.pending.is_some() {
            return;
        }
        self.selected = Some(a);
        self.doc = None;
        self.last_check = None;
        self.error = None;
        self.ask(TuneVerb::Show);
    }

    /// Land a finished job, if any.
    fn poll(&mut self) {
        let Some(p) = self.pending.as_mut() else { return };
        match self.runner.state(&p.job) {
            Some(JobState::Queued) => p.phase = "queued",
            Some(JobState::Running) => p.phase = "running",
            Some(JobState::Done(v)) => {
                if p.verb == TuneVerb::Check {
                    self.last_check = Some(v.clone());
                }
                if p.verb == TuneVerb::Approve {
                    self.note.clear();
                }
                self.doc = Some(v);
                self.error = None;
                self.pending = None;
            }
            Some(JobState::Failed(v)) => {
                self.error = Some(v["error"].as_str().unwrap_or("the verb failed").to_string());
                self.pending = None;
            }
            None => {
                self.error = Some(format!("job {} is not on the tune queue", p.job.job_id));
                self.pending = None;
            }
        }
    }

    fn review(&self) -> Option<&Value> {
        self.doc.as_ref().map(|d| &d["review"]).filter(|r| !r.is_null())
    }

    fn draw_head(&mut self, ui: &mut Ui) {
        ui.heading(TITLE);
        let artifacts = self.artifacts.get_or_insert_with(|| self.runner.artifacts()).clone();
        ui.horizontal_wrapped(|ui| {
            ui.label("Row:");
            if artifacts.is_empty() {
                ui.label(RichText::new("no row has profiles (.nornir/profiles/<row>/measure.toml) under the checkouts").italics());
            }
            for a in artifacts {
                let on = self.selected.as_deref() == Some(a.as_str());
                if ui.selectable_label(on, &a).clicked() {
                    self.select(a);
                }
            }
        });
        if self.selected.is_none() {
            return;
        }
        let idle = self.pending.is_none();
        ui.horizontal(|ui| {
            if ui
                .add_enabled(idle, egui::Button::new("Check"))
                .on_hover_text("dwarves tune check: drift (a call outside a profile, naming the test) and the approval")
                .clicked()
            {
                self.ask(TuneVerb::Check);
            }
            if ui
                .add_enabled(idle, egui::Button::new("Probe"))
                .on_hover_text("dwarves tune probe: re-measure the row's programs and regenerate the profiles")
                .clicked()
            {
                self.ask(TuneVerb::Probe);
            }
            let caption = ui.label("Note");
            ui.add(egui::TextEdit::singleline(&mut self.note).hint_text("why the profiles are fine").desired_width(260.0))
                .labelled_by(caption.id);
            if ui
                .add_enabled(idle, egui::Button::new("Approve"))
                .on_hover_text("dwarves tune approve: record the review (reviewer = git config user.name) in approved.toml")
                .clicked()
            {
                self.ask(TuneVerb::Approve);
            }
        });
        if let Some(p) = &self.pending {
            ui.label(format!("dwarves tune {} — {} in jera (job {})", p.verb.word(), p.phase, p.job.job_id));
        }
        if let Some(e) = &self.error {
            ui.colored_label(Color32::from_rgb(220, 70, 60), format!("refused: {e}"));
        }
    }

    fn draw_doc(&mut self, ui: &mut Ui) {
        let Some(doc) = self.doc.clone() else { return };
        let Some(review) = self.review().cloned() else { return };
        ui.label(RichText::new(format!("last verb: dwarves tune {} — {}", doc["verb"].as_str().unwrap_or("?"), if doc["ok"] == true { "ok" } else { "findings" })).weak());
        ui.separator();
        ui.strong("Approval");
        let files = review["files"].as_array().cloned().unwrap_or_default();
        if files.is_empty() {
            ui.label("no reviewed files yet: probe the row first");
        }
        for f in &files {
            let colour = match f["state"].as_str() {
                Some("approved") => Color32::from_rgb(80, 170, 90),
                Some("changed") => Color32::from_rgb(220, 160, 40),
                _ => Color32::from_rgb(220, 70, 60),
            };
            ui.horizontal(|ui| {
                ui.monospace(f["file"].as_str().unwrap_or("?"));
                ui.colored_label(colour, f["line"].as_str().unwrap_or("?"));
            });
        }
        if let Some(c) = &self.last_check {
            ui.separator();
            let ok = c["ok"] == true;
            ui.strong(if ok { "Last check: no drift" } else { "Last check: findings" });
            for d in c["drift"].as_array().into_iter().flatten() {
                let tests: Vec<&str> = d["tests"].as_array().into_iter().flatten().filter_map(Value::as_str).collect();
                ui.colored_label(
                    Color32::from_rgb(220, 70, 60),
                    format!(
                        "drift: {} ({}) new call {} ({}), made by test {}",
                        d["program"].as_str().unwrap_or("?"),
                        d["libc"].as_str().unwrap_or("?"),
                        d["call"].as_str().unwrap_or("?"),
                        d["action"].as_str().unwrap_or("?"),
                        tests.join(", ")
                    ),
                );
            }
            for p in c["problems"].as_array().into_iter().flatten().filter_map(Value::as_str) {
                if !p.starts_with("drift: ") {
                    ui.colored_label(Color32::from_rgb(220, 160, 40), p);
                }
            }
        }
        ui.separator();
        for p in review["programs"].as_array().into_iter().flatten() {
            let calls = p["calls"].as_array().cloned().unwrap_or_default();
            let near = calls.iter().filter(|c| c["near_miss"].as_array().is_some_and(|a| !a.is_empty())).count();
            let bare = calls.iter().filter(|c| c["bare"] == true).count();
            let head = format!(
                "{} ({} {}) — {} calls, {} near-miss, {} bare",
                p["program"].as_str().unwrap_or("?"),
                p["arch"].as_str().unwrap_or("?"),
                p["libc"].as_str().unwrap_or("?"),
                calls.len(),
                near,
                bare
            );
            egui::CollapsingHeader::new(head).default_open(true).show(ui, |ui| {
                ui.monospace(p["file"].as_str().unwrap_or("?"));
                egui::Grid::new(format!("tune-calls-{}-{}", p["program"], p["libc"])).striped(true).show(ui, |ui| {
                    for c in &calls {
                        ui.monospace(c["syscall"].as_str().unwrap_or("?"));
                        ui.label(c["action"].as_str().unwrap_or("?"));
                        ui.horizontal(|ui| {
                            if c["conditional"] == true {
                                ui.label(RichText::new("args checked").weak());
                            }
                            for n in c["near_miss"].as_array().into_iter().flatten().filter_map(Value::as_str) {
                                ui.colored_label(Color32::from_rgb(220, 160, 40), format!("near-miss {n}"));
                            }
                            if c["bare"] == true {
                                ui.colored_label(Color32::from_rgb(220, 70, 60), "BARE allow");
                            }
                            let opts: Vec<&str> = c["options"].as_array().into_iter().flatten().filter_map(Value::as_str).collect();
                            if !opts.is_empty() {
                                ui.label(RichText::new(format!("needs {}", opts.join(", "))).weak());
                            }
                        });
                        ui.end_row();
                    }
                });
                for (what, key) in [("W^X", "wx"), ("unknown", "unknown"), ("fixed", "fixed"), ("note", "notes")] {
                    for x in p[key].as_array().into_iter().flatten().filter_map(Value::as_str) {
                        ui.label(format!("{what}: {x}"));
                    }
                }
            });
        }
        let not_covered = review["not_covered"].as_array().cloned().unwrap_or_default();
        if !not_covered.is_empty() {
            ui.separator();
            ui.strong("Not covered off a VM");
            for n in &not_covered {
                ui.label(format!("{}: {}", n["program"].as_str().unwrap_or("?"), n["why"].as_str().unwrap_or("?")));
            }
        }
        ui.separator();
        match review["kernel"].as_object() {
            Some(k) => {
                ui.strong(format!(
                    "Kernel {} {} {}",
                    k["kernel"].as_str().unwrap_or("?"),
                    k["version"].as_str().unwrap_or("?"),
                    k["arch"].as_str().unwrap_or("?")
                ));
                for o in k["options"].as_array().into_iter().flatten().filter_map(Value::as_str) {
                    ui.monospace(o);
                }
                for v in k["vetoes"].as_array().into_iter().flatten() {
                    let line = v["line"].as_str().unwrap_or("?");
                    if v["near_miss"] == true {
                        ui.colored_label(Color32::from_rgb(220, 160, 40), format!("veto (warning, near-miss): {line}"));
                    } else {
                        ui.colored_label(Color32::from_rgb(220, 70, 60), format!("veto: {line}"));
                    }
                }
            }
            None => {
                ui.label(RichText::new(review["kernel_hint"].as_str().unwrap_or("")).italics());
            }
        }
    }
}

impl Facet for TunePane {
    fn title(&self) -> &str {
        TITLE
    }

    fn component(&self) -> &'static str {
        "tunepane"
    }

    fn ui(&mut self, ui: &mut Ui) {
        self.poll();
        if self.pending.is_some() {
            ui.ctx().request_repaint_after(std::time::Duration::from_millis(100));
        }
        egui::ScrollArea::vertical().auto_shrink([false, false]).show(ui, |ui| {
            self.draw_head(ui);
            self.draw_doc(ui);
        });
    }

    fn state_json(&self) -> Value {
        json!({
            "component": "tunepane",
            "artifacts": self.artifacts,
            "artifact": self.selected,
            "asked": self.asked,
            "pending": self.pending.as_ref().map(|p| json!({ "verb": p.verb.word(), "job_id": p.job.job_id, "phase": p.phase })),
            "error": self.error,
            "note": self.note,
            "verb": self.doc.as_ref().map(|d| d["verb"].clone()),
            "ok": self.doc.as_ref().map(|d| d["ok"].clone()),
            "review": self.review().cloned(),
            "last_check": self.last_check.as_ref().map(|c| json!({ "ok": c["ok"], "drift": c["drift"], "problems": c["problems"] })),
        })
    }

    fn update_json(&mut self, msg: &str) {
        let Ok(v) = serde_json::from_str::<Value>(msg) else { return };
        if let Some(a) = v["select"].as_str() {
            self.select(a.to_string());
        }
        match v["verb"].as_str() {
            Some("check") => self.ask(TuneVerb::Check),
            Some("probe") => self.ask(TuneVerb::Probe),
            Some("approve") => {
                if let Some(n) = v["note"].as_str() {
                    self.note = n.into();
                }
                self.ask(TuneVerb::Approve)
            }
            _ => {}
        }
    }
}