implement modify badness by matching with RE pattern

This commit is contained in:
Alexey Avramov 2018-12-10 02:01:26 +09:00
parent 3830179a68
commit 842ea7be35
3 changed files with 115 additions and 32 deletions

123
nohang
View File

@ -7,6 +7,8 @@ from argparse import ArgumentParser
from sys import stdout from sys import stdout
from signal import SIGKILL, SIGTERM from signal import SIGKILL, SIGTERM
start_time = time()
sig_dict = {SIGKILL: 'SIGKILL', sig_dict = {SIGKILL: 'SIGKILL',
SIGTERM: 'SIGTERM'} SIGTERM: 'SIGTERM'}
@ -357,16 +359,8 @@ def find_victim_and_send_signal(signal):
pid_badness_list = [] pid_badness_list = []
# not implemented, in progress
prefer_re_cmdline = ''
avoid_re_cmdline = ''
prefer_cmd_factor = 1
prefer_cmd_factor = 1
if regex_matching: if regex_matching:
for pid in os.listdir('/proc'): for pid in os.listdir('/proc'):
# только директории, имена которых состоят только из цифр, за исключением /proc/1/ # только директории, имена которых состоят только из цифр, за исключением /proc/1/
if pid[0].isdecimal() is False or pid == '1': if pid[0].isdecimal() is False or pid == '1':
@ -376,29 +370,40 @@ def find_victim_and_send_signal(signal):
badness = int(rline1('/proc/' + pid + '/oom_score')) badness = int(rline1('/proc/' + pid + '/oom_score'))
name = pid_to_name(pid) name = pid_to_name(pid)
cmdline = pid_to_cmdline(pid) cmdline = pid_to_cmdline(pid)
#uid = pid_to_uid(pid)
# skip kthreads # skip kthreads
if cmdline == '': if cmdline == '':
continue continue
#print([pid], [name], [cmdline]) #print([pid], [name], [cmdline])
if re.search(avoid_regex, name) is not None: if search(avoid_regex, name) is not None:
badness = int(badness / avoid_factor) badness = int(badness / avoid_factor)
print(' Name matches with avoid_regex \033[33m{}\033[0m: \033[33m{}\033[0m, CmdLine: {}'.format(
avoid_regex, name, cmdline))
if re.search(prefer_regex, name) is not None: if search(prefer_regex, name) is not None:
badness = int((badness + 1) * prefer_factor) badness = int((badness + 1) * prefer_factor)
print(' Name matches with prefer_regex \033[33m{}\033[0m: \033[33m{}\033[0m, CmdLine: {}'.format(
prefer_regex, name, cmdline))
if search(avoid_re_cmdline, cmdline) is not None:
if re.search(avoid_re_cmdline, cmdline) is not None: badness = int(badness / avoid_cmd_factor)
badness = int(badness / avoid_factor) print(' Cmdline matches with avoid_re_cmdline \033[33m{}\033[0m: \033[33m{}\033[0m, Name: {}'.format(
avoid_re_cmdline, cmdline, name))
if re.search(prefer_re_cmdline, cmdline) is not None: if search(prefer_re_cmdline, cmdline) is not None:
badness = int((badness + 1) * prefer_cmd_factor) badness = int((badness + 1) * prefer_cmd_factor)
print(' Cmdline matches with prefer_re_cmdline \033[33m{}\033[0m: \033[33m{}\033[0m, Name: {}'.format(
prefer_re_cmdline, cmdline, name))
except FileNotFoundError: except FileNotFoundError:
@ -409,7 +414,6 @@ def find_victim_and_send_signal(signal):
else: else:
for pid in os.listdir('/proc'): for pid in os.listdir('/proc'):
@ -433,6 +437,7 @@ def find_victim_and_send_signal(signal):
if victim_badness >= min_badness: # Try to send signal to found victim if victim_badness >= min_badness: # Try to send signal to found victim
pid = pid_tuple_list[0] pid = pid_tuple_list[0]
name = pid_to_name(pid) name = pid_to_name(pid)
# Get VmRSS and VmSwap and cmdline of victim process and try to send signal # Get VmRSS and VmSwap and cmdline of victim process and try to send signal
@ -448,12 +453,12 @@ def find_victim_and_send_signal(signal):
if n is vm_swap_index: if n is vm_swap_index:
vm_swap = kib_to_mib(int(line.split('\t')[1][:-4])) vm_swap = kib_to_mib(int(line.split('\t')[1][:-4]))
break break
with open('/proc/' + pid + '/cmdline') as file: with open('/proc/' + pid + '/cmdline') as file:
try: try:
cmdline = file.readlines()[0].replace('\x00', ' ') cmdline = file.readlines()[0].replace('\x00', ' ')
except IndexError: except IndexError:
cmdline = '' cmdline = ''
except FileNotFoundError: except FileNotFoundError:
pass pass
except ProcessLookupError: except ProcessLookupError:
@ -503,11 +508,12 @@ def find_victim_and_send_signal(signal):
response_time = time() - time0 response_time = time() - time0
victim_badness_is_too_small = ' victim badness {} < min_badness {}; nothing to do; response time: {} ms'.format( victim_badness_is_too_small = ' victim badness {} < min_badness {}; nothing to do; response time: {} ms'.format(
victim_badness, min_badness, round(response_time * 1000)) victim_badness,
min_badness,
round(response_time * 1000))
print(victim_badness_is_too_small) print(victim_badness_is_too_small)
stdout.flush()
sleep_after_send_signal(signal) sleep_after_send_signal(signal)
@ -528,13 +534,12 @@ def sleep_after_check_mem():
try: try:
if print_sleep_periods: if print_sleep_periods:
print( print('sleep', round(t, 2),
'sleep', round( ' (t_mem={}, t_swap={}, t_zram={})'.format(
t, 2), ' (t_mem={}, t_swap={}, t_zram={})'.format( round(t_mem, 2),
round( round(t_swap, 2),
t_mem, 2), round( round(t_zram, 2)))
t_swap, 2), round( stdout.flush()
t_zram, 2)))
sleep(t) sleep(t)
except KeyboardInterrupt: except KeyboardInterrupt:
exit() exit()
@ -816,6 +821,13 @@ execute_the_command = conf_parse_bool('execute_the_command')
prefer_regex = conf_parse_string('prefer_regex') prefer_regex = conf_parse_string('prefer_regex')
avoid_regex = conf_parse_string('avoid_regex') avoid_regex = conf_parse_string('avoid_regex')
prefer_re_cmdline = conf_parse_string('prefer_re_cmdline')
avoid_re_cmdline = conf_parse_string('avoid_re_cmdline')
mem_min_sigterm_kb, mem_min_sigterm_mb, mem_min_sigterm_percent = calculate_percent( mem_min_sigterm_kb, mem_min_sigterm_mb, mem_min_sigterm_percent = calculate_percent(
'mem_min_sigterm') 'mem_min_sigterm')
@ -1016,6 +1028,57 @@ else:
exit() exit()
if 'prefer_cmd_factor' in config_dict:
prefer_cmd_factor = string_to_float_convert_test(config_dict['prefer_cmd_factor'])
if prefer_cmd_factor is None:
print('Invalid prefer_cmd_factor value, not float\nExit')
exit()
if prefer_cmd_factor < 1 and prefer_cmd_factor > 1000:
print('prefer_cmd_factor value out of range [1; 1000]\nExit')
exit()
else:
print('prefer_cmd_factor not in config\nExit')
exit()
if 'avoid_cmd_factor' in config_dict:
avoid_cmd_factor = string_to_float_convert_test(config_dict['avoid_cmd_factor'])
if avoid_cmd_factor is None:
print('Invalid avoid_cmd_factor value, not float\nExit')
exit()
if avoid_cmd_factor < 1 and avoid_cmd_factor > 1000:
print('avoid_cmd_factor value out of range [1; 1000]\nExit')
exit()
else:
print('avoid_cmd_factor not in config\nExit')
exit()
if 'min_time_between_warnings' in config_dict: if 'min_time_between_warnings' in config_dict:
min_time_between_warnings = string_to_float_convert_test( min_time_between_warnings = string_to_float_convert_test(
config_dict['min_time_between_warnings']) config_dict['min_time_between_warnings'])
@ -1253,10 +1316,14 @@ warn_time_now = 0
warn_time_delta = 1000 warn_time_delta = 1000
warn_timer = 0 warn_timer = 0
x = time() - start_time
print(
'The duration of startup:', round(
x * 1000, 1), 'ms')
print('Monitoring started!') print('Monitoring started!')
stdout.flush()
########################################################################## ##########################################################################

