diff --git a/OOMAnalyser.py b/OOMAnalyser.py index fa3bcff..16bbbd5 100644 --- a/OOMAnalyser.py +++ b/OOMAnalyser.py @@ -153,6 +153,111 @@ class BaseKernelConfig: name = 'Base configuration for all kernels' """Name/description of this kernel configuration""" + EXTRACT_PATTERN = { + 'invoked oom-killer': ( + 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'oom_score_adj=(?P\d+)', + True, + ), + 'Trigger process and kernel version': ( + r'^CPU: \d+ PID: (?P\d+) ' + r'Comm: .* (Not tainted|Tainted:.*) ' + r'(?P\d[\w.-]+) #\d', + True, + ), + + # split caused by a limited number of iterations during converting PY regex into JS regex + 'Mem-Info (part 1)': ( + r'^Mem-Info:.*' + r'(?:\n)' + + # first line (starting w/o a space) + r'^active_anon:(?P\d+) inactive_anon:(?P\d+) ' + r'isolated_anon:(?P\d+)' + r'(?:\n)' + + # remaining lines (w/ leading space) + r'^ active_file:(?P\d+) inactive_file:(?P\d+) ' + r'isolated_file:(?P\d+)' + r'(?:\n)' + + r'^ unevictable:(?P\d+) dirty:(?P\d+) writeback:(?P\d+) ' + r'unstable:(?P\d+)', + True, + ), + 'Mem-Info (part 2)': ( + r'^ slab_reclaimable:(?P\d+) slab_unreclaimable:(?P\d+)' + r'(?:\n)' + r'^ mapped:(?P\d+) shmem:(?P\d+) pagetables:(?P\d+) ' + r'bounce:(?P\d+)' + r'(?:\n)' + r'^ free:(?P\d+) free_pcp:(?P\d+) free_cma:(?P\d+)', + True, + ), + 'Memory node information': ( + r'(^Node \d+ (DMA|Normal|hugepages).*(:?\n))+', + False, + ), + 'Page cache': ( + r'^(?P\d+) total pagecache pages.*$', + True, + ), + 'Swap usage information': ( + r'^(?P\d+) pages in swap cache' + r'(?:\n)' + r'^Swap cache stats: add \d+, delete \d+, find \d+\/\d+' + r'(?:\n)' + r'^Free swap = (?P\d+)kB' + r'(?:\n)' + r'^Total swap = (?P\d+)kB', + False, + ), + 'Page information': ( + r'^(?P\d+) pages RAM' + r'(' + r'(?:\n)' + r'^(?P\d+) pages HighMem/MovableOnly' + r')?' + r'(?:\n)' + r'^(?P\d+) pages reserved' + r'(' + r'(?:\n)' + r'^(?P\d+) pages cma reserved' + r')?' + r'(' + r'(?:\n)' + r'^(?P\d+) pages in pagetable cache' + r')?' + r'(' + r'(?:\n)' + r'^(?P\d+) pages hwpoisoned' + r')?', + True, + ), + 'Process killed by OOM': ( + r'^Out of memory: Kill process (?P\d+) \((?P[\S ]+)\) ' + r'score (?P\d+) or sacrifice child', + True, + ), + 'Details of process killed by OOM': ( + r'^Killed process \d+ \(.*\)' + r'(, UID \d+,)?' + r' total-vm:(?P\d+)kB, anon-rss:(?P\d+)kB, ' + r'file-rss:(?P\d+)kB, shmem-rss:(?P\d+)kB.*', + True, + ), + } + """ + RE pattern to extract information from OOM. + + The first item is the RE pattern and the second is whether it is mandatory to find this pattern. + + :type: dict(tuple(str, bool)) + """ + GFP_FLAGS = { 'GFP_ATOMIC': {'value': '__GFP_HIGH | __GFP_ATOMIC | __GFP_KSWAPD_RECLAIM'}, 'GFP_KERNEL': {'value': '__GFP_RECLAIM | __GFP_IO | __GFP_FS'}, @@ -212,6 +317,11 @@ class BaseKernelConfig: 'oom_score_adj'] """Elements of the process table""" + REC_PROCESS_LINE = re.compile( + r'^\[(?P[ \d]+)\]\s+(?P\d+)\s+(?P\d+)\s+(?P\d+)\s+(?P\d+)\s+' + r'(?P\d+)\s+(?P\d+)\s+(?P-?\d+)\s+(?P.+)\s*') + """Match content of process table""" + rec_version4kconfig = re.compile('.+') """RE to match kernel version to kernel configuration""" @@ -478,115 +588,6 @@ class OOMResult: class OOMAnalyser: """Analyse an OOM object and calculate additional values""" - EXTRACT_PATTERN = { - 'invoked oom-killer': ( - 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'oom_score_adj=(?P\d+)', - True, - ), - 'Trigger process and kernel version': ( - r'^CPU: \d+ PID: (?P\d+) ' - r'Comm: .* (Not tainted|Tainted:.*) ' - r'(?P\d[\w.-]+) #\d', - True, - ), - - # split caused by a limited number of iterations during converting PY regex into JS regex - 'Mem-Info (part 1)': ( - r'^Mem-Info:.*' - r'(?:\n)' - - # first line (starting w/o a space) - r'^active_anon:(?P\d+) inactive_anon:(?P\d+) ' - r'isolated_anon:(?P\d+)' - r'(?:\n)' - - # remaining lines (w/ leading space) - r'^ active_file:(?P\d+) inactive_file:(?P\d+) ' - r'isolated_file:(?P\d+)' - r'(?:\n)' - - r'^ unevictable:(?P\d+) dirty:(?P\d+) writeback:(?P\d+) ' - r'unstable:(?P\d+)', - True, - ), - 'Mem-Info (part 2)': ( - r'^ slab_reclaimable:(?P\d+) slab_unreclaimable:(?P\d+)' - r'(?:\n)' - r'^ mapped:(?P\d+) shmem:(?P\d+) pagetables:(?P\d+) ' - r'bounce:(?P\d+)' - r'(?:\n)' - r'^ free:(?P\d+) free_pcp:(?P\d+) free_cma:(?P\d+)', - True, - ), - 'Memory node information': ( - r'(^Node \d+ (DMA|Normal|hugepages).*(:?\n))+', - False, - ), - 'Page cache': ( - r'^(?P\d+) total pagecache pages.*$', - True, - ), - 'Swap usage information': ( - r'^(?P\d+) pages in swap cache' - r'(?:\n)' - r'^Swap cache stats: add \d+, delete \d+, find \d+\/\d+' - r'(?:\n)' - r'^Free swap = (?P\d+)kB' - r'(?:\n)' - r'^Total swap = (?P\d+)kB', - False, - ), - 'Page information': ( - r'^(?P\d+) pages RAM' - r'(' - r'(?:\n)' - r'^(?P\d+) pages HighMem/MovableOnly' - r')?' - r'(?:\n)' - r'^(?P\d+) pages reserved' - r'(' - r'(?:\n)' - r'^(?P\d+) pages cma reserved' - r')?' - r'(' - r'(?:\n)' - r'^(?P\d+) pages in pagetable cache' - r')?' - r'(' - r'(?:\n)' - r'^(?P\d+) pages hwpoisoned' - r')?', - True, - ), - 'Process killed by OOM': ( - r'^Out of memory: Kill process (?P\d+) \((?P[\S ]+)\) ' - r'score (?P\d+) or sacrifice child', - True, - ), - 'Details of process killed by OOM': ( - r'^Killed process \d+ \(.*\)' - r'(, UID \d+,)?' - r' total-vm:(?P\d+)kB, anon-rss:(?P\d+)kB, ' - r'file-rss:(?P\d+)kB, shmem-rss:(?P\d+)kB.*', - True, - ), - } - """ - RE pattern to extract information from OOM. - - The first item is the RE pattern and the second is whether it is mandatory to find this pattern. - - :type: dict(tuple(str, bool)) - """ - - REC_PROCESS_LINE = re.compile( - r'^\[(?P[ \d]+)\]\s+(?P\d+)\s+(?P\d+)\s+(?P\d+)\s+(?P\d+)\s+' - r'(?P\d+)\s+(?P\d+)\s+(?P-?\d+)\s+(?P.+)\s*') - oom_entity = None """ State of this OOM (unknown, incomplete, ...) @@ -594,7 +595,7 @@ class OOMAnalyser: :type: OOMEntityState """ - oom_result = None + oom_result = OOMResult() """ Store details of OOM analysis @@ -696,8 +697,8 @@ class OOMAnalyser: self.oom_result.details = {} # __pragma__ ('jsiter') - for k in self.EXTRACT_PATTERN: - pattern, is_mandatory = self.EXTRACT_PATTERN[k] + for k in self.oom_result.kconfig.EXTRACT_PATTERN: + pattern, is_mandatory = self.oom_result.kconfig.EXTRACT_PATTERN[k] rec = re.compile(pattern, re.MULTILINE) match = rec.search(self.oom_entity.text) if match: @@ -731,7 +732,7 @@ class OOMAnalyser: break if line.startswith('[ pid ]'): continue - match = self.REC_PROCESS_LINE.match(line) + match = self.oom_result.kconfig.REC_PROCESS_LINE.match(line) if match: details = match.groupdict() details['notes'] = '' diff --git a/test.py b/test.py index 60b7803..9d3d128 100755 --- a/test.py +++ b/test.py @@ -340,7 +340,7 @@ class TestPython(TestBase): def test_001_trigger_proc_space(self): """Test RE to find name of trigger process""" first = self.get_first_line(OOMAnalyser.OOMDisplay.example) - pattern = OOMAnalyser.OOMAnalyser.EXTRACT_PATTERN['invoked oom-killer'][0] + pattern = OOMAnalyser.OOMAnalyser.oom_result.kconfig.EXTRACT_PATTERN['invoked oom-killer'][0] rec = re.compile(pattern, re.MULTILINE) match = rec.search(first) self.assertTrue(match, "Error: re.search('invoked oom-killer') failed for simple process name") @@ -352,7 +352,7 @@ class TestPython(TestBase): def test_002_killed_proc_space(self): """Test RE to find name of killed process""" last = self.get_last_line(OOMAnalyser.OOMDisplay.example) - pattern = OOMAnalyser.OOMAnalyser.EXTRACT_PATTERN['Process killed by OOM'][0] + pattern = OOMAnalyser.OOMAnalyser.oom_result.kconfig.EXTRACT_PATTERN['Process killed by OOM'][0] rec = re.compile(pattern, re.MULTILINE) match = rec.search(last) self.assertTrue(match, "Error: re.search('Process killed by OOM') failed for simple process name")