sl4f_lib/tracing/
facade.rs1use crate::tracing::types::{
6 InitializeRequest, ResultsDestination, TerminateRequest, TerminateResponse,
7};
8use anyhow::Error;
9use base64::engine::general_purpose::STANDARD as BASE64_STANDARD;
10use base64::engine::Engine as _;
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::{from_value, to_value, 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 status = self.status.read();
124 let trace_controller = status
125 .controller
126 .as_ref()
127 .ok_or_else(|| format_err!("No trace session has been initialized"))?;
128 let options = StartOptions::default();
129 let response = trace_controller.start_tracing(&options).await?;
130 match response {
131 Ok(_) => Ok(to_value(())?),
132 Err(e) => match e {
133 StartError::NotInitialized => {
134 Err(format_err!("trace_manager reports trace not initialized"))
135 }
136 StartError::AlreadyStarted => Err(format_err!("Trace already started")),
137 StartError::Stopping => Err(format_err!("Trace is stopping")),
138 StartError::Terminating => Err(format_err!("Trace is terminating")),
139 _ => Err(format_err!("Unhandled error code during trace start")),
140 },
141 }
142 }
143
144 pub async fn stop(&self) -> Result<Value, Error> {
149 let status = self.status.read();
150 let trace_controller = status
151 .controller
152 .as_ref()
153 .ok_or_else(|| format_err!("No trace session has been initialized"))?;
154 let options = StopOptions::default();
155 let _ = trace_controller.stop_tracing(&options).await?;
156 Ok(to_value(())?)
157 }
158
159 pub async fn terminate(&self, args: Value) -> Result<Value, Error> {
170 let request: TerminateRequest = parse_args(args)?;
171
172 let _ = self.status.write().controller.take();
175
176 let result = match request.results_destination {
177 ResultsDestination::Ignore => TerminateResponse { data: None },
178 ResultsDestination::WriteAndReturn => {
179 let data_socket = self.status.write().data_socket.take();
180 let drain_result = drain_socket(data_socket).await?;
181
182 TerminateResponse { data: Some(BASE64_STANDARD.encode(&drain_result)) }
183 }
184 };
185
186 Ok(to_value(result)?)
187 }
188}
189
190fn parse_args<T: serde::de::DeserializeOwned>(args: Value) -> Result<T, serde_json::error::Error> {
193 if args.is_null() {
194 from_value(serde_json::value::Value::Object(serde_json::map::Map::new()))
195 } else {
196 from_value(args)
197 }
198}
199
200async fn drain_socket(socket: Option<zx::Socket>) -> Result<Vec<u8>, Error> {
201 let mut ret = Vec::new();
202 if let Some(socket) = socket {
203 let mut socket = fuchsia_async::Socket::from_socket(socket);
204 socket.read_to_end(&mut ret).await?;
205 }
206 Ok(ret)
207}
208
209impl Status {
210 fn new() -> Status {
211 Status { controller: None, data_socket: None }
212 }
213}