diff --git a/OOMAnalyser.html b/OOMAnalyser.html index d85677a..671bcd0 100644 --- a/OOMAnalyser.html +++ b/OOMAnalyser.html @@ -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) {

Summary

-

- 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 process "" (PID ) - requested - () memory. -

+
+

+ 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. +

+

+ The process "" (PID ) + requested + () memory. +

+
+
+

+ The OOM killer was manually triggered (e.g. with "echo f > /proc/sysrq-trigger") + 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. +

+

The process "" (PID ) with an OOM score of @@ -326,7 +345,7 @@ window.onerror = function (msg, url, lineNo, columnNo, error) { Trigger Process - + (PID ) This process requests memory and is triggering thereby the OOM situation. @@ -342,7 +361,7 @@ window.onerror = function (msg, url, lineNo, columnNo, error) { Bit mask indicating the cores on which the process can run. - + Requested memory
(order) (2) pages / @@ -842,6 +861,7 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {

  • Fix to allow process names with spaces
  • Rework removal of unused information
  • Report uncaught errors to the user
  • +
  • Add support for manually triggered OOM (suggested by Mikko Rantalainen)
  • ...
  • diff --git a/OOMAnalyser.py b/OOMAnalyser.py index 90cf918..c943847 100644 --- a/OOMAnalyser.py +++ b/OOMAnalyser.py @@ -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[\S ]+) invoked oom-killer: ' r'gfp_mask=(?P0x[a-z0-9]+)(\((?P[A-Z_|]+)\))?, ' r'(nodemask=(?P([\d,-]+|\(null\))), )?' - r'order=(?P\d+), ' + r'order=(?P-?\d+), ' r'oom_score_adj=(?P\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 diff --git a/test.py b/test.py index 30fa0ae..4d17f88 100755 --- a/test.py +++ b/test.py @@ -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):