Add support for manually triggered OOM

Suggested-by: Mikko Rantalainen <mikko.rantalainen@gmail.com>
This commit is contained in:
Carsten Grohmann 2021-10-04 20:46:43 +02:00
parent 7af0c1d7e8
commit 8ed7678a74
3 changed files with 91 additions and 22 deletions

View File

@ -23,6 +23,9 @@
.js-text--default-show {
/* empty just used to show elements in the default view */
}
.js-text--display-none {
display: none;
}
.table__sub-section--bold {
font-weight: bold;
@ -59,6 +62,13 @@
max-height: 200px;
}
.js-oom-automatic--show {
/* empty - used to show sections for automatically triggered OOMs */
}
.js-oom-manual--show {
/* empty - used to show sections for manually triggered OOMs */
}
.result__table {
border-collapse: collapse;
padding: 10px;
@ -149,9 +159,6 @@
color: #D8000C;
background-color: #FFD2D2;
}
.js-text--display-none {
display: none;
}
.license__text {
font-size: small;
@ -282,16 +289,28 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
<h3>Summary</h3>
<div id="explanation">
<p>
The system couldn't satisfy this request and started the OOM killer to free memory. The OOM killer
calculates a score for each process and terminates the process with the highest score to satisfy the
initial memory request.
</p>
<p>
The process &quot;<span class="trigger_proc_name"></span>&quot; (PID <span class="trigger_proc_pid"></span>)
requested <span class="trigger_proc_requested_memory_pages_kb"></span>
(<span class="trigger_proc_requested_memory_pages"></span>) memory.
</p>
<div class="js-text--default-show js-oom-automatic--show">
<p>
The OOM killer was automatically triggered to free memory, because the system couldn't satisfy the
memory request.
The OOM killer calculates a score for each process and terminates the process with the highest score
to satisfy the original memory request.
</p>
<p>
The process &quot;<span class="trigger_proc_name"></span>&quot; (PID <span
class="trigger_proc_pid"></span>)
requested <span class="trigger_proc_requested_memory_pages_kb"></span>
(<span class="trigger_proc_requested_memory_pages"></span>) memory.
</p>
</div>
<div class="js-text--default-show js-oom-manual--show">
<p>
The OOM killer was manually triggered (e.g. with &quot;<code>echo f > /proc/sysrq-trigger</code>&quot;)
by a user with root privileges.
There is no demand to free memory but the OOM killer started nevertheless.
The OOM killer calculates a score for each process and terminates the process with the highest score.
</p>
</div>
<p>
The process &quot;<span class="killed_proc_name"></span>&quot;
(PID <span class="killed_proc_pid"></span>) with an OOM score of <span class="killed_proc_score"></span>
@ -326,7 +345,7 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
<tr>
<th colspan="3" scope="row">Trigger Process</th>
</tr>
<tr>
<tr class="js-oom-automatic--show">
<td></td>
<td class="text--align-right"><span class="trigger_proc_name"></span> (PID <span class="trigger_proc_pid"></span>)</td>
<td>This process requests memory and is triggering thereby the OOM situation.</td>
@ -342,7 +361,7 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
<td class="trigger_proc_nodemask text--align-right"></td>
<td>Bit mask indicating the cores on which the process can run.</td>
</tr>
<tr>
<tr class="js-oom-automatic--show">
<td>Requested memory<br>(order)</td>
<td class="text--align-right">
<span class="trigger_proc_requested_memory_pages"></span> (2<span class="trigger_proc_order text__superscript"></span>) pages /
@ -842,6 +861,7 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
<li>Fix to allow process names with spaces</li>
<li>Rework removal of unused information</li>
<li>Report uncaught errors to the user</li>
<li>Add support for manually triggered OOM (suggested by Mikko Rantalainen)</li>
<li>...</li>
</ol>

View File

@ -63,6 +63,13 @@ class OOMEntityState:
complete = 4
class OOMEntityType:
"""Enum for the type of the OOM"""
unknown = 0
automatic = 1
manual = 2
def is_visible(element):
return element.offsetWidth > 0 and element.offsetHeight > 0
@ -79,6 +86,18 @@ def show_element(element_id):
element.classList.remove('js-text--display-none')
def hide_elements(selector):
"""Hide all matching elements by adding class js-text--display-none"""
for element in document.querySelectorAll(selector):
element.classList.add('js-text--display-none')
def show_elements(selector):
"""Show all matching elements by removing class js-text--display-none"""
for element in document.querySelectorAll(selector):
element.classList.remove('js-text--display-none')
def toggle(element_id):
"""Toggle the visibility of the given HTML element"""
element = document.getElementById(element_id)
@ -420,6 +439,13 @@ class OOMResult:
:type: OOMEntityState
"""
oom_type = OOMEntityType.unknown
"""
Type of this OOM (manually or automatically triggered)
:type: OOMEntityType
"""
error_msg = ""
"""
Error message
@ -450,7 +476,7 @@ class OOMAnalyser:
r'^(?P<trigger_proc_name>[\S ]+) invoked oom-killer: '
r'gfp_mask=(?P<trigger_proc_gfp_mask>0x[a-z0-9]+)(\((?P<trigger_proc_gfp_flags>[A-Z_|]+)\))?, '
r'(nodemask=(?P<trigger_proc_nodemask>([\d,-]+|\(null\))), )?'
r'order=(?P<trigger_proc_order>\d+), '
r'order=(?P<trigger_proc_order>-?\d+), '
r'oom_score_adj=(?P<trigger_proc_oomscore>\d+)',
True,
),
@ -674,6 +700,11 @@ class OOMAnalyser:
'does not find anything. This will cause subsequent errors.'.format(k, pattern))
# __pragma__ ('nojsiter')
if self.oom_result.details['trigger_proc_order'] == "-1":
self.oom_result.oom_type = OOMEntityType.manual
else:
self.oom_result.oom_type = OOMEntityType.automatic
self.oom_result.details['hardware_info'] = self._extract_block_from_next_pos('Hardware name:')
# strip "Call Trace" line at beginning and remove leading spaces
@ -1308,16 +1339,13 @@ Killed process 6576 (mysqld) total-vm:33914892kB, anon-rss:20629004kB, file-rss:
def set_HTML_defaults(self):
"""Reset the HTML document but don't clean elements"""
# hide all elements marked to be hidden by default
for element in document.querySelectorAll('.js-text--default-hide'):
element.classList.add('js-text--display-none')
hide_elements('.js-text--default-hide')
# show all elements marked to be shown by default
for element in document.querySelectorAll('.js-text--default-show'):
element.classList.remove('js-text--display-none')
show_elements('.js-text--default-show')
# show hidden rows
for element in document.querySelectorAll('table .js-text--display-none'):
element.classList.remove('js-text--display-none')
show_elements('table .js-text--display-none')
# clear notification box
element = document.getElementById('notify_box')
@ -1490,6 +1518,12 @@ Killed process 6576 (mysqld) total-vm:33914892kB, anon-rss:20629004kB, file-rss:
hide_element('input')
show_element('analysis')
if self.oom_result.oom_type == OOMEntityType.manual:
hide_elements('.js-oom-automatic--show')
show_elements('.js-oom-manual--show')
else:
show_elements('.js-oom-automatic--show')
hide_elements('.js-oom-manual--show')
for item in self.oom_result.details.keys():
# ignore internal items

15
test.py
View File

@ -190,6 +190,10 @@ class TestInBrowser(TestBase):
swap_total_kb = self.driver.find_element_by_class_name('swap_total_kb')
self.assertEqual(swap_total_kb.text, '8388604 kBytes')
explanation = self.driver.find_element_by_id('explanation')
self.assertTrue('OOM killer was automatically triggered' in explanation.text,
'Missing text "OOM killer was automatically triggered"')
def test_010_load_page(self):
"""Test if the page is loading"""
assert "OOM Analyser" in self.driver.title
@ -291,6 +295,17 @@ Killed process 6576 (java) total-vm:33914892kB, anon-rss:20629004kB, file-rss:0k
self.check_results()
self.click_reset()
def test_070_manually_triggered_OOM(self):
"""Test for manually triggered OOM"""
example = OOMAnalyser.OOMDisplay.example
example = example.replace('order=0', 'order=-1')
self.analyse_oom(example)
self.assert_on_warn_error()
explanation = self.driver.find_element_by_id('explanation')
self.assertTrue('OOM killer was manually triggered' in explanation.text,
'Missing text "OOM killer was manually triggered"')
class TestPython(TestBase):