add --check-config
This commit is contained in:
parent
cfea9eef8a
commit
35a7e03db9
40
README.md
40
README.md
@ -120,7 +120,7 @@ $ sudo systemctl enable nohang
|
||||
|
||||
```
|
||||
./nohang -h
|
||||
usage: nohang [-h] [-v] [-t] [-p] [-c CONFIG]
|
||||
usage: nohang [-h] [-v] [-p] [-c CONFIG]
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
@ -130,6 +130,8 @@ optional arguments:
|
||||
-c CONFIG, --config CONFIG
|
||||
path to the config file, default values:
|
||||
./nohang.conf, /etc/nohang/nohang.conf
|
||||
-cc CONFIG, --check-config CONFIG
|
||||
ckeck and print config
|
||||
```
|
||||
|
||||
## How to configure nohang
|
||||
@ -274,34 +276,6 @@ Process with highest badness (found in 55 ms):
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Logging
|
||||
|
||||
To view the latest entries in the log (for systemd users):
|
||||
@ -314,14 +288,6 @@ See also `man journalctl`.
|
||||
You can also enable `separate_log` in the config to logging in `/var/log/nohang/nohang.log`.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Additional tools: oom-sort, psi-top, psi-monitor, i-memhog
|
||||
|
||||
|
||||
|
344
nohang
344
nohang
@ -16,6 +16,180 @@ from signal import signal, SIGKILL, SIGTERM, SIGINT, SIGQUIT, SIGHUP
|
||||
# define functions
|
||||
|
||||
|
||||
|
||||
|
||||
def check_config():
|
||||
"""
|
||||
"""
|
||||
|
||||
log('#' * 79)
|
||||
|
||||
log('0. Common zram settings')
|
||||
|
||||
log(' ignore_zram: {}'.format(ignore_zram))
|
||||
|
||||
log('1. Thresholds below which a signal should be sent to the victim')
|
||||
|
||||
log(' mem_min_sigterm: {} MiB, {} %'.format(
|
||||
round(mem_min_sigterm_mb), round(mem_min_sigterm_percent, 1)))
|
||||
log(' mem_min_sigkill: {} MiB, {} %'.format(
|
||||
round(mem_min_sigkill_mb), round(mem_min_sigkill_percent, 1)))
|
||||
|
||||
log(' swap_min_sigterm: {}'.format(swap_min_sigterm))
|
||||
log(' swap_min_sigkill: {}'.format(swap_min_sigkill))
|
||||
|
||||
log(' zram_max_sigterm: {} MiB, {} %'.format(
|
||||
round(zram_max_sigterm_mb), round(zram_max_sigterm_percent, 1)))
|
||||
log(' zram_max_sigkill: {} MiB, {} %'.format(
|
||||
round(zram_max_sigkill_mb), round(zram_max_sigkill_percent, 1)))
|
||||
|
||||
log('2. Response on PSI memory metrics')
|
||||
|
||||
log(' ignore_psi: {}'.format(ignore_psi))
|
||||
log(' psi_path: {}'.format(psi_path))
|
||||
log(' psi_metrics: {}'.format(psi_metrics))
|
||||
log(' sigterm_psi_threshold: {}'.format(sigterm_psi_threshold))
|
||||
log(' sigkill_psi_threshold: {}'.format(sigkill_psi_threshold))
|
||||
log(' psi_excess_duration: {} sec'.format(psi_excess_duration))
|
||||
log(' psi_post_action_delay: {} sec'.format(psi_post_action_delay))
|
||||
|
||||
log('3. The frequency of checking the amount of available memory')
|
||||
|
||||
log(' rate_mem: {}'.format(rate_mem))
|
||||
log(' rate_swap: {}'.format(rate_swap))
|
||||
log(' rate_zram: {}'.format(rate_zram))
|
||||
log(' max_sleep: {} sec'.format(max_sleep))
|
||||
log(' min_sleep: {} sec'.format(min_sleep))
|
||||
log(' over_sleep: {} sec'.format(over_sleep))
|
||||
|
||||
log('4. The prevention of killing innocent victims')
|
||||
|
||||
log(' min_badness: {}'.format(min_badness))
|
||||
log(' min_delay_after_sigterm: {} sec'.format(min_delay_after_sigterm))
|
||||
log(' post_zombie_delay: {} sec'.format(post_zombie_delay))
|
||||
log(' victim_cache_time: {} sec'.format(victim_cache_time))
|
||||
log(' decrease_oom_score_adj: {}'.format(decrease_oom_score_adj))
|
||||
log(' oom_score_adj_max: {}'.format(oom_score_adj_max))
|
||||
|
||||
log('5. Impact on the badness of processes')
|
||||
|
||||
log('5.1. Matching process names with RE patterns')
|
||||
if len(badness_adj_re_name_list) > 0:
|
||||
log(' regexp: badness_adj:')
|
||||
for i in badness_adj_re_name_list:
|
||||
log(' {} {}'.format(i[1], i[0]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('5.2. Matching CGroup_v1-line with RE patterns')
|
||||
if len(badness_adj_re_cgroup_v1_list) > 0:
|
||||
log(' regexp: badness_adj:')
|
||||
for i in badness_adj_re_cgroup_v1_list:
|
||||
log(' {} {}'.format(i[1], i[0]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('5.3. Matching CGroup_v2-line with RE patterns')
|
||||
if len(badness_adj_re_cgroup_v2_list) > 0:
|
||||
log(' regexp: badness_adj:')
|
||||
for i in badness_adj_re_cgroup_v1_list:
|
||||
log(' {} {}'.format(i[1], i[0]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('5.4. Matching eUIDs with RE patterns')
|
||||
if len(badness_adj_re_cgroup_v2_list) > 0:
|
||||
log(' regexp: badness_adj:')
|
||||
for i in badness_adj_re_uid_list:
|
||||
log(' {} {}'.format(i[1], i[0]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('5.5. Matching realpath with RE patterns')
|
||||
if len(badness_adj_re_cgroup_v2_list) > 0:
|
||||
log(' regexp: badness_adj:')
|
||||
for i in badness_adj_re_realpath_list:
|
||||
log(' {} {}'.format(i[1], i[0]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('5.6. Matching cmdlines with RE patterns')
|
||||
if len(badness_adj_re_cgroup_v2_list) > 0:
|
||||
log(' regexp: badness_adj:')
|
||||
for i in badness_adj_re_cmdline_list:
|
||||
log(' {} {}'.format(i[1], i[0]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('5.7. Matching environ with RE patterns')
|
||||
if len(badness_adj_re_cgroup_v2_list) > 0:
|
||||
log(' regexp: badness_adj:')
|
||||
for i in badness_adj_re_environ_list:
|
||||
log(' {} {}'.format(i[1], i[0]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('6. Customize corrective actions')
|
||||
|
||||
if len(soft_actions_list) > 0:
|
||||
log(' Match by: regexp: command: ')
|
||||
for i in soft_actions_list:
|
||||
log(' {} {} {}'.format(i[0], i[1], i[2]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('7. GUI notifications')
|
||||
|
||||
log(' gui_notifications: {}'.format(gui_notifications))
|
||||
log(' gui_low_memory_warnings: {}'.format(gui_low_memory_warnings))
|
||||
log(' warning_exe: {}'.format(warning_exe))
|
||||
log(' mem_min_warnings: {} MiB, {} %'.format(
|
||||
round(mem_min_warnings_mb), round(mem_min_warnings_percent, 1)))
|
||||
log(' swap_min_warnings: {}'.format(swap_min_warnings))
|
||||
log(' zram_max_warnings: {} MiB, {} %'.format(
|
||||
round(zram_max_warnings_mb), round(zram_max_warnings_percent, 1)))
|
||||
log(' psi_avg_warnings: {}'.format(psi_avg_warnings))
|
||||
log(' min_time_between_warnings: {} sec'.format(
|
||||
min_time_between_warnings))
|
||||
|
||||
log('8. Verbosity')
|
||||
|
||||
log(' print_config: {}'.format(print_config))
|
||||
log(' print_mem_check_results: {}'.format(print_mem_check_results))
|
||||
log(' min_mem_report_interval: {} sec'.format(min_mem_report_interval))
|
||||
log(' print_sleep_periods: {}'.format(print_sleep_periods))
|
||||
log(' print_total_stat: {}'.format(print_total_stat))
|
||||
log(' print_proc_table: {}'.format(print_proc_table))
|
||||
log(' extra_table_info: {}'.format(extra_table_info))
|
||||
log(' print_victim_info: {}'.format(print_victim_info))
|
||||
log(' print_victim_cmdline: {}'.format(print_victim_cmdline))
|
||||
log(' max_ancestry_depth: {}'.format(max_ancestry_depth))
|
||||
log(' debug_gui_notifications: {}'.format(debug_gui_notifications))
|
||||
log(' separate_log: {}'.format(separate_log))
|
||||
log(' psi_debug: {}'.format(psi_debug))
|
||||
|
||||
log('9. Misc')
|
||||
|
||||
log(' max_post_sigterm_victim_lifetime: {} sec'.format(
|
||||
max_post_sigterm_victim_lifetime))
|
||||
log(' post_kill_exe: {}'.format(post_kill_exe))
|
||||
log(' forbid_negative_badness: {}'.format(
|
||||
forbid_negative_badness))
|
||||
|
||||
# log(': {}'.format())
|
||||
log('#' * 79)
|
||||
|
||||
if check_config_flag:
|
||||
log('config is OK')
|
||||
exit()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def encoder(string):
|
||||
"""
|
||||
"""
|
||||
@ -2032,7 +2206,7 @@ v_dict = dict()
|
||||
start_time = time()
|
||||
|
||||
|
||||
help_mess = """usage: nohang [-h] [-v] [-t] [-p] [-c CONFIG]
|
||||
help_mess = """usage: nohang [-h] [-v] [-p] [-c CONFIG] [-cc CONFIG]
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
@ -2041,7 +2215,9 @@ optional arguments:
|
||||
print table of processes with their badness values
|
||||
-c CONFIG, --config CONFIG
|
||||
path to the config file, default values:
|
||||
./nohang.conf, /etc/nohang/nohang.conf"""
|
||||
./nohang.conf, /etc/nohang/nohang.conf
|
||||
-cc CONFIG, --check-config CONFIG
|
||||
ckeck and print config"""
|
||||
|
||||
|
||||
SC_CLK_TCK = os.sysconf(os.sysconf_names['SC_CLK_TCK'])
|
||||
@ -2098,16 +2274,26 @@ pid_list = get_pid_list()
|
||||
|
||||
print_proc_table_flag = False
|
||||
|
||||
if len(argv) == 1:
|
||||
check_config_flag = False
|
||||
|
||||
|
||||
|
||||
if os.path.exists('./nohang.conf'):
|
||||
config = os.getcwd() + '/nohang.conf'
|
||||
else:
|
||||
config = '/etc/nohang/nohang.conf'
|
||||
|
||||
|
||||
|
||||
|
||||
if len(argv) == 1:
|
||||
pass
|
||||
elif len(argv) == 2:
|
||||
if argv[1] == '--help' or argv[1] == '-h':
|
||||
print(help_mess)
|
||||
exit()
|
||||
elif argv[1] == '--check-config' or argv[1] == '-cc':
|
||||
check_config_flag = True
|
||||
elif argv[1] == '--version' or argv[1] == '-v':
|
||||
print_version()
|
||||
elif argv[1] == '--print-proc-table' or argv[1] == '-p':
|
||||
@ -2119,14 +2305,15 @@ elif len(argv) == 2:
|
||||
else:
|
||||
errprint('Unknown option: {}'.format(argv[1]))
|
||||
exit(1)
|
||||
|
||||
elif len(argv) == 3:
|
||||
if argv[1] == '--config' or argv[1] == '-c':
|
||||
config = argv[2]
|
||||
elif argv[1] == '--check-config' or argv[1] == '-cc':
|
||||
config = argv[2]
|
||||
check_config_flag = True
|
||||
else:
|
||||
errprint('Unknown option: {}'.format(argv[1]))
|
||||
exit(1)
|
||||
|
||||
else:
|
||||
errprint('Invalid CLI input: too many options')
|
||||
exit(1)
|
||||
@ -2179,7 +2366,7 @@ except ValueError:
|
||||
# print('It is not Linux 4.5+')
|
||||
|
||||
|
||||
log('Config: ' + config)
|
||||
log('config: ' + config)
|
||||
|
||||
|
||||
##########################################################################
|
||||
@ -2888,164 +3075,23 @@ else:
|
||||
|
||||
##########################################################################
|
||||
|
||||
if print_config:
|
||||
|
||||
log('#' * 79)
|
||||
|
||||
log('0. Common zram settings')
|
||||
|
||||
log(' ignore_zram: {}'.format(ignore_zram))
|
||||
|
||||
log('1. Thresholds below which a signal should be sent to the victim')
|
||||
|
||||
log(' mem_min_sigterm: {} MiB, {} %'.format(
|
||||
round(mem_min_sigterm_mb), round(mem_min_sigterm_percent, 1)))
|
||||
log(' mem_min_sigkill: {} MiB, {} %'.format(
|
||||
round(mem_min_sigkill_mb), round(mem_min_sigkill_percent, 1)))
|
||||
|
||||
log(' swap_min_sigterm: {}'.format(swap_min_sigterm))
|
||||
log(' swap_min_sigkill: {}'.format(swap_min_sigkill))
|
||||
|
||||
log(' zram_max_sigterm: {} MiB, {} %'.format(
|
||||
round(zram_max_sigterm_mb), round(zram_max_sigterm_percent, 1)))
|
||||
log(' zram_max_sigkill: {} MiB, {} %'.format(
|
||||
round(zram_max_sigkill_mb), round(zram_max_sigkill_percent, 1)))
|
||||
if print_config or check_config_flag:
|
||||
check_config()
|
||||
|
||||
log('2. Response on PSI memory metrics')
|
||||
|
||||
log(' ignore_psi: {}'.format(ignore_psi))
|
||||
log(' psi_path: {}'.format(psi_path))
|
||||
log(' psi_metrics: {}'.format(psi_metrics))
|
||||
log(' sigterm_psi_threshold: {}'.format(sigterm_psi_threshold))
|
||||
log(' sigkill_psi_threshold: {}'.format(sigkill_psi_threshold))
|
||||
log(' psi_excess_duration: {} sec'.format(psi_excess_duration))
|
||||
log(' psi_post_action_delay: {} sec'.format(psi_post_action_delay))
|
||||
|
||||
log('3. The frequency of checking the amount of available memory')
|
||||
|
||||
log(' rate_mem: {}'.format(rate_mem))
|
||||
log(' rate_swap: {}'.format(rate_swap))
|
||||
log(' rate_zram: {}'.format(rate_zram))
|
||||
log(' max_sleep: {} sec'.format(max_sleep))
|
||||
log(' min_sleep: {} sec'.format(min_sleep))
|
||||
log(' over_sleep: {} sec'.format(over_sleep))
|
||||
|
||||
log('4. The prevention of killing innocent victims')
|
||||
|
||||
log(' min_badness: {}'.format(min_badness))
|
||||
log(' min_delay_after_sigterm: {} sec'.format(min_delay_after_sigterm))
|
||||
log(' post_zombie_delay: {} sec'.format(post_zombie_delay))
|
||||
log(' victim_cache_time: {} sec'.format(victim_cache_time))
|
||||
log(' decrease_oom_score_adj: {}'.format(decrease_oom_score_adj))
|
||||
log(' oom_score_adj_max: {}'.format(oom_score_adj_max))
|
||||
|
||||
log('5. Impact on the badness of processes')
|
||||
|
||||
log('5.1. Matching process names with RE patterns')
|
||||
if len(badness_adj_re_name_list) > 0:
|
||||
log(' regexp: badness_adj:')
|
||||
for i in badness_adj_re_name_list:
|
||||
log(' {} {}'.format(i[1], i[0]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('5.2. Matching CGroup_v1-line with RE patterns')
|
||||
if len(badness_adj_re_cgroup_v1_list) > 0:
|
||||
log(' regexp: badness_adj:')
|
||||
for i in badness_adj_re_cgroup_v1_list:
|
||||
log(' {} {}'.format(i[1], i[0]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('5.3. Matching CGroup_v2-line with RE patterns')
|
||||
if len(badness_adj_re_cgroup_v2_list) > 0:
|
||||
log(' regexp: badness_adj:')
|
||||
for i in badness_adj_re_cgroup_v1_list:
|
||||
log(' {} {}'.format(i[1], i[0]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('5.4. Matching eUIDs with RE patterns')
|
||||
if len(badness_adj_re_cgroup_v2_list) > 0:
|
||||
log(' regexp: badness_adj:')
|
||||
for i in badness_adj_re_uid_list:
|
||||
log(' {} {}'.format(i[1], i[0]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('5.5. Matching realpath with RE patterns')
|
||||
if len(badness_adj_re_cgroup_v2_list) > 0:
|
||||
log(' regexp: badness_adj:')
|
||||
for i in badness_adj_re_realpath_list:
|
||||
log(' {} {}'.format(i[1], i[0]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('5.6. Matching cmdlines with RE patterns')
|
||||
if len(badness_adj_re_cgroup_v2_list) > 0:
|
||||
log(' regexp: badness_adj:')
|
||||
for i in badness_adj_re_cmdline_list:
|
||||
log(' {} {}'.format(i[1], i[0]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('5.7. Matching environ with RE patterns')
|
||||
if len(badness_adj_re_cgroup_v2_list) > 0:
|
||||
log(' regexp: badness_adj:')
|
||||
for i in badness_adj_re_environ_list:
|
||||
log(' {} {}'.format(i[1], i[0]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('6. Customize corrective actions')
|
||||
|
||||
if len(soft_actions_list) > 0:
|
||||
log(' Match by: regexp: command: ')
|
||||
for i in soft_actions_list:
|
||||
log(' {} {} {}'.format(i[0], i[1], i[2]))
|
||||
else:
|
||||
log(' (not set)')
|
||||
|
||||
log('7. GUI notifications')
|
||||
|
||||
log(' gui_notifications: {}'.format(gui_notifications))
|
||||
log(' gui_low_memory_warnings: {}'.format(gui_low_memory_warnings))
|
||||
log(' warning_exe: {}'.format(warning_exe))
|
||||
log(' mem_min_warnings: {} MiB, {} %'.format(
|
||||
round(mem_min_warnings_mb), round(mem_min_warnings_percent, 1)))
|
||||
log(' swap_min_warnings: {}'.format(swap_min_warnings))
|
||||
log(' zram_max_warnings: {} MiB, {} %'.format(
|
||||
round(zram_max_warnings_mb), round(zram_max_warnings_percent, 1)))
|
||||
log(' psi_avg_warnings: {}'.format(psi_avg_warnings))
|
||||
log(' min_time_between_warnings: {} sec'.format(
|
||||
min_time_between_warnings))
|
||||
|
||||
log('8. Verbosity')
|
||||
|
||||
log(' print_config: {}'.format(print_config))
|
||||
log(' print_mem_check_results: {}'.format(print_mem_check_results))
|
||||
log(' min_mem_report_interval: {} sec'.format(min_mem_report_interval))
|
||||
log(' print_sleep_periods: {}'.format(print_sleep_periods))
|
||||
log(' print_total_stat: {}'.format(print_total_stat))
|
||||
log(' print_proc_table: {}'.format(print_proc_table))
|
||||
log(' extra_table_info: {}'.format(extra_table_info))
|
||||
log(' print_victim_info: {}'.format(print_victim_info))
|
||||
log(' print_victim_cmdline: {}'.format(print_victim_cmdline))
|
||||
log(' max_ancestry_depth: {}'.format(max_ancestry_depth))
|
||||
log(' debug_gui_notifications: {}'.format(debug_gui_notifications))
|
||||
log(' separate_log: {}'.format(separate_log))
|
||||
log(' psi_debug: {}'.format(psi_debug))
|
||||
|
||||
log('9. Misc')
|
||||
|
||||
log(' max_post_sigterm_victim_lifetime: {} sec'.format(
|
||||
max_post_sigterm_victim_lifetime))
|
||||
log(' post_kill_exe: {}'.format(post_kill_exe))
|
||||
log(' forbid_negative_badness: {}'.format(
|
||||
forbid_negative_badness))
|
||||
|
||||
# log(': {}'.format())
|
||||
log('#' * 79)
|
||||
|
||||
|
||||
##########################################################################
|
||||
|
3
nohang.1
3
nohang.1
@ -45,6 +45,9 @@ Nohang is a highly configurable daemon for Linux which is able to correctly prev
|
||||
path to the config file, default values:
|
||||
./nohang.conf, /etc/nohang/nohang.conf
|
||||
|
||||
-cc CONFIG, --check-config CONFIG
|
||||
ckeck and print config
|
||||
|
||||
.SH HOW TO CONFIGURE
|
||||
|
||||
The program can be configured by editing the config file.
|
||||
|
Loading…
Reference in New Issue
Block a user