linux/tools/perf/tests/vmlinux-kallsyms.c
<<
>>
Prefs
   1#include <linux/compiler.h>
   2#include <linux/rbtree.h>
   3#include <string.h>
   4#include "map.h"
   5#include "symbol.h"
   6#include "util.h"
   7#include "tests.h"
   8#include "debug.h"
   9#include "machine.h"
  10
  11static int vmlinux_matches_kallsyms_filter(struct map *map __maybe_unused,
  12                                           struct symbol *sym)
  13{
  14        bool *visited = symbol__priv(sym);
  15        *visited = true;
  16        return 0;
  17}
  18
  19#define UM(x) kallsyms_map->unmap_ip(kallsyms_map, (x))
  20
  21int test__vmlinux_matches_kallsyms(void)
  22{
  23        int err = -1;
  24        struct rb_node *nd;
  25        struct symbol *sym;
  26        struct map *kallsyms_map, *vmlinux_map, *map;
  27        struct machine kallsyms, vmlinux;
  28        enum map_type type = MAP__FUNCTION;
  29        struct maps *maps = &vmlinux.kmaps.maps[type];
  30        u64 mem_start, mem_end;
  31
  32        /*
  33         * Step 1:
  34         *
  35         * Init the machines that will hold kernel, modules obtained from
  36         * both vmlinux + .ko files and from /proc/kallsyms split by modules.
  37         */
  38        machine__init(&kallsyms, "", HOST_KERNEL_ID);
  39        machine__init(&vmlinux, "", HOST_KERNEL_ID);
  40
  41        /*
  42         * Step 2:
  43         *
  44         * Create the kernel maps for kallsyms and the DSO where we will then
  45         * load /proc/kallsyms. Also create the modules maps from /proc/modules
  46         * and find the .ko files that match them in /lib/modules/`uname -r`/.
  47         */
  48        if (machine__create_kernel_maps(&kallsyms) < 0) {
  49                pr_debug("machine__create_kernel_maps ");
  50                goto out;
  51        }
  52
  53        /*
  54         * Step 3:
  55         *
  56         * Load and split /proc/kallsyms into multiple maps, one per module.
  57         */
  58        if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms", type, NULL) <= 0) {
  59                pr_debug("dso__load_kallsyms ");
  60                goto out;
  61        }
  62
  63        /*
  64         * Step 4:
  65         *
  66         * kallsyms will be internally on demand sorted by name so that we can
  67         * find the reference relocation * symbol, i.e. the symbol we will use
  68         * to see if the running kernel was relocated by checking if it has the
  69         * same value in the vmlinux file we load.
  70         */
  71        kallsyms_map = machine__kernel_map(&kallsyms, type);
  72
  73        /*
  74         * Step 5:
  75         *
  76         * Now repeat step 2, this time for the vmlinux file we'll auto-locate.
  77         */
  78        if (machine__create_kernel_maps(&vmlinux) < 0) {
  79                pr_debug("machine__create_kernel_maps ");
  80                goto out;
  81        }
  82
  83        vmlinux_map = machine__kernel_map(&vmlinux, type);
  84
  85        /*
  86         * Step 6:
  87         *
  88         * Locate a vmlinux file in the vmlinux path that has a buildid that
  89         * matches the one of the running kernel.
  90         *
  91         * While doing that look if we find the ref reloc symbol, if we find it
  92         * we'll have its ref_reloc_symbol.unrelocated_addr and then
  93         * maps__reloc_vmlinux will notice and set proper ->[un]map_ip routines
  94         * to fixup the symbols.
  95         */
  96        if (machine__load_vmlinux_path(&vmlinux, type,
  97                                       vmlinux_matches_kallsyms_filter) <= 0) {
  98                pr_debug("Couldn't find a vmlinux that matches the kernel running on this machine, skipping test\n");
  99                err = TEST_SKIP;
 100                goto out;
 101        }
 102
 103        err = 0;
 104        /*
 105         * Step 7:
 106         *
 107         * Now look at the symbols in the vmlinux DSO and check if we find all of them
 108         * in the kallsyms dso. For the ones that are in both, check its names and
 109         * end addresses too.
 110         */
 111        for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) {
 112                struct symbol *pair, *first_pair;
 113                bool backwards = true;
 114
 115                sym  = rb_entry(nd, struct symbol, rb_node);
 116
 117                if (sym->start == sym->end)
 118                        continue;
 119
 120                mem_start = vmlinux_map->unmap_ip(vmlinux_map, sym->start);
 121                mem_end = vmlinux_map->unmap_ip(vmlinux_map, sym->end);
 122
 123                first_pair = machine__find_kernel_symbol(&kallsyms, type,
 124                                                         mem_start, NULL, NULL);
 125                pair = first_pair;
 126
 127                if (pair && UM(pair->start) == mem_start) {
 128next_pair:
 129                        if (strcmp(sym->name, pair->name) == 0) {
 130                                /*
 131                                 * kallsyms don't have the symbol end, so we
 132                                 * set that by using the next symbol start - 1,
 133                                 * in some cases we get this up to a page
 134                                 * wrong, trace_kmalloc when I was developing
 135                                 * this code was one such example, 2106 bytes
 136                                 * off the real size. More than that and we
 137                                 * _really_ have a problem.
 138                                 */
 139                                s64 skew = mem_end - UM(pair->end);
 140                                if (llabs(skew) >= page_size)
 141                                        pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
 142                                                 mem_start, sym->name, mem_end,
 143                                                 UM(pair->end));
 144
 145                                /*
 146                                 * Do not count this as a failure, because we
 147                                 * could really find a case where it's not
 148                                 * possible to get proper function end from
 149                                 * kallsyms.
 150                                 */
 151                                continue;
 152
 153                        } else {
 154                                struct rb_node *nnd;
 155detour:
 156                                nnd = backwards ? rb_prev(&pair->rb_node) :
 157                                                  rb_next(&pair->rb_node);
 158                                if (nnd) {
 159                                        struct symbol *next = rb_entry(nnd, struct symbol, rb_node);
 160
 161                                        if (UM(next->start) == mem_start) {
 162                                                pair = next;
 163                                                goto next_pair;
 164                                        }
 165                                }
 166
 167                                if (backwards) {
 168                                        backwards = false;
 169                                        pair = first_pair;
 170                                        goto detour;
 171                                }
 172
 173                                pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n",
 174                                         mem_start, sym->name, pair->name);
 175                        }
 176                } else
 177                        pr_debug("%#" PRIx64 ": %s not on kallsyms\n",
 178                                 mem_start, sym->name);
 179
 180                err = -1;
 181        }
 182
 183        if (!verbose)
 184                goto out;
 185
 186        pr_info("Maps only in vmlinux:\n");
 187
 188        for (map = maps__first(maps); map; map = map__next(map)) {
 189                struct map *
 190                /*
 191                 * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while
 192                 * the kernel will have the path for the vmlinux file being used,
 193                 * so use the short name, less descriptive but the same ("[kernel]" in
 194                 * both cases.
 195                 */
 196                pair = map_groups__find_by_name(&kallsyms.kmaps, type,
 197                                                (map->dso->kernel ?
 198                                                        map->dso->short_name :
 199                                                        map->dso->name));
 200                if (pair)
 201                        pair->priv = 1;
 202                else
 203                        map__fprintf(map, stderr);
 204        }
 205
 206        pr_info("Maps in vmlinux with a different name in kallsyms:\n");
 207
 208        for (map = maps__first(maps); map; map = map__next(map)) {
 209                struct map *pair;
 210
 211                mem_start = vmlinux_map->unmap_ip(vmlinux_map, map->start);
 212                mem_end = vmlinux_map->unmap_ip(vmlinux_map, map->end);
 213
 214                pair = map_groups__find(&kallsyms.kmaps, type, mem_start);
 215                if (pair == NULL || pair->priv)
 216                        continue;
 217
 218                if (pair->start == mem_start) {
 219                        pair->priv = 1;
 220                        pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as",
 221                                map->start, map->end, map->pgoff, map->dso->name);
 222                        if (mem_end != pair->end)
 223                                pr_info(":\n*%" PRIx64 "-%" PRIx64 " %" PRIx64,
 224                                        pair->start, pair->end, pair->pgoff);
 225                        pr_info(" %s\n", pair->dso->name);
 226                        pair->priv = 1;
 227                }
 228        }
 229
 230        pr_info("Maps only in kallsyms:\n");
 231
 232        maps = &kallsyms.kmaps.maps[type];
 233
 234        for (map = maps__first(maps); map; map = map__next(map)) {
 235                if (!map->priv)
 236                        map__fprintf(map, stderr);
 237        }
 238out:
 239        machine__exit(&kallsyms);
 240        machine__exit(&vmlinux);
 241        return err;
 242}
 243