1use std::{
4 error::Error,
5 fmt,
6 fs::File,
7 io::{BufRead, BufReader, Lines, Read},
8 os::unix::io::FromRawFd,
9 path::Path,
10 process::Command,
11};
12
13#[derive(Debug)]
14pub struct TraceError {
15 message: String,
16}
17
18impl fmt::Display for TraceError {
19 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
20 write!(f, "{}", self.message)
21 }
22}
23
24impl From<TraceError> for String {
25 fn from(err: TraceError) -> Self {
26 err.message
27 }
28}
29
30impl Error for TraceError {}
31
32impl TraceError {
33 fn new(message: &str) -> Self {
34 Self {
35 message: message.to_string(),
36 }
37 }
38}
39
40pub struct StraceLogStream(Box<dyn BufRead>);
42
43impl Read for StraceLogStream {
44 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
45 self.0.read(buf)
46 }
47}
48
49impl BufRead for StraceLogStream {
50 fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
51 self.0.fill_buf()
52 }
53
54 fn consume(&mut self, amt: usize) {
55 self.0.consume(amt)
56 }
57}
58
59impl StraceLogStream {
60 pub fn open_file<P: AsRef<Path>>(path: P) -> Result<Self, TraceError> {
62 let file = File::open(path.as_ref())
63 .map_err(|e| TraceError::new(&format!("Failed to open log file: {}", e)))?;
64 Ok(Self(Box::new(BufReader::new(file))))
65 }
66
67 pub fn run_cmd<P: AsRef<Path>>(path: P, args: Vec<&str>) -> Result<Self, TraceError> {
69 let mut command_str = path.as_ref().to_string_lossy().to_string();
70 for arg in args {
71 command_str.push(' ');
72 command_str.push_str(arg);
73 }
74
75 let (read_fd, write_fd) = nix::unistd::pipe()
77 .map_err(|e| TraceError::new(&format!("Failed to create pipe: {}", e)))?;
78
79 let read_file = unsafe { std::fs::File::from_raw_fd(read_fd) };
81
82 Command::new("strace")
84 .args([
85 "-o",
86 &format!("/proc/self/fd/{}", write_fd),
87 "-yy",
88 "-f",
89 "sh",
90 "-c",
91 &command_str,
92 ])
93 .spawn()
94 .map_err(|e| {
95 nix::unistd::close(read_fd).ok();
97 nix::unistd::close(write_fd).ok();
98
99 if e.kind() == std::io::ErrorKind::NotFound {
100 TraceError::new(
101 "strace command not found. Please install strace:\n\
102 - Debian/Ubuntu: sudo apt-get install strace\n\
103 - Fedora/RHEL: sudo dnf install strace",
104 )
105 } else {
106 TraceError::new(&format!(
107 "Failed to start strace: {}\n\
108 If this is a permission error, try:\n\
109 sudo sctrace <pattern_file> -- {}",
110 e, command_str
111 ))
112 }
113 })?;
114
115 nix::unistd::close(write_fd).ok();
117
118 Ok(Self(Box::new(BufReader::new(read_file))))
119 }
120
121 pub fn from_string(log_str: &str) -> Result<Self, TraceError> {
123 let cursor = std::io::Cursor::new(log_str.to_string().into_bytes());
124 Ok(Self(Box::new(BufReader::new(cursor))))
125 }
126
127 pub fn lines(self) -> Lines<Self> {
129 BufRead::lines(self)
130 }
131}