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