fix GUI notifications

This commit is contained in:
Alexey Avramov 2019-01-24 01:13:59 +09:00
parent f869b0bdbb
commit 2e824bce2d
2 changed files with 73 additions and 102 deletions

150
nohang
View File

@ -14,6 +14,7 @@ sig_dict = {SIGKILL: 'SIGKILL',
self_pid = str(os.getpid()) self_pid = str(os.getpid())
self_uid = os.geteuid() self_uid = os.geteuid()
if self_uid == 0: if self_uid == 0:
root = True root = True
else: else:
@ -22,14 +23,18 @@ else:
wait_time = 12 wait_time = 12
max_sleep_time = 2 max_sleep_time = 2
min_sleep_time = 0.1 min_sleep_time = 0.1
notify_helper_path = '/usr/sbin/nohang_notify_helper' notify_helper_path = '/usr/sbin/nohang_notify_helper'
psi_path = '/proc/pressure/memory' psi_path = '/proc/pressure/memory'
psi_support = os.path.exists(psi_path) psi_support = os.path.exists(psi_path)
HR = '~' * 70 HR = '~' * 79
print_total_stat = True
########################################################################## ##########################################################################
@ -38,17 +43,26 @@ HR = '~' * 70
def update_stat_dict_and_print(key): def update_stat_dict_and_print(key):
if key not in stat_dict: if key not in stat_dict:
stat_dict.update({key: 1}) stat_dict.update({key: 1})
else: else:
new_value = stat_dict[key] + 1 new_value = stat_dict[key] + 1
stat_dict.update({key: new_value}) stat_dict.update({key: new_value})
stats_msg = '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' \
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\033[4mUp' \ if print_total_stat:
'time: {}; corrective actions:\033[0m'.format(
format_time(time() - start_time)) stats_msg = '{}\n\033[4mThe following corrective actions have been implemented in the last {}:\033[0m'.format(
for i in stat_dict: HR, format_time(time() - start_time))
stats_msg += '\n- {}: {}'.format(i, stat_dict[i])
for i in stat_dict:
stats_msg += '\n- {}: {}'.format(i, stat_dict[i])
print(stats_msg)
def psi_mem_some_avg_total(): def psi_mem_some_avg_total():
@ -184,13 +198,6 @@ def rline1(path):
for line in f: for line in f:
return line[:-1] return line[:-1]
'''
def write(path, string):
"""Write string to path."""
with open(path, 'w') as f:
f.write(string)
'''
def kib_to_mib(num): def kib_to_mib(num):
"""Convert KiB values to MiB values.""" """Convert KiB values to MiB values."""
@ -306,7 +313,7 @@ def notify_helper(title, body):
except TimeoutExpired: except TimeoutExpired:
proc.kill() proc.kill()
print( print(
'TimeoutExpired: nohang_notify_helper {} {}'.format( 'TimeoutExpired: nohang_notify_helper: {} {}'.format(
title, body)) title, body))
@ -334,7 +341,11 @@ def send_notify_warn():
round(mem_available / mem_total * 100)) round(mem_available / mem_total * 100))
title = 'Low memory: {}'.format(low_mem_percent) title = 'Low memory: {}'.format(low_mem_percent)
body = 'Fattest process: <b>{}</b>, <b>{}</b>'.format(pid, name) body = 'Fattest process: <b>{}</b>, <b>{}</b>'.format(
pid, name.replace(
# symbol '&' can break notifications in some themes,
# therefore it is replaced by '*'
'&', '*'))
if root: # If nohang was started by root if root: # If nohang was started by root
# send notification to all active users with special script # send notification to all active users with special script
@ -366,27 +377,6 @@ def send_notify(signal, name, pid):
notify_send_wait(title, body) notify_send_wait(title, body)
'''
def send_notify(signal, name, pid):
"""
Notificate about OOM Preventing.
signal: key for notify_sig_dict
name: str process name
pid: str process pid
"""
title = 'Preventing OOM'
body = '<b>{}</b> process <b>{}</b>, <b>{}</b>'.format(
notify_sig_dict[signal], pid, name)
if root:
# send notification to all active users with notify-send
notify_helper(title, body)
else:
# send notification to user that runs this nohang
notify_send_wait(title, body)
'''
def send_notify_etc(pid, name, command): def send_notify_etc(pid, name, command):
""" """
Notificate about OOM Preventing. Notificate about OOM Preventing.
@ -400,7 +390,7 @@ def send_notify_etc(pid, name, command):
pid, name.replace('&', '*'), command.replace('&', '*')) pid, name.replace('&', '*'), command.replace('&', '*'))
if root: if root:
# send notification to all active users with notify-send # send notification to all active users with notify-send
notify_send_wait(title, body) notify_helper(title, body)
else: else:
# send notification to user that runs this nohang # send notification to user that runs this nohang
notify_send_wait(title, body) notify_send_wait(title, body)
@ -503,8 +493,8 @@ def find_victim_and_send_signal(signal):
# Try to send signal to found victim # Try to send signal to found victim
# Get VmRSS and VmSwap and cmdline of victim process and try to send # Get VmRSS and VmSwap and cmdline of victim process
# signal # and try to send a signal
try: try:
with open('/proc/' + pid + '/status') as f: with open('/proc/' + pid + '/status') as f:
@ -615,7 +605,7 @@ def find_victim_and_send_signal(signal):
len_vm = len(str(vm_size)) len_vm = len(str(vm_size))
if detailed_rss: if detailed_rss:
victim_info = '\033[4mFound a victim with highest badness:\033[0m' \ victim_info = '\033[4mFound a process with highest badness:\033[0m' \
'\n Name: \033[33m{}\033[0m' \ '\n Name: \033[33m{}\033[0m' \
'\n PID: \033[33m{}\033[0m' \ '\n PID: \033[33m{}\033[0m' \
'\n UID: \033[33m{}\033[0m' \ '\n UID: \033[33m{}\033[0m' \
@ -643,7 +633,7 @@ def find_victim_and_send_signal(signal):
str(vm_swap).rjust(len_vm), str(vm_swap).rjust(len_vm),
cmdline) cmdline)
else: else:
victim_info = '\033[4mFound a victim with highest badness:\033[0m' \ victim_info = '\033[4mFound a process with highest badness:\033[0m' \
'\n Name: \033[33m{}\033[0m' \ '\n Name: \033[33m{}\033[0m' \
'\n PID: \033[33m{}\033[0m' \ '\n PID: \033[33m{}\033[0m' \
'\n UID: \033[33m{}\033[0m' \ '\n UID: \033[33m{}\033[0m' \
@ -677,24 +667,23 @@ def find_victim_and_send_signal(signal):
response_time = time() - time0 response_time = time() - time0
etc_info = '{}' \ etc_info = '{}' \
'\n\033[4mImplement corrective action:\033[0m\n Execute the command: \033[4m{}\033[0m' \ '\n\033[4mImplement corrective action:\033[0m\n Run the command: \033[4m{}\033[0m' \
'\n Exit status: {}; response time: {} ms'.format( '\n Exit status: {}; response time: {} ms'.format(
victim_info, command.replace( victim_info, command.replace(
'$PID', pid).replace('$NAME', pid_to_name(pid)), exit_status, '$PID', pid).replace('$NAME', pid_to_name(pid)), exit_status,
round(response_time * 1000)) round(response_time * 1000))
# update stat_dict
key = "Run the command '\033[35m{}\033[0m'".format(
command.replace('$PID', pid).replace('$NAME', pid_to_name(pid)))
print(key)
update_stat_dict_and_print(key)
print(mem_info) print(mem_info)
print(etc_info) print(etc_info)
key = "Run the command '\033[35m{}\033[0m'".format(command)
update_stat_dict_and_print(key)
if gui_notifications: if gui_notifications:
send_notify_etc(pid, name, command.replace( send_notify_etc(
'$PID', pid).replace('$NAME', pid_to_name(pid))) pid,
name,
command.replace('$PID', pid).replace('$NAME', pid_to_name(pid)))
else: else:
@ -704,12 +693,14 @@ def find_victim_and_send_signal(signal):
send_result = '\033[32mOK\033[0m; response time: {} ms'.format( send_result = '\033[32mOK\033[0m; response time: {} ms'.format(
round(response_time * 1000)) round(response_time * 1000))
# update stat_dict preventing_oom_message = '{}' \
'\n\033[4mImplement a corrective action:\033[0m\n ' \
'Sending \033[4m{}\033[0m to the victim; {}'.format(
victim_info, sig_dict[signal], send_result)
key = 'Send \033[35m{}\033[0m to \033[35m{}\033[0m'.format( key = 'Send \033[35m{}\033[0m to \033[35m{}\033[0m'.format(
sig_dict[signal], name) sig_dict[signal], name)
update_stat_dict_and_print(key)
if gui_notifications: if gui_notifications:
send_notify(signal, name, pid) send_notify(signal, name, pid)
@ -717,37 +708,18 @@ def find_victim_and_send_signal(signal):
response_time = time() - time0 response_time = time() - time0
send_result = 'no such process; response time: {} ms'.format( send_result = 'no such process; response time: {} ms'.format(
round(response_time * 1000)) round(response_time * 1000))
key = 'The victim died in the search process: FileNotFoundError'
# update stat_dict
key = 'The victim died in the search process'
update_stat_dict_and_print(key)
except ProcessLookupError: except ProcessLookupError:
response_time = time() - time0 response_time = time() - time0
send_result = 'no such process; response time: {} ms'.format( send_result = 'no such process; response time: {} ms'.format(
round(response_time * 1000)) round(response_time * 1000))
key = 'The victim died in the search process: ProcessLookupError'
# update stat_dict
key = 'The victim died in the search process'
update_stat_dict_and_print(key)
preventing_oom_message = '{}' \
'\n\033[4mImplement a corrective action:\033[0m\n ' \
'Sending \033[4m{}\033[0m to the victim; {}'.format(
victim_info, sig_dict[signal], send_result)
print(mem_info) print(mem_info)
print(preventing_oom_message) print(preventing_oom_message)
stats_msg = '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'\ update_stat_dict_and_print(key)
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\033[4mUptime: {}; c' \
'orrective actions:\033[0m'.format(
format_time(time() - start_time))
for key in stat_dict:
stats_msg += '\n- {}: {}'.format(key, stat_dict[key])
print(stats_msg)
else: else:
@ -765,16 +737,6 @@ def find_victim_and_send_signal(signal):
key = 'victim badness < min_badness' key = 'victim badness < min_badness'
update_stat_dict_and_print(key) update_stat_dict_and_print(key)
stats_msg = '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' \
'~~~~~~~~~~~~~~~~~~~~~~~~\n\033[4mUptime: {}; correcti' \
've actions:\033[0m'.format(
format_time(time() - start_time))
for key in stat_dict:
stats_msg += '\n- {}: {}'.format(key, stat_dict[key])
print(stats_msg)
sleep_after_send_signal(signal) sleep_after_send_signal(signal)
@ -1481,12 +1443,12 @@ while True:
swap_free <= swap_min_sigkill_kb: swap_free <= swap_min_sigkill_kb:
time0 = time() time0 = time()
mem_info = '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' \ mem_info = '{}\n\033[4mMemory status that r' \
'~~~~~~~~~~~~~~~~~~~~~~~~~~\n\033[4mMemory status that r' \
'equires corrective actions:' \ 'equires corrective actions:' \
'\033[0m\n MemAvailable [{} MiB, {} %] <= mem_min_sig' \ '\033[0m\n MemAvailable [{} MiB, {} %] <= mem_min_sig' \
'kill [{} MiB, {} %]\n SwapFree [{} MiB, {} %] <= swa' \ 'kill [{} MiB, {} %]\n SwapFree [{} MiB, {} %] <= swa' \
'p_min_sigkill [{} MiB, {} %]'.format( 'p_min_sigkill [{} MiB, {} %]'.format(
HR,
kib_to_mib(mem_available), kib_to_mib(mem_available),
percent(mem_available / mem_total), percent(mem_available / mem_total),
kib_to_mib(mem_min_sigkill_kb), kib_to_mib(mem_min_sigkill_kb),
@ -1504,11 +1466,11 @@ while True:
elif mem_used_zram >= zram_max_sigkill_kb: elif mem_used_zram >= zram_max_sigkill_kb:
time0 = time() time0 = time()
mem_info = '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' \ mem_info = '{}\n\033[4mMemory statu' \
'~~~~~~~~~~~~~~~~~~~~~~~~~~\n\033[4mMemory statu' \
's that requires corrective actions:' \ 's that requires corrective actions:' \
'\033[0m\n MemUsedZram [{} MiB, {} %] >= zram_max_sig' \ '\033[0m\n MemUsedZram [{} MiB, {} %] >= zram_max_sig' \
'kill [{} MiB, {} %]'.format( 'kill [{} MiB, {} %]'.format(
HR,
kib_to_mib(mem_used_zram), kib_to_mib(mem_used_zram),
percent(mem_used_zram / mem_total), percent(mem_used_zram / mem_total),
kib_to_mib(zram_max_sigkill_kb), kib_to_mib(zram_max_sigkill_kb),
@ -1524,12 +1486,12 @@ while True:
time0 = time() time0 = time()
mem_info = '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' \ mem_info = '{}\n\033[4mMemory status tha' \
'~~~~~~~~~~~~~~~~~~~~~~~~\n\033[4mMemory status tha' \
't requires corrective actions:' \ 't requires corrective actions:' \
'\033[0m\n MemAvailable [{} MiB, {} %] <= mem_min_sig' \ '\033[0m\n MemAvailable [{} MiB, {} %] <= mem_min_sig' \
'term [{} MiB, {} %]\n SwapFree [{} MiB, {} %] <= swa' \ 'term [{} MiB, {} %]\n SwapFree [{} MiB, {} %] <= swa' \
'p_min_sigterm [{} MiB, {} %]'.format( 'p_min_sigterm [{} MiB, {} %]'.format(
HR,
kib_to_mib(mem_available), kib_to_mib(mem_available),
percent(mem_available / mem_total), percent(mem_available / mem_total),
kib_to_mib(mem_min_sigterm_kb), kib_to_mib(mem_min_sigterm_kb),
@ -1549,11 +1511,11 @@ while True:
elif mem_used_zram >= zram_max_sigterm_kb: elif mem_used_zram >= zram_max_sigterm_kb:
time0 = time() time0 = time()
mem_info = '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' \ mem_info = '{}\n\033[4mMemory status that r' \
'~~~~~~~~~~~~~~~~~~~~~~~~~~\n\033[4mMemory status that r' \
'equires corrective actions:' \ 'equires corrective actions:' \
'\033[0m\n MemUsedZram [{} MiB, {} %] >= ' \ '\033[0m\n MemUsedZram [{} MiB, {} %] >= ' \
'zram_max_sigterm [{} M, {} %]'.format( 'zram_max_sigterm [{} M, {} %]'.format(
HR,
kib_to_mib(mem_used_zram), kib_to_mib(mem_used_zram),
percent(mem_used_zram / mem_total), percent(mem_used_zram / mem_total),
kib_to_mib(zram_max_sigterm_kb), kib_to_mib(zram_max_sigterm_kb),

View File

@ -110,7 +110,7 @@ rate_zram = 1
Valid values are integers from the range [0; 1000]. Valid values are integers from the range [0; 1000].
min_badness = 30 min_badness = 20
Минимальная задержка после отправки соответствующих сигналов Минимальная задержка после отправки соответствующих сигналов
для предотвращения риска убийства сразу множества процессов. для предотвращения риска убийства сразу множества процессов.
@ -132,7 +132,7 @@ min_delay_after_sigkill = 1
Valid values are True and False. Valid values are True and False.
Values are case sensitive. Values are case sensitive.
decrease_oom_score_adj = True decrease_oom_score_adj = False
Valid values are integers from the range [0; 1000]. Valid values are integers from the range [0; 1000].
@ -160,7 +160,7 @@ oom_score_adj_max = 30
Valid values are True and False. Valid values are True and False.
regex_matching = True regex_matching = False
Syntax: Syntax:
@ -184,7 +184,7 @@ regex_matching = True
A good option that allows fine adjustment. A good option that allows fine adjustment.
re_match_cmdline = True re_match_cmdline = False
@CMDLINE_RE 300 /// -childID|--type=renderer @CMDLINE_RE 300 /// -childID|--type=renderer
@ -195,7 +195,7 @@ re_match_cmdline = True
The most slow option The most slow option
re_match_uid = True re_match_uid = False
@UID_RE -100 /// ^0$ @UID_RE -100 /// ^0$
@ -215,7 +215,7 @@ re_match_uid = True
Valid values are True and False. Valid values are True and False.
execute_the_command = True execute_the_command = False
The length of the process name can't exceed 15 characters. The length of the process name can't exceed 15 characters.
The syntax is as follows: lines starting with keyword $ETC are The syntax is as follows: lines starting with keyword $ETC are
@ -233,16 +233,25 @@ execute_the_command = True
If command will contain $PID pattern, this template ($PID) will If command will contain $PID pattern, this template ($PID) will
be replaced by PID of process which name match with RE pattern. be replaced by PID of process which name match with RE pattern.
Exmple: Exmple:
$ETC bash /// kill -KILL $PID $ETC bash /// kill -KILL $PID
It is way to send any signal instead of SIGTERM. It is way to send any signal instead of SIGTERM.
(run `kill -L` to see list of all signals) (run `kill -L` to see list of all signals)
Also $NAME will be replaced by process name. Also $NAME will be replaced by process name.
$ETC bash /// kill -9 $PID $ETC bash /// kill -9 $PID
$ETC firefox-esr /// kill -SEGV $PID $ETC firefox-esr /// kill -SEGV $PID
$ETC tail /// kill -9 $PID
$ETC apache2 /// systemctl restart apache2
##################################################################### #####################################################################
6. GUI notifications: 6. GUI notifications:
@ -258,12 +267,12 @@ $ETC firefox-esr /// kill -SEGV $PID
See also wiki.archlinux.org/index.php/Desktop_notifications See also wiki.archlinux.org/index.php/Desktop_notifications
Valid values are True and False. Valid values are True and False.
gui_notifications = True gui_notifications = False
Enable GUI notifications about the low level of available memory. Enable GUI notifications about the low level of available memory.
Valid values are True and False. Valid values are True and False.
gui_low_memory_warnings = True gui_low_memory_warnings = False
Минимальное время между отправками уведомлений в секундах. Минимальное время между отправками уведомлений в секундах.
Valid values are floating-point numbers from the range [1; 300]. Valid values are floating-point numbers from the range [1; 300].