Skip to main content

fxfs/
metrics.rs

1// Copyright 2022 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;
6use fuchsia_inspect::{HistogramProperty, Inspector, LazyNode, Node, NumericProperty};
7use fuchsia_sync::Mutex;
8use futures::future::BoxFuture;
9use std::sync::LazyLock;
10
11/// Holds a histogram of latencies and a counter for the cumulative time.
12pub 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, // 100us
31            step_multiplier: 2,
32            buckets: 30,
33        }
34    }
35}
36
37/// A scope that measures the duration of an operation and records it in a `DurationMeasure`.
38/// When the scope is dropped, the duration is added to the histogram and the cumulative time is
39/// incremented.
40pub 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
59/// Root node to which the filesystem Inspect tree will be attached.
60fn 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
70/// `fs.detail` node for holding fxfs-specific metrics.
71pub 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}