vmm/devices/virtio/rng/metrics.rs
1// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Defines the metrics system for entropy devices.
5//!
6//! # Metrics format
7//! The metrics are flushed in JSON when requested by vmm::logger::metrics::METRICS.write().
8//!
9//! ## JSON example with metrics:
10//! ```json
11//! "entropy": {
12//! "activate_fails": "SharedIncMetric",
13//! "entropy_event_fails": "SharedIncMetric",
14//! "entropy_event_count": "SharedIncMetric",
15//! ...
16//! }
17//! }
18//! ```
19//! Each `entropy` field in the example above is a serializable `EntropyDeviceMetrics` structure
20//! collecting metrics such as `activate_fails`, `entropy_event_fails` etc. for the entropy device.
21//! Since entropy doesn't support multiple devices, there is no per device metrics and
22//! `entropy` represents the aggregate entropy metrics.
23//!
24//! # Design
25//! The main design goals of this system are:
26//! * Have a consistent approach of keeping device related metrics in the individual devices
27//! modules.
28//! * To decouple entropy device metrics from logger module by moving EntropyDeviceMetrics out of
29//! FirecrackerDeviceMetrics.
30//! * Rely on `serde` to provide the actual serialization for writing the metrics.
31//!
32//! The system implements 1 type of metrics:
33//! * Shared Incremental Metrics (SharedIncMetrics) - dedicated for the metrics which need a counter
34//! (i.e the number of times an API request failed). These metrics are reset upon flush.
35
36use serde::ser::SerializeMap;
37use serde::{Serialize, Serializer};
38
39use crate::logger::SharedIncMetric;
40
41/// Stores aggregated entropy metrics
42pub(super) static METRICS: EntropyDeviceMetrics = EntropyDeviceMetrics::new();
43
44/// Called by METRICS.flush(), this function facilitates serialization of entropy device metrics.
45pub fn flush_metrics<S: Serializer>(serializer: S) -> Result<S::Ok, S::Error> {
46 let mut seq = serializer.serialize_map(Some(1))?;
47 seq.serialize_entry("entropy", &METRICS)?;
48 seq.end()
49}
50
51#[derive(Debug, Serialize)]
52pub(super) struct EntropyDeviceMetrics {
53 /// Number of device activation failures
54 pub activate_fails: SharedIncMetric,
55 /// Number of entropy queue event handling failures
56 pub entropy_event_fails: SharedIncMetric,
57 /// Number of entropy requests handled
58 pub entropy_event_count: SharedIncMetric,
59 /// Number of entropy bytes provided to guest
60 pub entropy_bytes: SharedIncMetric,
61 /// Number of errors while getting random bytes on host
62 pub host_rng_fails: SharedIncMetric,
63 /// Number of times an entropy request was rate limited
64 pub entropy_rate_limiter_throttled: SharedIncMetric,
65 /// Number of events associated with the rate limiter
66 pub rate_limiter_event_count: SharedIncMetric,
67}
68impl EntropyDeviceMetrics {
69 /// Const default construction.
70 const fn new() -> Self {
71 Self {
72 activate_fails: SharedIncMetric::new(),
73 entropy_event_fails: SharedIncMetric::new(),
74 entropy_event_count: SharedIncMetric::new(),
75 entropy_bytes: SharedIncMetric::new(),
76 host_rng_fails: SharedIncMetric::new(),
77 entropy_rate_limiter_throttled: SharedIncMetric::new(),
78 rate_limiter_event_count: SharedIncMetric::new(),
79 }
80 }
81}
82
83#[cfg(test)]
84pub mod tests {
85 use super::*;
86 use crate::logger::IncMetric;
87
88 #[test]
89 fn test_entropy_dev_metrics() {
90 let entropy_metrics: EntropyDeviceMetrics = EntropyDeviceMetrics::new();
91 let entropy_metrics_local: String = serde_json::to_string(&entropy_metrics).unwrap();
92 // the 1st serialize flushes the metrics and resets values to 0 so that
93 // we can compare the values with local metrics.
94 serde_json::to_string(&METRICS).unwrap();
95 let entropy_metrics_global: String = serde_json::to_string(&METRICS).unwrap();
96 assert_eq!(entropy_metrics_local, entropy_metrics_global);
97 entropy_metrics.entropy_event_count.inc();
98 assert_eq!(entropy_metrics.entropy_event_count.count(), 1);
99 }
100}