hyperlight_host/metrics/
mod.rs1pub(crate) static METRIC_GUEST_ERROR: &str = "guest_errors_total";
6pub(crate) static METRIC_GUEST_ERROR_LABEL_CODE: &str = "code";
7
8pub(crate) static METRIC_GUEST_CANCELLATION: &str = "guest_cancellations_total";
10
11pub(crate) static METRIC_ERRONEOUS_VCPU_KICKS: &str = "erroneous_vcpu_kicks_total";
16
17#[cfg(feature = "function_call_metrics")]
19pub(crate) static METRIC_GUEST_FUNC_DURATION: &str = "guest_call_duration_seconds";
20
21#[cfg(feature = "function_call_metrics")]
23pub(crate) static METRIC_HOST_FUNC_DURATION: &str = "host_call_duration_seconds";
24
25pub(crate) fn maybe_time_and_emit_guest_call<T, F: FnOnce() -> T>(
32 #[allow(unused_variables)] name: &str,
33 f: F,
34) -> T {
35 cfg_if::cfg_if! {
36 if #[cfg(feature = "function_call_metrics")] {
37 use std::time::Instant;
38
39 let start = Instant::now();
40 let result = f();
41 let duration = start.elapsed();
42
43 static LABEL_GUEST_FUNC_NAME: &str = "function_name";
44 metrics::histogram!(METRIC_GUEST_FUNC_DURATION, LABEL_GUEST_FUNC_NAME => name.to_string()).record(duration);
45 result
46 } else {
47 f()
48 }
49 }
50}
51
52pub(crate) fn maybe_time_and_emit_host_call<T, F: FnOnce() -> T>(
59 #[allow(unused_variables)] name: &str,
60 f: F,
61) -> T {
62 cfg_if::cfg_if! {
63 if #[cfg(feature = "function_call_metrics")] {
64 use std::time::Instant;
65
66 let start = Instant::now();
67 let result = f();
68 let duration = start.elapsed();
69
70 static LABEL_HOST_FUNC_NAME: &str = "function_name";
71 metrics::histogram!(METRIC_HOST_FUNC_DURATION, LABEL_HOST_FUNC_NAME => name.to_string()).record(duration);
72 result
73 } else {
74 f()
75 }
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use std::thread;
82 use std::time::Duration;
83
84 use hyperlight_testing::simple_guest_as_pathbuf;
85 use metrics::{Key, with_local_recorder};
86 use metrics_util::CompositeKey;
87
88 use super::*;
89 use crate::SandboxBuilder;
90
91 #[test]
92 fn test_metrics_are_emitted() {
93 let recorder = metrics_util::debugging::DebuggingRecorder::new();
94 let snapshotter = recorder.snapshotter();
95 let snapshot = with_local_recorder(&recorder, || {
96 let mut multi = SandboxBuilder::from_file(simple_guest_as_pathbuf())
97 .build()
98 .unwrap();
99 let interrupt_handle = multi.interrupt_handle();
100
101 let thread = thread::spawn(move || {
103 thread::sleep(Duration::from_secs(1));
104 assert!(interrupt_handle.kill());
105 });
106
107 multi
108 .call::<i32>("PrintOutput", "Hello".to_string())
109 .unwrap();
110
111 multi.call::<i32>("Spin", ()).unwrap_err();
112 thread.join().unwrap();
113
114 snapshotter.snapshot()
115 });
116
117 let snapshot = snapshot.into_hashmap();
119
120 cfg_if::cfg_if! {
121 if #[cfg(feature = "function_call_metrics")] {
122 use metrics::Label;
123
124 let expected_num_metrics = 4;
125
126 assert_eq!(snapshot.len(), expected_num_metrics);
128
129 let histogram_key = CompositeKey::new(
131 metrics_util::MetricKind::Histogram,
132 Key::from_parts(
133 METRIC_GUEST_FUNC_DURATION,
134 vec![Label::new("function_name", "PrintOutput")],
135 ),
136 );
137 let histogram_value = &snapshot.get(&histogram_key).unwrap().2;
138 assert!(
139 matches!(
140 histogram_value,
141 metrics_util::debugging::DebugValue::Histogram(histogram) if histogram.len() == 1
142 ),
143 "Histogram metric does not match expected value"
144 );
145
146 let counter_key = CompositeKey::new(
148 metrics_util::MetricKind::Counter,
149 Key::from_name(METRIC_GUEST_CANCELLATION),
150 );
151 assert_eq!(
152 snapshot.get(&counter_key).unwrap().2,
153 metrics_util::debugging::DebugValue::Counter(1)
154 );
155
156 let histogram_key = CompositeKey::new(
158 metrics_util::MetricKind::Histogram,
159 Key::from_parts(
160 METRIC_GUEST_FUNC_DURATION,
161 vec![Label::new("function_name", "Spin")],
162 ),
163 );
164 let histogram_value = &snapshot.get(&histogram_key).unwrap().2;
165 assert!(
166 matches!(
167 histogram_value,
168 metrics_util::debugging::DebugValue::Histogram(histogram) if histogram.len() == 1
169 ),
170 "Histogram metric does not match expected value"
171 );
172 } else {
173 assert_eq!(snapshot.len(), 1);
175
176 let counter_key = CompositeKey::new(
177 metrics_util::MetricKind::Counter,
178 Key::from_name(METRIC_GUEST_CANCELLATION),
179 );
180 assert_eq!(
181 snapshot.get(&counter_key).unwrap().2,
182 metrics_util::debugging::DebugValue::Counter(1)
183 );
184 }
185 }
186 }
187}