test_realm_helpers/
tracing.rs1use crate::trace_runner::{TerminationResult, TraceRunner};
6use anyhow::format_err;
7use log::{info, warn};
8use std::sync::atomic::{AtomicUsize, Ordering};
9use std::time::Duration;
10
11static NEXT_TRACING_ID: AtomicUsize = AtomicUsize::new(0);
12static DEFAULT_TRACE_TIMEOUT: Duration = Duration::from_mins(2);
13static DEFAULT_TRACE_FILE_MAX_BYTES: usize = 100 * 1024 * 1024;
14
15pub struct Tracing {
18 output_trace_path: std::ffi::OsString,
19 tracer: TraceRunner,
20 always_record_trace: bool,
21}
22
23pub(crate) struct HermeticityParameters {
24 pub(crate) service_prefix: String,
25}
26
27pub(crate) enum Hermeticity {
28 NonHermetic,
29 Hermetic(HermeticityParameters),
30}
31
32impl Hermeticity {
33 pub fn new_hermetic(service_prefix: &str) -> Self {
34 Self::Hermetic(HermeticityParameters { service_prefix: service_prefix.to_string() })
35 }
36}
37
38impl Tracing {
39 pub async fn start_non_hermetic(trace_file_prefix: &str) -> Result<Self, anyhow::Error> {
40 Self::start_(
41 trace_file_prefix,
42 Hermeticity::NonHermetic,
43 false,
44 DEFAULT_TRACE_TIMEOUT,
45 DEFAULT_TRACE_FILE_MAX_BYTES,
46 )
47 .await
48 }
49
50 pub async fn start_at(service_prefix: &str) -> Result<Self, anyhow::Error> {
51 Self::start_(
52 service_prefix.strip_prefix("/").ok_or_else(|| {
53 format_err!("Provided service prefix does not start with '/': {service_prefix}")
54 })?,
55 Hermeticity::new_hermetic(service_prefix),
56 false,
57 DEFAULT_TRACE_TIMEOUT,
58 DEFAULT_TRACE_FILE_MAX_BYTES,
59 )
60 .await
61 }
62
63 async fn start_<'a>(
64 trace_file_prefix: &'a str,
65 hermeticity: Hermeticity,
66 always_record_trace: bool,
67 trace_timeout: Duration,
68 trace_file_max_bytes: usize,
69 ) -> Result<Self, anyhow::Error> {
70 let tracing_id = NEXT_TRACING_ID.fetch_add(1, Ordering::SeqCst);
75 let output_trace_path = std::path::Path::new(
76 format!("/custom_artifacts/{tracing_id:04}-{trace_file_prefix}-trace.fxt").as_str(),
77 )
78 .to_path_buf();
79
80 let tracer = TraceRunner::start(
81 hermeticity,
82 output_trace_path.clone(),
83 trace_timeout,
84 trace_file_max_bytes,
85 )
86 .await?;
87
88 Ok(Tracing { output_trace_path: output_trace_path.into(), tracer, always_record_trace })
89 }
90}
91
92impl Drop for Tracing {
93 fn drop(&mut self) {
94 let always_record_trace = self.always_record_trace;
95 let output_trace_path = self.output_trace_path.clone();
96
97 match self.tracer.terminate_trace(format!("Tracing::drop")) {
98 TerminationResult { termination_signal: None, .. } => {
99 warn!("Terminate signal sent before Tracing::drop. Keeping trace.");
100 return;
101 }
102 TerminationResult { termination_signal: Some(Err(e)), .. } => {
103 warn!("Failed to signal termination of trace-writer: {e:?}");
104 return;
105 }
106 TerminationResult { trace_writer: Some(Err(e)), .. } => {
107 warn!("Failed to terminate trace-writer: {e:?}");
108 return;
109 }
110 _ => (),
111 }
112
113 if !always_record_trace {
114 info!("Discarding trace because Tracing instance dropped before a panic.");
115 let _: Result<(), ()> = std::fs::remove_file(&output_trace_path)
116 .map_err(|e| warn!("Failed to remove {}: {e:?}", output_trace_path.display()));
117 }
118 }
119}