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">
<div class="js-text--default-show js-oom-automatic--show">
<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.
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>)
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):