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}  
19-             f"({ threshold  /  2 ** 20 }  ,
20+             f"Memory usage ({ usage  /  1   <<   20 :.2f}  
21+             f"({ threshold  /  1   <<   20 }  ,
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 }  )
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