"Fossies" - the Fresh Open Source Software Archive

Member "stress-ng-0.13.05/stress-uprobe.c" (11 Oct 2021, 7169 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.

    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 static const stress_help_t help[] = {
   28     { NULL, "uprobe N", "start N workers that generate uprobe events" },
   29     { NULL, "uprobe-ops N", "stop after N uprobe events" },
   30     { NULL, NULL,       NULL }
   31 };
   32 
   33 /*
   34  *  stress_uprobe_supported()
   35  *      check if we can run this with CAP_SYS_ADMIN capability
   36  */
   37 static int stress_uprobe_supported(const char *name)
   38 {
   39 #if defined(__linux__)
   40     if (!stress_check_capability(SHIM_CAP_SYS_ADMIN)) {
   41         pr_inf_skip("%s stressor will be skipped, "
   42             "need to be running with CAP_SYS_ADMIN "
   43             "rights for this stressor\n", name);
   44         return -1;
   45     }
   46     return 0;
   47 #else
   48     pr_inf_skip("%s: stressor will be skipped, uprobe not available\n", name);
   49     return -1;
   50 #endif
   51 }
   52 
   53 #if defined(__linux__)
   54 #define X_STR_(x) #x
   55 #define X_STR(x) X_STR_(x)
   56 
   57 /*
   58  *  stress_uprobe_write
   59  *     write to a uprobe sysfs file a string, open using flags setting in flags
   60  */
   61 static int stress_uprobe_write(const char *path, int flags, const char *str)
   62 {
   63     int fd, rc = 0;
   64 
   65     fd = open(path, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
   66     if (fd < 0)
   67         return -errno;
   68     errno = 0;
   69     if (write(fd, str, strlen(str)) < 0)
   70         rc = -errno;
   71 
   72     (void)close(fd);
   73 
   74     return rc;
   75 }
   76 
   77 /*
   78  *  stress_uprobe_libc_start()
   79  *  find start address of libc text segment by scanning /proc/$PID/maps
   80  */
   81 static void *stress_uprobe_libc_start(const pid_t pid, char *libc_path)
   82 {
   83     char path[PATH_MAX], perm[5], buf[1024];
   84     FILE *fp;
   85     uint64_t start, end, offset, dev_major, dev_minor, inode;
   86     void *addr = NULL;
   87 
   88     (void)snprintf(path, sizeof(path), "/proc/%d/maps", (int)pid);
   89     fp = fopen(path, "r");
   90     if (!fp)
   91         return addr;
   92 
   93     while (fgets(buf, sizeof(buf), fp)) {
   94         int n;
   95 
   96         n = sscanf(buf, "%" SCNx64 "-%" SCNx64 "%4s %" SCNx64 " %" SCNx64
   97             ":%" SCNx64 " %" SCNu64 "%" X_STR(PATH_MAX) "s\n",
   98             &start, &end, perm, &offset, &dev_major, &dev_minor,
   99             &inode, libc_path);
  100 
  101         /*
  102          *  name /libc-*.so or /libc.so found?
  103          */
  104         if ((n == 8) && !strncmp(perm, "r-xp", 4) &&
  105             strstr(libc_path, ".so")) {
  106             if (strstr(libc_path, "/libc-") ||
  107                 strstr(libc_path, "/libc.so")) {
  108                 addr = (void *)(intptr_t)(start - offset);
  109                 break;
  110             }
  111         }
  112     }
  113     (void)fclose(fp);
  114 
  115     return addr;
  116 }
  117 
  118 /*
  119  *  stress_uprobe()
  120  *  stress uprobe events
  121  */
  122 static int stress_uprobe(const stress_args_t *args)
  123 {
  124     char buf[PATH_MAX + 256], libc_path[PATH_MAX];
  125     int ret;
  126     char event[128];
  127     ptrdiff_t offset;
  128     void *libc_addr;
  129     int rc = EXIT_SUCCESS;
  130     int fd;
  131     pid_t pid = getpid();
  132 
  133     libc_addr = stress_uprobe_libc_start(pid, libc_path);
  134     if (!libc_addr) {
  135         if (args->instance == 0)
  136             pr_inf_skip("%s: cannot find start of libc text section, skipping stressor\n",
  137                 args->name);
  138         return EXIT_NO_RESOURCE;
  139     }
  140     offset = ((char *)getpid - (char *)libc_addr);
  141 
  142     /* Make unique event name */
  143     (void)snprintf(event, sizeof(event), "stressngprobe%d%" PRIu32,
  144         getpid(), args->instance);
  145 
  146     ret = stress_uprobe_write("/sys/kernel/debug/tracing/current_tracer",
  147         O_WRONLY | O_CREAT | O_TRUNC, "nop\n");
  148     (void)ret;
  149     (void)snprintf(buf, sizeof(buf), "p:%s %s:%p\n", event, libc_path, (void *)offset);
  150     ret = stress_uprobe_write("/sys/kernel/debug/tracing/uprobe_events",
  151         O_WRONLY | O_CREAT | O_APPEND, buf);
  152     if (ret < 0) {
  153         pr_inf_skip("%s: cannot set uprobe_event: errno=%d (%s), skipping stressor\n",
  154             args->name, errno, strerror(errno));
  155         return EXIT_NO_RESOURCE;
  156     }
  157 
  158     /* Enable tracing */
  159     (void)snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/events/uprobes/%s/enable", event);
  160     ret = stress_uprobe_write(buf, O_WRONLY | O_CREAT | O_TRUNC, "1\n");
  161     if (ret < 0) {
  162         pr_inf_skip("%s: cannot enable uprobe_event: errno=%d (%s), skipping stressor\n",
  163             args->name, errno, strerror(errno));
  164         rc = EXIT_NO_RESOURCE;
  165         goto clear_events;
  166     }
  167     ret = stress_uprobe_write("/sys/kernel/debug/tracing/trace",
  168         O_WRONLY | O_CREAT | O_TRUNC, "\n");
  169     if (ret < 0) {
  170         pr_inf_skip("%s: cannot clear trace file, errno=%d (%s), skipping stressor\n",
  171             args->name, errno, strerror(errno));
  172         rc = EXIT_NO_RESOURCE;
  173         goto clear_events;
  174     }
  175 
  176     fd = open("/sys/kernel/debug/tracing/trace_pipe", O_RDONLY);
  177     if (fd < 0) {
  178         pr_inf_skip("%s: cannot open trace file: errno=%d (%s), skipping stressor\n",
  179             args->name, errno, strerror(errno));
  180         rc = EXIT_NO_RESOURCE;
  181         goto clear_events;
  182     }
  183 
  184     stress_set_proc_state(args->name, STRESS_STATE_RUN);
  185 
  186     do {
  187         /*
  188          *  Generate trace events on each stress_get_cpu call
  189          */
  190         int i;
  191         fd_set rfds;
  192         struct timeval tv;
  193 
  194         /* Generate some events */
  195         for (i = 0; i < 64; i++) {
  196             getpid();
  197         }
  198 
  199         while (keep_stressing(args)) {
  200             char data[4096];
  201             ssize_t n;
  202             char *ptr;
  203 
  204             FD_ZERO(&rfds);
  205             FD_SET(fd, &rfds);
  206 
  207             tv.tv_sec = 0;
  208             tv.tv_usec = 1000;
  209             ret = select(fd + 1, &rfds, NULL, NULL, &tv);
  210 
  211             if (ret <= 0)
  212                 break;
  213 
  214             n = read(fd, data, sizeof(data));
  215             if (n <= 0)
  216                 break;
  217 
  218             /*
  219              *  Quick and dirty ubprobe event parsing,
  220              *  this will undercount when text crosses
  221              *  a read boundary, however, setting the read
  222              *  size to be ~4K means we should always fill
  223              *  the buffer and not get any misses.
  224              */
  225             ptr = data;
  226             do {
  227                 ptr = strstr(ptr, event);
  228                 if (!ptr)
  229                     break;
  230                 ptr++;
  231                 inc_counter(args);
  232                 if (!keep_stressing(args))
  233                     goto terminate;
  234             } while (ptr < data + sizeof(data));
  235         }
  236     } while (keep_stressing(args));
  237 
  238 terminate:
  239     stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
  240 
  241     (void)close(fd);
  242     /* Stop events */
  243     ret = stress_uprobe_write("/sys/kernel/debug/tracing/events/uprobes/enable",
  244         O_WRONLY, "0\n");
  245     (void)ret;
  246 
  247 clear_events:
  248     stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
  249 
  250     /* Remove uprobe */
  251     snprintf(buf, sizeof(buf), "-:%s\n", event);
  252     ret = stress_uprobe_write("/sys/kernel/debug/tracing/uprobe_events",
  253         O_WRONLY | O_APPEND, buf);
  254     (void)ret;
  255 
  256     return rc;
  257 }
  258 
  259 stressor_info_t stress_uprobe_info = {
  260     .stressor = stress_uprobe,
  261     .class = CLASS_CPU,
  262     .supported = stress_uprobe_supported,
  263     .help = help
  264 };
  265 #else
  266 stressor_info_t stress_uprobe_info = {
  267     .stressor = stress_not_implemented,
  268     .class = CLASS_CPU,
  269     .supported = stress_uprobe_supported,
  270     .help = help
  271 };
  272 #endif