1use crate::TreeServerSendPreference;
8use anyhow::Error;
9use fidl_fuchsia_inspect::{
10 TreeContent, TreeMarker, TreeNameIteratorRequest, TreeNameIteratorRequestStream, TreeRequest,
11 TreeRequestStream,
12};
13use fidl_fuchsia_mem::Buffer;
14use fuchsia_async as fasync;
15use fuchsia_inspect::Inspector;
16use fuchsia_inspect::reader::ReadableTree;
17use futures::{FutureExt, TryFutureExt, TryStreamExt};
18use log::warn;
19use zx::sys::ZX_CHANNEL_MAX_MSG_BYTES;
20
21#[allow(clippy::manual_async_fn)] pub fn handle_request_stream(
25 inspector: Inspector,
26 settings: TreeServerSendPreference,
27 mut stream: TreeRequestStream,
28 scope: fasync::Scope,
29) -> impl futures::Future<Output = Result<(), Error>> + Send {
30 async move {
31 while let Some(request) = stream.try_next().await? {
32 match request {
33 TreeRequest::GetContent { responder } => {
34 let vmo = match settings {
36 TreeServerSendPreference::DeepCopy => inspector.copy_vmo(),
37 TreeServerSendPreference::Live => inspector.duplicate_vmo(),
38 TreeServerSendPreference::Frozen { ref on_failure } => {
39 match inspector.frozen_vmo_copy() {
40 Ok(vmo) => Some(vmo),
41 Err(_) => match **on_failure {
42 TreeServerSendPreference::DeepCopy => inspector.copy_vmo(),
43 TreeServerSendPreference::Live => inspector.duplicate_vmo(),
44 _ => None,
45 },
46 }
47 }
48 };
49
50 let buffer_data =
51 vmo.and_then(|vmo| vmo.get_size().ok().map(|size| (vmo, size)));
52 let content = TreeContent {
53 buffer: buffer_data.map(|data| Buffer { vmo: data.0, size: data.1 }),
54 ..Default::default()
55 };
56 responder.send(content)?;
57 }
58 TreeRequest::ListChildNames { tree_iterator, .. } => {
59 let request_stream = tree_iterator.into_stream();
60 let inspector = inspector.clone();
61 scope.spawn(async move {
62 inspector
63 .tree_names()
64 .map_err(|e| anyhow::anyhow!("{e:?}"))
65 .and_then(|values| {
66 run_tree_name_iterator_server(values, request_stream)
67 })
68 .map_err(|err| {
69 warn!(err:?; "failed to run tree name iterator server");
70 })
71 .map(|_| {})
72 .await
73 });
74 }
75 TreeRequest::OpenChild { child_name, tree, .. } => {
76 let nested = scope.new_child_with_name("nested_tree_server");
77 let inspector = inspector.clone();
78 let settings = settings.clone();
79 let stream = tree.into_stream();
80 scope.spawn(async move {
81 inspector
82 .read_tree(&child_name)
83 .map_err(|e| anyhow::anyhow!("{e:?}"))
84 .and_then(|inspector| {
85 handle_request_stream(inspector, settings, stream, nested)
86 })
87 .map_err(|err| {
88 warn!(err:?; "failed to run `fuchsia.inspect.Tree` server");
89 })
90 .map(|_| {})
91 .await
92 });
93 }
94 TreeRequest::_UnknownMethod { ordinal, method_type, .. } => {
95 warn!(ordinal, method_type:?; "Unknown request");
96 }
97 }
98 }
99
100 scope.join().await;
101
102 Ok(())
103 }
104}
105
106pub fn spawn_tree_server_with_stream(
113 inspector: Inspector,
114 settings: TreeServerSendPreference,
115 stream: TreeRequestStream,
116 scope: &fasync::ScopeHandle,
117) {
118 scope.spawn(
119 handle_request_stream(
120 inspector,
121 settings,
122 stream,
123 scope.new_child_with_name("tree_server"),
124 )
125 .map(|e| {
126 e.unwrap_or_else(
127 |err: Error| warn!(err:?; "failed to run `fuchsia.inspect.Tree` server"),
128 );
129 }),
130 );
131}
132
133pub fn spawn_tree_server(
136 inspector: Inspector,
137 settings: TreeServerSendPreference,
138 scope: &fasync::ScopeHandle,
139) -> fidl::endpoints::ClientEnd<TreeMarker> {
140 let (tree, server_end) = fidl::endpoints::create_endpoints::<TreeMarker>();
141 spawn_tree_server_with_stream(inspector, settings, server_end.into_stream(), scope);
142 tree
143}
144
145async fn run_tree_name_iterator_server(
148 values: Vec<String>,
149 mut stream: TreeNameIteratorRequestStream,
150) -> Result<(), anyhow::Error> {
151 let mut values_iter = values.into_iter().peekable();
152 while let Some(request) = stream.try_next().await? {
153 match request {
154 TreeNameIteratorRequest::GetNext { responder } => {
155 let mut bytes_used: usize = 32; let mut result = vec![];
157 loop {
158 match values_iter.peek() {
159 None => break,
160 Some(value) => {
161 bytes_used += 16; bytes_used += fidl::encoding::round_up_to_align(value.len(), 8);
163 if bytes_used > ZX_CHANNEL_MAX_MSG_BYTES as usize {
164 break;
165 }
166 result.push(values_iter.next().unwrap());
167 }
168 }
169 }
170 if result.is_empty() {
171 responder.send(&[])?;
172 return Ok(());
173 }
174 responder.send(&result)?;
175 }
176 TreeNameIteratorRequest::_UnknownMethod { ordinal, method_type, .. } => {
177 warn!(ordinal, method_type:?; "Unknown request");
178 }
179 }
180 }
181 Ok(())
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187 use diagnostics_assertions::{assert_data_tree, assert_json_diff};
188 use fidl_fuchsia_inspect::{TreeNameIteratorMarker, TreeNameIteratorProxy, TreeProxy};
189 use fuchsia_async::DurationExt;
190 use fuchsia_inspect::reader::{DiagnosticsHierarchy, PartialNodeHierarchy, read_with_timeout};
191 use std::sync::Arc;
192
193 use futures::FutureExt;
194 use std::time::Duration;
195
196 pub fn spawn_server_proxy(
199 inspector: Inspector,
200 settings: TreeServerSendPreference,
201 ) -> (Arc<fasync::Scope>, TreeProxy) {
202 let scope = Arc::new(fasync::Scope::new());
203 (scope.clone(), spawn_tree_server(inspector, settings, scope.as_handle()).into_proxy())
204 }
205
206 #[fuchsia::test]
207 async fn get_contents() -> Result<(), Error> {
208 let (_server, tree) =
209 spawn_server_proxy(test_inspector(), TreeServerSendPreference::default());
210 let tree_content = tree.get_content().await?;
211 let hierarchy = parse_content(tree_content)?;
212 assert_data_tree!(hierarchy, root: {
213 a: 1i64,
214 });
215 Ok(())
216 }
217
218 #[fuchsia::test]
219 async fn list_child_names() -> Result<(), Error> {
220 let (_server, tree) =
221 spawn_server_proxy(test_inspector(), TreeServerSendPreference::default());
222 let (name_iterator, server_end) = fidl::endpoints::create_proxy::<TreeNameIteratorMarker>();
223 tree.list_child_names(server_end)?;
224 verify_iterator(name_iterator, vec!["lazy-0".to_string()]).await?;
225 Ok(())
226 }
227
228 #[fuchsia::test]
229 async fn open_children() -> Result<(), Error> {
230 let (_server, tree) =
231 spawn_server_proxy(test_inspector(), TreeServerSendPreference::default());
232 let (child_tree, server_end) = fidl::endpoints::create_proxy::<TreeMarker>();
233 tree.open_child("lazy-0", server_end)?;
234 let tree_content = child_tree.get_content().await?;
235 let hierarchy = parse_content(tree_content)?;
236 assert_data_tree!(hierarchy, root: {
237 b: 2u64,
238 });
239 let (name_iterator, server_end) = fidl::endpoints::create_proxy::<TreeNameIteratorMarker>();
240 child_tree.list_child_names(server_end)?;
241 verify_iterator(name_iterator, vec!["lazy-vals-0".to_string()]).await?;
242
243 let (child_tree_2, server_end) = fidl::endpoints::create_proxy::<TreeMarker>();
244 child_tree.open_child("lazy-vals-0", server_end)?;
245 let tree_content = child_tree_2.get_content().await?;
246 let hierarchy = parse_content(tree_content)?;
247 assert_data_tree!(hierarchy, root: {
248 c: 3.0,
249 });
250 let (name_iterator, server_end) = fidl::endpoints::create_proxy::<TreeNameIteratorMarker>();
251 child_tree_2.list_child_names(server_end)?;
252 verify_iterator(name_iterator, vec![]).await?;
253
254 Ok(())
255 }
256
257 #[fuchsia::test]
258 async fn default_snapshots_are_private_on_success() -> Result<(), Error> {
259 let inspector = test_inspector();
260 let (_server, tree_copy) =
261 spawn_server_proxy(inspector.clone(), TreeServerSendPreference::default());
262 let tree_content_copy = tree_copy.get_content().await?;
263
264 inspector.root().record_int("new", 6);
265
266 let hierarchy = parse_content(tree_content_copy)?;
268 assert_data_tree!(hierarchy, root: {
269 a: 1i64,
270 });
271 Ok(())
272 }
273
274 #[fuchsia::test]
275 async fn force_live_snapshot() -> Result<(), Error> {
276 let inspector = test_inspector();
277 let (_server1, tree_cow) =
278 spawn_server_proxy(inspector.clone(), TreeServerSendPreference::default());
279 let (_server2, tree_live) =
280 spawn_server_proxy(inspector.clone(), TreeServerSendPreference::Live);
281 let tree_content_live = tree_live.get_content().await?;
282 let tree_content_cow = tree_cow.get_content().await?;
283
284 inspector.root().record_int("new", 6);
285
286 let hierarchy = parse_content(tree_content_cow)?;
288 assert_data_tree!(hierarchy, root: {
289 a: 1i64,
290 });
291
292 let hierarchy = parse_content(tree_content_live)?;
294 assert_data_tree!(hierarchy, root: {
295 a: 1i64,
296 new: 6i64,
297 });
298 Ok(())
299 }
300
301 #[fuchsia::test]
302 async fn read_hanging_lazy_node() -> Result<(), Error> {
303 let inspector = Inspector::default();
304 let root = inspector.root();
305 root.record_string("child", "value");
306
307 root.record_lazy_values("lazy-node-always-hangs", || {
308 async move {
309 fuchsia_async::Timer::new(zx::MonotonicDuration::from_minutes(30).after_now())
310 .await;
311 Ok(Inspector::default())
312 }
313 .boxed()
314 });
315
316 root.record_lazy_values("lazy-node-doesnt-hang", || {
317 async move {
318 let inspector = Inspector::default();
319 inspector.root().record_string("from", "non-hanging");
320 Ok(inspector)
321 }
322 .boxed()
323 });
324
325 root.record_int("int", 3);
326
327 let instrumentation = Inspector::default();
328 let counter = instrumentation.root().create_uint("counter", 0);
329
330 let (_server, proxy) = spawn_server_proxy(inspector, TreeServerSendPreference::default());
331 let result = read_with_timeout(&proxy, Duration::from_secs(5), &counter).await?;
332
333 assert_json_diff!(instrumentation, root: { counter: 1 });
334
335 assert_json_diff!(result, root: {
336 child: "value",
337 int: 3i64,
338 from: "non-hanging",
339 });
340
341 Ok(())
342 }
343
344 async fn verify_iterator(
345 name_iterator: TreeNameIteratorProxy,
346 values: Vec<String>,
347 ) -> Result<(), Error> {
348 if !values.is_empty() {
349 assert_eq!(name_iterator.get_next().await?, values);
350 }
351 assert!(name_iterator.get_next().await?.is_empty());
352 assert!(name_iterator.get_next().await.is_err());
353 Ok(())
354 }
355
356 fn parse_content(tree_content: TreeContent) -> Result<DiagnosticsHierarchy, Error> {
357 let buffer = tree_content.buffer.unwrap();
358 Ok(PartialNodeHierarchy::try_from(&buffer.vmo)?.into())
359 }
360
361 fn test_inspector() -> Inspector {
362 let inspector = Inspector::default();
363 inspector.root().record_int("a", 1);
364 inspector.root().record_lazy_child("lazy", || {
365 async move {
366 let inspector = Inspector::default();
367 inspector.root().record_uint("b", 2);
368 inspector.root().record_lazy_values("lazy-vals", || {
369 async move {
370 let inspector = Inspector::default();
371 inspector.root().record_double("c", 3.0);
372 Ok(inspector)
373 }
374 .boxed()
375 });
376 Ok(inspector)
377 }
378 .boxed()
379 });
380 inspector
381 }
382}