"Fossies" - the Fresh Open Source Software Archive

Member "stress-ng-0.13.05/core-out-of-memory.c" (11 Oct 2021, 8059 Bytes) of package /linux/privat/stress-ng-0.13.05.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "core-out-of-memory.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.13.04_vs_0.13.05.

    1 /*
    2  * Copyright (C) 2013-2021 Canonical, Ltd.
    3  *
    4  * This program is free software; you can redistribute it and/or
    5  * modify it under the terms of the GNU General Public License
    6  * as published by the Free Software Foundation; either version 2
    7  * of the License, or (at your option) any later version.
    8  *
    9  * This program is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with this program; if not, write to the Free Software
   16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
   17  *
   18  * This code is a complete clean re-write of the stress tool by
   19  * Colin Ian King <colin.king@canonical.com> and attempts to be
   20  * backwardly compatible with the stress tool by Amos Waterland
   21  * <apw@rossby.metr.ou.edu> but has more stress tests and more
   22  * functionality.
   23  *
   24  */
   25 #include "stress-ng.h"
   26 
   27 #if defined(__linux__)
   28 
   29 #define OOM_SCORE_ADJ_MIN   "-1000"
   30 #define OOM_SCORE_ADJ_MAX   "1000"
   31 
   32 #define OOM_ADJ_NO_OOM      "-17"
   33 #define OOM_ADJ_MIN     "-16"
   34 #define OOM_ADJ_MAX     "15"
   35 
   36 /*
   37  *  stress_process_oomed()
   38  *  check if a process has been logged as OOM killed
   39  */
   40 bool stress_process_oomed(const pid_t pid)
   41 {
   42     int fd;
   43     bool oomed = false;
   44 
   45     fd = open("/dev/kmsg", O_RDONLY | O_NONBLOCK);
   46     if (fd < 0)
   47         return oomed;
   48 
   49     for (;;) {
   50         char buf[4096], *ptr;
   51         ssize_t ret;
   52 
   53         ret = read(fd, buf, sizeof(buf) - 1);
   54         if (ret < 0)
   55             break;
   56         buf[ret] = '\0';
   57 
   58         /*
   59          * Look for 'Out of memory: Kill process 22566'
   60          */
   61         ptr = strstr(buf, "process");
   62         if (ptr && (strstr(buf, "Out of memory") ||
   63                 strstr(buf, "oom_reaper"))) {
   64             intmax_t oom_pid;
   65 
   66             if (sscanf(ptr + 7, "%10" SCNdMAX, &oom_pid) == 1) {
   67                 if (oom_pid == (intmax_t)pid) {
   68                     oomed = true;
   69                     break;
   70                 }
   71             }
   72         }
   73     }
   74     (void)close(fd);
   75 
   76     return oomed;
   77 }
   78 
   79 /*
   80  *    stress_set_adjustment()
   81  *  try to set OOM adjustment, retry if EAGAIN or EINTR, give up
   82  *  after multiple retries.  Returns 0 for success, -errno for
   83  *  on failure.
   84  */
   85 static int stress_set_adjustment(const char *procname, const char *name, const char *str)
   86 {
   87     const size_t len = strlen(str);
   88     int i, saved_errno = 0;
   89 
   90     for (i = 0; i < 32; i++) {
   91         ssize_t n;
   92         int fd;
   93 
   94         fd = open(procname, O_WRONLY);
   95         if (fd < 0)
   96             return -errno;
   97 
   98         n = write(fd, str, len);
   99         saved_errno = errno;
  100 
  101         (void)close(fd);
  102         if (n > 0)
  103             return 0;
  104         if (n < 0) {
  105             if ((saved_errno != EAGAIN) &&
  106                 (saved_errno != EINTR)) {
  107                 pr_dbg("%s: can't set oom_score_adj\n", name);
  108                 return -saved_errno;
  109             }
  110         }
  111     }
  112     /* Unexpected failure, report why */
  113     pr_dbg("%s: can't set oom_score_adj, errno=%d (%s)\n", name,
  114         saved_errno, strerror(saved_errno));
  115     return -1;
  116 }
  117 
  118 /*
  119  *  stress_set_oom_adjustment()
  120  *  attempt to stop oom killer
  121  *  if we have root privileges then try and make process
  122  *  unkillable by oom killer
  123  */
  124 void stress_set_oom_adjustment(const char *name, const bool killable)
  125 {
  126     bool high_priv;
  127     bool make_killable = killable;
  128     char *str;
  129     int ret;
  130 
  131     /*
  132      *  --no-oom-adjust option ignores any oom adjustments
  133      */
  134     if (g_opt_flags & OPT_FLAGS_NO_OOM_ADJUST)
  135         return;
  136 
  137     high_priv = (getuid() == 0) && (geteuid() == 0);
  138 
  139 
  140     /*
  141      *  main cannot be killable; if OPT_FLAGS_OOMABLE set make
  142      *  all child procs easily OOMable
  143      */
  144     if (strcmp(name, "main") && (g_opt_flags & OPT_FLAGS_OOMABLE))
  145         make_killable = true;
  146 
  147     /*
  148      *  Try modern oom interface
  149      */
  150     if (make_killable)
  151         str = OOM_SCORE_ADJ_MAX;
  152     else
  153         str = high_priv ? OOM_SCORE_ADJ_MIN : "0";
  154     ret = stress_set_adjustment("/proc/self/oom_score_adj", name, str);
  155     /*
  156      *  Success or some random failure that's not -ENOENT
  157      */
  158     if ((ret == 0) || (ret != -ENOENT))
  159         return;
  160     /*
  161      *  Fall back to old oom interface if we got -ENOENT
  162      */
  163     if (make_killable)
  164         str = high_priv ? OOM_ADJ_NO_OOM : OOM_ADJ_MIN;
  165     else
  166         str = OOM_ADJ_MAX;
  167 
  168     (void)stress_set_adjustment("/proc/self/oom_adj", name, str);
  169 }
  170 #else
  171 void stress_set_oom_adjustment(const char *name, const bool killable)
  172 {
  173     (void)name;
  174     (void)killable;
  175 }
  176 bool stress_process_oomed(const pid_t pid)
  177 {
  178     (void)pid;
  179 
  180     return false;
  181 }
  182 #endif
  183 
  184 /*
  185  *  stress_oomable_child()
  186  *      generic way to run a process that is possibly going to be
  187  *  OOM'd and we retry if it gets killed.
  188  */
  189 int stress_oomable_child(
  190     const stress_args_t *args,
  191     void *context,
  192     stress_oomable_child_func_t func,
  193     const int flag)
  194 {
  195     pid_t pid;
  196     int ooms = 0;
  197     int segvs = 0;
  198     int buserrs = 0;
  199     size_t signal_idx = 0;
  200     const bool not_quiet = !(flag & STRESS_OOMABLE_QUIET);
  201 
  202     /*
  203      *  Kill child multiple times, start with SIGALRM and work up
  204      */
  205     static const int signals[] = {
  206         SIGALRM,
  207         SIGALRM,
  208         SIGALRM,
  209         SIGALRM,
  210         SIGTERM,
  211         SIGKILL
  212     };
  213 
  214 again:
  215     if (!keep_stressing(args))
  216         return EXIT_SUCCESS;
  217     pid = fork();
  218     if (pid < 0) {
  219         /* Keep trying if we are out of resources */
  220         if ((errno == EAGAIN) || (errno == ENOMEM))
  221             goto again;
  222         if (not_quiet)
  223             pr_err("%s: fork failed: errno=%d: (%s)\n",
  224                 args->name, errno, strerror(errno));
  225         return -1;
  226     } else if (pid > 0) {
  227         /* Parent, wait for child */
  228         int status, ret;
  229 
  230         (void)setpgid(pid, g_pgrp);
  231 
  232 rewait:
  233         ret = waitpid(pid, &status, 0);
  234         if (ret < 0) {
  235             /* No longer alive? */
  236             if (errno == ECHILD)
  237                 goto report;
  238             if ((errno != EINTR) && not_quiet)
  239                 pr_dbg("%s: waitpid(): errno=%d (%s)\n",
  240                     args->name, errno, strerror(errno));
  241 
  242             (void)kill(pid, signals[signal_idx]);
  243             if (signal_idx < SIZEOF_ARRAY(signals))
  244                 signal_idx++;
  245             else
  246                 goto report;
  247 
  248             /*
  249              *  First time round do fast re-wait
  250              *  in case child can be reaped quickly,
  251              *  there after do slow backoff on each
  252              *  iteration until we give up and do
  253              *  the final SIGKILL
  254              */
  255             if (signal_idx > 1)
  256                 (void)shim_usleep(500000);
  257             goto rewait;
  258         } else if (WIFSIGNALED(status)) {
  259             if (not_quiet)
  260                 pr_dbg("%s: child died: %s (instance %d)\n",
  261                     args->name, stress_strsignal(WTERMSIG(status)),
  262                     args->instance);
  263             /* Bus error death? retry */
  264             if (WTERMSIG(status) == SIGBUS) {
  265                 buserrs++;
  266                 goto again;
  267             }
  268 
  269             /* If we got killed by OOM killer, re-start */
  270             if ((signals[signal_idx] != SIGKILL) && (WTERMSIG(status) == SIGKILL)) {
  271                 /*
  272                  *  The --oomable flag was enabled, so
  273                  *  the behaviour here is to no longer
  274                  *  retry.  The exit return is EXIT_SUCCESS
  275                  *  because the child is allowed to terminate
  276                  *  by being OOM'd.
  277                  */
  278                 if (g_opt_flags & OPT_FLAGS_OOMABLE) {
  279                     stress_log_system_mem_info();
  280                     if (not_quiet)
  281                         pr_dbg("%s: assuming killed by OOM "
  282                             "killer, bailing out "
  283                             "(instance %d)\n",
  284                             args->name, args->instance);
  285                     return EXIT_SUCCESS;
  286                 } else {
  287                     stress_log_system_mem_info();
  288                     if (not_quiet)
  289                         pr_dbg("%s: assuming killed by OOM "
  290                             "killer, restarting again "
  291                             "(instance %d)\n",
  292                             args->name, args->instance);
  293                     ooms++;
  294                     goto again;
  295                 }
  296             }
  297             /* If we got killed by sigsegv, re-start */
  298             if (WTERMSIG(status) == SIGSEGV) {
  299                 if (not_quiet)
  300                     pr_dbg("%s: killed by SIGSEGV, "
  301                         "restarting again "
  302                         "(instance %d)\n",
  303                         args->name, args->instance);
  304                 segvs++;
  305                 goto again;
  306             }
  307         }
  308     } else if (pid == 0) {
  309         /* Child */
  310 
  311         if (!keep_stressing(args))
  312             _exit(EXIT_SUCCESS);
  313 
  314         (void)setpgid(0, g_pgrp);
  315         stress_parent_died_alarm();
  316 
  317         /* Make sure this is killable by OOM killer */
  318         stress_set_oom_adjustment(args->name, true);
  319 
  320         /* Explicitly drop capabilities, makes it more OOM-able */
  321         if (flag & STRESS_OOMABLE_DROP_CAP) {
  322             int ret;
  323 
  324             ret = stress_drop_capabilities(args->name);
  325             (void)ret;
  326         }
  327         if (!keep_stressing(args))
  328             _exit(EXIT_SUCCESS);
  329         _exit(func(args, context));
  330     }
  331 
  332 report:
  333     if ((ooms + segvs + buserrs > 0) && not_quiet) {
  334         pr_dbg("%s: OOM restarts: %d"
  335             ", SIGSEGV restarts: %d"
  336             ", SIGBUS restarts: %d\n",
  337             args->name, ooms, segvs, buserrs);
  338     }
  339 
  340     return EXIT_SUCCESS;
  341 }