View File

@ -121,13 +121,14 @@ oom_score_adj_max = 30
##################################################################### #####################################################################
4. Impact on the badness of processes via matching their names 4. Impact on the badness of processes via matching their names
with regular expressions. with regular expressions (using re.search()).
See https://en.wikipedia.org/wiki/Regular_expression and See https://en.wikipedia.org/wiki/Regular_expression and
https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions https://en.wikipedia.org/wiki/Perl_Compatible_Regular_Expressions
Enabling this option slows down the search for the victim Enabling this option slows down the search for the victim
because the names of all processes are compared with the because the names or cmdlines of all processes
(except init and kthreads) are compared with the
specified regex patterns. specified regex patterns.
Valid values are True and False. Valid values are True and False.
@ -138,10 +139,11 @@ regex_matching = False
be calculated by the following formula: be calculated by the following formula:
badness = (oom_score + 1) * prefer_factor badness = (oom_score + 1) * prefer_factor
prefer_regex = Matching with process names
RE pattern must not be empty!
# prefer_re_cmdline = ^/usr/lib/firefox # not implemented, in progress prefer_regex = /usr/bin...
Valid values are floating-point numbers from the range [1; 1000]. Valid values are floating-point numbers from the range [1; 1000].
@ -159,6 +161,18 @@ avoid_regex = ^(sshd|Xorg)$
avoid_factor = 3 avoid_factor = 3
Matching with cmdlines
prefer_re_cmdline = ^/usr/lib/firefox
avoid_re_cmdline = ^/usr/lib/virtualbox
prefer_cmd_factor = 3
avoid_cmd_factor = 3
##################################################################### #####################################################################
5. The execution of a specific command instead of sending the 5. The execution of a specific command instead of sending the

View File

@ -104,3 +104,5 @@ if len(b) > 0:
], env={ ], env={
display_key: display_value, dbus_key: dbus_value display_key: display_value, dbus_key: dbus_value
}).wait(3) }).wait(3)
else:
print('Low memory warnings: nobody logged in with GUI. Nothing to do.')