Compare commits

...

10 Commits

Author SHA1 Message Date
Carsten Grohmann
4e12c22c10 Summary of the analysis revised one more time 2023-03-31 22:00:27 +02:00
Carsten Grohmann
77e7c9ca93 Replace except KeyError with if in for better JS
Catching a Python-like exeption like KeyError doesn't work well after
compiling to JavaScript with Transcrypt.
2023-03-29 20:46:01 +02:00
Carsten Grohmann
af59b6147e Remove swap cache statistics starting kernel 6.0
as changed in "mm/swap: remove swap_cache_info statistics"
(442701e7058b).
2023-03-29 20:35:16 +02:00
Carsten Grohmann
6e7db97f8f Add support for watermark boost in zoneinfo
as introduced with Kernel 5.16 in "mm/page_alloc.c: show
watermark_boost of zone in zoneinfo" (a6ea8b5b9f1c)
2023-03-28 22:40:06 +02:00
Carsten Grohmann
0ac8f4939a Add support for secondary page table counters
as with kernel 6.1 "mm: add NR_SECONDARY_PAGETABLE to count secondary
page table usage." (ebc97a52) added.
2023-03-28 22:40:06 +02:00
Carsten Grohmann
e710414116 Add GFP flags for kernel 6.0 2023-03-27 20:19:46 +02:00
Carsten Grohmann
3f19a50f17 Move regexs from OOMAnalyser to BaseKernelConfig 2023-03-25 20:50:09 +01:00
Carsten Grohmann
7e99d267a5 Rename BaseKernelConfig.rec_oom_{begin|end}
Rename BaseKernelConfig.rec_oom_{begin|end} to upper-case.
2023-03-25 14:58:40 +01:00
Carsten Grohmann
52d476cee2 Refactor and rename OOMEntity.back()
Rename OOMEntity.back() to goto_previous_line() and align code with
current usage.
2023-03-25 14:47:34 +01:00
Carsten Grohmann
cbd16d266f Hide node if none of the nodes has memory shortage 2023-03-24 21:01:16 +01:00
3 changed files with 251 additions and 93 deletions

View File

@ -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
&quot;<span class="trigger_proc_mem_zone"></span>&quot; 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>

View File

@ -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"""

View File

@ -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",
),
(