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",
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    _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}