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