1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
//! Per-box metrics (individual LiteBox statistics).
use std::sync::atomic::{AtomicU64, Ordering};
/// Storage for per-box metrics.
///
/// Stored in `BoxMetadata`, one instance per box.
/// All counters are monotonic (never decrease).
#[derive(Default, Debug)]
pub struct BoxMetricsStorage {
/// Commands executed on this box
pub(crate) commands_executed: AtomicU64,
/// Command execution errors on this box
pub(crate) exec_errors: AtomicU64,
/// Bytes sent to this box (via stdin)
pub(crate) bytes_sent: AtomicU64,
/// Bytes received from this box (via stdout/stderr)
pub(crate) bytes_received: AtomicU64,
// Timing metrics (set once, never change)
/// Total time from create() call to LiteBox ready (includes all stages)
pub(crate) total_create_duration_ms: Option<u128>,
/// Time from box subprocess spawn to guest agent ready
pub(crate) guest_boot_duration_ms: Option<u128>,
// Stage-level timing breakdown (set once during initialization)
/// Time to create box directory structure (Stage 1)
pub(crate) stage_filesystem_setup_ms: Option<u128>,
/// Time to pull and prepare container image layers (Stage 2)
pub(crate) stage_image_prepare_ms: Option<u128>,
/// Time to bootstrap guest rootfs (Stage 3, lazy initialization)
pub(crate) stage_guest_rootfs_ms: Option<u128>,
/// Time to build box configuration (Stage 4)
pub(crate) stage_box_config_ms: Option<u128>,
/// Time to spawn box subprocess (Stage 5, excludes guest boot)
pub(crate) stage_box_spawn_ms: Option<u128>,
/// Time to initialize container inside guest (Stage 6)
pub(crate) stage_container_init_ms: Option<u128>,
}
impl Clone for BoxMetricsStorage {
fn clone(&self) -> Self {
Self {
commands_executed: AtomicU64::new(self.commands_executed.load(Ordering::Relaxed)),
exec_errors: AtomicU64::new(self.exec_errors.load(Ordering::Relaxed)),
bytes_sent: AtomicU64::new(self.bytes_sent.load(Ordering::Relaxed)),
bytes_received: AtomicU64::new(self.bytes_received.load(Ordering::Relaxed)),
total_create_duration_ms: self.total_create_duration_ms,
guest_boot_duration_ms: self.guest_boot_duration_ms,
stage_filesystem_setup_ms: self.stage_filesystem_setup_ms,
stage_image_prepare_ms: self.stage_image_prepare_ms,
stage_guest_rootfs_ms: self.stage_guest_rootfs_ms,
stage_box_config_ms: self.stage_box_config_ms,
stage_box_spawn_ms: self.stage_box_spawn_ms,
stage_container_init_ms: self.stage_container_init_ms,
}
}
}
impl BoxMetricsStorage {
/// Create new per-box metrics storage.
pub fn new() -> Self {
Self::default()
}
/// Set total create duration (called once during box creation).
pub(crate) fn set_total_create_duration(&mut self, duration_ms: u128) {
self.total_create_duration_ms = Some(duration_ms);
}
/// Set guest boot duration (called once after guest is ready).
#[allow(dead_code)] // API designed but not yet wired up
pub(crate) fn set_guest_boot_duration(&mut self, duration_ms: u128) {
self.guest_boot_duration_ms = Some(duration_ms);
}
/// Set filesystem setup stage duration.
pub(crate) fn set_stage_filesystem_setup(&mut self, duration_ms: u128) {
self.stage_filesystem_setup_ms = Some(duration_ms);
}
/// Set image preparation stage duration.
pub(crate) fn set_stage_image_prepare(&mut self, duration_ms: u128) {
self.stage_image_prepare_ms = Some(duration_ms);
}
/// Set guest rootfs bootstrap stage duration.
pub(crate) fn set_stage_guest_rootfs(&mut self, duration_ms: u128) {
self.stage_guest_rootfs_ms = Some(duration_ms);
}
/// Set box config build stage duration.
#[allow(dead_code)] // API designed but not yet wired up
pub(crate) fn set_stage_box_config(&mut self, duration_ms: u128) {
self.stage_box_config_ms = Some(duration_ms);
}
/// Set box spawn stage duration.
pub(crate) fn set_stage_box_spawn(&mut self, duration_ms: u128) {
self.stage_box_spawn_ms = Some(duration_ms);
}
/// Set container initialization stage duration.
pub(crate) fn set_stage_container_init(&mut self, duration_ms: u128) {
self.stage_container_init_ms = Some(duration_ms);
}
/// Log init stage durations for debugging.
pub(crate) fn log_init_stages(&self) {
tracing::debug!(
total_create_duration_ms = self.total_create_duration_ms.unwrap_or(0),
stage_filesystem_setup_ms = self.stage_filesystem_setup_ms.unwrap_or(0),
stage_image_prepare_ms = self.stage_image_prepare_ms.unwrap_or(0),
stage_guest_rootfs_ms = self.stage_guest_rootfs_ms.unwrap_or(0),
stage_box_config_ms = self.stage_box_config_ms.unwrap_or(0),
stage_box_spawn_ms = self.stage_box_spawn_ms.unwrap_or(0),
stage_container_init_ms = self.stage_container_init_ms.unwrap_or(0),
"Box initialization stages completed"
);
}
/// Increment commands executed counter.
pub(crate) fn increment_commands_executed(&self) {
self.commands_executed.fetch_add(1, Ordering::Relaxed);
}
/// Increment execution errors counter.
pub(crate) fn increment_exec_errors(&self) {
self.exec_errors.fetch_add(1, Ordering::Relaxed);
}
/// Add bytes sent to counter.
#[allow(dead_code)]
pub(crate) fn add_bytes_sent(&self, bytes: u64) {
self.bytes_sent.fetch_add(bytes, Ordering::Relaxed);
}
/// Add bytes received to counter.
#[allow(dead_code)]
pub(crate) fn add_bytes_received(&self, bytes: u64) {
self.bytes_received.fetch_add(bytes, Ordering::Relaxed);
}
}
/// Handle for querying per-box metrics.
///
/// Snapshot of metrics at query time.
/// All counters are monotonic and never reset.
#[derive(Clone, Debug)]
pub struct BoxMetrics {
/// Commands executed on this box
pub commands_executed_total: u64,
/// Command execution errors on this box
pub exec_errors_total: u64,
/// Bytes sent to this box (via stdin)
pub bytes_sent_total: u64,
/// Bytes received from this box (via stdout/stderr)
pub bytes_received_total: u64,
/// Total time from create() call to LiteBox ready (milliseconds)
pub total_create_duration_ms: Option<u128>,
/// Time from box subprocess spawn to guest agent ready (milliseconds)
pub guest_boot_duration_ms: Option<u128>,
/// CPU usage percent (0.0-100.0)
pub cpu_percent: Option<f32>,
/// Memory usage in bytes
pub memory_bytes: Option<u64>,
/// Network bytes sent (host to guest)
pub network_bytes_sent: Option<u64>,
/// Network bytes received (guest to host)
pub network_bytes_received: Option<u64>,
/// Current TCP connections
pub network_tcp_connections: Option<u64>,
/// Total TCP connection errors
pub network_tcp_errors: Option<u64>,
// Stage-level timing breakdown
/// Time to create box directory structure (milliseconds)
pub stage_filesystem_setup_ms: Option<u128>,
/// Time to pull and prepare container image layers (milliseconds)
pub stage_image_prepare_ms: Option<u128>,
/// Time to bootstrap guest rootfs (milliseconds)
pub stage_guest_rootfs_ms: Option<u128>,
/// Time to build box configuration (milliseconds)
pub stage_box_config_ms: Option<u128>,
/// Time to spawn box subprocess (milliseconds)
pub stage_box_spawn_ms: Option<u128>,
/// Time to initialize container inside guest (milliseconds)
pub stage_container_init_ms: Option<u128>,
}
impl BoxMetrics {
/// Create snapshot from storage and system metrics.
pub(crate) fn from_storage(
storage: &BoxMetricsStorage,
cpu_percent: Option<f32>,
memory_bytes: Option<u64>,
network_bytes_sent: Option<u64>,
network_bytes_received: Option<u64>,
network_tcp_connections: Option<u64>,
network_tcp_errors: Option<u64>,
) -> Self {
Self {
commands_executed_total: storage.commands_executed.load(Ordering::Relaxed),
exec_errors_total: storage.exec_errors.load(Ordering::Relaxed),
bytes_sent_total: storage.bytes_sent.load(Ordering::Relaxed),
bytes_received_total: storage.bytes_received.load(Ordering::Relaxed),
total_create_duration_ms: storage.total_create_duration_ms,
guest_boot_duration_ms: storage.guest_boot_duration_ms,
cpu_percent,
memory_bytes,
network_bytes_sent,
network_bytes_received,
network_tcp_connections,
network_tcp_errors,
stage_filesystem_setup_ms: storage.stage_filesystem_setup_ms,
stage_image_prepare_ms: storage.stage_image_prepare_ms,
stage_guest_rootfs_ms: storage.stage_guest_rootfs_ms,
stage_box_config_ms: storage.stage_box_config_ms,
stage_box_spawn_ms: storage.stage_box_spawn_ms,
stage_container_init_ms: storage.stage_container_init_ms,
}
}
/// Total commands executed on this box.
///
/// Incremented on every `exec()` call.
/// Never decreases (monotonic counter).
pub fn commands_executed_total(&self) -> u64 {
self.commands_executed_total
}
/// Total command execution errors on this box.
///
/// Incremented when `exec()` returns error.
/// Never decreases (monotonic counter).
pub fn exec_errors_total(&self) -> u64 {
self.exec_errors_total
}
/// Total bytes sent to this box (stdin).
///
/// Never decreases (monotonic counter).
pub fn bytes_sent_total(&self) -> u64 {
self.bytes_sent_total
}
/// Total bytes received from this box (stdout/stderr).
///
/// Never decreases (monotonic counter).
pub fn bytes_received_total(&self) -> u64 {
self.bytes_received_total
}
/// Total time from create() call to box ready (milliseconds).
///
/// Includes all initialization stages: filesystem setup, image pull,
/// guest rootfs bootstrap, box config, box spawn, and container init.
/// Returns None if box not yet initialized.
pub fn total_create_duration_ms(&self) -> Option<u128> {
self.total_create_duration_ms
}
/// Time from box subprocess spawn to guest agent ready (milliseconds).
///
/// Measures guest boot time only (excludes image preparation).
/// Returns None if guest not yet ready.
pub fn guest_boot_duration_ms(&self) -> Option<u128> {
self.guest_boot_duration_ms
}
/// CPU usage percent (0.0-100.0).
///
/// Returns None if box not started or process not found.
pub fn cpu_percent(&self) -> Option<f32> {
self.cpu_percent
}
/// Memory usage in bytes.
///
/// Returns None if box not started or process not found.
pub fn memory_bytes(&self) -> Option<u64> {
self.memory_bytes
}
/// Network bytes sent from host to guest.
///
/// Returns None if network backend doesn't support metrics.
pub fn network_bytes_sent(&self) -> Option<u64> {
self.network_bytes_sent
}
/// Network bytes received from guest to host.
///
/// Returns None if network backend doesn't support metrics.
pub fn network_bytes_received(&self) -> Option<u64> {
self.network_bytes_received
}
/// Current TCP connections in ESTABLISHED state.
///
/// Returns None if network backend doesn't support metrics.
pub fn network_tcp_connections(&self) -> Option<u64> {
self.network_tcp_connections
}
/// Total failed TCP connection attempts.
///
/// Returns None if network backend doesn't support metrics.
pub fn network_tcp_errors(&self) -> Option<u64> {
self.network_tcp_errors
}
// Stage-level timing getters
/// Time to create box directory structure (milliseconds).
///
/// Stage 1 of initialization pipeline.
/// Returns None if stage not yet completed.
pub fn stage_filesystem_setup_ms(&self) -> Option<u128> {
self.stage_filesystem_setup_ms
}
/// Time to pull and prepare container image layers (milliseconds).
///
/// Stage 2 of initialization pipeline.
/// Includes image pull (if not cached) and layer extraction.
/// Returns None if stage not yet completed.
pub fn stage_image_prepare_ms(&self) -> Option<u128> {
self.stage_image_prepare_ms
}
/// Time to bootstrap guest rootfs (milliseconds).
///
/// Stage 3 of initialization pipeline.
/// Only non-zero on first box creation (lazy initialization).
/// Returns None if stage not yet completed.
pub fn stage_guest_rootfs_ms(&self) -> Option<u128> {
self.stage_guest_rootfs_ms
}
/// Time to build box configuration (milliseconds).
///
/// Stage 4 of initialization pipeline.
/// Includes disk setup and network configuration.
/// Returns None if stage not yet completed.
pub fn stage_box_config_ms(&self) -> Option<u128> {
self.stage_box_config_ms
}
/// Time to spawn box subprocess (milliseconds).
///
/// Stage 5 of initialization pipeline.
/// Includes subprocess spawn and waiting for guest agent.
/// Returns None if stage not yet completed.
pub fn stage_box_spawn_ms(&self) -> Option<u128> {
self.stage_box_spawn_ms
}
/// Time to initialize container inside guest (milliseconds).
///
/// Stage 6 of initialization pipeline.
/// Includes rootfs mount and container creation.
/// Returns None if stage not yet completed.
pub fn stage_container_init_ms(&self) -> Option<u128> {
self.stage_container_init_ms
}
}