| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| crate::file_handler, |
| anyhow::Error, |
| serde_json::{json, Value as JsonValue}, |
| tracing::*, |
| }; |
| |
| // Make sure extremely deep-tree data doesn't overflow a stack. |
| const MAX_TREE_DEPTH: u32 = 128; |
| |
| fn store_data(inspect_node: &fuchsia_inspect::Node, name: &str, data: &JsonValue, depth: u32) { |
| if depth > MAX_TREE_DEPTH { |
| return; |
| } |
| match data { |
| JsonValue::String(value) => inspect_node.record_string(name, value), |
| JsonValue::Number(value) => { |
| if value.is_i64() { |
| inspect_node.record_int(name, value.as_i64().unwrap()); |
| } else if value.is_u64() { |
| inspect_node.record_uint(name, value.as_u64().unwrap()); |
| } else if value.is_f64() { |
| inspect_node.record_double(name, value.as_f64().unwrap()); |
| } |
| } |
| JsonValue::Bool(value) => inspect_node.record_bool(name, *value), |
| JsonValue::Object(object) => inspect_node.record_child(name, |node| { |
| for (name, value) in object.iter() { |
| store_data(node, name, value, depth + 1); |
| } |
| }), |
| JsonValue::Null => {} |
| // TODO(https://fxbug.dev/42150693): If the array is all numbers, publish them (and test). |
| JsonValue::Array(_) => {} |
| } |
| } |
| |
| pub fn serve_persisted_data(persist_root: &fuchsia_inspect::Node) -> Result<(), Error> { |
| let remembered_data = file_handler::remembered_data()?; |
| for (service_name, service_data) in remembered_data.iter() { |
| persist_root.record_child(service_name, |service_node| { |
| for (tag_name, tag_data) in service_data.iter() { |
| let json_data = serde_json::from_str(tag_data).unwrap_or_else(|err| { |
| error!("Error {:?} parsing stored data", err); |
| json!("<<Error parsing saved data>>") |
| }); |
| store_data(service_node, tag_name, &json_data, 0); |
| } |
| }); |
| } |
| Ok(()) |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| use diagnostics_assertions::assert_data_tree; |
| use fuchsia_inspect::Inspector; |
| |
| // This tests all the types, and also the stack-safety mechanism by including data that should |
| // be clipped. |
| #[fuchsia::test] |
| fn test_json_export() -> Result<(), Error> { |
| let inspector = Inspector::default(); |
| let inspect = inspector.root(); |
| assert_data_tree!( |
| inspector, |
| root: contains { |
| } |
| ); |
| |
| let data_json = "{ negint: -5, int: 42, unsigned: 0, float: 45.6, \ |
| bool: true, obj: { child: 'child', grandchild: { hello: 'world', \ |
| great_grand: { should_be_clipped: true } } } }"; |
| let mut data_parsed = serde_json5::from_str::<serde_json::Value>(data_json)?; |
| // serde_json5::from_str() won't parse a number this big, so I have to insert it afterward. |
| *data_parsed.get_mut("unsigned").unwrap() = serde_json::json!(9223372036854775808u64); |
| store_data(inspect, "types", &data_parsed, MAX_TREE_DEPTH - 3); |
| |
| assert_data_tree!( |
| inspector, |
| root: { |
| types: { |
| negint: -5i64, |
| int: 42i64, |
| unsigned: 9223372036854775808u64, |
| float: 45.6f64, |
| bool: true, |
| obj: { |
| child: "child", |
| grandchild: { |
| hello: "world", |
| great_grand: {} |
| } |
| } |
| } |
| } |
| ); |
| Ok(()) |
| } |
| } |