1use crate::cargo::TestBinary;
2use crate::config::Config;
3#[cfg(ptrace_supported)]
4use crate::ptrace_control::*;
5#[cfg(ptrace_supported)]
6use crate::statemachine::ProcessInfo;
7#[cfg(ptrace_supported)]
8use crate::statemachine::TracerAction;
9use crate::traces::Location;
10#[cfg(ptrace_supported)]
11use crate::traces::TraceMap;
12use chrono::offset::Local;
13#[cfg(ptrace_supported)]
14use nix::libc::*;
15#[cfg(ptrace_supported)]
16use nix::sys::{signal::Signal, wait::WaitStatus};
17use serde::{Deserialize, Serialize};
18use std::cell::RefCell;
19use std::collections::HashSet;
20use std::fs::File;
21use std::path::PathBuf;
22use std::time::Instant;
23use tracing::{info, warn};
24
25#[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
26pub enum Event {
27 ConfigLaunch(String),
28 BinaryLaunch(TestBinary),
29 Trace(TraceEvent),
30 Marker(Option<()>),
31}
32
33#[derive(Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
34pub struct EventWrapper {
35 #[serde(flatten)]
36 event: Event,
37 created: f64,
39}
40
41impl EventWrapper {
42 fn new(event: Event, since: Instant) -> Self {
43 let created = Instant::now().duration_since(since).as_secs_f64();
44 Self { event, created }
45 }
46}
47
48#[derive(Clone, Default, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
49pub struct TraceEvent {
50 pid: Option<i64>,
51 child: Option<i64>,
52 signal: Option<String>,
53 addr: Option<u64>,
54 return_val: Option<i64>,
55 location: Option<Location>,
56 description: String,
57}
58
59impl TraceEvent {
60 #[cfg(ptrace_supported)]
61 pub(crate) fn new_from_action(action: &TracerAction<ProcessInfo>) -> Self {
62 match action {
63 TracerAction::TryContinue(t) => TraceEvent {
64 pid: Some(t.pid.as_raw().into()),
65 signal: t.signal.map(|x| x.to_string()),
66 description: "Try continue child".to_string(),
67 ..Default::default()
68 },
69 TracerAction::Continue(t) => TraceEvent {
70 pid: Some(t.pid.as_raw().into()),
71 signal: t.signal.map(|x| x.to_string()),
72 description: "Continue child".to_string(),
73 ..Default::default()
74 },
75 TracerAction::Step(t) => TraceEvent {
76 pid: Some(t.pid.as_raw().into()),
77 description: "Step child".to_string(),
78 ..Default::default()
79 },
80 TracerAction::Detach(t) => TraceEvent {
81 pid: Some(t.pid.as_raw().into()),
82 description: "Detach child".to_string(),
83 ..Default::default()
84 },
85 TracerAction::Nothing => TraceEvent {
86 description: "Do nothing".to_string(),
87 ..Default::default()
88 },
89 }
90 }
91
92 #[cfg(ptrace_supported)]
93 pub(crate) fn new_from_wait(wait: &WaitStatus, offset: u64, traces: &TraceMap) -> Self {
94 let pid = wait.pid().map(|p| p.as_raw().into());
95 let mut event = TraceEvent {
96 pid,
97 ..Default::default()
98 };
99 match wait {
100 WaitStatus::Exited(_, i) => {
101 event.description = "Exited".to_string();
102 event.return_val = Some((*i).into());
103 }
104 WaitStatus::Signaled(_, sig, _) => {
105 event.signal = Some(sig.to_string());
106 event.description = "Signaled".to_string();
107 }
108 WaitStatus::Stopped(c, sig) => {
109 event.signal = Some(sig.to_string());
110 if *sig == Signal::SIGTRAP {
111 event.description = "Stopped".to_string();
112 event.addr = current_instruction_pointer(*c).ok().map(|x| (x - 1) as u64);
113 if let Some(addr) = event.addr {
114 event.location = traces.get_location(addr - offset);
115 }
116 } else {
117 event.description = "Non-trace stop".to_string();
118 }
119 }
120 WaitStatus::PtraceEvent(pid, sig, val) => {
121 event.signal = Some(sig.to_string());
122 match *val {
123 PTRACE_EVENT_CLONE => {
124 event.description = "Ptrace Clone".to_string();
125 if *sig == Signal::SIGTRAP {
126 event.child = get_event_data(*pid).ok();
127 }
128 }
129 PTRACE_EVENT_FORK => {
130 event.description = "Ptrace fork".to_string();
131 if *sig == Signal::SIGTRAP {
132 event.child = get_event_data(*pid).ok();
133 }
134 }
135 PTRACE_EVENT_VFORK => {
136 event.description = "Ptrace vfork".to_string();
137 if *sig == Signal::SIGTRAP {
138 event.child = get_event_data(*pid).ok();
139 }
140 }
141 PTRACE_EVENT_EXEC => {
142 event.description = "Ptrace exec".to_string();
143 }
144 PTRACE_EVENT_EXIT => {
145 event.description = "Ptrace exit".to_string();
146 }
147 _ => {
148 event.description = "Ptrace unknown event".to_string();
149 }
150 }
151 }
152 WaitStatus::Continued(_) => {
153 event.description = "Continued".to_string();
154 }
155 WaitStatus::StillAlive => {
156 event.description = "StillAlive".to_string();
157 }
158 WaitStatus::PtraceSyscall(_) => {
159 event.description = "PtraceSyscall".to_string();
160 }
161 }
162 event
163 }
164}
165
166#[derive(Clone, PartialEq, Serialize, Deserialize)]
167pub struct EventLog {
168 events: RefCell<Vec<EventWrapper>>,
169 #[serde(skip)]
170 start: Option<Instant>,
171 manifest_paths: HashSet<PathBuf>,
172 #[serde(skip)]
173 output_folder: PathBuf,
174}
175
176impl EventLog {
177 pub fn new(manifest_paths: HashSet<PathBuf>, config: &Config) -> Self {
178 Self {
179 events: RefCell::new(vec![]),
180 start: Some(Instant::now()),
181 manifest_paths,
182 output_folder: config.output_dir(),
183 }
184 }
185
186 pub fn push_binary(&self, binary: TestBinary) {
187 self.events.borrow_mut().push(EventWrapper::new(
188 Event::BinaryLaunch(binary),
189 self.start.unwrap(),
190 ));
191 }
192
193 pub fn push_trace(&self, event: TraceEvent) {
194 self.events
195 .borrow_mut()
196 .push(EventWrapper::new(Event::Trace(event), self.start.unwrap()));
197 }
198
199 pub fn push_config(&self, name: String) {
200 self.events.borrow_mut().push(EventWrapper::new(
201 Event::ConfigLaunch(name),
202 self.start.unwrap(),
203 ));
204 }
205
206 pub fn push_marker(&self) {
207 if self
209 .events
210 .borrow()
211 .last()
212 .filter(|x| matches!(x.event, Event::Marker(_)))
213 .is_none()
214 {
215 self.events
216 .borrow_mut()
217 .push(EventWrapper::new(Event::Marker(None), self.start.unwrap()));
218 }
219 }
220}
221
222impl Drop for EventLog {
223 fn drop(&mut self) {
224 let fname = format!("tarpaulin_{}.json", Local::now().format("%Y%m%d%H%M%S"));
225 let path = self.output_folder.join(fname);
226 info!("Serializing tarpaulin debug log to {}", path.display());
227 if let Ok(output) = File::create(path) {
228 if let Err(e) = serde_json::to_writer(output, self) {
229 warn!("Failed to serialise or write result: {e}");
230 }
231 } else {
232 warn!("Failed to create log file");
233 }
234 }
235}