1use anyhow::Error;
6use fuchsia_inspect::{HistogramProperty, Inspector, LazyNode, Node, NumericProperty};
7use fuchsia_sync::Mutex;
8use futures::future::BoxFuture;
9use std::sync::LazyLock;
10
11pub struct DurationMeasure {
13 latency: fuchsia_inspect::UintExponentialHistogramProperty,
14 time: fuchsia_inspect::UintProperty,
15}
16
17impl DurationMeasure {
18 pub fn new(node: &Node, name: &str) -> Self {
19 Self {
20 latency: node.create_uint_exponential_histogram(
21 name.to_owned() + "_latency",
22 Self::latency_histogram_params(),
23 ),
24 time: node.create_uint(name.to_owned() + "_time_ns", 0),
25 }
26 }
27 fn latency_histogram_params() -> fuchsia_inspect::ExponentialHistogramParams<u64> {
28 fuchsia_inspect::ExponentialHistogramParams {
29 floor: 0,
30 initial_step: 100_000, step_multiplier: 2,
32 buckets: 30,
33 }
34 }
35}
36
37pub struct DurationMeasureScope<'a> {
41 start_time: std::time::Instant,
42 measure: &'a DurationMeasure,
43}
44
45impl<'a> DurationMeasureScope<'a> {
46 pub fn new(measure: &'a DurationMeasure) -> Self {
47 Self { start_time: std::time::Instant::now(), measure }
48 }
49}
50
51impl<'a> Drop for DurationMeasureScope<'a> {
52 fn drop(&mut self) {
53 let elapsed = self.start_time.elapsed().as_nanos() as u64;
54 self.measure.latency.insert(elapsed);
55 self.measure.time.add(elapsed);
56 }
57}
58
59fn root() -> Node {
61 #[cfg(target_os = "fuchsia")]
62 static FXFS_ROOT_NODE: LazyLock<Mutex<fuchsia_inspect::Node>> =
63 LazyLock::new(|| Mutex::new(fuchsia_inspect::component::inspector().root().clone_weak()));
64 #[cfg(not(target_os = "fuchsia"))]
65 static FXFS_ROOT_NODE: LazyLock<Mutex<Node>> = LazyLock::new(|| Mutex::new(Node::default()));
66
67 FXFS_ROOT_NODE.lock().clone_weak()
68}
69
70pub fn detail() -> Node {
72 static DETAIL_NODE: LazyLock<Mutex<Node>> =
73 LazyLock::new(|| Mutex::new(root().create_child("fs.detail")));
74
75 DETAIL_NODE.lock().clone_weak()
76}
77pub fn register_fs(
78 populate_stores_fn: impl Fn() -> BoxFuture<'static, Result<Inspector, Error>>
79 + Sync
80 + Send
81 + 'static,
82) -> LazyNode {
83 root().create_lazy_child("stores", populate_stores_fn)
84}
85
86pub struct LsmTreeMetrics {
87 pub find: DurationMeasure,
88 pub insert: DurationMeasure,
89 pub replace_or_insert: DurationMeasure,
90 pub merge_into: DurationMeasure,
91 _node: fuchsia_inspect::Node,
92}
93
94pub fn lsm_tree_metrics() -> &'static LsmTreeMetrics {
95 static METRICS: std::sync::LazyLock<LsmTreeMetrics> = std::sync::LazyLock::new(|| {
96 let node = detail().create_child("lsm_tree");
97 LsmTreeMetrics {
98 find: DurationMeasure::new(&node, "find"),
99 insert: DurationMeasure::new(&node, "insert"),
100 replace_or_insert: DurationMeasure::new(&node, "replace_or_insert"),
101 merge_into: DurationMeasure::new(&node, "merge_into"),
102 _node: node,
103 }
104 });
105 &METRICS
106}
107
108pub struct DirectoryMetrics {
109 pub lookup: DurationMeasure,
110 _node: fuchsia_inspect::Node,
111}
112
113pub fn directory_metrics() -> &'static DirectoryMetrics {
114 static METRICS: std::sync::LazyLock<DirectoryMetrics> = std::sync::LazyLock::new(|| {
115 let node = detail().create_child("directory");
116 DirectoryMetrics { lookup: DurationMeasure::new(&node, "lookup"), _node: node }
117 });
118 &METRICS
119}