fix oom-sort style
This commit is contained in:
parent
2458edb2b3
commit
244044bf3a
@ -25,7 +25,7 @@ Also look at [Why are low memory conditions handled so badly?](https://www.reddi
|
||||
|
||||
## Solution
|
||||
|
||||
- Use of [earlyoom](https://github.com/rfjakob/earlyoom). This is a simple and very lightweight OOM preventer written in C (the best choice for emedded and old systems). It has a minimum dependencies and can work with oldest kernels.
|
||||
- Use of [earlyoom](https://github.com/rfjakob/earlyoom). This is a simple and very lightweight OOM preventer written in C (the best choice for emedded and old servers). It has a minimum dependencies and can work with oldest kernels.
|
||||
- Use of [oomd](https://github.com/facebookincubator/oomd). This is a userspace OOM killer for linux systems whitten in C++ and developed by Facebook. Needs Linux 4.20+.
|
||||
- Use of nohang (maybe this is a good choice for modern desktops and servers if you need fine tuning).
|
||||
|
||||
|
66
oom-sort
66
oom-sort
@ -63,11 +63,6 @@ def get_max_pid_len():
|
||||
return len(line.strip())
|
||||
|
||||
|
||||
def human(num):
|
||||
'''Convert KiB to MiB and right align'''
|
||||
return str(round(num / 1024.0)).rjust(6, ' ')
|
||||
|
||||
|
||||
"""#######################################################################79"""
|
||||
|
||||
|
||||
@ -86,10 +81,9 @@ sort_dict = {
|
||||
"""#######################################################################79"""
|
||||
|
||||
|
||||
# parse input
|
||||
|
||||
# todo: input validation
|
||||
# parse CLI args
|
||||
|
||||
# @TODO: user input validation
|
||||
|
||||
parser = ArgumentParser()
|
||||
|
||||
@ -97,7 +91,7 @@ parser.add_argument(
|
||||
'--num',
|
||||
'-n',
|
||||
help="""max number of lines; default: 99999""",
|
||||
default=None,
|
||||
default=99999,
|
||||
type=str
|
||||
)
|
||||
|
||||
@ -105,8 +99,8 @@ parser.add_argument(
|
||||
'--len',
|
||||
'-l',
|
||||
help="""max cmdline length; default: 99999""",
|
||||
default=None,
|
||||
type=str
|
||||
default=99999,
|
||||
type=int
|
||||
)
|
||||
|
||||
|
||||
@ -114,7 +108,7 @@ parser.add_argument(
|
||||
'--sort',
|
||||
'-s',
|
||||
help="""sort by unit; default: oom_score""",
|
||||
default=None,
|
||||
default='oom_score',
|
||||
type=str
|
||||
)
|
||||
|
||||
@ -128,16 +122,6 @@ num_lines = args.num
|
||||
sort_by = args.sort
|
||||
|
||||
|
||||
if num_lines is None:
|
||||
num_lines = 99999
|
||||
|
||||
if display_cmdline is None:
|
||||
display_cmdline = 99999
|
||||
|
||||
if sort_by is None:
|
||||
sort_by = 'oom_score'
|
||||
|
||||
|
||||
if sort_by not in sort_dict:
|
||||
print('Invalid -s/--sort value. Valid values are:\nPID\noom_scor'
|
||||
'e [default value]\noom-sore_adj\nUID\nName\ncmdline\nVmR'
|
||||
@ -145,18 +129,14 @@ if sort_by not in sort_dict:
|
||||
exit()
|
||||
|
||||
|
||||
if sort_by == '1':
|
||||
print('Sort by:', sort_by)
|
||||
|
||||
|
||||
"""#######################################################################79"""
|
||||
|
||||
# find VmRSS, VmSwap and UID positions in /proc/*/status
|
||||
# find VmRSS, VmSwap and UID positions in /proc/*/status for further
|
||||
# searching positions of UID, VmRSS and VmSwap in each process
|
||||
|
||||
with open('/proc/self/status') as file:
|
||||
status_list = file.readlines()
|
||||
|
||||
# список имен из /proc/*/status для дальнейшего поиска позиций VmRSS and VmSwap
|
||||
status_names = []
|
||||
for s in status_list:
|
||||
status_names.append(s.split(':')[0])
|
||||
@ -176,7 +156,7 @@ oom_list = []
|
||||
|
||||
for pid in listdir('/proc'):
|
||||
|
||||
# пропускаем элементы, состоящие не из цифр и PID 1
|
||||
# skip non-numeric entries and PID 1
|
||||
if pid.isdigit() is False or pid == '1':
|
||||
continue
|
||||
|
||||
@ -207,6 +187,14 @@ oom_list_sorted = sorted(
|
||||
oom_list, key=itemgetter(int(sort_dict[sort_by])), reverse=True)
|
||||
|
||||
|
||||
"""#######################################################################79"""
|
||||
|
||||
# find width of columns
|
||||
|
||||
|
||||
max_pid_len = get_max_pid_len()
|
||||
|
||||
|
||||
max_uid_len = len(str(sorted(
|
||||
oom_list, key=itemgetter(5), reverse=True)[0][5]))
|
||||
|
||||
@ -214,21 +202,16 @@ max_uid_len = len(str(sorted(
|
||||
max_vm_rss_len = len(str(round(
|
||||
sorted(oom_list, key=itemgetter(6), reverse=True)[0][6] / 1024.0)))
|
||||
|
||||
|
||||
if max_vm_rss_len < 5:
|
||||
max_vm_rss_len = 5
|
||||
|
||||
|
||||
max_pid_len = get_max_pid_len()
|
||||
|
||||
|
||||
"""#######################################################################79"""
|
||||
|
||||
|
||||
# print table
|
||||
# print output
|
||||
|
||||
|
||||
if display_cmdline == '0':
|
||||
if display_cmdline == 0:
|
||||
|
||||
print(
|
||||
'oom_score oom_score_adj{}UID{}PID Name {}VmRSS VmSwap'.format(
|
||||
@ -249,7 +232,8 @@ if display_cmdline == '0':
|
||||
else:
|
||||
|
||||
print(
|
||||
'oom_score oom_score_adj{}UID{}PID Name {}VmRSS VmSwap cmdline'.format(
|
||||
'oom_score oom_score_adj{}UID{}PID Name {}VmRSS VmSwa'
|
||||
'p cmdline'.format(
|
||||
' ' * (max_uid_len - 2),
|
||||
' ' * (max_pid_len - 2),
|
||||
' ' * max_vm_rss_len
|
||||
@ -257,13 +241,15 @@ else:
|
||||
)
|
||||
|
||||
print(
|
||||
'--------- ------------- {} {} --------------- {}-- -------- -------'.format(
|
||||
'--------- ------------- {} {} --------------- {}-- ------'
|
||||
'-- -------'.format(
|
||||
'-' * max_uid_len,
|
||||
'-' * max_pid_len,
|
||||
'-' * max_vm_rss_len
|
||||
)
|
||||
)
|
||||
|
||||
# print processes stats sorted by sort_dict[sort_by]
|
||||
|
||||
for i in oom_list_sorted[:int(num_lines)]:
|
||||
|
||||
@ -284,7 +270,7 @@ for i in oom_list_sorted[:int(num_lines)]:
|
||||
str(pid).rjust(max_pid_len),
|
||||
name.ljust(15),
|
||||
str(round(vm_rss / 1024.0)).rjust(max_vm_rss_len, ' '),
|
||||
human(vm_swap),
|
||||
cmdline[:int(display_cmdline)]
|
||||
str(round(vm_swap / 1024.0)).rjust(6, ' '),
|
||||
cmdline[:display_cmdline]
|
||||
)
|
||||
)
|
||||
|
222
oom-sort.old
222
oom-sort.old
@ -1,222 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
sort processes by oom_score
|
||||
"""
|
||||
|
||||
# @TODO: user input validation
|
||||
|
||||
from os import listdir
|
||||
from argparse import ArgumentParser
|
||||
from time import sleep
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
"""
|
||||
parse CLI args
|
||||
"""
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument(
|
||||
'--num',
|
||||
'-n',
|
||||
help="""max number of lines; default: 99999""",
|
||||
default=99999,
|
||||
type=int
|
||||
)
|
||||
parser.add_argument(
|
||||
'--len',
|
||||
'-l',
|
||||
help="""max cmdline length; default: 99999""",
|
||||
default=99999,
|
||||
type=int
|
||||
)
|
||||
parser.add_argument(
|
||||
'--refresh',
|
||||
'-r',
|
||||
help='refresh interval (0 to disable); default: 0. '
|
||||
'Use it with --num/-n to also limit the output length',
|
||||
default=0,
|
||||
type=int
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def human_readable(num):
|
||||
"""
|
||||
KiB to MiB
|
||||
"""
|
||||
return str(round(num / 1024.0)).rjust(6, ' ')
|
||||
|
||||
|
||||
def clear_screen():
|
||||
"""
|
||||
print ANSI sequence to clear the screen
|
||||
"""
|
||||
print('\033c')
|
||||
|
||||
|
||||
class TableIndexes: # pylint: disable=too-few-public-methods
|
||||
"""
|
||||
table headers from /proc/*/status for further
|
||||
searching positions of VmRSS and VmSwap in each process output
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
with open('/proc/self/status') as status_file:
|
||||
status_list = status_file.readlines()
|
||||
|
||||
status_names = []
|
||||
for line in status_list:
|
||||
status_names.append(line.split(':')[0])
|
||||
|
||||
self.vm_rss = status_names.index('VmRSS')
|
||||
self.vm_swap = status_names.index('VmSwap')
|
||||
self.uid = status_names.index('Uid')
|
||||
|
||||
|
||||
INDEX = TableIndexes()
|
||||
|
||||
|
||||
class ProcessInfo:
|
||||
|
||||
pid = None
|
||||
cmdline = None
|
||||
oom_score = None
|
||||
oom_score_adj = None
|
||||
|
||||
name = None
|
||||
uid = None
|
||||
vm_rss = None
|
||||
vm_swap = None
|
||||
|
||||
@classmethod
|
||||
def from_pid(cls, pid):
|
||||
"""
|
||||
create ProcessInfo instance reading process info from /proc/{pid}/
|
||||
"""
|
||||
info = cls()
|
||||
info.pid = pid
|
||||
try:
|
||||
with open('/proc/' + pid + '/cmdline') as file:
|
||||
try:
|
||||
info.cmdline = file.readlines()[0].replace('\x00', ' ')
|
||||
except IndexError:
|
||||
return None
|
||||
with open('/proc/' + pid + '/oom_score') as file:
|
||||
info.oom_score = int(file.readlines()[0][:-1])
|
||||
with open('/proc/' + pid + '/oom_score_adj') as file:
|
||||
info.oom_score_adj = int(file.readlines()[0][:-1])
|
||||
except FileNotFoundError:
|
||||
return None
|
||||
except ProcessLookupError:
|
||||
return None
|
||||
return info
|
||||
|
||||
def read_status(self):
|
||||
"""
|
||||
return True if process have info in /proc/{pid}/status
|
||||
"""
|
||||
try:
|
||||
# @TODO: stop reading file after VmSwap value retrieved
|
||||
with open('/proc/' + self.pid + '/status') as file:
|
||||
status_list = file.readlines()
|
||||
self.vm_rss = int(
|
||||
status_list[INDEX.vm_rss].split(':')[1].split(' ')[-2]
|
||||
)
|
||||
self.vm_swap = int(
|
||||
status_list[INDEX.vm_swap].split(':')[1].split(' ')[-2]
|
||||
)
|
||||
self.name = status_list[0][:-1].split('\t')[1]
|
||||
self.uid = status_list[INDEX.uid].split('\t')[1]
|
||||
except FileNotFoundError:
|
||||
return False
|
||||
except ProcessLookupError:
|
||||
return False
|
||||
return True
|
||||
|
||||
def format_output(self, display_cmdline):
|
||||
"""
|
||||
format output for printing
|
||||
"""
|
||||
return '{} {} {} {} {} {} M {} M {}'.format(
|
||||
str(self.oom_score).rjust(9),
|
||||
str(self.oom_score_adj).rjust(13),
|
||||
self.uid.rjust(5),
|
||||
str(self.pid).rjust(5),
|
||||
self.name.ljust(15),
|
||||
human_readable(self.vm_rss),
|
||||
human_readable(self.vm_swap),
|
||||
self.cmdline[:display_cmdline]
|
||||
)
|
||||
|
||||
|
||||
class Application:
|
||||
"""
|
||||
oom-sort application
|
||||
"""
|
||||
|
||||
oom_list = None
|
||||
|
||||
def __init__(self):
|
||||
args = parse_arguments()
|
||||
self.num_lines = args.num
|
||||
self.display_cmdline = args.len
|
||||
self.refresh_interval = args.refresh
|
||||
|
||||
def print_stats(self):
|
||||
"""
|
||||
print processes stats sorted by OOM score
|
||||
"""
|
||||
|
||||
oom_list = []
|
||||
for pid in listdir('/proc'):
|
||||
# skip non-numeric entries and PID 1
|
||||
if pid.isdigit() is not True or pid == '1':
|
||||
continue
|
||||
proc_info = ProcessInfo.from_pid(pid)
|
||||
if proc_info:
|
||||
oom_list.append(proc_info)
|
||||
oom_list.sort(key=lambda p: p.oom_score, reverse=True)
|
||||
|
||||
if self.display_cmdline == 0:
|
||||
print('oom_score oom_score_adj UID PID Name VmRSS VmSwap')
|
||||
print('--------- ------------- ----- ----- --------------- -------- --------')
|
||||
else:
|
||||
print('oom_score oom_score_adj UID PID Name VmRSS VmSwap cmdline')
|
||||
print('--------- ------------- ----- ----- --------------- -------- -------- -------')
|
||||
|
||||
# iterate through list sorted by oom_score and print name, pid, etc
|
||||
for proc_info in oom_list[:self.num_lines]:
|
||||
if proc_info.read_status():
|
||||
print(
|
||||
proc_info.format_output(
|
||||
display_cmdline=self.display_cmdline
|
||||
)
|
||||
)
|
||||
|
||||
def oom_top(self):
|
||||
"""
|
||||
show `top`-like refreshing stats
|
||||
"""
|
||||
while True:
|
||||
try:
|
||||
clear_screen()
|
||||
print("Refreshing each {} seconds, press <Ctrl+C> to interrupt:".format(
|
||||
self.refresh_interval
|
||||
))
|
||||
self.print_stats()
|
||||
sleep(self.refresh_interval)
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
|
||||
def main(self):
|
||||
"""
|
||||
application entrypoint
|
||||
"""
|
||||
if not self.refresh_interval:
|
||||
self.print_stats()
|
||||
else:
|
||||
self.oom_top()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Application().main()
|
122
oom-sort.old.old
122
oom-sort.old.old
@ -1,122 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
sort processes by oom_score
|
||||
"""
|
||||
|
||||
# нужна еще валидация cli ввода
|
||||
|
||||
from operator import itemgetter
|
||||
from os import listdir
|
||||
from argparse import ArgumentParser
|
||||
|
||||
parser = ArgumentParser()
|
||||
|
||||
parser.add_argument(
|
||||
'--num',
|
||||
'-n',
|
||||
help="""max number of lines; default: 99999""",
|
||||
default=None,
|
||||
type=str
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--len',
|
||||
'-l',
|
||||
help="""max cmdline length; default: 99999""",
|
||||
default=None,
|
||||
type=str
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
display_cmdline = args.len
|
||||
num_lines = args.num
|
||||
if num_lines == None:
|
||||
num_lines = 99999
|
||||
|
||||
if display_cmdline == None:
|
||||
display_cmdline = 99999
|
||||
|
||||
def human(num):
|
||||
'''KiB to MiB'''
|
||||
return str(round(num / 1024.0)).rjust(6, ' ')
|
||||
|
||||
with open('/proc/self/status') as file:
|
||||
status_list = file.readlines()
|
||||
|
||||
# список имен из /proc/*/status для дальнейшего поиска позиций VmRSS and VmSwap
|
||||
status_names = []
|
||||
for s in status_list:
|
||||
status_names.append(s.split(':')[0])
|
||||
|
||||
vm_rss_index = status_names.index('VmRSS')
|
||||
vm_swap_index = status_names.index('VmSwap')
|
||||
uid_index = status_names.index('Uid')
|
||||
|
||||
oom_list = []
|
||||
for pid in listdir('/proc'):
|
||||
# пропускаем элементы, состоящие не из цифр и PID 1
|
||||
if pid.isdigit() is not True or pid == '1':
|
||||
continue
|
||||
try:
|
||||
with open('/proc/' + pid + '/cmdline') as file:
|
||||
try:
|
||||
cmdline = file.readlines()[0].replace('\x00', ' ')
|
||||
except IndexError:
|
||||
continue
|
||||
with open('/proc/' + pid + '/oom_score') as file:
|
||||
oom_score = int(file.readlines()[0][:-1])
|
||||
with open('/proc/' + pid + '/oom_score_adj') as file:
|
||||
oom_score_adj = int(file.readlines()[0][:-1])
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
except ProcessLookupError:
|
||||
continue
|
||||
|
||||
oom_list.append((pid, oom_score, oom_score_adj, cmdline))
|
||||
|
||||
# list sorted by oom_score
|
||||
oom_list_sorted = sorted(oom_list, key=itemgetter(1), reverse=True)
|
||||
|
||||
if display_cmdline == '0':
|
||||
print('oom_score oom_score_adj UID PID Name VmRSS VmSwap')
|
||||
print('--------- ------------- ----- ----- --------------- -------- --------')
|
||||
else:
|
||||
print('oom_score oom_score_adj UID PID Name VmRSS VmSwap cmdline')
|
||||
print('--------- ------------- ----- ----- --------------- -------- -------- -------')
|
||||
|
||||
# итерируемся по сортированному списку oom_score, печатая name, pid etc
|
||||
for i in oom_list_sorted[:int(num_lines)]:
|
||||
pid = i[0]
|
||||
oom_score = i[1]
|
||||
oom_score_adj = i[2]
|
||||
cmdline = i[3].strip()
|
||||
|
||||
try:
|
||||
# читать часть файла не дальше VmSwap - когда-нибудь
|
||||
with open('/proc/' + pid + '/status') as file:
|
||||
status_list = file.readlines()
|
||||
|
||||
vm_rss = int(status_list[vm_rss_index].split(':')[1].split(' ')[-2])
|
||||
vm_swap = int(status_list[vm_swap_index].split(':')[1].split(' ')[-2])
|
||||
name = status_list[0][:-1].split('\t')[1]
|
||||
uid = status_list[uid_index].split('\t')[1]
|
||||
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
|
||||
except ProcessLookupError:
|
||||
continue
|
||||
|
||||
print(
|
||||
'{} {} {} {} {} {} M {} M {}'.format(
|
||||
str(oom_score).rjust(9),
|
||||
str(oom_score_adj).rjust(13),
|
||||
uid.rjust(5),
|
||||
str(pid).rjust(5),
|
||||
name.ljust(15),
|
||||
human(vm_rss),
|
||||
human(vm_swap),
|
||||
cmdline[:int(display_cmdline)]
|
||||
)
|
||||
)
|
Loading…
Reference in New Issue
Block a user