1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
use anyhow::Result;
use std::io::Read;
use std::sync::{Arc, Mutex};
// If the logger is configured to use the module's path, the log message will always
// look like `[<timestamp> INFO ...util::printer] <message>` which is inconvenient
// because we loose the source of the message.
// However, if the logger is configured to print the target instead of the module path
// then it will look like `[<timestamp> INFO <target>] <message>`. Since the default
// target is the module's path, this allows for a non-breaking change by configuring
// the logger to always print the target instead of the module.
// Accumulates output from a process's `stdout`.
pub fn accumulate(stdout: impl Read, target: &str, accumulator: Arc<Mutex<String>>) {
if let Err(e) = worker(stdout, target, accumulator) {
log::error!("accumulate error: {:?}", e);
}
}
fn worker(mut stdout: impl Read, target: &str, accumulator: Arc<Mutex<String>>) -> Result<()> {
let mut s = String::default();
loop {
read(&mut stdout, &mut s)?;
let mut lines = s.split('\n').collect::<Vec<&str>>();
let next = if !s.ends_with('\n') {
// If we didn't read a complete line at the end, save it for the
// next read.
lines.pop()
} else {
None
};
for line in lines {
// see comment at the top of this module
log::info!(target: target, "{}", line.trim_end_matches('\r'));
}
accumulator.lock().unwrap().push_str(&s);
s = next.unwrap_or("").to_string();
}
}
fn read(stdout: &mut impl Read, s: &mut String) -> Result<()> {
let mut buf = [0u8; 256];
let n = stdout.read(&mut buf)?;
s.push_str(&String::from_utf8_lossy(&buf[..n]));
Ok(())
}