test_realm_helpers/
tracing.rs1use anyhow::{format_err, Context};
6use fidl::endpoints::Proxy;
7use fuchsia_component::client::connect_to_protocol_at;
8use fuchsia_sync::Mutex;
9use zx::prelude::*;
10
11use futures::AsyncReadExt;
12use log::{info, warn};
13use std::io::Write;
14use std::sync::Arc;
15
16pub struct Tracing {
22 trace_session: Arc<Mutex<Option<fidl_fuchsia_tracing_controller::SessionSynchronousProxy>>>,
23 tracing_collector: Arc<Mutex<Option<std::thread::JoinHandle<Result<Vec<u8>, anyhow::Error>>>>>,
24}
25
26impl Tracing {
27 pub async fn create_and_initialize_tracing(
28 test_ns_prefix: &str,
29 ) -> Result<Self, anyhow::Error> {
30 let launcher =
31 connect_to_protocol_at::<fidl_fuchsia_tracing_controller::ProvisionerMarker>(
32 test_ns_prefix,
33 )
34 .map_err(|e| format_err!("Failed to get tracing controller: {e:?}"))?;
35 let launcher = fidl_fuchsia_tracing_controller::ProvisionerSynchronousProxy::new(
36 fidl::Channel::from_handle(
37 launcher
38 .into_channel()
39 .map_err(|e| format_err!("Failed to get fidl::AsyncChannel from proxy: {e:?}"))?
40 .into_zx_channel()
41 .into_handle(),
42 ),
43 );
44 let (tracing_socket, tracing_socket_write) = fidl::Socket::create_stream();
45 let (trace_session, server) =
46 fidl::endpoints::create_sync_proxy::<fidl_fuchsia_tracing_controller::SessionMarker>();
47 launcher
48 .initialize_tracing(
49 server,
50 &fidl_fuchsia_tracing_controller::TraceConfig {
51 categories: Some(vec!["wlan".to_string()]),
52 buffer_size_megabytes_hint: Some(64),
53 ..Default::default()
54 },
55 tracing_socket_write,
56 )
57 .map_err(|e| format_err!("Failed to initialize tracing: {e:?}"))?;
58
59 let tracing_collector = std::thread::spawn(move || {
60 let mut executor = fuchsia_async::LocalExecutor::new();
61 executor.run_singlethreaded(async move {
62 let mut tracing_socket = fuchsia_async::Socket::from_socket(tracing_socket);
63 info!("draining trace record socket...");
64 let mut buf = Vec::new();
65 tracing_socket
66 .read_to_end(&mut buf)
67 .await
68 .map_err(|e| format_err!("Failed to drain trace record socket: {e:?}"))?;
69 info!("trace record socket drained.");
70 Ok(buf)
71 })
72 });
73
74 trace_session
75 .start_tracing(
76 &fidl_fuchsia_tracing_controller::StartOptions::default(),
77 zx::MonotonicInstant::INFINITE,
78 )
79 .map_err(|e| format_err!("Encountered FIDL error when starting trace: {e:?}"))?
80 .map_err(|e| format_err!("Failed to start tracing: {e:?}"))?;
81
82 let trace_session = Arc::new(Mutex::new(Some(trace_session)));
83 let tracing_collector = Arc::new(Mutex::new(Some(tracing_collector)));
84
85 let panic_hook = std::panic::take_hook();
86 let trace_session_clone = Arc::clone(&trace_session);
87 let tracing_collector_clone = Arc::clone(&tracing_collector);
88
89 std::panic::set_hook(Box::new(move |panic_info| {
94 let trace_session = trace_session_clone.lock().take().expect("No trace collector");
95 tracing_collector_clone.lock().take().map(move |tracing_collector| {
96 let _: Result<(), ()> =
97 Self::terminate_and_write_trace_(trace_session, tracing_collector)
98 .map_err(|e| warn!("{e:?}"));
99 });
100 panic_hook(panic_info);
101 }));
102
103 Ok(Self { trace_session, tracing_collector })
104 }
105
106 fn terminate_and_write_trace_(
107 trace_session: fidl_fuchsia_tracing_controller::SessionSynchronousProxy,
108 tracing_collector: std::thread::JoinHandle<Result<Vec<u8>, anyhow::Error>>,
109 ) -> Result<(), anyhow::Error> {
110 let _ = trace_session
114 .stop_tracing(
115 &fidl_fuchsia_tracing_controller::StopOptions {
116 write_results: Some(true),
117 ..Default::default()
118 },
119 zx::MonotonicInstant::INFINITE,
120 )
121 .map_err(|e| format_err!("Failed to stop tracing: {:?}", e));
122
123 let trace = tracing_collector
124 .join()
125 .map_err(|e| format_err!("Failed to join tracing collector thread: {e:?}"))?
126 .context(format_err!("Failed to collect trace."))?;
127
128 let fxt_path = format!("/custom_artifacts/trace.fxt");
129 let mut fxt_file = std::fs::File::create(&fxt_path)
130 .map_err(|e| format_err!("Failed to create {}: {e:?}", &fxt_path))?;
131 fxt_file
132 .write_all(&trace[..])
133 .map_err(|e| format_err!("Failed to write to {}: {e:?}", &fxt_path))?;
134 fxt_file.sync_all().map_err(|e| format_err!("Failed to sync to {}: {e:?}", &fxt_path))?;
135 Ok(())
136 }
137
138 fn terminate_and_write_trace(&mut self) {
139 let mut trace_controller_mx = self.trace_session.lock();
140 let trace_controller = trace_controller_mx.take().expect("No controller available");
141 *trace_controller_mx = None;
142 let _: Result<(), ()> = Self::terminate_and_write_trace_(
143 trace_controller,
144 self.tracing_collector
145 .lock()
146 .take()
147 .expect("Failed to acquire join handle for tracing collector."),
148 )
149 .map_err(|e| warn!("{e:?}"));
150 }
151}
152
153impl Drop for Tracing {
154 fn drop(&mut self) {
155 self.terminate_and_write_trace();
156 }
157}