diff --git a/src/cl.rs b/src/cl.rs index 3c6d8b7..302fbe4 100644 --- a/src/cl.rs +++ b/src/cl.rs @@ -36,6 +36,10 @@ pub enum Action { /// Generate profile data file to be merged with others instead of generating systemd options directly #[arg(short, long, default_value = None)] profile_data_path: Option, + /// Log strace output to this file. + /// Only use for debugging: this will slow down processing, and may generate a huge file. + #[arg(short = 'l', long, default_value = None)] + strace_log_path: Option, }, /// Merge profile data from previous runs to generate systemd options MergeProfileData { diff --git a/src/main.rs b/src/main.rs index cfc60bd..4a42740 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,13 +62,14 @@ fn main() -> anyhow::Result<()> { command, mode, profile_data_path, + strace_log_path, } => { // Build supported systemd options let sd_opts = sd_options(&sd_version, &kernel_version, &mode)?; // Run strace let cmd = command.iter().map(|a| &**a).collect::>(); - let st = strace::Strace::run(&cmd)?; + let st = strace::Strace::run(&cmd, strace_log_path)?; // Start signal handling thread let mut signals = signal_hook::iterator::Signals::new([ diff --git a/src/strace/parser/mod.rs b/src/strace/parser/mod.rs index ad1ec89..81aa1f3 100644 --- a/src/strace/parser/mod.rs +++ b/src/strace/parser/mod.rs @@ -1,6 +1,10 @@ //! Strace output parser -use std::io::BufRead; +use std::{ + fs::File, + io::{self, BufRead, BufWriter, Write}, + path::Path, +}; use crate::strace::Syscall; @@ -16,14 +20,22 @@ use regex::parse_line; pub struct LogParser { reader: Box, + log: Option>, buf: String, unfinished_syscalls: Vec, } impl LogParser { - pub fn new(reader: Box) -> anyhow::Result { + pub fn new(reader: Box, log_path: Option<&Path>) -> anyhow::Result { + let log = log_path + .map(|p| -> io::Result<_> { + let file = File::options().create(true).append(true).open(p)?; + Ok(BufWriter::with_capacity(64 * 1024, file)) + }) + .map_or(Ok(None), |v| v.map(Some))?; Ok(Self { reader, + log, buf: String::new(), unfinished_syscalls: Vec::new(), }) @@ -65,6 +77,12 @@ impl Iterator for LogParser { continue; } + if let Some(log) = self.log.as_mut() { + if let Err(e) = writeln!(log, "{line}") { + return Some(Err(e.into())); + } + } + match parse_line(line, &self.unfinished_syscalls) { Ok(ParseResult::Syscall(sc)) => { log::trace!("Parsed line: {line:?}"); @@ -1286,7 +1304,7 @@ mod tests { .as_bytes() .to_vec(), ); - let parser = LogParser::new(Box::new(lines)).unwrap(); + let parser = LogParser::new(Box::new(lines), None).unwrap(); let syscalls: Vec = parser.into_iter().collect::>().unwrap(); assert_eq!( diff --git a/src/strace/run.rs b/src/strace/run.rs index 33bb307..dac7bce 100644 --- a/src/strace/run.rs +++ b/src/strace/run.rs @@ -14,10 +14,12 @@ pub struct Strace { process: Child, /// Temp dir for pipe location pipe_dir: tempfile::TempDir, + /// Strace log mirror path + log_path: Option, } impl Strace { - pub fn run(command: &[&str]) -> anyhow::Result { + pub fn run(command: &[&str], log_path: Option) -> anyhow::Result { // Create named pipe let pipe_dir = tempfile::tempdir()?; let pipe_path = Self::pipe_path(&pipe_dir); @@ -57,6 +59,7 @@ impl Strace { Ok(Self { process: child, pipe_dir, + log_path, }) } @@ -67,7 +70,7 @@ impl Strace { pub fn log_lines(&self) -> anyhow::Result { let pipe_path = Self::pipe_path(&self.pipe_dir); let reader = BufReader::new(File::open(pipe_path)?); - LogParser::new(Box::new(reader)) + LogParser::new(Box::new(reader), self.log_path.as_deref()) } }