M OOMAnalyser.html => OOMAnalyser.html +35 -15
@@ 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 "<span class="trigger_proc_name"></span>" (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 "<span class="trigger_proc_name"></span>" (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 "<code>echo f > /proc/sysrq-trigger</code>")
+ 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 "<span class="killed_proc_name"></span>"
(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>
M OOMAnalyser.py => OOMAnalyser.py +41 -7
@@ 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
M test.py => test.py +15 -0
@@ 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):