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