fix unicode decode error

This commit is contained in:
Alexey Avramov 2019-01-23 22:16:27 +09:00
parent a5c2b28ae9
commit f869b0bdbb
3 changed files with 120 additions and 33 deletions

114
nohang
View File

@ -19,7 +19,7 @@ if self_uid == 0:
else: else:
root = False root = False
wait_time = 14 wait_time = 12
max_sleep_time = 2 max_sleep_time = 2
min_sleep_time = 0.1 min_sleep_time = 0.1
@ -29,6 +29,8 @@ 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
########################################################################## ##########################################################################
@ -182,11 +184,12 @@ def rline1(path):
for line in f: for line in f:
return line[:-1] return line[:-1]
'''
def write(path, string): def write(path, string):
"""Write string to path.""" """Write string to path."""
with open(path, 'w') as f: with open(path, 'w') as f:
f.write(string) f.write(string)
'''
def kib_to_mib(num): def kib_to_mib(num):
@ -247,12 +250,18 @@ def pid_to_name(pid):
""" """
try: try:
with open('/proc/' + pid + '/status') as f: with open('/proc/' + pid + '/status') as f:
f.seek(6)
for line in f: for line in f:
return line[:-1].split('\t')[1] return line[:-1]
except FileNotFoundError: except FileNotFoundError:
return '' return ''
except ProcessLookupError: except ProcessLookupError:
return '' return ''
except UnicodeDecodeError:
with open('/proc/' + pid + '/status', 'rb') as f:
f.seek(6)
return f.read(15).decode(
'utf-8', 'ignore').partition('\n')[0]
def pid_to_cmdline(pid): def pid_to_cmdline(pid):
@ -262,22 +271,25 @@ def pid_to_cmdline(pid):
pid: str pid of required process pid: str pid of required process
returns string cmdline returns string cmdline
""" """
with open('/proc/' + pid + '/cmdline') as file: with open('/proc/' + pid + '/cmdline') as f:
try: return f.read().replace('\x00', ' ').rstrip()
return file.readlines()[0].replace('\x00', ' ').strip()
except IndexError:
return ''
def pid_to_uid(pid): def pid_to_uid(pid):
'''return euid''' '''return euid'''
try:
with open('/proc/' + pid + '/status') as f: with open('/proc/' + pid + '/status') as f:
for n, line in enumerate(f): for n, line in enumerate(f):
if n is uid_index: if n is uid_index:
return line.split('\t')[2] return line.split('\t')[2]
except UnicodeDecodeError:
with open('/proc/' + pid + '/status', 'rb') as f:
f_list = f.read().decode('utf-8', 'ignore').split('\n')
return f_list[uid_index].split('\t')[2]
def notify_send_wait(title, body): def notify_send_wait(title, body):
'''GUI notifications with UID != 0'''
with Popen(['notify-send', '--icon=dialog-warning', title, body]) as proc: with Popen(['notify-send', '--icon=dialog-warning', title, body]) as proc:
try: try:
proc.wait(timeout=wait_time) proc.wait(timeout=wait_time)
@ -287,6 +299,7 @@ def notify_send_wait(title, body):
def notify_helper(title, body): def notify_helper(title, body):
'''GUI notification with UID = 0'''
with Popen([notify_helper_path, title, body]) as proc: with Popen([notify_helper_path, title, body]) as proc:
try: try:
proc.wait(timeout=wait_time) proc.wait(timeout=wait_time)
@ -342,8 +355,8 @@ def send_notify(signal, name, pid):
title = 'Preventing OOM' title = 'Preventing OOM'
body = '<b>{}</b> process <b>{}</b>, <b>{}</b>'.format( body = '<b>{}</b> process <b>{}</b>, <b>{}</b>'.format(
notify_sig_dict[signal], pid, name.replace( notify_sig_dict[signal], pid, name.replace(
# сивол & может ломать уведомления в некоторых темах оформления, # symbol '&' can break notifications in some themes,
# поэтому заменяется на * # therefore it is replaced by '*'
'&', '*')) '&', '*'))
if root: if root:
# send notification to all active users with notify-send # send notification to all active users with notify-send
@ -353,6 +366,27 @@ 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.
@ -472,6 +506,7 @@ def find_victim_and_send_signal(signal):
# Get VmRSS and VmSwap and cmdline of victim process and try to send # Get VmRSS and VmSwap and cmdline of victim process and try to send
# signal # signal
try: try:
with open('/proc/' + pid + '/status') as f: with open('/proc/' + pid + '/status') as f:
for n, line in enumerate(f): for n, line in enumerate(f):
@ -516,24 +551,67 @@ def find_victim_and_send_signal(signal):
except FileNotFoundError: except FileNotFoundError:
print(mem_info) print(mem_info)
print('The victim died in the search process') print('The victim died in the search process: FileNotFoundError')
update_stat_dict_and_print('The victim died in the search process') update_stat_dict_and_print('The victim died in the search process: FileNotFoundError')
return None return None
except ProcessLookupError: except ProcessLookupError:
print(mem_info) print(mem_info)
print('The victim died in the search process') print('The victim died in the search process: ProcessLookupError')
update_stat_dict_and_print('The victim died in the search process') update_stat_dict_and_print('The victim died in the search process: ProcessLookupError')
return None return None
except UnicodeDecodeError:
# тут надо снова все исключ обработать
with open('/proc/' + pid + '/status', 'rb') as f:
f_list = f.read().decode('utf-8', 'ignore').split('\n')
for i in range(len(f_list)):
if i is uid_index:
uid = f_list[i].split('\t')[2]
if i is vm_size_index:
vm_size = kib_to_mib(int(f_list[i].split('\t')[1][:-3]))
if i is vm_rss_index:
vm_rss = kib_to_mib(int(f_list[i].split('\t')[1][:-3]))
if detailed_rss:
if i is anon_index:
anon_rss = kib_to_mib(
int(f_list[i].split('\t')[1][:-3]))
if i is file_index:
file_rss = kib_to_mib(
int(f_list[i].split('\t')[1][:-3]))
if i is shmem_index:
shmem_rss = kib_to_mib(
int(f_list[i].split('\t')[1][:-3]))
if i is vm_swap_index:
vm_swap = kib_to_mib(int(f_list[i].split('\t')[1][:-3]))
with open('/proc/' + pid + '/cmdline') as file:
cmdline = file.readlines()[0].replace('\x00', ' ')
oom_score = rline1('/proc/' + pid + '/oom_score')
oom_score_adj = rline1('/proc/' + pid + '/oom_score_adj')
except IndexError: except IndexError:
print(mem_info) print(mem_info)
print('The victim died in the search process') print('The victim died in the search process: IndexError')
update_stat_dict_and_print('The victim died in the search process') update_stat_dict_and_print('The victim died in the search process: IndexError')
return None return None
except ValueError: except ValueError:
print(mem_info) print(mem_info)
print('The victim died in the search process') print('The victim died in the search process: ValueError')
update_stat_dict_and_print('The victim died in the search process') update_stat_dict_and_print('The victim died in the search process: ValueError')
return None return None
len_vm = len(str(vm_size)) len_vm = len(str(vm_size))
if detailed_rss: if detailed_rss:

