resctl-bench/src/progress.rs (78 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use log::info; use std::thread::{spawn, JoinHandle}; use rd_util::JournalTailer; pub struct BenchProgress { main: Option<MultiProgress>, bars: Vec<ProgressBar>, tailers: Vec<JournalTailer>, main_jh: Option<JoinHandle<()>>, term_width: usize, intv_cnt: u32, } impl BenchProgress { const LOG_INTV: u32 = 5; pub fn new() -> Self { let main = MultiProgress::new(); let first_bar = main.add(ProgressBar::new(0)); first_bar.set_style( ProgressStyle::default_bar().template("{spinner:.green} [{elapsed_precise}] {msg}"), ); first_bar.tick(); Self { main: Some(main), bars: vec![first_bar], tailers: vec![], main_jh: None, term_width: term_size::dimensions_stderr().unwrap_or((80, 0)).0, intv_cnt: 0, } } pub fn monitor_systemd_unit(mut self, unit: &str) -> Self { if !console::user_attended_stderr() { return self; } let bar = self.main.as_ref().unwrap().add(ProgressBar::new(0)); let prefix = unit.rsplitn(2, '.').last().unwrap(); bar.set_prefix(prefix.to_string()); bar.set_style(ProgressStyle::default_bar().template(" {prefix:.green} {msg}")); bar.tick(); self.bars.push(bar.clone()); let msg_width = self.term_width.checked_sub(prefix.len() + 5).unwrap_or(0); self.tailers.push(JournalTailer::new( &[unit], 1, Box::new(move |msgs, flush| { if flush { let msg: String = msgs[0].msg.chars().take(msg_width).collect(); bar.set_message(msg); } }), )); self } pub fn set_status(&mut self, status: &str) { if let Some(main) = self.main.take() { self.main_jh = Some(spawn(move || { main.join_and_clear().unwrap(); })); } if console::user_attended_stderr() { self.bars[0].set_message(status.to_string()); } else { if self.intv_cnt % Self::LOG_INTV == 0 { info!("{}", status); } self.intv_cnt += 1; } } } impl Drop for BenchProgress { fn drop(&mut self) { self.tailers.clear(); self.bars.clear(); if let Some(jh) = self.main_jh.take() { jh.join().unwrap(); } } }