Add support for newer process table format

This commit is contained in:
Carsten Grohmann 2021-12-21 20:32:49 +01:00
parent f657ac5816
commit 9404c87519
3 changed files with 211 additions and 87 deletions

View File

@ -73,6 +73,37 @@
/* empty - used to show sections for manually triggered OOMs */ /* empty - used to show sections for manually triggered OOMs */
} }
.js-pstable_sort_col0 {
/* empty - used to sort process table */
}
.js-pstable_sort_col1 {
/* empty - used to sort process table */
}
.js-pstable_sort_col2 {
/* empty - used to sort process table */
}
.js-pstable_sort_col3 {
/* empty - used to sort process table */
}
.js-pstable_sort_col4 {
/* empty - used to sort process table */
}
.js-pstable_sort_col5 {
/* empty - used to sort process table */
}
.js-pstable_sort_col6 {
/* empty - used to sort process table */
}
.js-pstable_sort_col7 {
/* empty - used to sort process table */
}
.js-pstable_sort_col8 {
/* empty - used to sort process table */
}
.js-pstable_sort_col9 {
/* empty - used to sort process table */
}
.js-swap-active--show { .js-swap-active--show {
/* empty - used to show if the swap space is activated */ /* empty - used to show if the swap space is activated */
} }
@ -144,6 +175,9 @@
.pstable__row-oom-score-adj--width { .pstable__row-oom-score-adj--width {
width: 16ch; width: 16ch;
} }
.pstable__row-notes--width {
width: 20ch;
}
.pstable__row-sort--width { .pstable__row-sort--width {
padding-left: unset; padding-left: unset;
padding-right: unset; padding-right: unset;
@ -749,51 +783,51 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
<td></td> <td></td>
<td class="terminal " colspan="2"> <td class="terminal " colspan="2">
<table class="pstable__table--noborder"> <table class="pstable__table--noborder">
<thead> <thead id="pstable_header">
<tr> <tr>
<td class="pstable__row-numeric--width">pid <td class="pstable__row-numeric--width"><span>pid</span>
<a class="pstable__row-sort--width" href="javascript:void(0);" <a class="pstable__row-sort--width" href="javascript:void(0);"
id="pstable_sort_pid" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable('pid')"></a> id="js-pstable_sort_col0" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable(0)"></a>
</td> </td>
<td class="pstable__row-numeric--width">uid <td class="pstable__row-numeric--width">uid
<a class="pstable__row-sort--width" href="javascript:void(0);" <a class="pstable__row-sort--width" href="javascript:void(0);"
id="pstable_sort_uid" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable('uid')"></a> id="js-pstable_sort_col1" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable(1)"></a>
</td> </td>
<td class="pstable__row-numeric--width">tgid <td class="pstable__row-numeric--width">tgid
<a class="pstable__row-sort--width" href="javascript:void(0);" <a class="pstable__row-sort--width" href="javascript:void(0);"
id="pstable_sort_tgid" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable('tgid')"></a> id="js-pstable_sort_col2" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable(2)"></a>
</td> </td>
<td class="pstable__row-pages--width">total_vm <td class="pstable__row-pages--width">total_vm
<a class="pstable__row-sort--width" href="javascript:void(0);" <a class="pstable__row-sort--width" href="javascript:void(0);"
id="pstable_sort_total_vm_pages" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable('total_vm_pages')"></a> id="js-pstable_sort_col3" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable(3)"></a>
</td> </td>
<td class="pstable__row-pages--width">rss <td class="pstable__row-pages--width">rss
<a class="pstable__row-sort--width" href="javascript:void(0);" <a class="pstable__row-sort--width" href="javascript:void(0);"
id="pstable_sort_rss_pages" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable('rss_pages')"></a> id="js-pstable_sort_col4" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable(4)"></a>
</td> </td>
<td class="pstable__row-pages--width">nr_ptes <td class="pstable__row-pages--width">nr_ptes
<a class="pstable__row-sort--width" href="javascript:void(0);" <a class="pstable__row-sort--width" href="javascript:void(0);"
id="pstable_sort_nr_ptes_pages" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable('nr_ptes_pages')"></a> id="js-pstable_sort_col5" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable(5)"></a>
</td> </td>
<td class="pstable__row-pages--width">swapents <td class="pstable__row-pages--width">swapents
<a class="pstable__row-sort--width" href="javascript:void(0);" <a class="pstable__row-sort--width" href="javascript:void(0);"
id="pstable_sort_swapents_pages" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable('swapents_pages')"></a> id="js-pstable_sort_col6" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable(6)"></a>
</td> </td>
<td class="pstable__row-oom-score-adj--width">oom_score_adj <td class="pstable__row-oom-score-adj--width">oom_score_adj
<a class="pstable__row-sort--width" href="javascript:void(0);" <a class="pstable__row-sort--width" href="javascript:void(0);"
id="pstable_sort_oom_score_adj" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable('oom_score_adj')"></a> id="js-pstable_sort_col7" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable(7)"></a>
</td> </td>
<td>name <td class="pstable__row-notes--width">name
<a class="pstable__row-sort--width" href="javascript:void(0);" <a class="pstable__row-sort--width" href="javascript:void(0);"
id="pstable_sort_name" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable('name')"></a> id="js-pstable_sort_col8" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable(8)"></a>
</td> </td>
<td>notes <td class="pstable__row-notes--width">notes
<a class="pstable__row-sort--width" href="javascript:void(0);" <a class="pstable__row-sort--width" href="javascript:void(0);"
id="pstable_sort_notes" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable('notes')"></a> id="js-pstable_sort_col9" onclick="OOMAnalyser.OOMDisplayInstance.sort_pstable(9)"></a>
</td> </td>
</tr> </tr>
</thead> </thead>
<tbody id="process_table"> <tbody id="pstable_content">
</tbody> </tbody>
</table> </table>
</td> </td>
@ -898,6 +932,7 @@ window.onerror = function (msg, url, lineNo, columnNo, error) {
<li>Add support for systems w/o swap (suggested by Mikko Rantalainen)</li> <li>Add support for systems w/o swap (suggested by Mikko Rantalainen)</li>
<li>Add support for newer kernels (suggested by Mikko Rantalainen)</li> <li>Add support for newer kernels (suggested by Mikko Rantalainen)</li>
<li>Add support for journalctl output (suggested by Mikko Rantalainen)</li> <li>Add support for journalctl output (suggested by Mikko Rantalainen)</li>
<li>Add support for newer process table format</li>
<li>...</li> <li>...</li>
</ol> </ol>

View File

@ -347,15 +347,31 @@ class BaseKernelConfig:
(see https://github.com/torvalds/linux/commit/e67d4ca79aaf9d13a00d229b1b1c96b86828e8ba#diff-020720d0699e3ae1afb6fcd815ca8500) (see https://github.com/torvalds/linux/commit/e67d4ca79aaf9d13a00d229b1b1c96b86828e8ba#diff-020720d0699e3ae1afb6fcd815ca8500)
""" """
ps_table_items = ['pid', 'uid', 'tgid', 'total_vm_pages', 'rss_pages', 'nr_ptes_pages', 'swapents_pages', pstable_items = ['pid', 'uid', 'tgid', 'total_vm_pages', 'rss_pages', 'nr_ptes_pages', 'swapents_pages',
'oom_score_adj'] 'oom_score_adj', 'name', 'notes']
"""Elements of the process table""" """Elements of the process table"""
pstable_html = ['PID', 'UID', 'TGID', 'Total VM', 'RSS', 'Page Table Entries', 'Swap Entries', 'OOM Adjustment',
'Name', 'Notes']
"""
Headings of the process table columns
"""
pstable_non_ints = ['pid', 'name', 'notes']
"""Columns that are not converted to an integer"""
REC_PROCESS_LINE = re.compile( 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<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*') 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""" """Match content of process table"""
pstable_start = '[ pid ]'
"""
Pattern to find the start of the process table
:type: str
"""
rec_version4kconfig = re.compile('.+') rec_version4kconfig = re.compile('.+')
"""RE to match kernel version to kernel configuration""" """RE to match kernel version to kernel configuration"""
@ -416,7 +432,32 @@ class KernelConfig_4_9(KernelConfig_4_6):
self.EXTRACT_PATTERN.update(self.EXTRACT_PATTERN_OVERLAY_49) self.EXTRACT_PATTERN.update(self.EXTRACT_PATTERN_OVERLAY_49)
class KernelConfig_5_0(KernelConfig_4_9): class KernelConfig_4_15(KernelConfig_4_9):
# Support changes:
# * mm: consolidate page table accounting (af5b0f6a09e42c9f4fa87735f2a366748767b686)
# nr_ptes -> pgtables_bytes
# pr_info("[ pid ] uid tgid total_vm rss nr_ptes nr_pmds nr_puds swapents oom_score_adj name\n");
# pr_info("[ pid ] uid tgid total_vm rss pgtables_bytes swapents oom_score_adj name\n");
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<pgtables_bytes>\d+)\s+(?P<swapents_pages>\d+)\s+(?P<oom_score_adj>-?\d+)\s+(?P<name>.+)\s*')
pstable_items = ['pid', 'uid', 'tgid', 'total_vm_pages', 'rss_pages', 'pgtables_bytes', 'swapents_pages',
'oom_score_adj', 'name', 'notes']
pstable_html = ['PID', 'UID', 'TGID', 'Total VM', 'RSS', 'Page Table Bytes', 'Swap Entries Pages',
'OOM Adjustment', 'Name', 'Notes']
class KernelConfig_4_19(KernelConfig_4_15):
# Support changes:
# * mm, oom: describe task memory unit, larger PID pad (c3b78b11efbb2865433abf9d22c004ffe4a73f5c)
pstable_start = '[ pid ]'
class KernelConfig_5_0(KernelConfig_4_19):
# Support changes: # Support changes:
# * "mm, oom: reorganize the oom report in dump_header" (ef8444ea01d7442652f8e1b8a8b94278cb57eafd) # * "mm, oom: reorganize the oom report in dump_header" (ef8444ea01d7442652f8e1b8a8b94278cb57eafd)
@ -488,6 +529,8 @@ class KernelConfigRhel7(BaseKernelConfig):
AllKernelConfigs = [ AllKernelConfigs = [
KernelConfig_5_8(), KernelConfig_5_8(),
KernelConfig_5_0(), KernelConfig_5_0(),
KernelConfig_4_15(),
KernelConfig_4_19(),
KernelConfig_4_9(), KernelConfig_4_9(),
KernelConfig_4_6(), KernelConfig_4_6(),
KernelConfigRhel7(), KernelConfigRhel7(),
@ -931,21 +974,24 @@ class OOMAnalyser:
call_trace += "{}\n".format(line.strip()) call_trace += "{}\n".format(line.strip())
self.oom_result.details['call_trace'] = call_trace self.oom_result.details['call_trace'] = call_trace
# extract process table self._extract_pstable()
self.oom_result.details['_ps'] = {}
self.oom_entity.find_text('[ pid ]') def _extract_pstable(self):
"""Extract process table"""
self.oom_result.details['_pstable'] = {}
self.oom_entity.find_text(self.oom_result.kconfig.pstable_start)
for line in self.oom_entity: for line in self.oom_entity:
if not line.startswith('['): if not line.startswith('['):
break break
if line.startswith('[ pid ]'): if line.startswith(self.oom_result.kconfig.pstable_start):
continue continue
match = self.oom_result.kconfig.REC_PROCESS_LINE.match(line) match = self.oom_result.kconfig.REC_PROCESS_LINE.match(line)
if match: if match:
details = match.groupdict() details = match.groupdict()
details['notes'] = '' details['notes'] = ''
pid = details.pop('pid') pid = details.pop('pid')
self.oom_result.details['_ps'][pid] = {} self.oom_result.details['_pstable'][pid] = {}
self.oom_result.details['_ps'][pid].update(details) self.oom_result.details['_pstable'][pid].update(details)
def _hex2flags(self, hexvalue, flag_definition): def _hex2flags(self, hexvalue, flag_definition):
"""\ """\
@ -1025,7 +1071,7 @@ class OOMAnalyser:
if self.oom_result.details[item] is None: if self.oom_result.details[item] is None:
self.oom_result.details[item] = '<not found>' self.oom_result.details[item] = '<not found>'
continue continue
if item.endswith('_kb') or item.endswith('_pages') or item.endswith('_pid') or \ if item.endswith('_bytes') or item.endswith('_kb') or item.endswith('_pages') or item.endswith('_pid') or \
item in ['killed_proc_score', 'trigger_proc_order', 'trigger_proc_oomscore']: item in ['killed_proc_score', 'trigger_proc_order', 'trigger_proc_oomscore']:
try: try:
self.oom_result.details[item] = int(self.oom_result.details[item]) self.oom_result.details[item] = int(self.oom_result.details[item])
@ -1034,9 +1080,9 @@ class OOMAnalyser:
# __pragma__ ('nojsiter') # __pragma__ ('nojsiter')
def _convert_numeric_process_values_to_integer(self): def _convert_pstable_values_to_integer(self):
"""Convert numeric values in process table to integer values""" """Convert numeric values in process table to integer values"""
ps = self.oom_result.details['_ps'] ps = self.oom_result.details['_pstable']
ps_index = [] ps_index = []
# TODO Check if transcrypt issue: pragma jsiter for the whole block "for pid_str in ps: ..." # TODO Check if transcrypt issue: pragma jsiter for the whole block "for pid_str in ps: ..."
# sets item in "for item in ['uid',..." to 0 instead of 'uid' # sets item in "for item in ['uid',..." to 0 instead of 'uid'
@ -1044,13 +1090,17 @@ class OOMAnalyser:
for pid_str in ps.keys(): for pid_str in ps.keys():
converted = {} converted = {}
process = ps[pid_str] process = ps[pid_str]
for item in self.oom_result.kconfig.ps_table_items: for item in self.oom_result.kconfig.pstable_items:
if item == 'pid': if item in self.oom_result.kconfig.pstable_non_ints:
continue continue
try: try:
converted[item] = int(process[item]) converted[item] = int(process[item])
except: except:
error('Converting process parameter "{}={}" to integer failed'.format(item, process[item])) if item not in process:
pitem = '<not in process table>'
else:
pitem = process[item]
error('Converting process parameter "{}={}" to integer failed'.format(item, pitem))
converted['name'] = process['name'] converted['name'] = process['name']
converted['notes'] = process['notes'] converted['notes'] = process['notes']
@ -1060,7 +1110,7 @@ class OOMAnalyser:
ps_index.append(pid_int) ps_index.append(pid_int)
ps_index.sort(key=int) ps_index.sort(key=int)
self.oom_result.details['_ps_index'] = ps_index self.oom_result.details['_pstable_index'] = ps_index
def _calc_pstable_values(self): def _calc_pstable_values(self):
"""Set additional notes to processes listed in the process table""" """Set additional notes to processes listed in the process table"""
@ -1068,12 +1118,12 @@ class OOMAnalyser:
kpid = self.oom_result.details['killed_proc_pid'] kpid = self.oom_result.details['killed_proc_pid']
# sometimes the trigger process isn't part of the process table # sometimes the trigger process isn't part of the process table
if tpid in self.oom_result.details['_ps']: if tpid in self.oom_result.details['_pstable']:
self.oom_result.details['_ps'][tpid]['notes'] = 'trigger process' self.oom_result.details['_pstable'][tpid]['notes'] = 'trigger process'
# assume the killed process may also not part of the process table # assume the killed process may also not part of the process table
if kpid in self.oom_result.details['_ps']: if kpid in self.oom_result.details['_pstable']:
self.oom_result.details['_ps'][kpid]['notes'] = 'killed process' self.oom_result.details['_pstable'][kpid]['notes'] = 'killed process'
def _calc_trigger_process_values(self): def _calc_trigger_process_values(self):
"""Calculate all values related with the trigger process""" """Calculate all values related with the trigger process"""
@ -1138,8 +1188,8 @@ class OOMAnalyser:
else: else:
self.oom_result.details['system_total_ramswap_kb'] = self.oom_result.details['system_total_ram_kb'] self.oom_result.details['system_total_ramswap_kb'] = self.oom_result.details['system_total_ram_kb']
total_rss_pages = 0 total_rss_pages = 0
for pid in self.oom_result.details['_ps'].keys(): for pid in self.oom_result.details['_pstable'].keys():
total_rss_pages += self.oom_result.details['_ps'][pid]['rss_pages'] total_rss_pages += self.oom_result.details['_pstable'][pid]['rss_pages']
self.oom_result.details['system_total_ram_used_kb'] = total_rss_pages * self.oom_result.details['page_size_kb'] self.oom_result.details['system_total_ram_used_kb'] = total_rss_pages * self.oom_result.details['page_size_kb']
self.oom_result.details['system_total_used_percent'] = int(100 * self.oom_result.details['system_total_used_percent'] = int(100 *
@ -1176,7 +1226,7 @@ class OOMAnalyser:
@see: self.details @see: self.details
""" """
self._convert_numeric_results_to_integer() self._convert_numeric_results_to_integer()
self._convert_numeric_process_values_to_integer() self._convert_pstable_values_to_integer()
self._calc_pstable_values() self._calc_pstable_values()
self._determinate_platform_and_distribution() self._determinate_platform_and_distribution()
@ -1442,8 +1492,12 @@ oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,glob
Out of memory: Killed process 651 (unattended-upgr) total-vm:108020kB, anon-rss:8380kB, file-rss:12548kB, shmem-rss:0kB, UID:0 pgtables:104kB oom_score_adj:0 Out of memory: Killed process 651 (unattended-upgr) total-vm:108020kB, anon-rss:8380kB, file-rss:12548kB, shmem-rss:0kB, UID:0 pgtables:104kB oom_score_adj:0
''' '''
sorted_column = None sorted_column_number = None
"""Processes will sort by values in this column""" """
Processes will sort by values in this column
@type: int
"""
sort_order = None sort_order = None
"""Sort order for process values""" """Sort order for process values"""
@ -1534,6 +1588,12 @@ Out of memory: Killed process 651 (unattended-upgr) total-vm:108020kB, anon-rss:
else: else:
content = "{} pages".format(content) content = "{} pages".format(content)
if item.endswith('_bytes') and isinstance(content, int):
if content == 1:
content = "{} Byte".format(content)
else:
content = "{} Bytes".format(content)
if item.endswith('_kb') and isinstance(content, int): if item.endswith('_kb') and isinstance(content, int):
if content == 1: if content == 1:
content = "{} kByte".format(content) content = "{} kByte".format(content)
@ -1568,21 +1628,41 @@ Out of memory: Killed process 651 (unattended-upgr) total-vm:108020kB, anon-rss:
toc_content.innerHTML = new_toc toc_content.innerHTML = new_toc
def update_process_table(self): def pstable_fill_HTML(self):
""" """
Re-create the process table with additional information Create the process table with additional information
""" """
new_table = '' # update table heading
table_content = document.getElementById('process_table') for i, element in enumerate(document.querySelectorAll('#pstable_header > tr > td')):
element.classList.remove('pstable__row-pages--width', 'pstable__row-numeric--width',
'pstable__row-oom-score-adj--width')
for pid in self.oom_result.details['_ps_index']: key = self.oom_result.kconfig.pstable_items[i]
if key in ['notes', 'names']:
klass = 'pstable__row-notes--width'
elif key == 'oom_score_adj':
klass = 'pstable__row-oom-score-adj--width'
elif key.endswith('_bytes') or key.endswith('_kb') or key.endswith('_pages'):
klass = 'pstable__row-pages--width'
else:
klass = "pstable__row-numeric--width"
element.firstChild.textContent = self.oom_result.kconfig.pstable_html[i]
element.classList.add(klass)
# create new table
new_table = ''
table_content = document.getElementById('pstable_content')
for pid in self.oom_result.details['_pstable_index']:
if pid == self.oom_result.details['trigger_proc_pid']: if pid == self.oom_result.details['trigger_proc_pid']:
css_class = 'class="js-pstable__triggerproc--bgcolor"' css_class = 'class="js-pstable__triggerproc--bgcolor"'
elif pid == self.oom_result.details['killed_proc_pid']: elif pid == self.oom_result.details['killed_proc_pid']:
css_class = 'class="js-pstable__killedproc--bgcolor"' css_class = 'class="js-pstable__killedproc--bgcolor"'
else: else:
css_class = '' css_class = ''
process = self.oom_result.details['_ps'][pid] process = self.oom_result.details['_pstable'][pid]
fmt_list = [process[i] for i in self.oom_result.kconfig.pstable_items if not i == 'pid']
fmt_list.insert(0, css_class)
fmt_list.insert(1, pid)
line = """ line = """
<tr {}> <tr {}>
<td>{}</td> <td>{}</td>
@ -1596,32 +1676,22 @@ Out of memory: Killed process 651 (unattended-upgr) total-vm:108020kB, anon-rss:
<td>{}</td> <td>{}</td>
<td>{}</td> <td>{}</td>
</tr> </tr>
""".format(css_class, pid, process['uid'], process['tgid'], process['total_vm_pages'], process['rss_pages'], """.format(*fmt_list)
process['nr_ptes_pages'], process['swapents_pages'], process['oom_score_adj'], process['name'],
process['notes'])
new_table += line new_table += line
table_content.innerHTML = new_table table_content.innerHTML = new_table
self.set_sort_triangle() def pstable_set_sort_triangle(self):
def set_sort_triangle(self):
"""Set the sorting symbols for all columns in the process table""" """Set the sorting symbols for all columns in the process table"""
# TODO Check operator overloading for column_name in self.oom_result.kconfig.pstable_items:
# Operator overloading (Pragma opov) does not work in this context. column_number = self.oom_result.kconfig.pstable_items.index(column_name)
# self.oom_result.kconfig.ps_table_items + ['notes'] will compile to a string element_id = "js-pstable_sort_col{}".format(column_number)
# "pid,uid,tgid,total_vm_pages,rss_pages,nr_ptes_pages,swapents_pages,oom_score_adjNotes" and not to an
# array
ps_table_and_notes = self.oom_result.kconfig.ps_table_items[:]
ps_table_and_notes.append('notes')
for column_name in ps_table_and_notes:
element_id = "pstable_sort_{}".format(column_name)
element = document.getElementById(element_id) element = document.getElementById(element_id)
if not element: if not element:
internal_error('Missing id "{}" in process table.'.format(element_id)) internal_error('Missing id "{}" in process table.'.format(element_id))
continue continue
if column_name == self.sorted_column: if column_number == self.sorted_column_number:
if self.sort_order == 'descending': if self.sort_order == 'descending':
element.innerHTML = self.svg_array_down element.innerHTML = self.svg_array_down
else: else:
@ -1645,22 +1715,31 @@ Out of memory: Killed process 651 (unattended-upgr) total-vm:108020kB, anon-rss:
while element.firstChild: while element.firstChild:
element.removeChild(element.firstChild) element.removeChild(element.firstChild)
# clear process table
element = document.getElementById('process_table')
while element.firstChild:
element.removeChild(element.firstChild)
# reset sort triangles
self.sorted_column = None
self.sort_order = None
self.set_sort_triangle()
# remove svg charts # remove svg charts
for element_id in ('svg_swap', 'svg_ram'): for element_id in ('svg_swap', 'svg_ram'):
element = document.getElementById(element_id) element = document.getElementById(element_id)
while element.firstChild: while element.firstChild:
element.removeChild(element.firstChild) element.removeChild(element.firstChild)
self._clear_pstable()
def _clear_pstable(self):
"""Clear process table"""
element = document.getElementById('pstable_content')
while element.firstChild:
element.removeChild(element.firstChild)
# reset sort triangles
self.sorted_column_number = None
self.sort_order = None
self.pstable_set_sort_triangle()
# reset table heading
for i, element in enumerate(document.querySelectorAll('#pstable_header > tr > td')):
element.classList.remove('pstable__row-pages--width', 'pstable__row-numeric--width',
'pstable__row-oom-score-adj--width')
element.firstChild.textContent = "col {}".format(i + 1)
def svg_create_element(self, height, width, css_class): def svg_create_element(self, height, width, css_class):
"""Return an empty SVG element""" """Return an empty SVG element"""
svg = document.createElementNS(self.svg_namespace, 'svg') svg = document.createElementNS(self.svg_namespace, 'svg')
@ -1832,7 +1911,8 @@ Out of memory: Killed process 651 (unattended-upgr) total-vm:108020kB, anon-rss:
hide_elements('.js-killed-proc-score--show') hide_elements('.js-killed-proc-score--show')
# generate process table # generate process table
self.update_process_table() self.pstable_fill_HTML()
self.pstable_set_sort_triangle()
# show/hide swap space # show/hide swap space
if self.oom_result.swap_active: if self.oom_result.swap_active:
@ -1883,23 +1963,28 @@ Out of memory: Killed process 651 (unattended-upgr) total-vm:108020kB, anon-rss:
element.textContent = self.oom_result.oom_text element.textContent = self.oom_result.oom_text
self.toggle_oom(show=False) self.toggle_oom(show=False)
def sort_pstable(self, column_name): def sort_pstable(self, column_number):
"""Sort process table by the values in the given column""" """
Sort process table by values
:param int column_number: Number of column to sort
"""
# TODO Check operator overloading # TODO Check operator overloading
# Operator overloading (Pragma opov) does not work in this context. # Operator overloading (Pragma opov) does not work in this context.
# self.oom_result.kconfig.ps_table_items + ['notes'] will compile to a string # self.oom_result.kconfig.pstable_items + ['notes'] will compile to a string
# "pid,uid,tgid,total_vm_pages,rss_pages,nr_ptes_pages,swapents_pages,oom_score_adjNotes" and not to an # "pid,uid,tgid,total_vm_pages,rss_pages,nr_ptes_pages,swapents_pages,oom_score_adjNotes" and not to an
# array # array
ps_table_and_notes = self.oom_result.kconfig.ps_table_items[:] ps_table_and_notes = self.oom_result.kconfig.pstable_items[:]
ps_table_and_notes.append('notes') ps_table_and_notes.append('notes')
column_name = ps_table_and_notes[column_number]
if column_name not in ps_table_and_notes: if column_name not in ps_table_and_notes:
internal_error('Can not sort process table with an unknown column name "{}"'.format(column_name)) internal_error('Can not sort process table with an unknown column name "{}"'.format(column_name))
return return
# reset sort order if the column has changes # reset sort order if the column has changes
if column_name != self.sorted_column: if column_number != self.sorted_column_number:
self.sort_order = None self.sort_order = None
self.sorted_column = column_name self.sorted_column_number = column_number
if not self.sort_order or self.sort_order == 'descending': if not self.sort_order or self.sort_order == 'descending':
self.sort_order = 'ascending' self.sort_order = 'ascending'
@ -1908,19 +1993,17 @@ Out of memory: Killed process 651 (unattended-upgr) total-vm:108020kB, anon-rss:
self.sort_order = 'descending' self.sort_order = 'descending'
self.sort_psindex_by_column(column_name, True) self.sort_psindex_by_column(column_name, True)
self.sorted_column = column_name self.pstable_fill_HTML()
self.pstable_set_sort_triangle()
self.update_process_table()
def sort_psindex_by_column(self, column_name, reverse=False): def sort_psindex_by_column(self, column_name, reverse=False):
""" """
Sort the pid list '_ps_index' based on the values in the process dict '_ps'. Sort the pid list '_pstable_index' based on the values in the process dict '_pstable'.
Is uses bubble sort with all disadvantages but just a few lines of code Is uses bubble sort with all disadvantages but just a few lines of code
""" """
ps = self.oom_result.details['_pstable']
ps = self.oom_result.details['_ps'] ps_index = self.oom_result.details['_pstable_index']
ps_index = self.oom_result.details['_ps_index']
def getvalue(column, pos): def getvalue(column, pos):
if column == 'pid': if column == 'pid':
@ -1928,7 +2011,7 @@ Out of memory: Killed process 651 (unattended-upgr) total-vm:108020kB, anon-rss:
else: else:
value = ps[ps_index[pos]][column] value = ps[ps_index[pos]][column]
# JS sorts alphanumeric by default, convert values explicit to integers to sort numerically # JS sorts alphanumeric by default, convert values explicit to integers to sort numerically
if column not in ['name', 'notes'] and value is not js_undefined: if column not in self.oom_result.kconfig.pstable_non_ints and value is not js_undefined:
value = int(value) value = int(value)
return value return value

View File

@ -199,6 +199,9 @@ class TestInBrowser(TestBase):
self.assertTrue('OOM killer was automatically triggered' in explanation.text, self.assertTrue('OOM killer was automatically triggered' in explanation.text,
'Missing text "OOM killer was automatically triggered"') 'Missing text "OOM killer was automatically triggered"')
head = self.driver.find_element_by_id('pstable_header')
self.assertTrue('Page Table Entries' in head.text, 'Missing column head line "Page Table Entries"')
self.check_swap_active() self.check_swap_active()
def check_results_ubuntu2110(self): def check_results_ubuntu2110(self):
@ -216,6 +219,9 @@ class TestInBrowser(TestBase):
self.assertFalse('with an OOM score of' in explanation.text, self.assertFalse('with an OOM score of' in explanation.text,
'No OOM score but text "with an OOM score of"') 'No OOM score but text "with an OOM score of"')
head = self.driver.find_element_by_id('pstable_header')
self.assertTrue('Page Table Bytes' in head.text, 'Missing column head line "Page Table Bytes"')
self.check_swap_inactive() self.check_swap_inactive()
def check_swap_inactive(self): def check_swap_inactive(self):