1use anyhow::{format_err, Error};
6use fidl_fuchsia_inspect_deprecated::{InspectProxy, MetricValue, PropertyValue};
7use fuchsia_async as fasync;
8use fuchsia_inspect::reader::{DiagnosticsHierarchy, Property};
9
10pub async fn load_hierarchy_from_path(path: &str) -> Result<DiagnosticsHierarchy, Error> {
12 let (client, server) = zx::Channel::create();
13 fdio::service_connect(path, server)?;
14 let inspect_proxy = InspectProxy::new(fasync::Channel::from_channel(client));
15 let hierarchy = load_hierarchy(inspect_proxy).await?;
16 Ok(hierarchy)
17}
18
19pub async fn load_hierarchy(proxy: InspectProxy) -> Result<DiagnosticsHierarchy, Error> {
21 let mut pending_nodes = vec![];
22 let root = read_node(proxy).await?;
23 pending_nodes.push(root);
24 while let Some(mut current_node) = pending_nodes.pop() {
25 if current_node.pending_children.is_empty() {
26 if pending_nodes.is_empty() {
27 return Ok(current_node.hierarchy);
28 }
29 match pending_nodes.pop() {
30 Some(mut parent) => {
31 parent.hierarchy.children.push(current_node.hierarchy);
32 pending_nodes.push(parent);
33 }
34 None => return Err(format_err!("failed to load hierarchy")),
35 }
36 } else {
37 let next_child = current_node.pending_children.pop().unwrap();
38 let (client, server) = zx::Channel::create();
39 current_node
40 .proxy
41 .open_child(&next_child, fidl::endpoints::ServerEnd::new(server))
42 .await?;
43 let child_proxy = InspectProxy::new(fasync::Channel::from_channel(client));
44 let child_node = read_node(child_proxy).await?;
45 pending_nodes.push(current_node);
46 pending_nodes.push(child_node);
47 }
48 }
49 Err(format_err!("failed to load hierarchy"))
50}
51
52struct PartialNodeHierarchy {
53 proxy: InspectProxy,
54 hierarchy: DiagnosticsHierarchy,
55 pending_children: Vec<String>,
56}
57
58async fn read_node(proxy: InspectProxy) -> Result<PartialNodeHierarchy, Error> {
60 let object = proxy.read_data().await?;
61 let mut properties = object
62 .properties
63 .into_iter()
64 .map(|property| match property.value {
65 PropertyValue::Str(v) => Property::String(property.key, v),
66 PropertyValue::Bytes(v) => Property::Bytes(property.key, v),
67 })
68 .collect::<Vec<Property>>();
69
70 properties.extend(object.metrics.into_iter().map(|metric| match metric.value {
71 MetricValue::IntValue(v) => Property::Int(metric.key, v),
72 MetricValue::DoubleValue(v) => Property::Double(metric.key, v),
73 MetricValue::UintValue(v) => Property::Uint(metric.key, v),
74 }));
75
76 let pending_children = proxy.list_children().await?;
77 let hierarchy = DiagnosticsHierarchy::new(&object.name, properties, vec![]);
78 Ok(PartialNodeHierarchy { proxy, hierarchy, pending_children })
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use diagnostics_assertions::assert_data_tree;
85 use fidl_fuchsia_inspect_deprecated::{
86 InspectMarker, InspectRequest, InspectRequestStream, Metric, Object, Property,
87 };
88 use futures::{TryFutureExt, TryStreamExt};
89 use maplit::{hashmap, hashset};
90 use std::collections::{HashMap, HashSet};
91 use std::sync::LazyLock;
92
93 #[fuchsia::test]
94 async fn test_load_hierarchy() -> Result<(), Error> {
95 let (client_proxy, server_stream) =
96 fidl::endpoints::create_proxy_and_stream::<InspectMarker>();
97 spawn_server(server_stream, "root".to_string());
98 let hierarchy = load_hierarchy(client_proxy).await?;
99 assert_data_tree!(hierarchy, root: {
100 double_value: 5.2,
101 a: {
102 int_value: -3i64,
103 uint_value: 2u64,
104 c: {}
105 },
106 b: {
107 bytes_value: vec![0x12u8, 0x34, 0x56],
108 string_value: "test",
109 }
110 });
111 Ok(())
112 }
113
114 static OBJECTS: LazyLock<HashMap<String, TestObject>> = LazyLock::new(|| {
115 hashmap! {
116 "root".to_string() => TestObject {
117 object: Object {
118 name: "root".to_string(),
119 metrics: vec![
120 Metric {
121 key: "double_value".to_string(),
122 value: MetricValue::DoubleValue(5.2),
123 },
124 ],
125 properties: vec![],
126 },
127 children: hashset!("a".to_string(), "b".to_string()),
128 },
129 "a".to_string() => TestObject {
130 object: Object {
131 name: "a".to_string(),
132 metrics: vec![
133 Metric {
134 key: "int_value".to_string(),
135 value: MetricValue::IntValue(-3),
136 },
137 Metric {
138 key: "uint_value".to_string(),
139 value: MetricValue::UintValue(2),
140 },
141 ],
142 properties: vec![],
143 },
144 children: hashset!("c".to_string()),
145 },
146 "b".to_string() => TestObject {
147 object: Object {
148 name: "b".to_string(),
149 metrics: vec![],
150 properties: vec![
151 Property{
152 key: "string_value".to_string(),
153 value: PropertyValue::Str("test".to_string()),
154 },
155 Property {
156 key: "bytes_value".to_string(),
157 value: PropertyValue::Bytes(vec![0x12u8, 0x34, 0x56]),
158 },
159 ],
160 },
161 children: hashset!(),
162 },
163 "c".to_string() => TestObject {
164 object: Object {
165 name: "c".to_string(),
166 metrics: vec![],
167 properties: vec![],
168 },
169 children: hashset!(),
170 }
171 }
172 });
173
174 struct TestObject {
175 object: Object,
176 children: HashSet<String>,
177 }
178
179 fn spawn_server(mut stream: InspectRequestStream, object_name: String) {
180 fasync::Task::spawn(
181 async move {
182 let object = OBJECTS.get(&object_name).unwrap();
183 while let Some(req) = stream.try_next().await? {
184 match req {
185 InspectRequest::ReadData { responder } => {
186 responder.send(&object.object)?;
187 }
188 InspectRequest::ListChildren { responder } => {
189 responder.send(&object.children.iter().cloned().collect::<Vec<_>>())?;
190 }
191 InspectRequest::OpenChild { child_name, child_channel, responder } => {
192 let stream = child_channel.into_stream();
193 if object.children.contains(&child_name) {
194 spawn_server(stream, child_name);
195 responder.send(true)?;
196 } else {
197 responder.send(false)?;
198 }
199 }
200 }
201 }
202 Ok(())
203 }
204 .unwrap_or_else(|e: Error| eprintln!("error running inspect server: {:?}", e)),
205 )
206 .detach();
207 }
208}