diff --git a/MANPAGE.md b/MANPAGE.md index c7cf7e3..f7a4f1e 100644 --- a/MANPAGE.md +++ b/MANPAGE.md @@ -159,6 +159,9 @@ that might have occurred due to the processes attaining a high oom_score. Use this option with caution as other processes might be sacrificed in place of the ignored processes when earlyoom determines to kill processes. +### \-\-sort-by-rss +find process with the largest rss (default oom_score) + #### \-\-dryrun dry run (do not kill any processes) diff --git a/kill.c b/kill.c index 299c87a..e8f283e 100644 --- a/kill.c +++ b/kill.c @@ -24,6 +24,11 @@ // Processes matching "--avoid REGEX" get BADNESS_AVOID added to their badness #define BADNESS_AVOID -300 +// Processes matching "--prefer REGEX" get VMRSS_PREFER added to their VmRSSkiB +#define VMRSS_PREFER 3145728 +// Processes matching "--avoid REGEX" get VMRSS_AVOID added to their VmRSSkiB +#define VMRSS_AVOID -3145728 + // Buffer size for UID/GID/PID string conversion #define UID_BUFSIZ 128 // At most 1 notification per second when --dryrun is active @@ -289,6 +294,15 @@ bool is_larger(const poll_loop_args_t* args, const procinfo_t* victim, procinfo_ cur->badness = res; } + { + long long res = get_vm_rss_kib(cur->pid); + if (res < 0) { + debug("pid %d: error reading rss: %s\n", cur->pid, strerror((int)-res)); + return false; + } + cur->VmRSSkiB = res; + } + if ((args->prefer_regex || args->avoid_regex || args->ignore_regex)) { int res = get_comm(cur->pid, cur->name, sizeof(cur->name)); if (res < 0) { @@ -296,35 +310,47 @@ bool is_larger(const poll_loop_args_t* args, const procinfo_t* victim, procinfo_ return false; } if (args->prefer_regex && regexec(args->prefer_regex, cur->name, (size_t)0, NULL, 0) == 0) { - cur->badness += BADNESS_PREFER; + if (args->sort_by_rss) { + cur->VmRSSkiB += VMRSS_PREFER; + } else { + cur->badness += BADNESS_PREFER; + } } if (args->avoid_regex && regexec(args->avoid_regex, cur->name, (size_t)0, NULL, 0) == 0) { - cur->badness += BADNESS_AVOID; + if (args->sort_by_rss) { + cur->VmRSSkiB += VMRSS_AVOID; + } else { + cur->badness += BADNESS_AVOID; + } } if (args->ignore_regex && regexec(args->ignore_regex, cur->name, (size_t)0, NULL, 0) == 0) { return false; } } - if (cur->badness < victim->badness) { + if (cur->VmRSSkiB == 0) { + // Kernel threads have zero rss return false; } - { - long long res = get_vm_rss_kib(cur->pid); - if (res < 0) { - debug("pid %d: error reading rss: %s\n", cur->pid, strerror((int)-res)); + if (args->sort_by_rss) { + /* find process with the largest rss */ + if (cur->VmRSSkiB < victim->VmRSSkiB) { return false; } - cur->VmRSSkiB = res; - } - if (cur->VmRSSkiB == 0) { - // Kernel threads have zero rss - return false; - } - if (cur->badness == victim->badness && cur->VmRSSkiB <= victim->VmRSSkiB) { - return false; + if (cur->VmRSSkiB == victim->VmRSSkiB && cur->badness <= victim->badness) { + return false; + } + } else { + /* find process with the largest oom_score */ + if (cur->badness < victim->badness) { + return false; + } + + if (cur->badness == victim->badness && cur->VmRSSkiB <= victim->VmRSSkiB) { + return false; + } } // Skip processes with oom_score_adj = -1000, like the @@ -371,7 +397,7 @@ void debug_print_procinfo(const procinfo_t* cur) } /* - * Find the process with the largest oom_score. + * Find the process with the largest oom_score or rss(when flag --sort-by-rss is set). */ procinfo_t find_largest_process(const poll_loop_args_t* args) { diff --git a/kill.h b/kill.h index 735cc19..a8ddfc3 100644 --- a/kill.h +++ b/kill.h @@ -22,6 +22,8 @@ typedef struct { bool kill_process_group; /* do not kill processes owned by root */ bool ignore_root_user; + /* find process with the largest rss */ + bool sort_by_rss; /* prefer/avoid killing these processes. NULL = no-op. */ regex_t* prefer_regex; regex_t* avoid_regex; diff --git a/main.c b/main.c index 1f2cd5f..8540654 100644 --- a/main.c +++ b/main.c @@ -42,6 +42,7 @@ enum { LONG_OPT_IGNORE, LONG_OPT_IGNORE_ROOT, LONG_OPT_USE_SYSLOG, + LONG_OPT_SORT_BY_RSS, }; static int set_oom_score_adj(int); @@ -89,6 +90,7 @@ int main(int argc, char* argv[]) .swap_kill_percent = 5, .report_interval_ms = 1000, .ignore_root_user = false, + .sort_by_rss = false, /* omitted fields are set to zero */ }; int set_my_priority = 0; @@ -131,6 +133,7 @@ int main(int argc, char* argv[]) { "ignore", required_argument, NULL, LONG_OPT_IGNORE }, { "dryrun", no_argument, NULL, LONG_OPT_DRYRUN }, { "ignore-root-user", no_argument, NULL, LONG_OPT_IGNORE_ROOT }, + { "sort-by-rss", no_argument, NULL, LONG_OPT_SORT_BY_RSS }, { "syslog", no_argument, NULL, LONG_OPT_USE_SYSLOG }, { "help", no_argument, NULL, 'h' }, { 0, 0, NULL, 0 } /* end-of-array marker */ @@ -224,6 +227,10 @@ int main(int argc, char* argv[]) args.ignore_root_user = true; fprintf(stderr, "Processes owned by root will not be killed\n"); break; + case LONG_OPT_SORT_BY_RSS: + args.sort_by_rss = true; + fprintf(stderr, "Find process with the largest rss\n"); + break; case LONG_OPT_PREFER: prefer_cmds = optarg; break; @@ -264,6 +271,7 @@ int main(int argc, char* argv[]) " -p set niceness of earlyoom to -20 and oom_score_adj to\n" " -100\n" " --ignore-root-user do not kill processes owned by root\n" + " --sort-by-rss find process with the largest rss (default oom_score)\n" " --prefer REGEX prefer to kill processes matching REGEX\n" " --avoid REGEX avoid killing processes matching REGEX\n" " --ignore REGEX ignore processes matching REGEX\n" diff --git a/testsuite_cli_test.go b/testsuite_cli_test.go index db6fc2e..a3320b2 100644 --- a/testsuite_cli_test.go +++ b/testsuite_cli_test.go @@ -102,10 +102,11 @@ func TestCli(t *testing.T) { // We use {"-r=0"} instead of {"-r", "0"} so runEarlyoom() can detect that there will be no output {args: []string{"-r=0"}, code: -1, stderrContains: startupMsg, stdoutEmpty: true}, {args: []string{"-r", "0.1"}, code: -1, stderrContains: startupMsg, stdoutContains: memReport}, - // Test --avoid, --prefer and -ignore-root-user + // Test --avoid, --prefer, --ignore-root-user and --sort-by-rss {args: []string{"--avoid", "MyProcess1"}, code: -1, stderrContains: "Will avoid killing", stdoutContains: memReport}, {args: []string{"--prefer", "MyProcess2"}, code: -1, stderrContains: "Preferring to kill", stdoutContains: memReport}, {args: []string{"--ignore-root-user"}, code: -1, stderrContains: "Processes owned by root will not be killed", stdoutContains: memReport}, + {args: []string{"--sort-by-rss"}, code: -1, stderrContains: "Find process with the largest rss", stdoutContains: memReport}, {args: []string{"-i"}, code: -1, stderrContains: "Option -i is ignored"}, // Extra arguments should error out {args: []string{"xyz"}, code: 13, stderrContains: "extra argument not understood", stdoutEmpty: true},