sl4f_lib/tracing/
facade.rs1use crate::tracing::types::{
6 InitializeRequest, ResultsDestination, TerminateRequest, TerminateResponse,
7};
8use anyhow::Error;
9use base64::engine::Engine as _;
10use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
11use fidl_fuchsia_tracing_controller::{
12 ProvisionerMarker, SessionMarker, SessionProxy, StartError, StartOptions, StopOptions,
13 TraceConfig,
14};
15use fuchsia_component::{self as app};
16use fuchsia_sync::RwLock;
17
18use futures::io::AsyncReadExt;
19use serde_json::{Value, from_value, to_value};
20
21const DEFAULT_CATEGORIES: &[&'static str] = &[
25 "app",
26 "audio",
27 "benchmark",
28 "blobfs",
29 "fxfs",
30 "gfx",
31 "input",
32 "kernel:meta",
33 "kernel:sched",
34 "magma",
35 "memory:kernel",
36 "minfs",
37 "modular",
38 "net",
39 "storage",
40 "system_metrics",
41 "view",
42 "flutter",
43 "dart",
44 "dart:compiler",
45 "dart:dart",
46 "dart:debugger",
47 "dart:embedder",
48 "dart:gc",
49 "dart:isolate",
50 "dart:profiler",
51 "dart:vm",
52];
53#[derive(Debug)]
62pub struct TracingFacade {
63 status: RwLock<Status>,
64}
65
66#[derive(Debug)]
67pub struct Status {
68 controller: Option<SessionProxy>,
69 data_socket: Option<zx::Socket>,
70}
71
72impl TracingFacade {
73 pub fn new() -> TracingFacade {
74 TracingFacade { status: RwLock::new(Status::new()) }
75 }
76
77 pub async fn initialize(&self, args: Value) -> Result<Value, Error> {
90 let request: InitializeRequest = parse_args(args)?;
91
92 let trace_provisioner = app::client::connect_to_protocol::<ProvisionerMarker>()?;
93 let (trace_controller, server_end) = fidl::endpoints::create_proxy::<SessionMarker>();
94 let (write_socket, read_socket) = zx::Socket::create_stream();
95 let mut config = TraceConfig::default();
96 match request.categories {
97 Some(cats) => {
98 config.categories = Some(cats);
99 }
100 None => {
101 config.categories =
102 Some(DEFAULT_CATEGORIES.iter().map(|&s| s.to_owned()).collect());
103 }
104 }
105 config.buffer_size_megabytes_hint = request.buffer_size;
106
107 trace_provisioner.initialize_tracing(server_end, &config, write_socket)?;
108
109 {
110 let mut status = self.status.write();
111 status.data_socket = Some(read_socket);
112 status.controller = Some(trace_controller);
113 }
114
115 Ok(to_value(())?)
116 }
117
118 pub async fn start(&self) -> Result<Value, Error> {
123 let trace_controller = self
124 .status
125 .read()
126 .controller
127 .clone()
128 .ok_or_else(|| format_err!("No trace session has been initialized"))?;
129 let options = StartOptions::default();
130 let response = trace_controller.start_tracing(&options).await?;
131 match response {
132 Ok(_) => Ok(to_value(())?),
133 Err(e) => match e {
134 StartError::NotInitialized => {
135 Err(format_err!("trace_manager reports trace not initialized"))
136 }
137 StartError::AlreadyStarted => Err(format_err!("Trace already started")),
138 StartError::Stopping => Err(format_err!("Trace is stopping")),
139 StartError::Terminating => Err(format_err!("Trace is terminating")),
140 _ => Err(format_err!("Unhandled error code during trace start")),
141 },
142 }
143 }
144
145 pub async fn stop(&self) -> Result<Value, Error> {
150 let trace_controller = self
151 .status
152 .read()
153 .controller
154 .clone()
155 .ok_or_else(|| format_err!("No trace session has been initialized"))?;
156 let options = StopOptions::default();
157 let _ = trace_controller.stop_tracing(&options).await?;
158 Ok(to_value(())?)
159 }
160
161 pub async fn terminate(&self, args: Value) -> Result<Value, Error> {
172 let request: TerminateRequest = parse_args(args)?;
173
174 let _ = self.status.write().controller.take();
177
178 let result = match request.results_destination {
179 ResultsDestination::Ignore => TerminateResponse { data: None },
180 ResultsDestination::WriteAndReturn => {
181 let data_socket = self.status.write().data_socket.take();
182 let drain_result = drain_socket(data_socket).await?;
183
184 TerminateResponse { data: Some(BASE64_STANDARD.encode(&drain_result)) }
185 }
186 };
187
188 Ok(to_value(result)?)
189 }
190}
191
192fn parse_args<T: serde::de::DeserializeOwned>(args: Value) -> Result<T, serde_json::error::Error> {
195 if args.is_null() {
196 from_value(serde_json::value::Value::Object(serde_json::map::Map::new()))
197 } else {
198 from_value(args)
199 }
200}
201
202async fn drain_socket(socket: Option<zx::Socket>) -> Result<Vec<u8>, Error> {
203 let mut ret = Vec::new();
204 if let Some(socket) = socket {
205 let mut socket = fuchsia_async::Socket::from_socket(socket);
206 socket.read_to_end(&mut ret).await?;
207 }
208 Ok(ret)
209}
210
211impl Status {
212 fn new() -> Status {
213 Status { controller: None, data_socket: None }
214 }
215}