Skip to content

Commit ac67603

Browse files
committed
tests: fix MMIO gaps in memory monitor tool
The memory monitor was only assuming a single MMIO gap on x86_64 when calculating the memory regions that corresponded to guest memory. Now we need to account for two MMIO gaps in x86 and one in ARM. Signed-off-by: Babis Chalios <[email protected]>
1 parent 2f521ad commit ac67603

File tree

1 file changed

+62
-17
lines changed

1 file changed

+62
-17
lines changed

tests/host_tools/memory.py

Lines changed: 62 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,17 @@
88

99
import psutil
1010

11+
from framework.properties import global_props
12+
1113

1214
class MemoryUsageExceededError(Exception):
1315
"""A custom exception containing details on excessive memory usage."""
1416

1517
def __init__(self, usage, threshold, *args):
1618
"""Compose the error message containing the memory consumption."""
1719
super().__init__(
18-
f"Memory usage ({usage / 2**20:.2f} MiB) exceeded maximum threshold "
19-
f"({threshold / 2**20} MiB)",
20+
f"Memory usage ({usage / 1 << 20:.2f} MiB) exceeded maximum threshold "
21+
f"({threshold / 1 << 20} MiB)",
2022
*args,
2123
)
2224

@@ -28,10 +30,20 @@ class MemoryMonitor(Thread):
2830
VMM memory usage.
2931
"""
3032

31-
# If guest memory is >3328MB, it is split in a 2nd region
32-
X86_MEMORY_GAP_START = 3328 * 2**20
33-
34-
def __init__(self, vm, threshold=5 * 2**20, period_s=0.05):
33+
# If guest memory is >3GiB, it is split in a 2nd region
34+
# Gap starts at 3GiBs and is 1GiB long
35+
X86_32BIT_MEMORY_GAP_START = 3 << 30
36+
X86_32BIT_MEMORY_GAP_SIZE = 1 << 30
37+
# If guest memory is >255GiB, it is split in a 3rd region
38+
# Gap starts at 256 GiB and is 256GiB long
39+
X86_64BIT_MEMORY_GAP_START = 256 << 30
40+
# On ARM64 we just have a single gap, but memory starts at an offset
41+
# Gap starts at 256 GiB and is GiB long
42+
# Memory starts at 2GiB
43+
ARM64_64BIT_MEMORY_GAP_START = 256 << 30
44+
ARM64_MEMORY_START = 2 << 30
45+
46+
def __init__(self, vm, threshold=5 << 20, period_s=0.01):
3547
"""Initialize monitor attributes."""
3648
Thread.__init__(self)
3749
self._vm = vm
@@ -72,7 +84,9 @@ def run(self):
7284
mem_total = 0
7385
for mmap in mmaps:
7486
if self.is_guest_mem(mmap.size, guest_mem_bytes):
87+
print(f"Region {mmap} is guest memory")
7588
continue
89+
7690
mem_total += mmap.rss
7791
self._current_rss = mem_total
7892
if mem_total > self.threshold:
@@ -81,24 +95,55 @@ def run(self):
8195

8296
time.sleep(self._period_s)
8397

84-
def is_guest_mem(self, size, guest_mem_bytes):
98+
def is_guest_mem_x86(self, size, guest_mem_bytes):
8599
"""
86-
If the address is recognised as a guest memory region,
87-
return True, otherwise return False.
100+
Checks if a region is a guest memory region based on
101+
x86_64 physical memory layout
88102
"""
103+
return size in (
104+
# memory fits before the first gap
105+
guest_mem_bytes,
106+
# guest memory spans at least two regions & memory fits before the second gap
107+
self.X86_32BIT_MEMORY_GAP_START,
108+
# guest memory spans exactly two regions
109+
guest_mem_bytes - self.X86_32BIT_MEMORY_GAP_START,
110+
# guest memory fills the space between the two gaps
111+
self.X86_64BIT_MEMORY_GAP_START
112+
- self.X86_32BIT_MEMORY_GAP_START
113+
- self.X86_32BIT_MEMORY_GAP_SIZE,
114+
# guest memory spans 3 regions, this is what remains past the second gap
115+
guest_mem_bytes
116+
- self.X86_64BIT_MEMORY_GAP_START
117+
+ self.X86_32BIT_MEMORY_GAP_SIZE,
118+
)
89119

90-
# If x86_64 guest memory exceeds 3328M, it will be split
91-
# in 2 regions: 3328M and the rest. We have 3 cases here
92-
# to recognise a guest memory region:
93-
# - its size matches the guest memory exactly
94-
# - its size is 3328M
95-
# - its size is guest memory minus 3328M.
120+
def is_guest_mem_arch64(self, size, guest_mem_bytes):
121+
"""
122+
Checks if a region is a guest memory region based on
123+
ARM64 physical memory layout
124+
"""
96125
return size in (
126+
# guest memory fits before the gap
97127
guest_mem_bytes,
98-
self.X86_MEMORY_GAP_START,
99-
guest_mem_bytes - self.X86_MEMORY_GAP_START,
128+
# guest memory fills the space before the gap
129+
self.ARM64_64BIT_MEMORY_GAP_START - self.ARM64_MEMORY_START,
130+
# guest memory spans 2 regions, this is what remains past the gap
131+
guest_mem_bytes
132+
- self.ARM64_64BIT_MEMORY_GAP_START
133+
+ self.ARM64_MEMORY_START,
100134
)
101135

136+
def is_guest_mem(self, size, guest_mem_bytes):
137+
"""
138+
If the address is recognised as a guest memory region,
139+
return True, otherwise return False.
140+
"""
141+
142+
if global_props.cpu_architecture == "x86_64":
143+
return self.is_guest_mem_x86(size, guest_mem_bytes)
144+
145+
return self.is_guest_mem_arch64(size, guest_mem_bytes)
146+
102147
def check_samples(self):
103148
"""Check that there are no samples over the threshold."""
104149
if self._exceeded is not None:

0 commit comments

Comments
 (0)