88
99import psutil
1010
11+ from framework .properties import global_props
12+
1113
1214class 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