Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/firecracker
Path: blob/main/tests/host_tools/memory.py
1956 views
1
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
# SPDX-License-Identifier: Apache-2.0
3
"""Utilities for measuring memory utilization for a process."""
4
from queue import Queue
5
import time
6
from threading import Thread
7
8
import framework.utils as utils
9
10
11
class MemoryUsageExceededException(Exception):
12
"""A custom exception containing details on excessive memory usage."""
13
14
def __init__(self, usage, threshold):
15
"""Compose the error message containing the memory consumption."""
16
super().__init__(
17
'Memory usage ({} KiB) exceeded maximum threshold ({} KiB).\n'
18
.format(usage, threshold)
19
)
20
21
22
class MemoryMonitor(Thread):
23
"""Class to represent a RSS memory monitor for a Firecracker process.
24
25
The guest's memory region is skipped, as the main interest is the
26
VMM memory usage.
27
"""
28
29
MEMORY_THRESHOLD = 5 * 1024
30
MEMORY_SAMPLE_TIMEOUT_S = 1
31
32
def __init__(self):
33
"""Initialize monitor attributes."""
34
Thread.__init__(self)
35
self._pid = None
36
self._guest_mem_mib = None
37
self._guest_mem_start = None
38
self._exceeded_queue = Queue()
39
self._threshold = self.MEMORY_THRESHOLD
40
self._should_stop = False
41
42
@property
43
def pid(self):
44
"""Get the pid."""
45
return self._pid
46
47
@property
48
def guest_mem_mib(self):
49
"""Get the guest memory in MiB."""
50
return self._guest_mem_mib
51
52
@property
53
def threshold(self):
54
"""Get the memory threshold."""
55
return self._threshold
56
57
@property
58
def exceeded_queue(self):
59
"""Get the exceeded queue."""
60
return self._exceeded_queue
61
62
@guest_mem_mib.setter
63
def guest_mem_mib(self, guest_mem_mib):
64
"""Set the guest memory MiB."""
65
self._guest_mem_mib = guest_mem_mib
66
67
@pid.setter
68
def pid(self, pid):
69
"""Set the pid."""
70
self._pid = pid
71
72
@threshold.setter
73
def threshold(self, threshold):
74
"""Set the threshold."""
75
self._threshold = threshold
76
77
def signal_stop(self):
78
"""Signal that the thread should stop."""
79
self._should_stop = True
80
81
def run(self):
82
"""Thread for monitoring the RSS memory usage of a Firecracker process.
83
84
`pmap` is used to compute the memory overhead. If it exceeds
85
the maximum value, it is pushed in a thread safe queue and memory
86
monitoring ceases. It is up to the caller to check the queue.
87
"""
88
pmap_cmd = 'pmap -xq {}'.format(self.pid)
89
90
while not self._should_stop:
91
mem_total = 0
92
try:
93
_, stdout, _ = utils.run_cmd(pmap_cmd)
94
pmap_out = stdout.split("\n")
95
except ChildProcessError:
96
return
97
for line in pmap_out:
98
tokens = line.split()
99
if not tokens:
100
break
101
try:
102
address = int(tokens[0])
103
total_size = int(tokens[1])
104
rss = int(tokens[2])
105
except ValueError:
106
# This line doesn't contain memory related information.
107
continue
108
if self._guest_mem_start is None and \
109
total_size == self.guest_mem_mib * 1024:
110
# This is the start of the guest's memory region.
111
self._guest_mem_start = address
112
continue
113
if self.is_in_guest_mem_region(address):
114
continue
115
mem_total += rss
116
117
if mem_total > self.threshold:
118
self.exceeded_queue.put(mem_total)
119
return
120
121
if not mem_total:
122
return
123
124
time.sleep(self.MEMORY_SAMPLE_TIMEOUT_S)
125
126
def is_in_guest_mem_region(self, address):
127
"""Check if the address is inside the guest memory region."""
128
if self._guest_mem_start is None:
129
return False
130
guest_mem_end = self._guest_mem_start + self.guest_mem_mib
131
return self._guest_mem_start <= address < guest_mem_end
132
133
def check_samples(self):
134
"""Check that there are no samples over the threshold."""
135
if not self.exceeded_queue.empty():
136
raise MemoryUsageExceededException(
137
self.exceeded_queue.get(), self.threshold)
138
139