Skip to content

Commit cf0e384

Browse files
committed
Add integration and unit tests for PVTime
- General and snapshotting integration tests - Unit tests for PVTime Signed-off-by: Dakshin Devanand <[email protected]>
1 parent 307d208 commit cf0e384

File tree

2 files changed

+161
-0
lines changed

2 files changed

+161
-0
lines changed

Diff for: src/vmm/src/arch/aarch64/pvtime.rs

+38
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,41 @@ impl<'a> Persist<'a> for PVTime {
169169
))
170170
}
171171
}
172+
173+
#[cfg(test)]
174+
mod tests {
175+
use super::*;
176+
use crate::device_manager::resources::ResourceAllocator;
177+
178+
#[test]
179+
fn test_get_steal_time_region_addr() {
180+
let base = GuestAddress(0x1000);
181+
let dev = PVTime::from_base(base, 3);
182+
183+
assert_eq!(
184+
dev.get_steal_time_region_addr(0).unwrap(),
185+
GuestAddress(0x1000)
186+
);
187+
assert_eq!(
188+
dev.get_steal_time_region_addr(1).unwrap(),
189+
GuestAddress(0x1000 + STEALTIME_STRUCT_MEM_SIZE)
190+
);
191+
assert!(matches!(
192+
dev.get_steal_time_region_addr(3),
193+
Err(PVTimeError::InvalidVcpuIndex(3))
194+
));
195+
}
196+
197+
#[test]
198+
fn test_new_pvtime_allocates_correctly() {
199+
let mut allocator = ResourceAllocator::new().unwrap();
200+
let dev = PVTime::new(&mut allocator, 2).unwrap();
201+
202+
assert_eq!(dev.vcpu_count, 2);
203+
assert_eq!(
204+
dev.base_ipa.0 % STEALTIME_STRUCT_MEM_SIZE,
205+
0,
206+
"Base IPA should be aligned"
207+
);
208+
}
209+
}

Diff for: tests/integration_tests/functional/test_pvtime.py

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""Tests for verifying the PVTime device behavior under contention and across snapshots."""
5+
6+
import re
7+
import time
8+
9+
10+
def test_pvtime_steal_time_increases(uvm_plain):
11+
"""
12+
Test that PVTime steal time increases when both vCPUs are contended on the same pCPU.
13+
"""
14+
vm = uvm_plain
15+
vm.spawn()
16+
vm.basic_config()
17+
vm.add_net_iface()
18+
vm.start()
19+
20+
# Pin both vCPUs to the same physical CPU to induce contention
21+
vm.pin_vcpu(0, 0)
22+
vm.pin_vcpu(1, 0)
23+
vm.pin_vmm(1)
24+
vm.pin_api(2)
25+
26+
# Start two infinite loops to hog CPU time
27+
hog_cmd = "nohup bash -c 'while true; do :; done' >/dev/null 2>&1 &"
28+
vm.ssh.run(hog_cmd)
29+
vm.ssh.run(hog_cmd)
30+
31+
time.sleep(2)
32+
33+
# Measure steal time before
34+
_, out_before, _ = vm.ssh.run("grep '^cpu[0-9]' /proc/stat")
35+
steal_before = sum(
36+
int(re.split(r"\s+", line.strip())[8])
37+
for line in out_before.strip().splitlines()
38+
)
39+
40+
time.sleep(2)
41+
42+
# Measure steal time after
43+
_, out_after, _ = vm.ssh.run("grep '^cpu[0-9]' /proc/stat")
44+
steal_after = sum(
45+
int(re.split(r"\s+", line.strip())[8])
46+
for line in out_after.strip().splitlines()
47+
)
48+
49+
# Require increase in steal time
50+
assert (
51+
steal_after - steal_before >= 200
52+
), f"Steal time did not increase as expected. Before: {steal_before}, After: {steal_after}"
53+
54+
55+
def test_pvtime_snapshot(uvm_plain, microvm_factory):
56+
"""
57+
Test that PVTime steal time is preserved across snapshot/restore
58+
and continues increasing post-resume.
59+
"""
60+
vm = uvm_plain
61+
vm.spawn()
62+
vm.basic_config()
63+
vm.add_net_iface()
64+
vm.start()
65+
66+
vm.pin_vcpu(0, 0)
67+
vm.pin_vcpu(1, 0)
68+
vm.pin_vmm(1)
69+
vm.pin_api(2)
70+
71+
hog_cmd = "nohup bash -c 'while true; do :; done' >/dev/null 2>&1 &"
72+
vm.ssh.run(hog_cmd)
73+
vm.ssh.run(hog_cmd)
74+
75+
time.sleep(1)
76+
77+
# Snapshot pre-steal time
78+
_, out_before_snap, _ = vm.ssh.run("grep '^cpu[0-9]' /proc/stat")
79+
steal_before = [
80+
int(re.split(r"\s+", line.strip())[8])
81+
for line in out_before_snap.strip().splitlines()
82+
]
83+
84+
snapshot = vm.snapshot_full()
85+
vm.kill()
86+
87+
# Restore microVM from snapshot and resume
88+
restored_vm = microvm_factory.build()
89+
restored_vm.spawn()
90+
restored_vm.restore_from_snapshot(snapshot, resume=False)
91+
snapshot.delete()
92+
93+
restored_vm.pin_vcpu(0, 0)
94+
restored_vm.pin_vcpu(1, 0)
95+
restored_vm.pin_vmm(1)
96+
restored_vm.pin_api(2)
97+
restored_vm.resume()
98+
99+
time.sleep(1)
100+
101+
# Steal time just after restoring
102+
_, out_after_snap, _ = restored_vm.ssh.run("grep '^cpu[0-9]' /proc/stat")
103+
steal_after_snap = [
104+
int(re.split(r"\s+", line.strip())[8])
105+
for line in out_after_snap.strip().splitlines()
106+
]
107+
108+
time.sleep(2)
109+
110+
# Steal time after running resumed VM
111+
_, out_after_resume, _ = restored_vm.ssh.run("grep '^cpu[0-9]' /proc/stat")
112+
steal_after_resume = [
113+
int(re.split(r"\s+", line.strip())[8])
114+
for line in out_after_resume.strip().splitlines()
115+
]
116+
117+
# Ensure steal time persisted and continued increasing
118+
persisted = sum(steal_before) + 100 <= sum(steal_after_snap)
119+
increased = sum(steal_after_resume) > sum(steal_after_snap)
120+
121+
assert (
122+
persisted and increased
123+
), "Steal time did not persist through snapshot or failed to increase after resume"

0 commit comments

Comments
 (0)