"Fossies" - the Fresh Open Source Software Archive

Member "stress-ng-0.09.56/stress-vdso.c" (15 Mar 2019, 10476 Bytes) of package /linux/privat/stress-ng-0.09.56.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 "stress-vdso.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 0.09.52_vs_0.09.54.

    1 /*
    2  * Copyright (C) 2013-2019 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 /*
   28  *  stress_set_vdso_func()
   29  *      set the default vdso function
   30  */
   31 int stress_set_vdso_func(const char *name)
   32 {
   33     return set_setting("vdso-func", TYPE_ID_STR, name);
   34 }
   35 
   36 #if defined(HAVE_SYS_AUXV_H) && \
   37     defined(HAVE_LINK_H) && \
   38     defined(HAVE_GETAUXVAL) && \
   39     defined(AT_SYSINFO_EHDR)
   40 
   41 typedef void (*func_t)(void *);
   42 
   43 /*
   44  *  Symbol name to wrapper function lookup
   45  */
   46 typedef struct wrap_func {
   47     func_t func;        /* Wrapper function */
   48     char *name;     /* Function name */
   49 } wrap_func_t;
   50 
   51 /*
   52  *  vDSO symbol mapping name to address and wrapper function
   53  */
   54 typedef struct vdso_sym {
   55     struct vdso_sym *next;  /* Next symbol in list */
   56     char *name;     /* Function name */
   57     void *addr;     /* Function address in vDSO */
   58     func_t func;        /* Wrapper function */
   59     bool duplicate;     /* True if a duplicate call */
   60 } vdso_sym_t;
   61 
   62 static vdso_sym_t *vdso_sym_list;
   63 
   64 /*
   65  *  wrap_getcpu()
   66  *  invoke getcpu()
   67  */
   68 static void wrap_getcpu(void *vdso_func)
   69 {
   70     unsigned cpu, node;
   71 
   72     int (*vdso_getcpu)(unsigned *cpu, unsigned *node, void *tcache);
   73 
   74     *(void **)(&vdso_getcpu) = vdso_func;
   75     (void)vdso_getcpu(&cpu, &node, NULL);
   76 }
   77 
   78 /*
   79  *  wrap_gettimeofday()
   80  *  invoke gettimeofday()
   81  */
   82 static void wrap_gettimeofday(void *vdso_func)
   83 {
   84     int (*vdso_gettimeofday)(struct timeval *tv, struct timezone *tz);
   85     struct timeval tv;
   86 
   87     *(void **)(&vdso_gettimeofday) = vdso_func;
   88     (void)vdso_gettimeofday(&tv, NULL);
   89 }
   90 
   91 /*
   92  *  wrap_time()
   93  *  invoke time()
   94  */
   95 static void wrap_time(void *vdso_func)
   96 {
   97     time_t (*vdso_time)(time_t *tloc);
   98     time_t t;
   99 
  100     *(void **)(&vdso_time) = vdso_func;
  101     (void)vdso_time(&t);
  102 }
  103 
  104 #if defined(HAVE_CLOCK_GETTIME)
  105 /*
  106  *  wrap_clock_gettime()
  107  *  invoke clock_gettime()
  108  */
  109 static void wrap_clock_gettime(void *vdso_func)
  110 {
  111     int (*vdso_clock_gettime)(clockid_t clk_id, struct timespec *tp);
  112     struct timespec tp;
  113 
  114     *(void **)(&vdso_clock_gettime) = vdso_func;
  115     vdso_clock_gettime(CLOCK_MONOTONIC, &tp);
  116 }
  117 #endif
  118 
  119 /*
  120  *  mapping of wrappers to function symbol name
  121  */
  122 wrap_func_t wrap_funcs[] = {
  123 #if defined(HAVE_CLOCK_GETTIME)
  124     { wrap_clock_gettime,   "clock_gettime" },
  125     { wrap_clock_gettime,   "__vdso_clock_gettime" },
  126     { wrap_clock_gettime,   "__kernel_clock_gettime" },
  127 #endif
  128     { wrap_getcpu,      "getcpu" },
  129     { wrap_getcpu,      "__vdso_getcpu" },
  130     { wrap_getcpu,      "__kernel_getcpu" },
  131     { wrap_gettimeofday,    "gettimeofday" },
  132     { wrap_gettimeofday,    "__vdso_gettimeofday" },
  133     { wrap_gettimeofday,    "__kernel_gettimeofday" },
  134     { wrap_time,        "time" },
  135     { wrap_time,        "__vdso_time" },
  136     { wrap_time,        "__kernel_time" },
  137 };
  138 
  139 /*
  140  *  func_find()
  141  *  find wrapper function by symbol name
  142  */
  143 static func_t func_find(char *name)
  144 {
  145     size_t i;
  146 
  147     for (i = 0; i < SIZEOF_ARRAY(wrap_funcs); i++) {
  148         if (!strcmp(name, wrap_funcs[i].name))
  149             return wrap_funcs[i].func;
  150     }
  151     return NULL;
  152 }
  153 
  154 /*
  155  *  dl_wrapback()
  156  *  find vDSO symbols
  157  */
  158 static int dl_wrapback(struct dl_phdr_info* info, size_t info_size, void *vdso)
  159 {
  160     ElfW(Word) i;
  161     void *load_offset = NULL;
  162 
  163     (void)info_size;
  164 
  165     for (i = 0; i < info->dlpi_phnum; i++) {
  166         if (info->dlpi_phdr[i].p_type == PT_LOAD) {
  167             load_offset = (void *)(info->dlpi_addr +
  168                                 + (uintptr_t)info->dlpi_phdr[i].p_offset
  169                                 - (uintptr_t)info->dlpi_phdr[i].p_vaddr);
  170 
  171         }
  172         if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
  173             ElfW(Dyn *) dyn;
  174             ElfW(Word *) hash = NULL;
  175             ElfW(Word) j;
  176             ElfW(Word) buckets = 0;
  177             ElfW(Sym *) symtab;
  178             ElfW(Word *) bucket = NULL;
  179             ElfW(Word *) chain = NULL;
  180             char *strtab = NULL;
  181 
  182             if (!load_offset)
  183                 continue;
  184 
  185             if ((void *)info->dlpi_addr != vdso)
  186                 continue;
  187 
  188             dyn = (ElfW(Dyn)*)(info->dlpi_addr +  info->dlpi_phdr[i].p_vaddr);
  189 
  190             while (dyn->d_tag != DT_NULL) {
  191                 switch (dyn->d_tag) {
  192                 case DT_HASH:
  193                     hash = (ElfW(Word *))(dyn->d_un.d_ptr + info->dlpi_addr);
  194                     buckets = hash[0];
  195                     bucket = &hash[2];
  196                     chain = &hash[buckets + 2];
  197                     break;
  198 
  199                 case DT_STRTAB:
  200                     strtab = (char *)(dyn->d_un.d_ptr + info->dlpi_addr);
  201                     break;
  202 
  203                 case DT_SYMTAB:
  204                     symtab = (ElfW(Sym *))(dyn->d_un.d_ptr + info->dlpi_addr);
  205 
  206                     if ((!hash) || (!strtab))
  207                         break;
  208 
  209                     /*
  210                      *  Scan through all the chains in each bucket looking
  211                      *  for relevant symbols
  212                      */
  213                     for (j = 0; j < buckets; j++) {
  214                         ElfW(Word) ch;
  215 
  216                         for (ch = bucket[j]; ch != STN_UNDEF; ch = chain[ch]) {
  217                             ElfW(Sym) *sym = &symtab[ch];
  218                             vdso_sym_t *vdso_sym;
  219                             char *name = strtab + sym->st_name;
  220                             func_t func;
  221 
  222                             if ((ELF64_ST_TYPE(sym->st_info) != STT_FUNC) ||
  223                                 ((ELF64_ST_BIND(sym->st_info) != STB_GLOBAL) &&
  224                                  (ELF64_ST_BIND(sym->st_info) != STB_WEAK)) ||
  225                                 (sym->st_shndx == SHN_UNDEF))
  226                                 continue;
  227 
  228                             /*
  229                              *  Do we have a wrapper for this function?
  230                              */
  231                             func = func_find(name);
  232                             if (!func)
  233                                 continue;
  234 
  235                             /*
  236                              *  Add to list of wrapable vDSO functions
  237                              */
  238                             vdso_sym = calloc(1, sizeof(*vdso_sym));
  239                             if (vdso_sym == NULL)
  240                                 return -1;
  241 
  242                             vdso_sym->name = name;
  243                             vdso_sym->addr = sym->st_value + load_offset;
  244                             vdso_sym->func = func;
  245                             vdso_sym->next = vdso_sym_list;
  246                             vdso_sym_list = vdso_sym;
  247                         }
  248                     }
  249                     break;
  250                 }
  251                 dyn++;
  252             }
  253         }
  254     }
  255     return 0;
  256 }
  257 
  258 /*
  259  *  vdso_sym_list_str()
  260  *  gather symbol names into a string
  261  */
  262 static char *vdso_sym_list_str(void)
  263 {
  264     char *str = NULL;
  265     size_t len = 0;
  266     vdso_sym_t *vdso_sym;
  267 
  268     for (vdso_sym = vdso_sym_list; vdso_sym; vdso_sym = vdso_sym->next) {
  269         char *tmp;
  270         len += (strlen(vdso_sym->name) + 2);
  271         tmp = realloc(str, len);
  272         if (!tmp) {
  273             free(str);
  274             return NULL;
  275         }
  276         if (!str) {
  277             *tmp = '\0';
  278         } else {
  279             (void)strcat(tmp, " ");
  280             str = tmp;
  281         }
  282         (void)strcat(tmp, vdso_sym->name);
  283         str = tmp;
  284     }
  285     return str;
  286 }
  287 
  288 /*
  289  *  vdso_sym_list_free()
  290  *  free up the symbols
  291  */
  292 static void vdso_sym_list_free(vdso_sym_t **list)
  293 {
  294     vdso_sym_t *vdso_sym = *list;
  295 
  296     while (vdso_sym) {
  297         vdso_sym_t *next = vdso_sym->next;
  298 
  299         free(vdso_sym);
  300         vdso_sym = next;
  301     }
  302     *list = NULL;
  303 }
  304 
  305 /*
  306  *  remove_sym
  307  *  find and remove a symbol from the symbol list
  308  */
  309 static void remove_sym(vdso_sym_t **list, vdso_sym_t *dup)
  310 {
  311     while (*list) {
  312         if (*list == dup) {
  313             *list = dup->next;
  314             free(dup);
  315             return;
  316         }
  317         list = &(*list)->next;
  318     }
  319 }
  320 
  321 /*
  322  *  vdso_sym_list_remove_duplicates()
  323  *  remove duplicated system calls
  324  */
  325 static void vdso_sym_list_remove_duplicates(vdso_sym_t **list)
  326 {
  327     vdso_sym_t *vs1;
  328 
  329     for (vs1 = *list; vs1; vs1 = vs1->next) {
  330         vdso_sym_t *vs2;
  331 
  332         if (vs1->name[0] == '_') {
  333             for (vs2 = *list; vs2; vs2 = vs2->next) {
  334                 if ((vs1 != vs2) && (vs1->addr == vs2->addr))
  335                     vs1->duplicate = true;
  336             }
  337         }
  338     }
  339 
  340     vs1 = *list;
  341     while (vs1) {
  342         vdso_sym_t *next = vs1->next;
  343 
  344         if (vs1->duplicate)
  345             remove_sym(list, vs1);
  346         vs1 = next;
  347     }
  348 }
  349 
  350 /*
  351  *  stress_vdso_supported()
  352  *  early sanity check to see if functionality is supported
  353  */
  354 static int stress_vdso_supported(void)
  355 {
  356     void *vdso = (void *)getauxval(AT_SYSINFO_EHDR);
  357 
  358     if (vdso == NULL) {
  359         pr_inf("vdso stressor will be skipped, failed to find vDSO address\n");
  360         return -1;
  361     }
  362 
  363     vdso_sym_list = NULL;
  364     dl_iterate_phdr(dl_wrapback, vdso);
  365 
  366     if (!vdso_sym_list) {
  367         pr_inf("vsdo stressor will be skipped, failed to find relevant vDSO functions\n");
  368         return -1;
  369     }
  370 
  371     return 0;
  372 }
  373 
  374 /*
  375  *  vdso_sym_list_check_vdso_func()
  376  *  if a vdso-func has been specified, locate it and
  377  *  remove all other symbols from the list so just
  378  *  this one function is used.
  379  */
  380 static int vdso_sym_list_check_vdso_func(vdso_sym_t **list)
  381 {
  382     vdso_sym_t *vs1;
  383     char *name;
  384 
  385     if (!get_setting("vdso-func", &name))
  386         return 0;
  387 
  388     for (vs1 = vdso_sym_list; vs1; vs1 = vs1->next) {
  389         if (!strcmp(vs1->name, name))
  390             break;
  391     }
  392     if (!vs1) {
  393         (void)fprintf(stderr, "invalid vdso-func '%s', must be one of:", name);
  394         for (vs1 = vdso_sym_list; vs1; vs1 = vs1->next)
  395                     (void)fprintf(stderr, " %s", vs1->name);
  396             (void)fprintf(stderr, "\n");
  397         return -1;
  398         }
  399 
  400     vs1 = *list;
  401     while (vs1) {
  402         vdso_sym_t *next = vs1->next;
  403 
  404         if (strcmp(vs1->name, name))
  405             remove_sym(list, vs1);
  406         vs1 = next;
  407     }
  408     return 0;
  409 }
  410 
  411 /*
  412  *  stress_vdso()
  413  *  stress system wraps in vDSO
  414  */
  415 static int stress_vdso(const args_t *args)
  416 {
  417     char *str;
  418     double t1, t2;
  419 
  420     if (!vdso_sym_list) {
  421         /* Should not fail, but worth checking to avoid breakage */
  422         pr_inf("%s: could not find any vDSO functions, skipping\n",
  423             args->name);
  424         return EXIT_NOT_IMPLEMENTED;
  425     }
  426     vdso_sym_list_remove_duplicates(&vdso_sym_list);
  427     if (vdso_sym_list_check_vdso_func(&vdso_sym_list) < 0) {
  428         return EXIT_FAILURE;
  429     }
  430 
  431     if (args->instance == 0) {
  432         str = vdso_sym_list_str();
  433         if (str) {
  434             pr_inf("%s: exercising vDSO functions: %s\n",
  435                 args->name, str);
  436             free(str);
  437         }
  438     }
  439 
  440     t1 = time_now();
  441     do {
  442         vdso_sym_t *vdso_sym;
  443 
  444         for (vdso_sym = vdso_sym_list; vdso_sym; vdso_sym = vdso_sym->next) {
  445             vdso_sym->func(vdso_sym->addr);
  446             inc_counter(args);
  447         }
  448     } while (keep_stressing());
  449     t2 = time_now();
  450 
  451     pr_inf("%s: %.2f nanoseconds per call\n",
  452         args->name,
  453         ((t2 - t1) * 1000000000.0) / (double)get_counter(args));
  454 
  455     vdso_sym_list_free(&vdso_sym_list);
  456 
  457     return EXIT_SUCCESS;
  458 }
  459 
  460 stressor_info_t stress_vdso_info = {
  461     .stressor = stress_vdso,
  462     .supported = stress_vdso_supported,
  463     .class = CLASS_OS
  464 };
  465 #else
  466 stressor_info_t stress_vdso_info = {
  467     .stressor = stress_not_implemented,
  468     .class = CLASS_OS
  469 };
  470 #endif