From 244044bf3aab2b97cf7414d255cfa471ed17b9c6 Mon Sep 17 00:00:00 2001 From: Alexey Avramov Date: Wed, 30 Jan 2019 17:02:29 +0900 Subject: [PATCH] fix oom-sort style --- README.md | 2 +- oom-sort | 66 ++++++-------- oom-sort.old | 222 ----------------------------------------------- oom-sort.old.old | 122 -------------------------- 4 files changed, 27 insertions(+), 385 deletions(-) delete mode 100755 oom-sort.old delete mode 100755 oom-sort.old.old diff --git a/README.md b/README.md index 067d47e..b3a5fa3 100644 --- a/README.md +++ b/README.md @@ -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). diff --git a/oom-sort b/oom-sort index 6e77894..e9accaf 100755 --- a/oom-sort +++ b/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] ) ) diff --git a/oom-sort.old b/oom-sort.old deleted file mode 100755 index 71a1939..0000000 --- a/oom-sort.old +++ /dev/null @@ -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 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() diff --git a/oom-sort.old.old b/oom-sort.old.old deleted file mode 100755 index 1b9e5f8..0000000 --- a/oom-sort.old.old +++ /dev/null @@ -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)] - ) - )