diff --git a/README.md b/README.md index 670029e..97a03f4 100644 --- a/README.md +++ b/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): - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## 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 diff --git a/nohang b/nohang index 2215339..db7ce85 100755 --- a/nohang +++ b/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: - if os.path.exists('./nohang.conf'): - config = os.getcwd() + '/nohang.conf' - else: - config = '/etc/nohang/nohang.conf' +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) ########################################################################## diff --git a/nohang.1 b/nohang.1 index 08533a5..9274593 100644 --- a/nohang.1 +++ b/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.