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 let next_bytes = bytes_used
162 + 16 + fidl::encoding::round_up_to_align(value.len(), 8);
164 if next_bytes > ZX_CHANNEL_MAX_MSG_BYTES as usize {
165 if result.is_empty() {
166 warn!("Dropping tree name that exceeds channel message size");
167 values_iter.next();
168 continue;
169 }
170 break;
171 }
172 bytes_used = next_bytes;
173 result.push(values_iter.next().unwrap());
174 }
175 }
176 }
177 if result.is_empty() {
178 responder.send(&[])?;
179 return Ok(());
180 }
181 responder.send(&result)?;
182 }
183 TreeNameIteratorRequest::_UnknownMethod { ordinal, method_type, .. } => {
184 warn!(ordinal, method_type:?; "Unknown request");
185 }
186 }
187 }
188 Ok(())
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194 use diagnostics_assertions::{assert_data_tree, assert_json_diff};
195 use fidl_fuchsia_inspect::{TreeNameIteratorMarker, TreeNameIteratorProxy, TreeProxy};
196 use fuchsia_async::DurationExt;
197 use fuchsia_inspect::reader::{DiagnosticsHierarchy, PartialNodeHierarchy, read_with_timeout};
198 use std::sync::Arc;
199
200 use futures::FutureExt;
201 use std::time::Duration;
202
203 pub fn spawn_server_proxy(
206 inspector: Inspector,
207 settings: TreeServerSendPreference,
208 ) -> (Arc<fasync::Scope>, TreeProxy) {
209 let scope = Arc::new(fasync::Scope::new());
210 (scope.clone(), spawn_tree_server(inspector, settings, scope.as_handle()).into_proxy())
211 }
212
213 #[fuchsia::test]
214 async fn get_contents() -> Result<(), Error> {
215 let (_server, tree) =
216 spawn_server_proxy(test_inspector(), TreeServerSendPreference::default());
217 let tree_content = tree.get_content().await?;
218 let hierarchy = parse_content(tree_content)?;
219 assert_data_tree!(hierarchy, root: {
220 a: 1i64,
221 });
222 Ok(())
223 }
224
225 #[fuchsia::test]
226 async fn list_child_names() -> Result<(), Error> {
227 let (_server, tree) =
228 spawn_server_proxy(test_inspector(), TreeServerSendPreference::default());
229 let (name_iterator, server_end) = fidl::endpoints::create_proxy::<TreeNameIteratorMarker>();
230 tree.list_child_names(server_end)?;
231 verify_iterator(name_iterator, vec!["lazy-0".to_string()]).await?;
232 Ok(())
233 }
234
235 #[fuchsia::test]
236 async fn oversized_tree_name_is_dropped() -> Result<(), Error> {
237 let (name_iterator, server_end) = fidl::endpoints::create_proxy::<TreeNameIteratorMarker>();
238 let scope = fasync::Scope::new();
239 scope.spawn(async move {
240 let oversized = "a".repeat(ZX_CHANNEL_MAX_MSG_BYTES as usize);
241 run_tree_name_iterator_server(
242 vec![oversized, "normal".to_string()],
243 server_end.into_stream(),
244 )
245 .await
246 .unwrap();
247 });
248 verify_iterator(name_iterator, vec!["normal".to_string()]).await?;
249 Ok(())
250 }
251
252 #[fuchsia::test]
253 async fn open_children() -> Result<(), Error> {
254 let (_server, tree) =
255 spawn_server_proxy(test_inspector(), TreeServerSendPreference::default());
256 let (child_tree, server_end) = fidl::endpoints::create_proxy::<TreeMarker>();
257 tree.open_child("lazy-0", server_end)?;
258 let tree_content = child_tree.get_content().await?;
259 let hierarchy = parse_content(tree_content)?;
260 assert_data_tree!(hierarchy, root: {
261 b: 2u64,
262 });
263 let (name_iterator, server_end) = fidl::endpoints::create_proxy::<TreeNameIteratorMarker>();
264 child_tree.list_child_names(server_end)?;
265 verify_iterator(name_iterator, vec!["lazy-vals-0".to_string()]).await?;
266
267 let (child_tree_2, server_end) = fidl::endpoints::create_proxy::<TreeMarker>();
268 child_tree.open_child("lazy-vals-0", server_end)?;
269 let tree_content = child_tree_2.get_content().await?;
270 let hierarchy = parse_content(tree_content)?;
271 assert_data_tree!(hierarchy, root: {
272 c: 3.0,
273 });
274 let (name_iterator, server_end) = fidl::endpoints::create_proxy::<TreeNameIteratorMarker>();
275 child_tree_2.list_child_names(server_end)?;
276 verify_iterator(name_iterator, vec![]).await?;
277
278 Ok(())
279 }
280
281 #[fuchsia::test]
282 async fn default_snapshots_are_private_on_success() -> Result<(), Error> {
283 let inspector = test_inspector();
284 let (_server, tree_copy) =
285 spawn_server_proxy(inspector.clone(), TreeServerSendPreference::default());
286 let tree_content_copy = tree_copy.get_content().await?;
287
288 inspector.root().record_int("new", 6);
289
290 let hierarchy = parse_content(tree_content_copy)?;
292 assert_data_tree!(hierarchy, root: {
293 a: 1i64,
294 });
295 Ok(())
296 }
297
298 #[fuchsia::test]
299 async fn force_live_snapshot() -> Result<(), Error> {
300 let inspector = test_inspector();
301 let (_server1, tree_cow) =
302 spawn_server_proxy(inspector.clone(), TreeServerSendPreference::default());
303 let (_server2, tree_live) =
304 spawn_server_proxy(inspector.clone(), TreeServerSendPreference::Live);
305 let tree_content_live = tree_live.get_content().await?;
306 let tree_content_cow = tree_cow.get_content().await?;
307
308 inspector.root().record_int("new", 6);
309
310 let hierarchy = parse_content(tree_content_cow)?;
312 assert_data_tree!(hierarchy, root: {
313 a: 1i64,
314 });
315
316 let hierarchy = parse_content(tree_content_live)?;
318 assert_data_tree!(hierarchy, root: {
319 a: 1i64,
320 new: 6i64,
321 });
322 Ok(())
323 }
324
325 #[fuchsia::test]
326 async fn read_hanging_lazy_node() -> Result<(), Error> {
327 let inspector = Inspector::default();
328 let root = inspector.root();
329 root.record_string("child", "value");
330
331 root.record_lazy_values("lazy-node-always-hangs", || {
332 async move {
333 fuchsia_async::Timer::new(zx::MonotonicDuration::from_minutes(30).after_now())
334 .await;
335 Ok(Inspector::default())
336 }
337 .boxed()
338 });
339
340 root.record_lazy_values("lazy-node-doesnt-hang", || {
341 async move {
342 let inspector = Inspector::default();
343 inspector.root().record_string("from", "non-hanging");
344 Ok(inspector)
345 }
346 .boxed()
347 });
348
349 root.record_int("int", 3);
350
351 let instrumentation = Inspector::default();
352 let counter = instrumentation.root().create_uint("counter", 0);
353
354 let (_server, proxy) = spawn_server_proxy(inspector, TreeServerSendPreference::default());
355 let result = read_with_timeout(&proxy, Duration::from_secs(5), &counter).await?;
356
357 assert_json_diff!(instrumentation, root: { counter: 1 });
358
359 assert_json_diff!(result, root: {
360 child: "value",
361 int: 3i64,
362 from: "non-hanging",
363 });
364
365 Ok(())
366 }
367
368 async fn verify_iterator(
369 name_iterator: TreeNameIteratorProxy,
370 values: Vec<String>,
371 ) -> Result<(), Error> {
372 if !values.is_empty() {
373 assert_eq!(name_iterator.get_next().await?, values);
374 }
375 assert!(name_iterator.get_next().await?.is_empty());
376 assert!(name_iterator.get_next().await.is_err());
377 Ok(())
378 }
379
380 fn parse_content(tree_content: TreeContent) -> Result<DiagnosticsHierarchy, Error> {
381 let buffer = tree_content.buffer.unwrap();
382 Ok(PartialNodeHierarchy::try_from(&buffer.vmo)?.into())
383 }
384
385 fn test_inspector() -> Inspector {
386 let inspector = Inspector::default();
387 inspector.root().record_int("a", 1);
388 inspector.root().record_lazy_child("lazy", || {
389 async move {
390 let inspector = Inspector::default();
391 inspector.root().record_uint("b", 2);
392 inspector.root().record_lazy_values("lazy-vals", || {
393 async move {
394 let inspector = Inspector::default();
395 inspector.root().record_double("c", 3.0);
396 Ok(inspector)
397 }
398 .boxed()
399 });
400 Ok(inspector)
401 }
402 .boxed()
403 });
404 inspector
405 }
406}