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