Skip to main content

inspect_validator/data/
fetch.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::{Error, format_err};
6use fidl::endpoints::create_proxy;
7use fidl_fuchsia_inspect::{TreeMarker, TreeNameIteratorMarker, TreeProxy};
8use std::collections::HashMap;
9use std::future::Future;
10use std::pin::Pin;
11use zx::Vmo;
12
13/// A tree representation of an Inspect-formatted VMO.
14/// It contains the root VMO and all loaded child VMOs.
15#[derive(Debug)]
16pub struct LazyNode {
17    vmo: Vmo,
18    children: Option<HashMap<String, LazyNode>>,
19}
20
21impl LazyNode {
22    /// Creates a VMO tree using the channel given as root and the children trees as read from `channel.open_child`.
23    /// In order to support async recursion, we have to Pin and Box the Future type.
24    pub fn new(
25        channel: TreeProxy,
26    ) -> Pin<Box<dyn Future<Output = Result<LazyNode, Error>> + Send>> {
27        Box::pin(async move {
28            let fetcher = LazyNodeFetcher::new(channel);
29            let vmo = fetcher.get_vmo().await?;
30            let mut children = HashMap::new();
31            for child_name in fetcher.get_child_names().await?.iter() {
32                let child_channel = fetcher.get_child_tree_channel(child_name).await?;
33                let lazy_node = LazyNode::new(child_channel).await?;
34                children.insert(child_name.to_string(), lazy_node);
35            }
36            Ok(LazyNode { vmo, children: Some(children) })
37        })
38    }
39
40    /// Returns VMO ref held by this node.
41    pub fn vmo(&self) -> &Vmo {
42        &self.vmo
43    }
44
45    /// Returns the children nodes held by this node.
46    /// This is a move-operation wherein doing this will result in the underlying child tree being set to None.
47    /// Subsequent calls to this method will yield None.
48    pub fn take_children(&mut self) -> Option<HashMap<String, LazyNode>> {
49        self.children.take()
50    }
51}
52
53// Utility class that wraps around the methods specified Tree protocol.
54// FIDL API is defined here: https://fuchsia.googlesource.com/fuchsia/+/HEAD/sdk/fidl/fuchsia.inspect/tree.fidl#43
55struct LazyNodeFetcher {
56    channel: TreeProxy,
57}
58
59impl LazyNodeFetcher {
60    fn new(channel: TreeProxy) -> LazyNodeFetcher {
61        LazyNodeFetcher { channel }
62    }
63
64    async fn get_vmo(&self) -> Result<Vmo, Error> {
65        let tree_content = self.channel.get_content().await?;
66        tree_content.buffer.map(|b| b.vmo).ok_or_else(|| format_err!("Failed to fetch VMO."))
67    }
68
69    async fn get_child_names(&self) -> Result<Vec<String>, Error> {
70        let (name_iterator, server_end) = create_proxy::<TreeNameIteratorMarker>();
71        self.channel.list_child_names(server_end)?;
72        let mut names = vec![];
73        loop {
74            let subset = name_iterator.get_next().await?;
75            if subset.is_empty() {
76                return Ok(names);
77            }
78            names.extend(subset);
79        }
80    }
81
82    async fn get_child_tree_channel(&self, name: &str) -> Result<TreeProxy, Error> {
83        let (child_channel, server_end) = create_proxy::<TreeMarker>();
84        self.channel.open_child(name, server_end)?;
85        Ok(child_channel)
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92    use anyhow::Context;
93    use fidl_fuchsia_inspect::{
94        TreeContent, TreeNameIteratorRequest, TreeNameIteratorRequestStream, TreeRequest,
95        TreeRequestStream,
96    };
97    use fidl_fuchsia_mem::Buffer;
98    use fuchsia_async as fasync;
99    use futures::{TryFutureExt, TryStreamExt};
100    use log::{error, warn};
101    use std::sync::Arc;
102
103    const MAX_TREE_NAME_LIST_SIZE: usize = 1;
104    const SHARED_VMO: &str = "SHARED";
105    const SINGLE_CHILD_ROOT: &str = "SINGLE_CHILD_ROOT";
106    const NAMED_CHILD_NODE: &str = "NAMED_CHILD_NODE";
107    const NAMED_GRANDCHILD_NODE: &str = "NAMED_GRANDCHILD_NODE";
108    const MULTI_CHILD_ROOT: &str = "MULTI_CHILD_ROOT";
109    const ALL_NODES: [&str; 4] =
110        [SINGLE_CHILD_ROOT, NAMED_CHILD_NODE, NAMED_GRANDCHILD_NODE, MULTI_CHILD_ROOT];
111
112    #[fuchsia::test]
113    async fn complete_vmo_tree_gets_read() -> Result<(), Error> {
114        let vmos = Arc::new(TestVmos::new());
115        let tree = spawn_root_tree_server(SINGLE_CHILD_ROOT, Arc::clone(&vmos))?;
116
117        let root_tree = LazyNode::new(tree).await?;
118        let child_tree = root_tree.children.as_ref().unwrap().get(NAMED_CHILD_NODE).unwrap();
119        let grandchild_tree =
120            child_tree.children.as_ref().unwrap().get(NAMED_GRANDCHILD_NODE).unwrap();
121
122        assert_has_size(root_tree.children.as_ref().unwrap(), 1);
123        assert_has_size(child_tree.children.as_ref().unwrap(), 1);
124        assert_has_size(grandchild_tree.children.as_ref().unwrap(), 0);
125        assert_has_vmo(&vmos, SINGLE_CHILD_ROOT, &root_tree);
126        assert_has_vmo(&vmos, NAMED_CHILD_NODE, child_tree);
127        assert_has_vmo(&vmos, NAMED_GRANDCHILD_NODE, grandchild_tree);
128
129        Ok(())
130    }
131
132    #[fuchsia::test]
133    async fn repeatedly_calls_list_children_until_all_names_are_fetched() -> Result<(), Error> {
134        let vmos = Arc::new(TestVmos::new());
135        let tree = spawn_root_tree_server(MULTI_CHILD_ROOT, Arc::clone(&vmos))?;
136        let mut root_tree = LazyNode::new(tree).await?;
137
138        let children = root_tree.take_children().unwrap();
139
140        assert!(root_tree.children.is_none());
141        assert_has_size(&children, child_names(MULTI_CHILD_ROOT).len());
142        for name in child_names(MULTI_CHILD_ROOT).iter() {
143            assert!(children.contains_key(name));
144        }
145
146        Ok(())
147    }
148
149    fn spawn_root_tree_server(tree_name: &str, vmos: Arc<TestVmos>) -> Result<TreeProxy, Error> {
150        let (tree, request_stream) = fidl::endpoints::create_proxy_and_stream::<TreeMarker>();
151        spawn_tree_server(tree_name.to_string(), vmos, request_stream);
152        Ok(tree)
153    }
154
155    fn spawn_tree_server(name: String, vmos: Arc<TestVmos>, stream: TreeRequestStream) {
156        fasync::Task::spawn(async move {
157            handle_request_stream(name, vmos, stream)
158                .await
159                .unwrap_or_else(|err: Error| error!(err:?; "Couldn't run tree server"));
160        })
161        .detach();
162    }
163
164    async fn handle_request_stream(
165        name: String,
166        vmos: Arc<TestVmos>,
167        mut stream: TreeRequestStream,
168    ) -> Result<(), Error> {
169        while let Some(request) = stream.try_next().await.context("Error running tree server")? {
170            match request {
171                TreeRequest::GetContent { responder } => {
172                    let vmo = vmos.get(&name);
173                    let size = vmo.get_size()?;
174                    let content =
175                        TreeContent { buffer: Some(Buffer { vmo, size }), ..Default::default() };
176                    responder.send(content)?;
177                }
178                TreeRequest::ListChildNames { tree_iterator, .. } => {
179                    let request_stream = tree_iterator.into_stream();
180                    spawn_tree_name_iterator_server(child_names(&name), request_stream)
181                }
182                TreeRequest::OpenChild { child_name, tree, .. } => {
183                    spawn_tree_server(child_name, Arc::clone(&vmos), tree.into_stream())
184                }
185                TreeRequest::_UnknownMethod { ordinal, method_type, .. } => {
186                    warn!(ordinal, method_type:?; "Unknown request");
187                }
188            }
189        }
190        Ok(())
191    }
192
193    fn spawn_tree_name_iterator_server(
194        values: Vec<String>,
195        mut stream: TreeNameIteratorRequestStream,
196    ) {
197        fasync::Task::spawn(
198            async move {
199                let mut values_iter = values.into_iter();
200                while let Some(request) =
201                    stream.try_next().await.context("Error running tree iterator server")?
202                {
203                    match request {
204                        TreeNameIteratorRequest::GetNext { responder } => {
205                            let result = values_iter
206                                .by_ref()
207                                .take(MAX_TREE_NAME_LIST_SIZE)
208                                .collect::<Vec<String>>();
209                            if result.is_empty() {
210                                responder.send(&[])?;
211                                return Ok(());
212                            }
213                            responder.send(&result)?;
214                        }
215                        TreeNameIteratorRequest::_UnknownMethod {
216                            ordinal, method_type, ..
217                        } => {
218                            warn!(ordinal, method_type:?; "Unknown request");
219                        }
220                    }
221                }
222                Ok(())
223            }
224            .unwrap_or_else(|err: Error| {
225                error!(err:?; "Failed to running tree name iterator server");
226            }),
227        )
228        .detach()
229    }
230
231    struct TestVmos {
232        vmos: HashMap<String, zx::Vmo>,
233        default: zx::Vmo,
234    }
235
236    impl TestVmos {
237        fn new() -> TestVmos {
238            let mut vmos = HashMap::new();
239            vmos.insert(SHARED_VMO.to_string(), create_vmo(SHARED_VMO));
240            for value in ALL_NODES.iter() {
241                let vmo = create_vmo(value);
242                vmos.insert(value.to_string(), vmo);
243            }
244            TestVmos { vmos, default: create_vmo(SHARED_VMO) }
245        }
246
247        fn get(&self, value: &str) -> zx::Vmo {
248            duplicate_vmo(self.vmos.get(value).unwrap_or(&self.default))
249        }
250    }
251
252    fn assert_has_vmo(vmos: &TestVmos, name: &str, node: &LazyNode) {
253        let actual = get_vmo_as_buf(node.vmo()).unwrap();
254        let expected = get_vmo_as_buf(&vmos.get(name)).unwrap();
255        assert_eq!(actual, expected);
256    }
257
258    fn assert_has_size(map: &HashMap<String, LazyNode>, size: usize) {
259        assert_eq!(map.len(), size);
260    }
261
262    fn get_vmo_as_buf(vmo: &Vmo) -> Result<Vec<u8>, Error> {
263        let size = vmo.get_size()?;
264        let mut buffer = vec![0u8; size as usize];
265        vmo.read(&mut buffer[..], 0)?;
266        Ok(buffer)
267    }
268
269    // Create a VMO with the contents of the string copied into it.
270    fn create_vmo(value: &str) -> zx::Vmo {
271        let vmo = zx::Vmo::create(value.len().try_into().unwrap()).unwrap();
272        vmo.write(value.as_bytes(), 0).unwrap();
273        vmo
274    }
275
276    fn duplicate_vmo(vmo: &Vmo) -> Vmo {
277        vmo.duplicate_handle(zx::Rights::BASIC | zx::Rights::READ | zx::Rights::MAP).ok().unwrap()
278    }
279
280    fn child_names(value: &str) -> Vec<String> {
281        match value {
282            SINGLE_CHILD_ROOT => vec![NAMED_CHILD_NODE.to_string()],
283            NAMED_CHILD_NODE => vec![NAMED_GRANDCHILD_NODE.to_string()],
284            MULTI_CHILD_ROOT => vec!["a".to_string(), "b".to_string(), "c".to_string()],
285            _ => vec![],
286        }
287    }
288}