diff options
Diffstat (limited to '')
-rw-r--r-- | tools/perf/tests/vmlinux-kallsyms.c | 301 |
1 files changed, 216 insertions, 85 deletions
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index 193b7c91b4e2..e30fd55f8e51 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -3,6 +3,7 @@ #include <linux/rbtree.h> #include <inttypes.h> #include <string.h> +#include <ctype.h> #include <stdlib.h> #include "dso.h" #include "map.h" @@ -12,18 +13,191 @@ #include "debug.h" #include "machine.h" -#define UM(x) kallsyms_map->unmap_ip(kallsyms_map, (x)) +#define UM(x) map__unmap_ip(kallsyms_map, (x)) -int test__vmlinux_matches_kallsyms(struct test *test __maybe_unused, int subtest __maybe_unused) +static bool is_ignored_symbol(const char *name, char type) { - int err = -1; + /* Symbol names that exactly match to the following are ignored.*/ + static const char * const ignored_symbols[] = { + /* + * Symbols which vary between passes. Passes 1 and 2 must have + * identical symbol lists. The kallsyms_* symbols below are + * only added after pass 1, they would be included in pass 2 + * when --all-symbols is specified so exclude them to get a + * stable symbol list. + */ + "kallsyms_addresses", + "kallsyms_offsets", + "kallsyms_relative_base", + "kallsyms_num_syms", + "kallsyms_names", + "kallsyms_markers", + "kallsyms_token_table", + "kallsyms_token_index", + /* Exclude linker generated symbols which vary between passes */ + "_SDA_BASE_", /* ppc */ + "_SDA2_BASE_", /* ppc */ + NULL + }; + + /* Symbol names that begin with the following are ignored.*/ + static const char * const ignored_prefixes[] = { + "$", /* local symbols for ARM, MIPS, etc. */ + ".L", /* local labels, .LBB,.Ltmpxxx,.L__unnamed_xx,.LASANPC, etc. */ + "__crc_", /* modversions */ + "__efistub_", /* arm64 EFI stub namespace */ + "__kvm_nvhe_$", /* arm64 local symbols in non-VHE KVM namespace */ + "__kvm_nvhe_.L", /* arm64 local symbols in non-VHE KVM namespace */ + "__AArch64ADRPThunk_", /* arm64 lld */ + "__ARMV5PILongThunk_", /* arm lld */ + "__ARMV7PILongThunk_", + "__ThumbV7PILongThunk_", + "__LA25Thunk_", /* mips lld */ + "__microLA25Thunk_", + NULL + }; + + /* Symbol names that end with the following are ignored.*/ + static const char * const ignored_suffixes[] = { + "_from_arm", /* arm */ + "_from_thumb", /* arm */ + "_veneer", /* arm */ + NULL + }; + + /* Symbol names that contain the following are ignored.*/ + static const char * const ignored_matches[] = { + ".long_branch.", /* ppc stub */ + ".plt_branch.", /* ppc stub */ + NULL + }; + + const char * const *p; + + for (p = ignored_symbols; *p; p++) + if (!strcmp(name, *p)) + return true; + + for (p = ignored_prefixes; *p; p++) + if (!strncmp(name, *p, strlen(*p))) + return true; + + for (p = ignored_suffixes; *p; p++) { + int l = strlen(name) - strlen(*p); + + if (l >= 0 && !strcmp(name + l, *p)) + return true; + } + + for (p = ignored_matches; *p; p++) { + if (strstr(name, *p)) + return true; + } + + if (type == 'U' || type == 'u') + return true; + /* exclude debugging symbols */ + if (type == 'N' || type == 'n') + return true; + + if (toupper(type) == 'A') { + /* Keep these useful absolute symbols */ + if (strcmp(name, "__kernel_syscall_via_break") && + strcmp(name, "__kernel_syscall_via_epc") && + strcmp(name, "__kernel_sigtramp") && + strcmp(name, "__gp")) + return true; + } + + return false; +} + +struct test__vmlinux_matches_kallsyms_cb_args { + struct machine kallsyms; + struct map *vmlinux_map; + bool header_printed; +}; + +static int test__vmlinux_matches_kallsyms_cb1(struct map *map, void *data) +{ + struct test__vmlinux_matches_kallsyms_cb_args *args = data; + struct dso *dso = map__dso(map); + /* + * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while + * the kernel will have the path for the vmlinux file being used, so use + * the short name, less descriptive but the same ("[kernel]" in both + * cases. + */ + struct map *pair = maps__find_by_name(args->kallsyms.kmaps, + (dso__kernel(dso) ? dso__short_name(dso) : dso__name(dso))); + + if (pair) { + map__set_priv(pair, 1); + map__put(pair); + } else { + if (!args->header_printed) { + pr_info("WARN: Maps only in vmlinux:\n"); + args->header_printed = true; + } + map__fprintf(map, stderr); + } + return 0; +} + +static int test__vmlinux_matches_kallsyms_cb2(struct map *map, void *data) +{ + struct test__vmlinux_matches_kallsyms_cb_args *args = data; + struct map *pair; + u64 mem_start = map__unmap_ip(args->vmlinux_map, map__start(map)); + u64 mem_end = map__unmap_ip(args->vmlinux_map, map__end(map)); + + pair = maps__find(args->kallsyms.kmaps, mem_start); + + if (pair != NULL && !map__priv(pair) && map__start(pair) == mem_start) { + struct dso *dso = map__dso(map); + + if (!args->header_printed) { + pr_info("WARN: Maps in vmlinux with a different name in kallsyms:\n"); + args->header_printed = true; + } + + pr_info("WARN: %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as", + map__start(map), map__end(map), map__pgoff(map), dso__name(dso)); + if (mem_end != map__end(pair)) + pr_info(":\nWARN: *%" PRIx64 "-%" PRIx64 " %" PRIx64, + map__start(pair), map__end(pair), map__pgoff(pair)); + pr_info(" %s\n", dso__name(dso)); + map__set_priv(pair, 1); + } + map__put(pair); + return 0; +} + +static int test__vmlinux_matches_kallsyms_cb3(struct map *map, void *data) +{ + struct test__vmlinux_matches_kallsyms_cb_args *args = data; + + if (!map__priv(map)) { + if (!args->header_printed) { + pr_info("WARN: Maps only in kallsyms:\n"); + args->header_printed = true; + } + map__fprintf(map, stderr); + } + return 0; +} + +static int test__vmlinux_matches_kallsyms(struct test_suite *test __maybe_unused, + int subtest __maybe_unused) +{ + int err = TEST_FAIL; struct rb_node *nd; struct symbol *sym; - struct map *kallsyms_map, *vmlinux_map, *map; - struct machine kallsyms, vmlinux; - struct maps *maps = machine__kernel_maps(&vmlinux); + struct map *kallsyms_map; + struct machine vmlinux; + struct maps *maps; u64 mem_start, mem_end; - bool header_printed; + struct test__vmlinux_matches_kallsyms_cb_args args; /* * Step 1: @@ -31,9 +205,11 @@ int test__vmlinux_matches_kallsyms(struct test *test __maybe_unused, int subtest * Init the machines that will hold kernel, modules obtained from * both vmlinux + .ko files and from /proc/kallsyms split by modules. */ - machine__init(&kallsyms, "", HOST_KERNEL_ID); + machine__init(&args.kallsyms, "", HOST_KERNEL_ID); machine__init(&vmlinux, "", HOST_KERNEL_ID); + maps = machine__kernel_maps(&vmlinux); + /* * Step 2: * @@ -41,8 +217,9 @@ int test__vmlinux_matches_kallsyms(struct test *test __maybe_unused, int subtest * load /proc/kallsyms. Also create the modules maps from /proc/modules * and find the .ko files that match them in /lib/modules/`uname -r`/. */ - if (machine__create_kernel_maps(&kallsyms) < 0) { - pr_debug("machine__create_kernel_maps "); + if (machine__create_kernel_maps(&args.kallsyms) < 0) { + pr_debug("machine__create_kernel_maps failed"); + err = TEST_SKIP; goto out; } @@ -57,8 +234,9 @@ int test__vmlinux_matches_kallsyms(struct test *test __maybe_unused, int subtest * be compacted against the list of modules found in the "vmlinux" * code and with the one got from /proc/modules from the "kallsyms" code. */ - if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms") <= 0) { - pr_debug("dso__load_kallsyms "); + if (machine__load_kallsyms(&args.kallsyms, "/proc/kallsyms") <= 0) { + pr_debug("machine__load_kallsyms failed"); + err = TEST_SKIP; goto out; } @@ -70,7 +248,7 @@ int test__vmlinux_matches_kallsyms(struct test *test __maybe_unused, int subtest * to see if the running kernel was relocated by checking if it has the * same value in the vmlinux file we load. */ - kallsyms_map = machine__kernel_map(&kallsyms); + kallsyms_map = machine__kernel_map(&args.kallsyms); /* * Step 5: @@ -78,11 +256,11 @@ int test__vmlinux_matches_kallsyms(struct test *test __maybe_unused, int subtest * Now repeat step 2, this time for the vmlinux file we'll auto-locate. */ if (machine__create_kernel_maps(&vmlinux) < 0) { - pr_debug("machine__create_kernel_maps "); + pr_info("machine__create_kernel_maps failed"); goto out; } - vmlinux_map = machine__kernel_map(&vmlinux); + args.vmlinux_map = machine__kernel_map(&vmlinux); /* * Step 6: @@ -96,7 +274,7 @@ int test__vmlinux_matches_kallsyms(struct test *test __maybe_unused, int subtest * to fixup the symbols. */ if (machine__load_vmlinux_path(&vmlinux) <= 0) { - pr_debug("Couldn't find a vmlinux that matches the kernel running on this machine, skipping test\n"); + pr_info("Couldn't find a vmlinux that matches the kernel running on this machine, skipping test\n"); err = TEST_SKIP; goto out; } @@ -109,7 +287,7 @@ int test__vmlinux_matches_kallsyms(struct test *test __maybe_unused, int subtest * in the kallsyms dso. For the ones that are in both, check its names and * end addresses too. */ - map__for_each_symbol(vmlinux_map, sym, nd) { + map__for_each_symbol(args.vmlinux_map, sym, nd) { struct symbol *pair, *first_pair; sym = rb_entry(nd, struct symbol, rb_node); @@ -117,10 +295,10 @@ int test__vmlinux_matches_kallsyms(struct test *test __maybe_unused, int subtest if (sym->start == sym->end) continue; - mem_start = vmlinux_map->unmap_ip(vmlinux_map, sym->start); - mem_end = vmlinux_map->unmap_ip(vmlinux_map, sym->end); + mem_start = map__unmap_ip(args.vmlinux_map, sym->start); + mem_end = map__unmap_ip(args.vmlinux_map, sym->end); - first_pair = machine__find_kernel_symbol(&kallsyms, mem_start, NULL); + first_pair = machine__find_kernel_symbol(&args.kallsyms, mem_start, NULL); pair = first_pair; if (pair && UM(pair->start) == mem_start) { @@ -149,7 +327,8 @@ next_pair: */ continue; } else { - pair = machine__find_kernel_symbol_by_name(&kallsyms, sym->name, NULL); + pair = machine__find_kernel_symbol_by_name(&args.kallsyms, + sym->name, NULL); if (pair) { if (UM(pair->start) == mem_start) goto next_pair; @@ -163,12 +342,17 @@ next_pair: continue; } - } else if (mem_start == kallsyms.vmlinux_map->end) { + } else if (mem_start == map__end(args.kallsyms.vmlinux_map)) { /* * Ignore aliases to _etext, i.e. to the end of the kernel text area, * such as __indirect_thunk_end. */ continue; + } else if (is_ignored_symbol(sym->name, sym->type)) { + /* + * Ignore hidden symbols, see scripts/kallsyms.c for the details + */ + continue; } else { pr_debug("ERR : %#" PRIx64 ": %s not on kallsyms\n", mem_start, sym->name); @@ -180,73 +364,20 @@ next_pair: if (verbose <= 0) goto out; - header_printed = false; - - maps__for_each_entry(maps, map) { - struct map * - /* - * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while - * the kernel will have the path for the vmlinux file being used, - * so use the short name, less descriptive but the same ("[kernel]" in - * both cases. - */ - pair = maps__find_by_name(&kallsyms.kmaps, (map->dso->kernel ? - map->dso->short_name : - map->dso->name)); - if (pair) { - pair->priv = 1; - } else { - if (!header_printed) { - pr_info("WARN: Maps only in vmlinux:\n"); - header_printed = true; - } - map__fprintf(map, stderr); - } - } - - header_printed = false; - - maps__for_each_entry(maps, map) { - struct map *pair; - - mem_start = vmlinux_map->unmap_ip(vmlinux_map, map->start); - mem_end = vmlinux_map->unmap_ip(vmlinux_map, map->end); - - pair = maps__find(&kallsyms.kmaps, mem_start); - if (pair == NULL || pair->priv) - continue; - - if (pair->start == mem_start) { - if (!header_printed) { - pr_info("WARN: Maps in vmlinux with a different name in kallsyms:\n"); - header_printed = true; - } - - pr_info("WARN: %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as", - map->start, map->end, map->pgoff, map->dso->name); - if (mem_end != pair->end) - pr_info(":\nWARN: *%" PRIx64 "-%" PRIx64 " %" PRIx64, - pair->start, pair->end, pair->pgoff); - pr_info(" %s\n", pair->dso->name); - pair->priv = 1; - } - } + args.header_printed = false; + maps__for_each_map(maps, test__vmlinux_matches_kallsyms_cb1, &args); - header_printed = false; + args.header_printed = false; + maps__for_each_map(maps, test__vmlinux_matches_kallsyms_cb2, &args); - maps = machine__kernel_maps(&kallsyms); + args.header_printed = false; + maps = machine__kernel_maps(&args.kallsyms); + maps__for_each_map(maps, test__vmlinux_matches_kallsyms_cb3, &args); - maps__for_each_entry(maps, map) { - if (!map->priv) { - if (!header_printed) { - pr_info("WARN: Maps only in kallsyms:\n"); - header_printed = true; - } - map__fprintf(map, stderr); - } - } out: - machine__exit(&kallsyms); + machine__exit(&args.kallsyms); machine__exit(&vmlinux); return err; } + +DEFINE_SUITE("vmlinux symtab matches kallsyms", vmlinux_matches_kallsyms); |