intercept/
trace.rs

1/*  Copyright (C) 2012-2018 by László Nagy
2    This file is part of Bear.
3
4    Bear is a tool to generate compilation database for clang tooling.
5
6    Bear is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation, either version 3 of the License, or
9    (at your option) any later version.
10
11    Bear is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20use std::env;
21use std::io;
22use std::fs;
23use std::path;
24use libc;
25use serde_json;
26
27use Result;
28use Error;
29
30#[derive(Serialize, Deserialize)]
31pub struct Trace {
32    pid: libc::pid_t,
33    cwd: path::PathBuf,
34    cmd: Vec<String>
35}
36
37impl Trace {
38    pub fn new(pid: libc::pid_t, cwd: path::PathBuf, cmd: Vec<String>) -> Trace {
39        Trace { pid: pid, cwd: cwd, cmd: cmd }
40    }
41
42    pub fn get_pid(&self) -> libc::pid_t {
43        self.pid
44    }
45
46    pub fn get_cwd(&self) -> &path::Path {
47        &self.cwd
48    }
49
50    pub fn get_cmd(&self) -> &[String] {
51        &self.cmd
52    }
53
54    /// Create a Trace report object from the given arguments.
55    /// Capture the current process id and working directory.
56    pub fn create(args: &Vec<String>) -> Result<Trace> {
57        let pid: libc::pid_t = unsafe { libc::getpid() };
58        let cwd = env::current_dir()?;
59
60        Ok(Trace::new(pid, cwd, args.clone()))
61    }
62
63    /// Write a single trace entry into the given target.
64    pub fn write(target: &mut io::Write, value: &Trace) -> Result<()> {
65        let result = serde_json::to_writer(target, value)?;
66        Ok(result)
67    }
68
69    /// Read a single trace file content from given source.
70    pub fn read(source: &mut io::Read) -> Result<Trace> {
71        let result = serde_json::from_reader(source)?;
72        Ok(result)
73    }
74}
75
76
77pub struct TraceDirectory {
78    input: fs::ReadDir
79}
80
81impl TraceDirectory {
82
83    /// Create a TraceDirectory object from the directory path.
84    pub fn new(path: &path::Path) -> Result<TraceDirectory> {
85        if path.is_dir() {
86            let input = fs::read_dir(path)?;
87            Ok(TraceDirectory { input: input })
88        } else {
89            Err(Error::RuntimeError("TraceSource should be directory".to_string()))
90        }
91    }
92
93    fn is_execution_trace(path: &path::Path) -> bool {
94        const EXTENSION: &'static str = ".process_start.json";
95
96        path.to_str().map_or(false, |str| { str.ends_with(EXTENSION) })
97    }
98}
99
100impl Iterator for TraceDirectory {
101    type Item = path::PathBuf;
102
103    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
104        match self.input.next() {
105            Some(Ok(entry)) => {
106                let path = entry.path();
107                if path.is_dir() {
108                    self.next()
109                } else if TraceDirectory::is_execution_trace(&path) {
110                    Some(path.to_path_buf())
111                } else {
112                    self.next()
113                }
114            },
115            Some(Err(_)) => self.next(),
116            _ => None
117        }
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn is_execution_trace() {
127        let valid = path::Path::new("/tmp/5432.process_start.json");
128        assert!(TraceDirectory::is_execution_trace(valid));
129        let invalid = path::Path::new("/tmp/something.json");
130        assert!(!TraceDirectory::is_execution_trace(invalid));
131    }
132
133    #[test]
134    fn read_works_on_unkown_fields() {
135        let mut data = r#"{
136                    "pid": 12,
137                    "ppid": 11,
138                    "pppid": 10,
139                    "cwd": "/usr/home/user",
140                    "cmd": [
141                      "program",
142                      "arg1",
143                      "arg2"
144                    ]
145                  }"#.as_bytes();
146
147        let result = Trace::read(&mut data).unwrap();
148
149        assert_eq!(12i32, result.get_pid());
150        assert_eq!(path::Path::new("/usr/home/user"), result.get_cwd());
151        assert_eq!(["program", "arg1", "arg2"], result.get_cmd());
152    }
153
154    #[test]
155    fn write_and_read_works() {
156        let expected = Trace::new(12i32,
157                                  path::PathBuf::from("/home/user"),
158                                  vec!["program".to_string(), "arg".to_string()]);
159
160        let mut data: [u8; 100] = [0; 100];
161        {
162            let mut buffer = &mut data[..];
163            let _result = Trace::write(&mut buffer, &expected).unwrap();
164        }
165        {
166            let end = data.iter().position(|&c| c == 0).unwrap();
167            let mut buffer = &data[..end];
168            let result = Trace::read(&mut buffer).unwrap();
169
170            assert_eq!(expected.get_pid(), result.get_pid());
171            assert_eq!(expected.get_cwd(), result.get_cwd());
172            assert_eq!(expected.get_cmd(), result.get_cmd());
173        }
174    }
175}