From d0e845d3ac4530cf281e90d8a3634355d153c8be Mon Sep 17 00:00:00 2001 From: iximeow Date: Tue, 4 Jul 2023 14:21:58 -0700 Subject: adjust metrics to show multi-host summaries? --- src/main.rs | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 103 insertions(+), 17 deletions(-) (limited to 'src/main.rs') diff --git a/src/main.rs b/src/main.rs index b6e744b..2658e9d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use chrono::{Utc, TimeZone}; use lazy_static::lazy_static; use std::sync::RwLock; +use std::collections::HashMap; use serde_derive::{Deserialize, Serialize}; use tokio::spawn; use std::path::PathBuf; @@ -612,22 +613,7 @@ async fn handle_commit_status(Path(path): Path<(String, String, String)>, State( } } - let metrics = ctx.dbctx.metrics_for_run(run.id).unwrap(); - let metrics_section = if metrics.len() > 0 { - let mut section = String::new(); - section.push_str("
"); - section.push_str("

metrics

"); - section.push_str(""); - section.push_str(""); - for metric in metrics { - section.push_str(&format!("", &metric.name, &metric.value)); - } - section.push_str("
namevalue
{}{}
"); - section.push_str("
"); - Some(section) - } else { - None - }; + let metrics = summarize_job_metrics(&ctx.dbctx, run.id, run.job_id).unwrap(); let mut html = String::new(); html.push_str("\n"); @@ -646,7 +632,7 @@ async fn handle_commit_status(Path(path): Path<(String, String, String)>, State( html.push_str("
artifacts
\n"); html.push_str(&artifacts_fragment); } - if let Some(metrics) = metrics_section { + if let Some(metrics) = metrics { html.push_str(&metrics); } html.push_str(" \n"); @@ -655,6 +641,86 @@ async fn handle_commit_status(Path(path): Path<(String, String, String)>, State( (StatusCode::OK, Html(html)) } +fn summarize_job_metrics(dbctx: &Arc, run_id: u64, job_id: u64) -> Result, String> { + let runs = dbctx.runs_for_job_one_per_host(job_id)?; + + let mut section = String::new(); + section.push_str("
\n"); + section.push_str("

metrics

\n"); + section.push_str("\n"); + + if runs.len() == 1 { + let metrics = dbctx.metrics_for_run(run_id).unwrap(); + if metrics.is_empty() { + return Ok(None); + } + + section.push_str(""); + for metric in metrics { + section.push_str(&format!("", &metric.name, &metric.value)); + } + } else { + // very silly ordering issue: need an authoritative ordering of metrics to display metrics + // in a consistent order across runs (though they SHOULD all be ordered the same). + // + // the first run might not have all metrics (first run could be on the slowest build host + // f.ex), so for now just assume that metrics *will* be consistently ordered and build a + // list of metrics from the longest list of metrics we've seen. builders do not support + // concurrency so at least the per-run metrics-order-consistency assumption should hold.. + let mut all_names: Vec = Vec::new(); + + let all_metrics: Vec<(HashMap, HostDesc)> = runs.iter().map(|run| { + let metrics = dbctx.metrics_for_run(run.id).unwrap(); + + let mut metrics_map = HashMap::new(); + for metric in metrics.into_iter() { + if !all_names.contains(&metric.name) { + all_names.push(metric.name.clone()); + } + metrics_map.insert(metric.name, metric.value); + } + + let (hostname, cpu_vendor_id, cpu_family, cpu_model) = match run.host_id { + Some(host_id) => { + dbctx.host_model_info(host_id).unwrap() + } + None => { + ("unknown".to_string(), "unknown".to_string(), "0".to_string(), "0".to_string()) + } + }; + + (metrics_map, HostDesc::from_parts(hostname, cpu_vendor_id, cpu_family, cpu_model)) + }).collect(); + + if all_metrics.is_empty() { + return Ok(None); + } + + let mut header = "".to_string(); + for (_, host) in all_metrics.iter() { + header.push_str(&format!("", &host.hostname, &host.cpu_desc)); + } + header.push_str("\n"); + section.push_str(&header); + + for name in all_names.iter() { + let mut row = format!("", &name); + for (metrics, _) in all_metrics.iter() { + let value = metrics.get(name) + .map(|x| x.clone()) + .unwrap_or_else(String::new); + row.push_str(&format!("", value)); + } + row.push_str("\n"); + section.push_str(&row); + } + }; + section.push_str("
namevalue
{}{}
name{} - {}
{}{}
\n"); + section.push_str("
\n"); + + Ok(Some(section)) +} + async fn handle_get_artifact(Path(path): Path<(String, String)>, State(ctx): State) -> impl IntoResponse { eprintln!("get artifact, run={}, artifact={}", path.0, path.1); let run: u64 = path.0.parse().unwrap(); @@ -1002,3 +1068,23 @@ async fn main() { tokio::time::sleep(std::time::Duration::from_millis(1000)).await; } } + +struct HostDesc { + hostname: String, + cpu_desc: String, +} +impl HostDesc { + fn from_parts(hostname: String, vendor_id: String, cpu_family: String, model: String) -> Self { + let cpu_desc = match (vendor_id.as_str(), cpu_family.as_str(), model.as_str()) { + ("Arm Limited", "8", "0xd03") => "aarch64 A53".to_string(), + ("GenuineIntel", "6", "85") => "x86_64 Skylake".to_string(), + ("AuthenticAMD", "23", "113") => "x86_64 Matisse".to_string(), + (vendor, family, model) => format!("unknown {}:{}:{}", vendor, family, model) + }; + + HostDesc { + hostname, + cpu_desc, + } + } +} -- cgit v1.1