diff options
Diffstat (limited to '')
-rw-r--r-- | tools/perf/tests/code-reading.c | 223 |
1 files changed, 102 insertions, 121 deletions
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 035c9123549a..27c82cfb7e7d 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -16,7 +16,6 @@ #include "dso.h" #include "env.h" #include "parse-events.h" -#include "trace-event.h" #include "evlist.h" #include "evsel.h" #include "thread_map.h" @@ -26,7 +25,9 @@ #include "event.h" #include "record.h" #include "util/mmap.h" +#include "util/string2.h" #include "util/synthetic-events.h" +#include "util/util.h" #include "thread.h" #include "tests.h" @@ -41,15 +42,6 @@ struct state { size_t done_cnt; }; -static unsigned int hex(char c) -{ - if (c >= '0' && c <= '9') - return c - '0'; - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - return c - 'A' + 10; -} - static size_t read_objdump_chunk(const char **line, unsigned char **buf, size_t *buf_len) { @@ -87,7 +79,7 @@ static size_t read_objdump_chunk(const char **line, unsigned char **buf, * see disassemble_bytes() at binutils/objdump.c for details * how objdump chooses display endian) */ - if (bytes_read > 1 && !bigendian()) { + if (bytes_read > 1 && !host_is_bigendian()) { unsigned char *chunk_end = chunk_start + bytes_read - 1; unsigned char tmp; @@ -193,7 +185,7 @@ static int read_via_objdump(const char *filename, u64 addr, void *buf, int ret; fmt = "%s -z -d --start-address=0x%"PRIx64" --stop-address=0x%"PRIx64" %s"; - ret = snprintf(cmd, sizeof(cmd), fmt, "objdump", addr, addr + len, + ret = snprintf(cmd, sizeof(cmd), fmt, test_objdump_path, addr, addr + len, filename); if (ret <= 0 || (size_t)ret >= sizeof(cmd)) return -1; @@ -237,33 +229,35 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, struct thread *thread, struct state *state) { struct addr_location al; - unsigned char buf1[BUFSZ]; - unsigned char buf2[BUFSZ]; + unsigned char buf1[BUFSZ] = {0}; + unsigned char buf2[BUFSZ] = {0}; size_t ret_len; u64 objdump_addr; const char *objdump_name; char decomp_name[KMOD_DECOMP_LEN]; bool decomp = false; - int ret; + int ret, err = 0; + struct dso *dso; pr_debug("Reading object code for memory address: %#"PRIx64"\n", addr); - if (!thread__find_map(thread, cpumode, addr, &al) || !al.map->dso) { + addr_location__init(&al); + if (!thread__find_map(thread, cpumode, addr, &al) || !map__dso(al.map)) { if (cpumode == PERF_RECORD_MISC_HYPERVISOR) { pr_debug("Hypervisor address can not be resolved - skipping\n"); - return 0; + goto out; } pr_debug("thread__find_map failed\n"); - return -1; + err = -1; + goto out; } + dso = map__dso(al.map); + pr_debug("File is: %s\n", dso__long_name(dso)); - pr_debug("File is: %s\n", al.map->dso->long_name); - - if (al.map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && - !dso__is_kcore(al.map->dso)) { + if (dso__symtab_type(dso) == DSO_BINARY_TYPE__KALLSYMS && !dso__is_kcore(dso)) { pr_debug("Unexpected kernel address - skipping\n"); - return 0; + goto out; } pr_debug("On file address is: %#"PRIx64"\n", al.addr); @@ -272,49 +266,63 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, len = BUFSZ; /* Do not go off the map */ - if (addr + len > al.map->end) - len = al.map->end - addr; + if (addr + len > map__end(al.map)) + len = map__end(al.map) - addr; + + /* + * Some architectures (ex: powerpc) have stubs (trampolines) in kernel + * modules to manage long jumps. Check if the ip offset falls in stubs + * sections for kernel modules. And skip module address after text end + */ + if (dso__is_kmod(dso) && al.addr > dso__text_end(dso)) { + pr_debug("skipping the module address %#"PRIx64" after text end\n", al.addr); + goto out; + } /* Read the object code using perf */ - ret_len = dso__data_read_offset(al.map->dso, thread->maps->machine, + ret_len = dso__data_read_offset(dso, maps__machine(thread__maps(thread)), al.addr, buf1, len); if (ret_len != len) { pr_debug("dso__data_read_offset failed\n"); - return -1; + err = -1; + goto out; } /* * Converting addresses for use by objdump requires more information. * map__load() does that. See map__rip_2objdump() for details. */ - if (map__load(al.map)) - return -1; + if (map__load(al.map)) { + err = -1; + goto out; + } /* objdump struggles with kcore - try each map only once */ - if (dso__is_kcore(al.map->dso)) { + if (dso__is_kcore(dso)) { size_t d; for (d = 0; d < state->done_cnt; d++) { - if (state->done[d] == al.map->start) { + if (state->done[d] == map__start(al.map)) { pr_debug("kcore map tested already"); pr_debug(" - skipping\n"); - return 0; + goto out; } } if (state->done_cnt >= ARRAY_SIZE(state->done)) { pr_debug("Too many kcore maps - skipping\n"); - return 0; + goto out; } - state->done[state->done_cnt++] = al.map->start; + state->done[state->done_cnt++] = map__start(al.map); } - objdump_name = al.map->dso->long_name; - if (dso__needs_decompress(al.map->dso)) { - if (dso__decompress_kmodule_path(al.map->dso, objdump_name, + objdump_name = dso__long_name(dso); + if (dso__needs_decompress(dso)) { + if (dso__decompress_kmodule_path(dso, objdump_name, decomp_name, sizeof(decomp_name)) < 0) { pr_debug("decompression failed\n"); - return -1; + err = -1; + goto out; } decomp = true; @@ -338,22 +346,23 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, len -= ret; if (len) { pr_debug("Reducing len to %zu\n", len); - } else if (dso__is_kcore(al.map->dso)) { + } else if (dso__is_kcore(dso)) { /* * objdump cannot handle very large segments * that may be found in kcore. */ pr_debug("objdump failed for kcore"); pr_debug(" - skipping\n"); - return 0; } else { - return -1; + err = -1; } + goto out; } } if (ret < 0) { pr_debug("read_via_objdump failed\n"); - return -1; + err = -1; + goto out; } /* The results should be identical */ @@ -363,11 +372,13 @@ static int read_object_code(u64 addr, size_t len, u8 cpumode, dump_buf(buf1, len); pr_debug("buf2 (objdump):\n"); dump_buf(buf2, len); - return -1; + err = -1; + goto out; } pr_debug("Bytes read match those read by objdump\n"); - - return 0; +out: + addr_location__exit(&al); + return err; } static int process_sample_event(struct machine *machine, @@ -378,8 +389,8 @@ static int process_sample_event(struct machine *machine, struct thread *thread; int ret; - if (perf_evlist__parse_sample(evlist, event, &sample)) { - pr_debug("perf_evlist__parse_sample failed\n"); + if (evlist__parse_sample(evlist, event, &sample)) { + pr_debug("evlist__parse_sample failed\n"); return -1; } @@ -500,38 +511,6 @@ static void fs_something(void) } } -#ifdef __s390x__ -#include "header.h" // for get_cpuid() -#endif - -static const char *do_determine_event(bool excl_kernel) -{ - const char *event = excl_kernel ? "cycles:u" : "cycles"; - -#ifdef __s390x__ - char cpuid[128], model[16], model_c[16], cpum_cf_v[16]; - unsigned int family; - int ret, cpum_cf_a; - - if (get_cpuid(cpuid, sizeof(cpuid))) - goto out_clocks; - ret = sscanf(cpuid, "%*[^,],%u,%[^,],%[^,],%[^,],%x", &family, model_c, - model, cpum_cf_v, &cpum_cf_a); - if (ret != 5) /* Not available */ - goto out_clocks; - if (excl_kernel && (cpum_cf_a & 4)) - return event; - if (!excl_kernel && (cpum_cf_a & 2)) - return event; - - /* Fall through: missing authorization */ -out_clocks: - event = excl_kernel ? "cpu-clock:u" : "cpu-clock"; - -#endif - return event; -} - static void do_something(void) { fs_something(); @@ -572,7 +551,10 @@ static int do_test_code_reading(bool try_kcore) int err = -1, ret; pid_t pid; struct map *map; - bool have_vmlinux, have_kcore, excl_kernel = false; + bool have_vmlinux, have_kcore; + struct dso *dso; + const char *events[] = { "cycles", "cycles:u", "cpu-clock", "cpu-clock:u", NULL }; + int evidx = 0; pid = getpid(); @@ -596,8 +578,9 @@ static int do_test_code_reading(bool try_kcore) pr_debug("map__load failed\n"); goto out_err; } - have_vmlinux = dso__is_vmlinux(map->dso); - have_kcore = dso__is_kcore(map->dso); + dso = map__dso(map); + have_vmlinux = dso__is_vmlinux(dso); + have_kcore = dso__is_kcore(dso); /* 2nd time through we just try kcore */ if (try_kcore && !have_kcore) @@ -605,7 +588,7 @@ static int do_test_code_reading(bool try_kcore) /* No point getting kernel events if there is no kernel object */ if (!have_vmlinux && !have_kcore) - excl_kernel = true; + evidx++; threads = thread_map__new_by_tid(pid); if (!threads) { @@ -614,7 +597,8 @@ static int do_test_code_reading(bool try_kcore) } ret = perf_event__synthesize_thread_map(NULL, threads, - perf_event__process, machine, false); + perf_event__process, machine, + true, false); if (ret < 0) { pr_debug("perf_event__synthesize_thread_map failed\n"); goto out_err; @@ -626,67 +610,67 @@ static int do_test_code_reading(bool try_kcore) goto out_put; } - cpus = perf_cpu_map__new(NULL); + cpus = perf_cpu_map__new_online_cpus(); if (!cpus) { pr_debug("perf_cpu_map__new failed\n"); goto out_put; } - while (1) { + while (events[evidx]) { const char *str; evlist = evlist__new(); if (!evlist) { - pr_debug("perf_evlist__new failed\n"); + pr_debug("evlist__new failed\n"); goto out_put; } perf_evlist__set_maps(&evlist->core, cpus, threads); - str = do_determine_event(excl_kernel); + str = events[evidx]; pr_debug("Parsing event '%s'\n", str); - ret = parse_events(evlist, str, NULL); + ret = parse_event(evlist, str); if (ret < 0) { pr_debug("parse_events failed\n"); goto out_put; } - perf_evlist__config(evlist, &opts, NULL); - - evsel = evlist__first(evlist); + evlist__config(evlist, &opts, NULL); - evsel->core.attr.comm = 1; - evsel->core.attr.disabled = 1; - evsel->core.attr.enable_on_exec = 0; + evlist__for_each_entry(evlist, evsel) { + evsel->core.attr.comm = 1; + evsel->core.attr.disabled = 1; + evsel->core.attr.enable_on_exec = 0; + } ret = evlist__open(evlist); if (ret < 0) { - if (!excl_kernel) { - excl_kernel = true; - /* - * Both cpus and threads are now owned by evlist - * and will be freed by following perf_evlist__set_maps - * call. Getting refference to keep them alive. - */ - perf_cpu_map__get(cpus); - perf_thread_map__get(threads); - perf_evlist__set_maps(&evlist->core, NULL, NULL); - evlist__delete(evlist); - evlist = NULL; - continue; - } + evidx++; - if (verbose > 0) { + if (events[evidx] == NULL && verbose > 0) { char errbuf[512]; evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf)); pr_debug("perf_evlist__open() failed!\n%s\n", errbuf); } - goto out_put; + /* + * Both cpus and threads are now owned by evlist + * and will be freed by following perf_evlist__set_maps + * call. Getting reference to keep them alive. + */ + perf_cpu_map__get(cpus); + perf_thread_map__get(threads); + perf_evlist__set_maps(&evlist->core, NULL, NULL); + evlist__delete(evlist); + evlist = NULL; + continue; } break; } + if (events[evidx] == NULL) + goto out_put; + ret = evlist__mmap(evlist, UINT_MAX); if (ret < 0) { pr_debug("evlist__mmap failed\n"); @@ -707,27 +691,22 @@ static int do_test_code_reading(bool try_kcore) err = TEST_CODE_READING_NO_KERNEL_OBJ; else if (!have_vmlinux && !try_kcore) err = TEST_CODE_READING_NO_VMLINUX; - else if (excl_kernel) + else if (strstr(events[evidx], ":u")) err = TEST_CODE_READING_NO_ACCESS; else err = TEST_CODE_READING_OK; out_put: thread__put(thread); out_err: - - if (evlist) { - evlist__delete(evlist); - } else { - perf_cpu_map__put(cpus); - perf_thread_map__put(threads); - } - machine__delete_threads(machine); + evlist__delete(evlist); + perf_cpu_map__put(cpus); + perf_thread_map__put(threads); machine__delete(machine); return err; } -int test__code_reading(struct test *test __maybe_unused, int subtest __maybe_unused) +static int test__code_reading(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { int ret; @@ -754,3 +733,5 @@ int test__code_reading(struct test *test __maybe_unused, int subtest __maybe_unu return -1; }; } + +DEFINE_SUITE("Object code reading", code_reading); |