Compare commits
10 Commits
bd3085c667
...
4e12c22c10
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4e12c22c10 | ||
![]() |
77e7c9ca93 | ||
![]() |
af59b6147e | ||
![]() |
6e7db97f8f | ||
![]() |
0ac8f4939a | ||
![]() |
e710414116 | ||
![]() |
3f19a50f17 | ||
![]() |
7e99d267a5 | ||
![]() |
52d476cee2 | ||
![]() |
cbd16d266f |
@ -115,6 +115,10 @@ THIS PROGRAM COMES WITH NO WARRANTY
|
||||
/* empty - used to hide/show details for memory fragmentation */
|
||||
}
|
||||
|
||||
.js-memory-shortage-node--hide {
|
||||
/* empty - used to show the NUMA node with memory shortage */
|
||||
}
|
||||
|
||||
.js-oom-automatic--show {
|
||||
/* empty - used to show sections for automatically triggered OOMs */
|
||||
}
|
||||
@ -445,7 +449,7 @@ window.onerror = function (msg, url, lineNo, columnNo, errorObj) {
|
||||
requested a memory chunk of order <span class="trigger_proc_order"></span> from the
|
||||
"<span class="trigger_proc_mem_zone"></span>" memory zone.
|
||||
|
||||
That are 2<span class="text__superscript">order</span> pages ==
|
||||
This is 2<span class="text__superscript">order</span> pages ==
|
||||
2<span class="trigger_proc_order text__superscript"></span> pages ==
|
||||
<span class="trigger_proc_requested_memory_pages"></span>
|
||||
a <span class="page_size_kb"></span> ==
|
||||
@ -455,17 +459,17 @@ window.onerror = function (msg, url, lineNo, columnNo, errorObj) {
|
||||
<div class="js-text--default-hide js-text--display-none js-alloc-failure--show">
|
||||
<p>
|
||||
<span class="js-text--default-hide js-text--display-none js-alloc-failure-below-low-watermark--show">
|
||||
The request failed because after its fulfillment the free memory would be below the memory
|
||||
low watermark.
|
||||
The request failed because the free memory would be below the memory low watermark
|
||||
after its completion.
|
||||
</span>
|
||||
<span class="js-text--default-hide js-text--display-none js-alloc-failure-no-free-chunks--show">
|
||||
If this requirement were met, the free memory would still be above the low memory watermark.
|
||||
If this requirement was satisfied, the free memory would still be above the low memory watermark.
|
||||
The request failed because there is no free chunk in the current or higher order.
|
||||
</span>
|
||||
<span class="js-text--default-hide js-text--display-none js-alloc-failure-unknown-reason-show">
|
||||
The request failed, but the reason is unknown.
|
||||
</span>
|
||||
The memory shortage triggers the OOM process.
|
||||
This memory shortage triggers the OOM process.
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
@ -558,7 +562,7 @@ window.onerror = function (msg, url, lineNo, columnNo, errorObj) {
|
||||
<td class="trigger_proc_mem_zone text--align-right"></td>
|
||||
<td>Memory zone from which the requested storage chunk should come.</td>
|
||||
</tr>
|
||||
<tr class="js-oom-automatic--show">
|
||||
<tr class="js-text--default-hide js-oom-automatic--show js-memory-shortage-node--hide">
|
||||
<td>Requested memory: node</td>
|
||||
<td class="trigger_proc_numa_node text--align-right"></td>
|
||||
<td>
|
||||
@ -1087,6 +1091,7 @@ window.onerror = function (msg, url, lineNo, columnNo, errorObj) {
|
||||
<li>Add check for heavy memory fragmentation</li>
|
||||
<li>Summary of the analysis revised</li>
|
||||
<li>Show memory watermarks together will all details</li>
|
||||
<li>Add support for kernel 6.0 and newer</li>
|
||||
<li>...</li>
|
||||
</ol>
|
||||
|
||||
|
321
OOMAnalyser.py
321
OOMAnalyser.py
@ -277,7 +277,8 @@ class BaseKernelConfig:
|
||||
True,
|
||||
),
|
||||
# split caused by a limited number of iterations during converting PY regex into JS regex
|
||||
"Mem-Info (part 1)": (
|
||||
# Source: mm/page_alloc.c:__show_free_areas()
|
||||
"Overall Mem-Info (part 1)": (
|
||||
r"^Mem-Info:.*" r"(?:\n)"
|
||||
# first line (starting w/o a space)
|
||||
r"^active_anon:(?P<active_anon_pages>\d+) inactive_anon:(?P<inactive_anon_pages>\d+) "
|
||||
@ -291,7 +292,7 @@ class BaseKernelConfig:
|
||||
r"unstable:(?P<unstable_pages>\d+)",
|
||||
True,
|
||||
),
|
||||
"Mem-Info (part 2)": (
|
||||
"Overall Mem-Info (part 2)": (
|
||||
r"^ slab_reclaimable:(?P<slab_reclaimable_pages>\d+) slab_unreclaimable:(?P<slab_unreclaimable_pages>\d+)"
|
||||
r"(?:\n)"
|
||||
r"^ mapped:(?P<mapped_pages>\d+) shmem:(?P<shmem_pages>\d+) pagetables:(?P<pagetables_pages>\d+) "
|
||||
@ -312,6 +313,7 @@ class BaseKernelConfig:
|
||||
r"^(?P<pagecache_total_pages>\d+) total pagecache pages.*$",
|
||||
True,
|
||||
),
|
||||
# Source:mm/swap_state.c:show_swap_cache_info()
|
||||
"Swap usage information": (
|
||||
r"^(?P<swap_cache_pages>\d+) pages in swap cache"
|
||||
r"(?:\n)"
|
||||
@ -534,12 +536,6 @@ class BaseKernelConfig:
|
||||
pstable_non_ints = ["pid", "name", "notes"]
|
||||
"""Columns that are not converted to an integer"""
|
||||
|
||||
REC_PROCESS_LINE = re.compile(
|
||||
r"^\[(?P<pid>[ \d]+)\]\s+(?P<uid>\d+)\s+(?P<tgid>\d+)\s+(?P<total_vm_pages>\d+)\s+(?P<rss_pages>\d+)\s+"
|
||||
r"(?P<nr_ptes_pages>\d+)\s+(?P<swapents_pages>\d+)\s+(?P<oom_score_adj>-?\d+)\s+(?P<name>.+)\s*"
|
||||
)
|
||||
"""Match content of process table"""
|
||||
|
||||
pstable_start = "[ pid ]"
|
||||
"""
|
||||
Pattern to find the start of the process table
|
||||
@ -560,12 +556,40 @@ class BaseKernelConfig:
|
||||
@type: (int, int, str)
|
||||
"""
|
||||
|
||||
rec_oom_begin = re.compile(r"invoked oom-killer:", re.MULTILINE)
|
||||
REC_FREE_MEMORY_CHUNKS = re.compile(
|
||||
"Node (?P<node>\d+) (?P<zone>DMA|DMA32|Normal): (?P<zone_usage>.*) = (?P<total_free_kb_per_node>\d+)kB"
|
||||
)
|
||||
"""RE to extract free memory chunks of a memory zone"""
|
||||
|
||||
REC_OOM_BEGIN = re.compile(r"invoked oom-killer:", re.MULTILINE)
|
||||
"""RE to match the first line of an OOM block"""
|
||||
|
||||
rec_oom_end = re.compile(r"^Killed process \d+", re.MULTILINE)
|
||||
REC_OOM_END = re.compile(r"^Killed process \d+", re.MULTILINE)
|
||||
"""RE to match the last line of an OOM block"""
|
||||
|
||||
REC_PAGE_SIZE = re.compile("Node 0 DMA: \d+\*(?P<page_size>\d+)kB")
|
||||
"""RE to extract the page size from buddyinfo DMA zone"""
|
||||
|
||||
REC_PROCESS_LINE = re.compile(
|
||||
r"^\[(?P<pid>[ \d]+)\]\s+(?P<uid>\d+)\s+(?P<tgid>\d+)\s+(?P<total_vm_pages>\d+)\s+(?P<rss_pages>\d+)\s+"
|
||||
r"(?P<nr_ptes_pages>\d+)\s+(?P<swapents_pages>\d+)\s+(?P<oom_score_adj>-?\d+)\s+(?P<name>.+)\s*"
|
||||
)
|
||||
"""Match content of process table"""
|
||||
|
||||
REC_WATERMARK = re.compile(
|
||||
"Node (?P<node>\d+) (?P<zone>DMA|DMA32|Normal) "
|
||||
"free:(?P<free>\d+)kB "
|
||||
"min:(?P<min>\d+)kB "
|
||||
"low:(?P<low>\d+)kB "
|
||||
"high:(?P<high>\d+)kB "
|
||||
".*"
|
||||
)
|
||||
"""
|
||||
RE to extract watermark information in a memory zone
|
||||
|
||||
Source: mm/page_alloc.c:__show_free_areas()
|
||||
"""
|
||||
|
||||
watermark_start = "Node 0 DMA free:"
|
||||
"""
|
||||
Pattern to find the start of the memory watermark information
|
||||
@ -1425,7 +1449,7 @@ class KernelConfig_4_6(KernelConfig_4_5):
|
||||
}
|
||||
|
||||
# The "oom_reaper" line is optionally
|
||||
rec_oom_end = re.compile(
|
||||
REC_OOM_END = re.compile(
|
||||
r"^((Out of memory.*|Memory cgroup out of memory): Killed process \d+|oom_reaper:)",
|
||||
re.MULTILINE,
|
||||
)
|
||||
@ -1527,7 +1551,6 @@ class KernelConfig_4_8(KernelConfig_4_6):
|
||||
class KernelConfig_4_9(KernelConfig_4_8):
|
||||
# Supported changes:
|
||||
# * "mm: oom: deduplicate victim selection code for memcg and global oom" (7c5f64f84483bd13886348edda8b3e7b799a7fdb)
|
||||
# * update GFP flags
|
||||
|
||||
name = "Configuration for Linux kernel 4.9 or later"
|
||||
release = (4, 9, "")
|
||||
@ -2104,7 +2127,6 @@ class KernelConfig_4_18(KernelConfig_4_15):
|
||||
class KernelConfig_4_19(KernelConfig_4_18):
|
||||
# Supported changes:
|
||||
# * mm, oom: describe task memory unit, larger PID pad (c3b78b11efbb2865433abf9d22c004ffe4a73f5c)
|
||||
# * update GFP flags
|
||||
|
||||
name = "Configuration for Linux kernel 4.19 or later"
|
||||
release = (4, 19, "")
|
||||
@ -2115,7 +2137,6 @@ class KernelConfig_4_19(KernelConfig_4_18):
|
||||
class KernelConfig_5_0(KernelConfig_4_19):
|
||||
# Supported changes:
|
||||
# * "mm, oom: reorganize the oom report in dump_header" (ef8444ea01d7442652f8e1b8a8b94278cb57eafd)
|
||||
# * update GFP flags
|
||||
|
||||
name = "Configuration for Linux kernel 5.0 or later"
|
||||
release = (5, 0, "")
|
||||
@ -2222,13 +2243,12 @@ class KernelConfig_5_1(KernelConfig_5_0):
|
||||
class KernelConfig_5_8(KernelConfig_5_1):
|
||||
# Supported changes:
|
||||
# * "mm/writeback: discard NR_UNSTABLE_NFS, use NR_WRITEBACK instead" (8d92890bd6b8502d6aee4b37430ae6444ade7a8c)
|
||||
# * update GFP flags
|
||||
|
||||
name = "Configuration for Linux kernel 5.8 or later"
|
||||
release = (5, 8, "")
|
||||
|
||||
EXTRACT_PATTERN_OVERLAY_58 = {
|
||||
"Mem-Info (part 1)": (
|
||||
"Overall Mem-Info (part 1)": (
|
||||
r"^Mem-Info:.*" r"(?:\n)"
|
||||
# first line (starting w/o a space)
|
||||
r"^active_anon:(?P<active_anon_pages>\d+) inactive_anon:(?P<inactive_anon_pages>\d+) "
|
||||
@ -2336,7 +2356,25 @@ class KernelConfig_5_14(KernelConfig_5_8):
|
||||
}
|
||||
|
||||
|
||||
class KernelConfig_5_18(KernelConfig_5_14):
|
||||
class KernelConfig_5_16(KernelConfig_5_14):
|
||||
# Supported changes:
|
||||
# * mm/page_alloc.c: show watermark_boost of zone in zoneinfo (a6ea8b5b9f1c)
|
||||
|
||||
name = "Configuration for Linux kernel 5.16 or later"
|
||||
release = (5, 16, "")
|
||||
|
||||
REC_WATERMARK = re.compile(
|
||||
"Node (?P<node>\d+) (?P<zone>DMA|DMA32|Normal) "
|
||||
"free:(?P<free>\d+)kB "
|
||||
"boost:(?P<boost>\d+)kB "
|
||||
"min:(?P<min>\d+)kB "
|
||||
"low:(?P<low>\d+)kB "
|
||||
"high:(?P<high>\d+)kB "
|
||||
".*"
|
||||
)
|
||||
|
||||
|
||||
class KernelConfig_5_18(KernelConfig_5_16):
|
||||
# Supported changes:
|
||||
# * update GFP flags
|
||||
|
||||
@ -2428,8 +2466,146 @@ class KernelConfig_5_18(KernelConfig_5_14):
|
||||
}
|
||||
|
||||
|
||||
class KernelConfig_6_0(KernelConfig_5_18):
|
||||
# Supported changes:
|
||||
# * update GFP flags
|
||||
# * "mm/swap: remove swap_cache_info statistics" (442701e7058b)
|
||||
|
||||
name = "Configuration for Linux kernel 6.0 or later"
|
||||
release = (6, 0, "")
|
||||
|
||||
# NOTE: These flags are automatically extracted from a gfp.h file.
|
||||
# Please do not change them manually!
|
||||
GFP_FLAGS = {
|
||||
#
|
||||
#
|
||||
# Useful GFP flag combinations:
|
||||
"GFP_ATOMIC": {"value": "__GFP_HIGH | __GFP_ATOMIC | __GFP_KSWAPD_RECLAIM"},
|
||||
"GFP_HIGHUSER": {"value": "GFP_USER | __GFP_HIGHMEM"},
|
||||
"GFP_HIGHUSER_MOVABLE": {
|
||||
"value": "GFP_HIGHUSER | __GFP_MOVABLE | __GFP_SKIP_KASAN_POISON | __GFP_SKIP_KASAN_UNPOISON"
|
||||
},
|
||||
"GFP_KERNEL": {"value": "__GFP_RECLAIM | __GFP_IO | __GFP_FS"},
|
||||
"GFP_KERNEL_ACCOUNT": {"value": "GFP_KERNEL | __GFP_ACCOUNT"},
|
||||
"GFP_NOFS": {"value": "__GFP_RECLAIM | __GFP_IO"},
|
||||
"GFP_NOIO": {"value": "__GFP_RECLAIM"},
|
||||
"GFP_NOWAIT": {"value": "__GFP_KSWAPD_RECLAIM"},
|
||||
"GFP_TRANSHUGE": {"value": "GFP_TRANSHUGE_LIGHT | __GFP_DIRECT_RECLAIM"},
|
||||
"GFP_TRANSHUGE_LIGHT": {
|
||||
"value": "GFP_HIGHUSER_MOVABLE | __GFP_COMP | __GFP_NOMEMALLOC | __GFP_NOWARN & ~__GFP_RECLAIM"
|
||||
},
|
||||
"GFP_USER": {"value": "__GFP_RECLAIM | __GFP_IO | __GFP_FS | __GFP_HARDWALL"},
|
||||
#
|
||||
#
|
||||
# Modifier, mobility and placement hints:
|
||||
"__GFP_ACCOUNT": {"value": "___GFP_ACCOUNT"},
|
||||
"__GFP_ATOMIC": {"value": "___GFP_ATOMIC"},
|
||||
"__GFP_COMP": {"value": "___GFP_COMP"},
|
||||
"__GFP_DIRECT_RECLAIM": {"value": "___GFP_DIRECT_RECLAIM"},
|
||||
"__GFP_DMA": {"value": "___GFP_DMA"},
|
||||
"__GFP_DMA32": {"value": "___GFP_DMA32"},
|
||||
"__GFP_FS": {"value": "___GFP_FS"},
|
||||
"__GFP_HARDWALL": {"value": "___GFP_HARDWALL"},
|
||||
"__GFP_HIGH": {"value": "___GFP_HIGH"},
|
||||
"__GFP_HIGHMEM": {"value": "___GFP_HIGHMEM"},
|
||||
"__GFP_IO": {"value": "___GFP_IO"},
|
||||
"__GFP_KSWAPD_RECLAIM": {"value": "___GFP_KSWAPD_RECLAIM"},
|
||||
"__GFP_MEMALLOC": {"value": "___GFP_MEMALLOC"},
|
||||
"__GFP_MOVABLE": {"value": "___GFP_MOVABLE"},
|
||||
"__GFP_NOFAIL": {"value": "___GFP_NOFAIL"},
|
||||
"__GFP_NOLOCKDEP": {"value": "___GFP_NOLOCKDEP"},
|
||||
"__GFP_NOMEMALLOC": {"value": "___GFP_NOMEMALLOC"},
|
||||
"__GFP_NORETRY": {"value": "___GFP_NORETRY"},
|
||||
"__GFP_NOWARN": {"value": "___GFP_NOWARN"},
|
||||
"__GFP_RECLAIM": {"value": "___GFP_DIRECT_RECLAIM | ___GFP_KSWAPD_RECLAIM"},
|
||||
"__GFP_RECLAIMABLE": {"value": "___GFP_RECLAIMABLE"},
|
||||
"__GFP_RETRY_MAYFAIL": {"value": "___GFP_RETRY_MAYFAIL"},
|
||||
"__GFP_SKIP_KASAN_POISON": {"value": "___GFP_SKIP_KASAN_POISON"},
|
||||
"__GFP_SKIP_KASAN_UNPOISON": {"value": "___GFP_SKIP_KASAN_UNPOISON"},
|
||||
"__GFP_SKIP_ZERO": {"value": "___GFP_SKIP_ZERO"},
|
||||
"__GFP_WRITE": {"value": "___GFP_WRITE"},
|
||||
"__GFP_ZERO": {"value": "___GFP_ZERO"},
|
||||
"__GFP_ZEROTAGS": {"value": "___GFP_ZEROTAGS"},
|
||||
#
|
||||
#
|
||||
# Plain integer GFP bitmasks (for internal use only):
|
||||
"___GFP_DMA": {"value": 0x01},
|
||||
"___GFP_HIGHMEM": {"value": 0x02},
|
||||
"___GFP_DMA32": {"value": 0x04},
|
||||
"___GFP_MOVABLE": {"value": 0x08},
|
||||
"___GFP_RECLAIMABLE": {"value": 0x10},
|
||||
"___GFP_HIGH": {"value": 0x20},
|
||||
"___GFP_IO": {"value": 0x40},
|
||||
"___GFP_FS": {"value": 0x80},
|
||||
"___GFP_ZERO": {"value": 0x100},
|
||||
"___GFP_ATOMIC": {"value": 0x200},
|
||||
"___GFP_DIRECT_RECLAIM": {"value": 0x400},
|
||||
"___GFP_KSWAPD_RECLAIM": {"value": 0x800},
|
||||
"___GFP_WRITE": {"value": 0x1000},
|
||||
"___GFP_NOWARN": {"value": 0x2000},
|
||||
"___GFP_RETRY_MAYFAIL": {"value": 0x4000},
|
||||
"___GFP_NOFAIL": {"value": 0x8000},
|
||||
"___GFP_NORETRY": {"value": 0x10000},
|
||||
"___GFP_MEMALLOC": {"value": 0x20000},
|
||||
"___GFP_COMP": {"value": 0x40000},
|
||||
"___GFP_NOMEMALLOC": {"value": 0x80000},
|
||||
"___GFP_HARDWALL": {"value": 0x100000},
|
||||
"___GFP_ACCOUNT": {"value": 0x400000},
|
||||
"___GFP_ZEROTAGS": {"value": 0x800000},
|
||||
"___GFP_SKIP_ZERO": {"value": 0x1000000},
|
||||
"___GFP_SKIP_KASAN_UNPOISON": {"value": 0x2000000},
|
||||
"___GFP_SKIP_KASAN_POISON": {"value": 0x4000000},
|
||||
"___GFP_NOLOCKDEP": {"value": 0x8000000},
|
||||
}
|
||||
|
||||
EXTRACT_PATTERN_OVERLAY_60 = {
|
||||
"Swap usage information": (
|
||||
r"^(?P<swap_cache_pages>\d+) pages in swap cache"
|
||||
r"(?:\n)"
|
||||
r"^Free swap = (?P<swap_free_kb>\d+)kB"
|
||||
r"(?:\n)"
|
||||
r"^Total swap = (?P<swap_total_kb>\d+)kB",
|
||||
False,
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.EXTRACT_PATTERN.update(self.EXTRACT_PATTERN_OVERLAY_60)
|
||||
|
||||
|
||||
class KernelConfig_6_1(KernelConfig_6_0):
|
||||
# Supported changes:
|
||||
# * "mm: add NR_SECONDARY_PAGETABLE to count secondary page table uses." (ebc97a52b5d6)
|
||||
|
||||
name = "Configuration for Linux kernel 6.1 or later"
|
||||
release = (6, 1, "")
|
||||
|
||||
EXTRACT_PATTERN_OVERLAY_61 = {
|
||||
"Overall Mem-Info (part 2)": (
|
||||
r"^ slab_reclaimable:(?P<slab_reclaimable_pages>\d+) slab_unreclaimable:(?P<slab_unreclaimable_pages>\d+)"
|
||||
r"(?:\n)"
|
||||
r"^ mapped:(?P<mapped_pages>\d+) shmem:(?P<shmem_pages>\d+) pagetables:(?P<pagetables_pages>\d+)"
|
||||
r"(?:\n)"
|
||||
r"^ sec_pagetables:(?P<sec_pagetables>\d+) bounce:(?P<bounce_pages>\d+)"
|
||||
r"(?:\n)"
|
||||
r"^ kernel_misc_reclaimable:(?P<kernel_misc_reclaimable>\d+)"
|
||||
r"(?:\n)"
|
||||
r"^ free:(?P<free_pages>\d+) free_pcp:(?P<free_pcp_pages>\d+) free_cma:(?P<free_cma_pages>\d+)",
|
||||
True,
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.EXTRACT_PATTERN.update(self.EXTRACT_PATTERN_OVERLAY_61)
|
||||
|
||||
|
||||
AllKernelConfigs = [
|
||||
KernelConfig_6_1(),
|
||||
KernelConfig_6_0(),
|
||||
KernelConfig_5_18(),
|
||||
KernelConfig_5_16(),
|
||||
KernelConfig_5_14(),
|
||||
KernelConfig_5_8(),
|
||||
KernelConfig_5_1(),
|
||||
@ -2527,7 +2703,7 @@ class OOMEntity:
|
||||
|
||||
@see: _rsyslog_unescape_lf()
|
||||
"""
|
||||
pattern = r"^\s+ (active_file|unevictable|slab_reclaimable|mapped|free):.+$"
|
||||
pattern = r"^\s+ (active_file|unevictable|slab_reclaimable|mapped|sec_pagetables|kernel_misc_reclaimable|free):.+$"
|
||||
rec = re.compile(pattern)
|
||||
|
||||
add_cols = ""
|
||||
@ -2664,12 +2840,18 @@ class OOMEntity:
|
||||
|
||||
return stripped_lines
|
||||
|
||||
def back(self):
|
||||
"""Return the previous line"""
|
||||
if self.current_line - 1 < 0:
|
||||
raise StopIteration()
|
||||
self.current_line -= 1
|
||||
return self.lines[self.current_line]
|
||||
def goto_previous_line(self):
|
||||
"""Set line pointer to previous line
|
||||
|
||||
If using in front of an iterator:
|
||||
The line pointer in self.current_line points to the first line of a block.
|
||||
An iterator based loop starts with a next() call (as defined by the iterator
|
||||
protocol). This causes the current line to be skipped. Therefore, the line
|
||||
pointer is set to the previous line.
|
||||
"""
|
||||
if self.current_line > 0:
|
||||
self.current_line -= 1
|
||||
return
|
||||
|
||||
def current(self):
|
||||
"""Return the current line"""
|
||||
@ -2798,42 +2980,7 @@ class OOMAnalyser:
|
||||
)
|
||||
"""RE to match the OOM line with kernel version"""
|
||||
|
||||
REC_FREE_MEMORY_CHUNKS = re.compile(
|
||||
"Node (?P<node>\d+) (?P<zone>DMA|DMA32|Normal): (?P<zone_usage>.*) = (?P<total_free_kb_per_node>\d+)kB"
|
||||
)
|
||||
"""RE to extract free memory chunks of a memor zone"""
|
||||
|
||||
REC_PAGE_SIZE = re.compile("Node 0 DMA: \d+\*(?P<page_size>\d+)kB")
|
||||
"""RE to extract the page size from buddyinfo DMA zone"""
|
||||
|
||||
REC_WATERMARK = re.compile(
|
||||
"Node (?P<node>\d+) (?P<zone>DMA|DMA32|Normal) "
|
||||
"free:(?P<free>\d+)kB "
|
||||
"min:(?P<min>\d+)kB "
|
||||
"low:(?P<low>\d+)kB "
|
||||
"high:(?P<high>\d+)kB "
|
||||
".*"
|
||||
)
|
||||
"""RE to extract watermark information in a memory zone"""
|
||||
|
||||
def __init__(self, oom):
|
||||
self.oom_entity = oom
|
||||
self.oom_result = OOMResult()
|
||||
|
||||
def _identify_kernel_version(self):
|
||||
"""
|
||||
Identify the used kernel version and
|
||||
|
||||
@rtype: bool
|
||||
"""
|
||||
match = self.REC_KERNEL_VERSION.search(self.oom_entity.text)
|
||||
if not match:
|
||||
self.oom_result.error_msg = "Failed to extract kernel version from OOM text"
|
||||
return False
|
||||
self.oom_result.kversion = match.group("kernel_version")
|
||||
return True
|
||||
|
||||
rec_split_kversion = re.compile(
|
||||
REC_SPLIT_KVERSION = re.compile(
|
||||
r"(?P<kernel_version>"
|
||||
r"(?P<major>\d+)\.(?P<minor>\d+)" # major . minor
|
||||
r"(\.\d+)?" # optional: patch level
|
||||
@ -2852,6 +2999,23 @@ class OOMAnalyser:
|
||||
- 3.10.0-514.6.1.el7.x86_64 #1
|
||||
"""
|
||||
|
||||
def __init__(self, oom):
|
||||
self.oom_entity = oom
|
||||
self.oom_result = OOMResult()
|
||||
|
||||
def _identify_kernel_version(self):
|
||||
"""
|
||||
Identify the used kernel version and
|
||||
|
||||
@rtype: bool
|
||||
"""
|
||||
match = self.REC_KERNEL_VERSION.search(self.oom_entity.text)
|
||||
if not match:
|
||||
self.oom_result.error_msg = "Failed to extract kernel version from OOM text"
|
||||
return False
|
||||
self.oom_result.kversion = match.group("kernel_version")
|
||||
return True
|
||||
|
||||
def _check_kversion_greater_equal(self, kversion, min_version):
|
||||
"""
|
||||
Returns True if the kernel version is greater or equal to the minimum version
|
||||
@ -2860,7 +3024,7 @@ class OOMAnalyser:
|
||||
@param (int, int, str) min_version: Minimum version
|
||||
@rtype: bool
|
||||
"""
|
||||
match = self.rec_split_kversion.match(kversion)
|
||||
match = self.REC_SPLIT_KVERSION.match(kversion)
|
||||
|
||||
if not match:
|
||||
self.oom_result.error_msg = (
|
||||
@ -2929,12 +3093,12 @@ class OOMAnalyser:
|
||||
self.oom_state = OOMEntityState.unknown
|
||||
self.oom_result.error_msg = "Unknown OOM format"
|
||||
|
||||
if not self.oom_result.kconfig.rec_oom_begin.search(self.oom_entity.text):
|
||||
if not self.oom_result.kconfig.REC_OOM_BEGIN.search(self.oom_entity.text):
|
||||
self.state = OOMEntityState.invalid
|
||||
self.oom_result.error_msg = "The inserted text is not a valid OOM block! The initial pattern was not found!"
|
||||
return False
|
||||
|
||||
if not self.oom_result.kconfig.rec_oom_end.search(self.oom_entity.text):
|
||||
if not self.oom_result.kconfig.REC_OOM_END.search(self.oom_entity.text):
|
||||
self.state = OOMEntityState.started
|
||||
self.oom_result.error_msg = (
|
||||
"The inserted OOM is incomplete! The initial pattern was found but not the "
|
||||
@ -2959,7 +3123,7 @@ class OOMAnalyser:
|
||||
block += "{}\n".format(line)
|
||||
for line in self.oom_entity:
|
||||
if ":" in line:
|
||||
self.oom_entity.back()
|
||||
self.oom_entity.goto_previous_line()
|
||||
break
|
||||
block += "{}\n".format(line)
|
||||
return block
|
||||
@ -3033,7 +3197,7 @@ class OOMAnalyser:
|
||||
|
||||
def _extract_page_size(self):
|
||||
"""Extract page size from buddyinfo DMZ zone"""
|
||||
match = self.REC_PAGE_SIZE.search(self.oom_entity.text)
|
||||
match = self.oom_result.kconfig.REC_PAGE_SIZE.search(self.oom_entity.text)
|
||||
if match:
|
||||
self.oom_result.details["page_size_kb"] = int(match.group("page_size"))
|
||||
self.oom_result.details["_page_size_guessed"] = False
|
||||
@ -3073,14 +3237,9 @@ class OOMAnalyser:
|
||||
buddy_info = self.oom_result.buddyinfo
|
||||
self.oom_entity.find_text(self.oom_result.kconfig.zoneinfo_start)
|
||||
|
||||
# Currently omm_entity point to the first line of the buddyinfo.
|
||||
# The iterator protocol uses the next() call. However, this will cause the
|
||||
# current line to be skipped.
|
||||
# Therefore, we reset the counter by one line.
|
||||
self.oom_entity.back()
|
||||
|
||||
self.oom_entity.goto_previous_line()
|
||||
for line in self.oom_entity:
|
||||
match = self.REC_FREE_MEMORY_CHUNKS.match(line)
|
||||
match = self.oom_result.kconfig.REC_FREE_MEMORY_CHUNKS.match(line)
|
||||
if not match:
|
||||
continue
|
||||
node = int(match.group("node"))
|
||||
@ -3135,16 +3294,11 @@ class OOMAnalyser:
|
||||
watermark_info = self.oom_result.watermarks
|
||||
self.oom_entity.find_text(self.oom_result.kconfig.watermark_start)
|
||||
|
||||
# Currently omm_entity point to the first line of the watermark information.
|
||||
# The iterator protocol uses the next() call. However, this will cause the
|
||||
# current line to be skipped.
|
||||
# Therefore, we reset the counter by one line.
|
||||
self.oom_entity.back()
|
||||
|
||||
node = None
|
||||
zone = None
|
||||
self.oom_entity.goto_previous_line()
|
||||
for line in self.oom_entity:
|
||||
match = self.REC_WATERMARK.match(line)
|
||||
match = self.oom_result.kconfig.REC_WATERMARK.match(line)
|
||||
if not match:
|
||||
if line.startswith("lowmem_reserve[]:"):
|
||||
# zone and node are defined in the previous round
|
||||
@ -3351,7 +3505,7 @@ class OOMAnalyser:
|
||||
)
|
||||
return
|
||||
|
||||
# Search node with memory shortage: watermark "free" < "min"
|
||||
# Node with memory shortage: watermark "free" < "min"
|
||||
node = self.oom_result.details["trigger_proc_numa_node"]
|
||||
if node is None:
|
||||
return
|
||||
@ -3447,11 +3601,8 @@ class OOMAnalyser:
|
||||
|
||||
def _calc_swap_values(self):
|
||||
"""Calculate all swap related values"""
|
||||
try:
|
||||
if "swap_total_kb" in self.oom_result.details:
|
||||
self.oom_result.swap_active = self.oom_result.details["swap_total_kb"] > 0
|
||||
except KeyError:
|
||||
self.oom_result.swap_active = False
|
||||
|
||||
if not self.oom_result.swap_active:
|
||||
return
|
||||
|
||||
@ -4456,6 +4607,8 @@ Out of memory: Killed process 651 (unattended-upgr) total-vm:108020kB, anon-rss:
|
||||
show_elements(".js-memory-heavy-fragmentation--show")
|
||||
else:
|
||||
show_elements(".js-memory-no-heavy-fragmentation--show")
|
||||
if self.oom_result.details["trigger_proc_numa_node"] is None:
|
||||
hide_elements(".js-memory-shortage-node--hide")
|
||||
|
||||
def _show_page_size(self):
|
||||
"""Show page size"""
|
||||
|
6
test.py
6
test.py
@ -37,8 +37,8 @@ class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
|
||||
|
||||
class TestBase(unittest.TestCase):
|
||||
text_alloc_failed_below_low_watermark = (
|
||||
"The request failed because after its fulfillment the free memory would "
|
||||
"be below the memory low watermark."
|
||||
"The request failed because the free memory would be below the memory low "
|
||||
"watermark after its completion."
|
||||
)
|
||||
text_alloc_failed_no_free_chunks = (
|
||||
"The request failed because there is no free chunk in the current or "
|
||||
@ -776,7 +776,7 @@ Hardware name: HP ProLiant DL385 G7, BIOS A18 12/08/2012
|
||||
"CPU: 4 PID: 29481 Comm: sed Not tainted 5.23.0 #1",
|
||||
),
|
||||
(
|
||||
OOMAnalyser.KernelConfig_5_18(),
|
||||
OOMAnalyser.KernelConfig_6_1(),
|
||||
"CPU: 4 PID: 29481 Comm: sed Not tainted 6.12.0 #1",
|
||||
),
|
||||
(
|
||||
|
Loading…
x
Reference in New Issue
Block a user