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_ns",
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 pub compaction_layer_stack_depth: fuchsia_inspect::UintExponentialHistogramProperty,
92 pub journal_compactions_total: fuchsia_inspect::UintProperty,
93 pub journal_compaction_bytes_written: fuchsia_inspect::UintProperty,
94 pub journal_compaction_time: DurationMeasure,
95 _node: fuchsia_inspect::Node,
96}
97
98pub fn lsm_tree_metrics() -> &'static LsmTreeMetrics {
99 static METRICS: std::sync::LazyLock<LsmTreeMetrics> = std::sync::LazyLock::new(|| {
100 let node = detail().create_child("lsm_tree");
101 LsmTreeMetrics {
102 find: DurationMeasure::new(&node, "find"),
103 insert: DurationMeasure::new(&node, "insert"),
104 replace_or_insert: DurationMeasure::new(&node, "replace_or_insert"),
105 merge_into: DurationMeasure::new(&node, "merge_into"),
106 compaction_layer_stack_depth: node.create_uint_exponential_histogram(
107 "compaction_layer_stack_depth",
108 fuchsia_inspect::ExponentialHistogramParams {
109 floor: 0,
110 initial_step: 1,
111 step_multiplier: 2,
112 buckets: 8,
113 },
114 ),
115 journal_compactions_total: node.create_uint("journal_compactions_total", 0),
116 journal_compaction_bytes_written: node
117 .create_uint("journal_compaction_bytes_written", 0),
118 journal_compaction_time: DurationMeasure::new(&node, "journal_compaction"),
119 _node: node,
120 }
121 });
122 &METRICS
123}
124
125pub struct DirectoryMetrics {
126 pub lookup: DurationMeasure,
127 _node: fuchsia_inspect::Node,
128}
129
130pub fn directory_metrics() -> &'static DirectoryMetrics {
131 static METRICS: std::sync::LazyLock<DirectoryMetrics> = std::sync::LazyLock::new(|| {
132 let node = detail().create_child("directory");
133 DirectoryMetrics { lookup: DurationMeasure::new(&node, "lookup"), _node: node }
134 });
135 &METRICS
136}