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