diff --git a/OOMAnalyser.html b/OOMAnalyser.html index f874cdc..038ac32 100644 --- a/OOMAnalyser.html +++ b/OOMAnalyser.html @@ -818,6 +818,7 @@ function read_and_display_file(file) {
  • Improve SVG chart colour palette
  • Add Selenium based unit tests
  • Fix to allow process names with spaces
  • +
  • Rework removal of unused information
  • ...
  • diff --git a/OOMAnalyser.py b/OOMAnalyser.py index 105f571..99f66c2 100644 --- a/OOMAnalyser.py +++ b/OOMAnalyser.py @@ -201,8 +201,9 @@ class OOMEntity(object): return oom_lines = self._remove_non_oom_lines(oom_lines) - oom_lines = self._strip_needless_columns(oom_lines) + oom_lines = self._remove_kernel_colon(oom_lines) oom_lines = self._rsyslog_unescape_lf(oom_lines) + oom_lines = self._strip_needless_columns(oom_lines) self.lines = oom_lines self.text = '\n'.join(oom_lines) @@ -223,21 +224,13 @@ class OOMEntity(object): columns = first_line.split(" ") # Examples: - # [11686.888109] sed invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0 - # Apr 01 14:13:32 mysrv kernel: sed invoked OOM-killer: gfp_mask=0x201da, order=0 - # Apr 01 14:13:32 mysrv kernel: [11686.888109] sed invoked oom-killer: gfp_mask=0x84d0, order=0, oom_adj=0, oom_score_adj=0 + # [11686.888109] CPU: 4 PID: 29481 Comm: sed Not tainted 3.10.0-514.6.1.el7.x86_64 #1 + # Apr 01 14:13:32 mysrv kernel: CPU: 4 PID: 29481 Comm: sed Not tainted 3.10.0-514.6.1.el7.x86_64 #1 + # Apr 01 14:13:32 mysrv kernel: [11686.888109] CPU: 4 PID: 29481 Comm: sed Not tainted 3.10.0-514.6.1.el7.x86_64 #1 try: - # strip all incl. "kernel:" - if 'kernel:' in first_line: - to_strip = columns.index("kernel:") - # increase to include "kernel:" - to_strip += 1 - - # check if next column is a timestamp like "[11686.888109]" and remove it too - rec = re.compile('\[\d+\.\d+\]') - if rec.match(columns[to_strip]): - # increase to include timestamp - to_strip += 1 + # strip all excl. "CPU:" + if 'CPU:' in first_line: + to_strip = columns.index("CPU:") except ValueError: pass @@ -283,6 +276,16 @@ class OOMEntity(object): return lines + def _remove_kernel_colon(self, oom_lines): + """ + Remove the "kernel:" pattern w/o leading and tailing spaces. + + Some OOM messages don't have a space between "kernel:" and the process name. _strip_needless_columns() will + fail in such cases. Therefore the pattern is removed. + """ + oom_lines = [i.replace('kernel:', '') for i in oom_lines] + return oom_lines + def _strip_needless_columns(self, oom_lines): """ Remove needless columns at the start of every line. @@ -291,7 +294,7 @@ class OOMEntity(object): syslog priority/facility. """ stripped_lines = [] - cols_to_strip = self._number_of_columns_to_strip(oom_lines[0]) + cols_to_strip = self._number_of_columns_to_strip(oom_lines[2]) for line in oom_lines: # remove empty lines @@ -412,7 +415,7 @@ class OOMAnalyser(object): REC_SWAP = re.compile( r'^(?P\d+) pages in swap cache' r'(?:\n)' - r'^Swap cache stats: add \d+, delete \d+, find \d+/\d+' + r'^Swap cache stats: add \d+, delete \d+, find \d+\/\d+' r'(?:\n)' r'^Free swap = (?P\d+)kB' r'(?:\n)' @@ -989,6 +992,7 @@ Killed process 6576 (mysqld) total-vm:33914892kB, anon-rss:20629004kB, file-rss: """SVG graphics with one black triangle DOWN for sorting""" def __init__(self): + self.oom = None self.set_HTML_defaults() self.update_toc() diff --git a/test.py b/test.py index 215849a..bca8289 100755 --- a/test.py +++ b/test.py @@ -18,6 +18,7 @@ # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import http.server +import logging import os import socketserver import threading @@ -101,13 +102,21 @@ class TestInBrowser(TestBase): def assert_on_warn(self): notify_box = self.driver.find_element_by_id('notify_box') - with self.assertRaises(NoSuchElementException): - notify_box.find_element_by_class_name('js-notify_box__msg--warning') + try: + warning = notify_box.find_element_by_class_name('js-notify_box__msg--warning') + except NoSuchElementException: + pass + else: + self.fail('Unexpected warning message: %s' % warning.text) def assert_on_error(self): notify_box = self.driver.find_element_by_id('notify_box') - with self.assertRaises(NoSuchElementException): - notify_box.find_element_by_class_name('js-notify_box__msg--error') + try: + error = notify_box.find_element_by_class_name('js-notify_box__msg--error') + except NoSuchElementException: + pass + else: + self.fail('Unexpected error message: %s' % error.text) for event in self.driver.get_log('browser'): # ignore favicon.ico errors @@ -115,13 +124,23 @@ class TestInBrowser(TestBase): continue self.fail('Error on browser console reported: %s' % event) + def assert_on_warn_error(self): + self.assert_on_warn() + self.assert_on_error() + def click_analyse(self): analyse = self.driver.find_element_by_xpath('//button[text()="Analyse"]') analyse.click() def click_reset(self): + # OOMAnalyser.OOMDisplayInstance.reset_form() reset = self.driver.find_element_by_xpath('//button[text()="Reset"]') - reset.click() + if reset.is_displayed(): + reset.click() + else: + new_analysis = self.driver.find_element_by_xpath('//a[contains(text(), "Step 1 - Enter your OOM message")]') + # new_analysis = self.driver.find_element_by_link_text('Run a new analysis') + new_analysis.click() self.assert_on_warn_error() def analyse_oom(self, text): @@ -141,9 +160,28 @@ class TestInBrowser(TestBase): self.click_analyse() - def assert_on_warn_error(self): - self.assert_on_warn() - self.assert_on_error() + def check_results(self): + """Check the results of the analysis of the default example""" + self.assert_on_warn_error() + h3_summary = self.driver.find_element_by_xpath('//h3[text()="Summary"]') + self.assertTrue(h3_summary.is_displayed(), "Analysis details incl.

    Summary

    should be displayed") + + trigger_proc_name = self.driver.find_element_by_class_name('trigger_proc_name') + self.assertEqual(trigger_proc_name.text, 'sed', 'Unexpected trigger process name') + trigger_proc_pid = self.driver.find_element_by_class_name('trigger_proc_pid') + self.assertEqual(trigger_proc_pid.text, '29481', 'Unexpected trigger process pid') + + killed_proc_score = self.driver.find_element_by_class_name('killed_proc_score') + self.assertEqual(killed_proc_score.text, '651', 'Unexpected OOM score of killed process') + + swap_cache_kb = self.driver.find_element_by_class_name('swap_cache_kb') + self.assertEqual(swap_cache_kb.text, '45368 kBytes') + swap_used_kb = self.driver.find_element_by_class_name('swap_used_kb') + self.assertEqual(swap_used_kb.text, '8343236 kBytes') + swap_free_kb = self.driver.find_element_by_class_name('swap_free_kb') + self.assertEqual(swap_free_kb.text, '0 kBytes') + swap_total_kb = self.driver.find_element_by_class_name('swap_total_kb') + self.assertEqual(swap_total_kb.text, '8388604 kBytes') def test_001_load_page(self): """Test if the page is loading""" @@ -166,32 +204,14 @@ class TestInBrowser(TestBase): self.assertFalse(h3_summary.is_displayed(), "Analysis details incl.

    Summary

    should be not displayed") self.click_analyse() - - self.assert_on_warn_error() - self.assertTrue(h3_summary.is_displayed(), "Analysis details incl.

    Summary

    should be displayed") - - trigger_proc_name = self.driver.find_element_by_class_name('trigger_proc_name') - self.assertEqual(trigger_proc_name.text, 'sed', 'Unexpected trigger process name') - trigger_proc_pid = self.driver.find_element_by_class_name('trigger_proc_pid') - self.assertEqual(trigger_proc_pid.text, '29481', 'Unexpected trigger process pid') - - killed_proc_score = self.driver.find_element_by_class_name('killed_proc_score') - self.assertEqual(killed_proc_score.text, '651', 'Unexpected OOM score of killed process') - - swap_cache_kb = self.driver.find_element_by_class_name('swap_cache_kb') - self.assertEqual(swap_cache_kb.text, '45368 kBytes') - swap_used_kb = self.driver.find_element_by_class_name('swap_used_kb') - self.assertEqual(swap_used_kb.text, '8343236 kBytes') - swap_free_kb = self.driver.find_element_by_class_name('swap_free_kb') - self.assertEqual(swap_free_kb.text, '0 kBytes') - swap_total_kb = self.driver.find_element_by_class_name('swap_total_kb') - self.assertEqual(swap_total_kb.text, '8388604 kBytes') + self.check_results() def test_004_begin_but_no_end(self): """Test incomplete OOM text - just the beginning""" example = """\ sed invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0 sed cpuset=/ mems_allowed=0-1 +CPU: 4 PID: 29481 Comm: sed Not tainted 3.10.0-514.6.1.el7.x86_64 #1 """ self.analyse_oom(example) @@ -239,6 +259,33 @@ Killed process 6576 (java) total-vm:33914892kB, anon-rss:20629004kB, file-rss:0k h3_summary = self.driver.find_element_by_xpath('//h3[text()="Summary"]') self.assertTrue(h3_summary.is_displayed(), "Analysis details incl.

    Summary

    should be displayed") + def test_removal_of_leading_but_useless_columns(self): + """Test removal of leading but useless columns""" + self.analyse_oom(OOMAnalyser.OOMDisplay.example) + self.check_results() + self.click_reset() + for prefix in ["[11686.888109] ", + "Apr 01 14:13:32 mysrv: ", + "Apr 01 14:13:32 mysrv kernel: ", + "Apr 01 14:13:32 mysrv kernel: ", + "Apr 01 14:13:32 mysrv kernel: [11686.888109] ", + "kernel:", + "Apr 01 14:13:32 mysrv kernel:", + ]: + lines = OOMAnalyser.OOMDisplay.example.split('\n') + lines = ["{}{}".format(prefix, line) for line in lines] + oom_text = "\n".join(lines) + self.analyse_oom(oom_text) + + try: + self.check_results() + except AssertionError: + logging.error('prefix %s', prefix) + import pdb; pdb.set_trace() + oom = OOMAnalyser.OOMEntity(oom_text) + print(oom.text) + self.click_reset() + class TestPython(TestBase): @@ -272,9 +319,9 @@ class TestPython(TestBase): """Test stripping useless / leading columns""" oom_entity = OOMAnalyser.OOMEntity(OOMAnalyser.OOMDisplay.example) for pos, line in [ - (1, '[11686.888109] sed invoked oom-killer: gfp_mask=0x201da, order=0, oom_adj=0, oom_score_adj=0'), - (5, 'Apr 01 14:13:32 mysrv kernel: sed invoked OOM-killer: gfp_mask=0x201da, order=0'), - (6, 'Apr 01 14:13:32 mysrv kernel: [11686.888109] sed invoked oom-killer: gfp_mask=0x84d0, order=0, oom_adj=0, oom_score_adj=0'), + (1, '[11686.888109] CPU: 4 PID: 29481 Comm: sed Not tainted 3.10.0-514.6.1.el7.x86_64 #1'), + (5, 'Apr 01 14:13:32 mysrv kernel: CPU: 4 PID: 29481 Comm: sed Not tainted 3.10.0-514.6.1.el7.x86_64 #1'), + (6, 'Apr 01 14:13:32 mysrv kernel: [11686.888109] CPU: 4 PID: 29481 Comm: sed Not tainted 3.10.0-514.6.1.el7.x86_64 #1'), ]: to_strip = oom_entity._number_of_columns_to_strip(line) self.assertEqual(to_strip, pos, 'Calc wrong number of columns to strip for "%s": got: %d, expect: %d' % (