View File

@ -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 = False decrease_oom_score_adj = True
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 = False regex_matching = True
Syntax: Syntax:
@ -184,7 +184,7 @@ regex_matching = False
A good option that allows fine adjustment. A good option that allows fine adjustment.
re_match_cmdline = False re_match_cmdline = True
@CMDLINE_RE 300 /// -childID|--type=renderer @CMDLINE_RE 300 /// -childID|--type=renderer
@ -195,7 +195,7 @@ re_match_cmdline = False
The most slow option The most slow option
re_match_uid = False re_match_uid = True
@UID_RE -100 /// ^0$ @UID_RE -100 /// ^0$
@ -215,7 +215,7 @@ re_match_uid = False
Valid values are True and False. Valid values are True and False.
execute_the_command = False execute_the_command = True
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
@ -240,7 +240,7 @@ execute_the_command = False
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
##################################################################### #####################################################################
@ -258,12 +258,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 = False gui_notifications = True
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 = False gui_low_memory_warnings = True
Минимальное время между отправками уведомлений в секундах. Минимальное время между отправками уведомлений в секундах.
Valid values are floating-point numbers from the range [1; 300]. Valid values are floating-point numbers from the range [1; 300].

View File

@ -3,7 +3,7 @@
# Usage: # Usage:
# ./nohang_notify_helper "title" "body" # ./nohang_notify_helper "title" "body"
from sys import argv from sys import argv, stdout
from os import listdir from os import listdir
from subprocess import Popen, TimeoutExpired from subprocess import Popen, TimeoutExpired
@ -93,10 +93,17 @@ def root_notify_env():
list_with_envs = root_notify_env() list_with_envs = root_notify_env()
list_len = len(list_with_envs)
# if somebody logged in with GUI # if somebody logged in with GUI
if len(list_with_envs) > 0: if list_len > 0:
for i in list_with_envs:
print('Send GUI notification:', argv[1], argv[2], i)
stdout.flush()
# iterating over logged-in users # iterating over logged-in users
for i in list_with_envs: for i in list_with_envs:
username, display_env, dbus_env = i[0], i[1], i[2] username, display_env, dbus_env = i[0], i[1], i[2]
@ -118,4 +125,6 @@ if len(list_with_envs) > 0:
proc.kill() proc.kill()
print('TimeoutExpired: notify user:' + username) print('TimeoutExpired: notify user:' + username)
else: else:
print('Nobody logged-in with GUI. Nothing to do.') print('Not send GUI notification: [', argv[1], argv[2], ']. Nobody logged-in with GUI. Nothing to do.')
stdout.flush()