diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/perf/Makefile.perf | 4 | ||||
| -rw-r--r-- | tools/perf/arch/x86/tests/dwarf-unwind.c | 2 | ||||
| -rw-r--r-- | tools/perf/perf.h | 6 | ||||
| -rw-r--r-- | tools/perf/tests/builtin-test.c | 12 | ||||
| -rw-r--r-- | tools/perf/tests/hists_common.c | 148 | ||||
| -rw-r--r-- | tools/perf/tests/hists_common.h | 44 | ||||
| -rw-r--r-- | tools/perf/tests/hists_filter.c | 315 | ||||
| -rw-r--r-- | tools/perf/tests/hists_link.c | 141 | ||||
| -rw-r--r-- | tools/perf/tests/mmap-thread-lookup.c | 233 | ||||
| -rw-r--r-- | tools/perf/tests/tests.h | 3 | ||||
| -rw-r--r-- | tools/perf/tests/thread-mg-share.c | 90 | ||||
| -rw-r--r-- | tools/perf/ui/stdio/hist.c | 2 | ||||
| -rw-r--r-- | tools/perf/util/event.c | 2 | ||||
| -rw-r--r-- | tools/perf/util/machine.c | 11 | ||||
| -rw-r--r-- | tools/perf/util/map.c | 23 | ||||
| -rw-r--r-- | tools/perf/util/map.h | 12 | ||||
| -rw-r--r-- | tools/perf/util/thread.c | 52 | ||||
| -rw-r--r-- | tools/perf/util/thread.h | 3 |
18 files changed, 948 insertions, 155 deletions
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index e96923310d57..46e0c32ad23b 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
| @@ -397,7 +397,9 @@ LIB_OBJS += $(OUTPUT)tests/rdpmc.o | |||
| 397 | LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o | 397 | LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o |
| 398 | LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o | 398 | LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o |
| 399 | LIB_OBJS += $(OUTPUT)tests/pmu.o | 399 | LIB_OBJS += $(OUTPUT)tests/pmu.o |
| 400 | LIB_OBJS += $(OUTPUT)tests/hists_common.o | ||
| 400 | LIB_OBJS += $(OUTPUT)tests/hists_link.o | 401 | LIB_OBJS += $(OUTPUT)tests/hists_link.o |
| 402 | LIB_OBJS += $(OUTPUT)tests/hists_filter.o | ||
| 401 | LIB_OBJS += $(OUTPUT)tests/python-use.o | 403 | LIB_OBJS += $(OUTPUT)tests/python-use.o |
| 402 | LIB_OBJS += $(OUTPUT)tests/bp_signal.o | 404 | LIB_OBJS += $(OUTPUT)tests/bp_signal.o |
| 403 | LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o | 405 | LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o |
| @@ -414,6 +416,8 @@ ifeq ($(ARCH),x86) | |||
| 414 | LIB_OBJS += $(OUTPUT)tests/dwarf-unwind.o | 416 | LIB_OBJS += $(OUTPUT)tests/dwarf-unwind.o |
| 415 | endif | 417 | endif |
| 416 | endif | 418 | endif |
| 419 | LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o | ||
| 420 | LIB_OBJS += $(OUTPUT)tests/thread-mg-share.o | ||
| 417 | 421 | ||
| 418 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o | 422 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o |
| 419 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o | 423 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o |
diff --git a/tools/perf/arch/x86/tests/dwarf-unwind.c b/tools/perf/arch/x86/tests/dwarf-unwind.c index b8c0102c70c8..c916656259f7 100644 --- a/tools/perf/arch/x86/tests/dwarf-unwind.c +++ b/tools/perf/arch/x86/tests/dwarf-unwind.c | |||
| @@ -23,7 +23,7 @@ static int sample_ustack(struct perf_sample *sample, | |||
| 23 | 23 | ||
| 24 | sp = (unsigned long) regs[PERF_REG_X86_SP]; | 24 | sp = (unsigned long) regs[PERF_REG_X86_SP]; |
| 25 | 25 | ||
| 26 | map = map_groups__find(&thread->mg, MAP__FUNCTION, (u64) sp); | 26 | map = map_groups__find(thread->mg, MAP__FUNCTION, (u64) sp); |
| 27 | if (!map) { | 27 | if (!map) { |
| 28 | pr_debug("failed to get stack map\n"); | 28 | pr_debug("failed to get stack map\n"); |
| 29 | free(buf); | 29 | free(buf); |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 5c11ecad02a9..ebdad3376c67 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
| @@ -15,6 +15,9 @@ | |||
| 15 | #ifndef __NR_futex | 15 | #ifndef __NR_futex |
| 16 | # define __NR_futex 240 | 16 | # define __NR_futex 240 |
| 17 | #endif | 17 | #endif |
| 18 | #ifndef __NR_gettid | ||
| 19 | # define __NR_gettid 224 | ||
| 20 | #endif | ||
| 18 | #endif | 21 | #endif |
| 19 | 22 | ||
| 20 | #if defined(__x86_64__) | 23 | #if defined(__x86_64__) |
| @@ -29,6 +32,9 @@ | |||
| 29 | #ifndef __NR_futex | 32 | #ifndef __NR_futex |
| 30 | # define __NR_futex 202 | 33 | # define __NR_futex 202 |
| 31 | #endif | 34 | #endif |
| 35 | #ifndef __NR_gettid | ||
| 36 | # define __NR_gettid 186 | ||
| 37 | #endif | ||
| 32 | #endif | 38 | #endif |
| 33 | 39 | ||
| 34 | #ifdef __powerpc__ | 40 | #ifdef __powerpc__ |
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index b11bf8a08430..0d5afaf72944 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
| @@ -124,6 +124,18 @@ static struct test { | |||
| 124 | #endif | 124 | #endif |
| 125 | #endif | 125 | #endif |
| 126 | { | 126 | { |
| 127 | .desc = "Test filtering hist entries", | ||
| 128 | .func = test__hists_filter, | ||
| 129 | }, | ||
| 130 | { | ||
| 131 | .desc = "Test mmap thread lookup", | ||
| 132 | .func = test__mmap_thread_lookup, | ||
| 133 | }, | ||
| 134 | { | ||
| 135 | .desc = "Test thread mg sharing", | ||
| 136 | .func = test__thread_mg_share, | ||
| 137 | }, | ||
| 138 | { | ||
| 127 | .func = NULL, | 139 | .func = NULL, |
| 128 | }, | 140 | }, |
| 129 | }; | 141 | }; |
diff --git a/tools/perf/tests/hists_common.c b/tools/perf/tests/hists_common.c new file mode 100644 index 000000000000..44655b395bb9 --- /dev/null +++ b/tools/perf/tests/hists_common.c | |||
| @@ -0,0 +1,148 @@ | |||
| 1 | #include "perf.h" | ||
| 2 | #include "util/debug.h" | ||
| 3 | #include "util/symbol.h" | ||
| 4 | #include "util/sort.h" | ||
| 5 | #include "util/evsel.h" | ||
| 6 | #include "util/evlist.h" | ||
| 7 | #include "util/machine.h" | ||
| 8 | #include "util/thread.h" | ||
| 9 | #include "tests/hists_common.h" | ||
| 10 | |||
| 11 | static struct { | ||
| 12 | u32 pid; | ||
| 13 | const char *comm; | ||
| 14 | } fake_threads[] = { | ||
| 15 | { 100, "perf" }, | ||
| 16 | { 200, "perf" }, | ||
| 17 | { 300, "bash" }, | ||
| 18 | }; | ||
| 19 | |||
| 20 | static struct { | ||
| 21 | u32 pid; | ||
| 22 | u64 start; | ||
| 23 | const char *filename; | ||
| 24 | } fake_mmap_info[] = { | ||
| 25 | { 100, 0x40000, "perf" }, | ||
| 26 | { 100, 0x50000, "libc" }, | ||
| 27 | { 100, 0xf0000, "[kernel]" }, | ||
| 28 | { 200, 0x40000, "perf" }, | ||
| 29 | { 200, 0x50000, "libc" }, | ||
| 30 | { 200, 0xf0000, "[kernel]" }, | ||
| 31 | { 300, 0x40000, "bash" }, | ||
| 32 | { 300, 0x50000, "libc" }, | ||
| 33 | { 300, 0xf0000, "[kernel]" }, | ||
| 34 | }; | ||
| 35 | |||
| 36 | struct fake_sym { | ||
| 37 | u64 start; | ||
| 38 | u64 length; | ||
| 39 | const char *name; | ||
| 40 | }; | ||
| 41 | |||
| 42 | static struct fake_sym perf_syms[] = { | ||
| 43 | { 700, 100, "main" }, | ||
| 44 | { 800, 100, "run_command" }, | ||
| 45 | { 900, 100, "cmd_record" }, | ||
| 46 | }; | ||
| 47 | |||
| 48 | static struct fake_sym bash_syms[] = { | ||
| 49 | { 700, 100, "main" }, | ||
| 50 | { 800, 100, "xmalloc" }, | ||
| 51 | { 900, 100, "xfree" }, | ||
| 52 | }; | ||
| 53 | |||
| 54 | static struct fake_sym libc_syms[] = { | ||
| 55 | { 700, 100, "malloc" }, | ||
| 56 | { 800, 100, "free" }, | ||
| 57 | { 900, 100, "realloc" }, | ||
| 58 | }; | ||
| 59 | |||
| 60 | static struct fake_sym kernel_syms[] = { | ||
| 61 | { 700, 100, "schedule" }, | ||
| 62 | { 800, 100, "page_fault" }, | ||
| 63 | { 900, 100, "sys_perf_event_open" }, | ||
| 64 | }; | ||
| 65 | |||
| 66 | static struct { | ||
| 67 | const char *dso_name; | ||
| 68 | struct fake_sym *syms; | ||
| 69 | size_t nr_syms; | ||
| 70 | } fake_symbols[] = { | ||
| 71 | { "perf", perf_syms, ARRAY_SIZE(perf_syms) }, | ||
| 72 | { "bash", bash_syms, ARRAY_SIZE(bash_syms) }, | ||
| 73 | { "libc", libc_syms, ARRAY_SIZE(libc_syms) }, | ||
| 74 | { "[kernel]", kernel_syms, ARRAY_SIZE(kernel_syms) }, | ||
| 75 | }; | ||
| 76 | |||
| 77 | struct machine *setup_fake_machine(struct machines *machines) | ||
| 78 | { | ||
| 79 | struct machine *machine = machines__find(machines, HOST_KERNEL_ID); | ||
| 80 | size_t i; | ||
| 81 | |||
| 82 | if (machine == NULL) { | ||
| 83 | pr_debug("Not enough memory for machine setup\n"); | ||
| 84 | return NULL; | ||
| 85 | } | ||
| 86 | |||
| 87 | for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { | ||
| 88 | struct thread *thread; | ||
| 89 | |||
| 90 | thread = machine__findnew_thread(machine, fake_threads[i].pid, | ||
| 91 | fake_threads[i].pid); | ||
| 92 | if (thread == NULL) | ||
| 93 | goto out; | ||
| 94 | |||
| 95 | thread__set_comm(thread, fake_threads[i].comm, 0); | ||
| 96 | } | ||
| 97 | |||
| 98 | for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) { | ||
| 99 | union perf_event fake_mmap_event = { | ||
| 100 | .mmap = { | ||
| 101 | .header = { .misc = PERF_RECORD_MISC_USER, }, | ||
| 102 | .pid = fake_mmap_info[i].pid, | ||
| 103 | .tid = fake_mmap_info[i].pid, | ||
| 104 | .start = fake_mmap_info[i].start, | ||
| 105 | .len = 0x1000ULL, | ||
| 106 | .pgoff = 0ULL, | ||
| 107 | }, | ||
| 108 | }; | ||
| 109 | |||
| 110 | strcpy(fake_mmap_event.mmap.filename, | ||
| 111 | fake_mmap_info[i].filename); | ||
| 112 | |||
| 113 | machine__process_mmap_event(machine, &fake_mmap_event, NULL); | ||
| 114 | } | ||
| 115 | |||
| 116 | for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) { | ||
| 117 | size_t k; | ||
| 118 | struct dso *dso; | ||
| 119 | |||
| 120 | dso = __dsos__findnew(&machine->user_dsos, | ||
| 121 | fake_symbols[i].dso_name); | ||
| 122 | if (dso == NULL) | ||
| 123 | goto out; | ||
| 124 | |||
| 125 | /* emulate dso__load() */ | ||
| 126 | dso__set_loaded(dso, MAP__FUNCTION); | ||
| 127 | |||
| 128 | for (k = 0; k < fake_symbols[i].nr_syms; k++) { | ||
| 129 | struct symbol *sym; | ||
| 130 | struct fake_sym *fsym = &fake_symbols[i].syms[k]; | ||
| 131 | |||
| 132 | sym = symbol__new(fsym->start, fsym->length, | ||
| 133 | STB_GLOBAL, fsym->name); | ||
| 134 | if (sym == NULL) | ||
| 135 | goto out; | ||
| 136 | |||
| 137 | symbols__insert(&dso->symbols[MAP__FUNCTION], sym); | ||
| 138 | } | ||
| 139 | } | ||
| 140 | |||
| 141 | return machine; | ||
| 142 | |||
| 143 | out: | ||
| 144 | pr_debug("Not enough memory for machine setup\n"); | ||
| 145 | machine__delete_threads(machine); | ||
| 146 | machine__delete(machine); | ||
| 147 | return NULL; | ||
| 148 | } | ||
diff --git a/tools/perf/tests/hists_common.h b/tools/perf/tests/hists_common.h new file mode 100644 index 000000000000..2528b8fc105a --- /dev/null +++ b/tools/perf/tests/hists_common.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | #ifndef __PERF_TESTS__HISTS_COMMON_H__ | ||
| 2 | #define __PERF_TESTS__HISTS_COMMON_H__ | ||
| 3 | |||
| 4 | struct machine; | ||
| 5 | struct machines; | ||
| 6 | |||
| 7 | /* | ||
| 8 | * The setup_fake_machine() provides a test environment which consists | ||
| 9 | * of 3 processes that have 3 mappings and in turn, have 3 symbols | ||
| 10 | * respectively. See below table: | ||
| 11 | * | ||
| 12 | * Command: Pid Shared Object Symbol | ||
| 13 | * ............. ............. ................... | ||
| 14 | * perf: 100 perf main | ||
| 15 | * perf: 100 perf run_command | ||
| 16 | * perf: 100 perf comd_record | ||
| 17 | * perf: 100 libc malloc | ||
| 18 | * perf: 100 libc free | ||
| 19 | * perf: 100 libc realloc | ||
| 20 | * perf: 100 [kernel] schedule | ||
| 21 | * perf: 100 [kernel] page_fault | ||
| 22 | * perf: 100 [kernel] sys_perf_event_open | ||
| 23 | * perf: 200 perf main | ||
| 24 | * perf: 200 perf run_command | ||
| 25 | * perf: 200 perf comd_record | ||
| 26 | * perf: 200 libc malloc | ||
| 27 | * perf: 200 libc free | ||
| 28 | * perf: 200 libc realloc | ||
| 29 | * perf: 200 [kernel] schedule | ||
| 30 | * perf: 200 [kernel] page_fault | ||
| 31 | * perf: 200 [kernel] sys_perf_event_open | ||
| 32 | * bash: 300 bash main | ||
| 33 | * bash: 300 bash xmalloc | ||
| 34 | * bash: 300 bash xfree | ||
| 35 | * bash: 300 libc malloc | ||
| 36 | * bash: 300 libc free | ||
| 37 | * bash: 300 libc realloc | ||
| 38 | * bash: 300 [kernel] schedule | ||
| 39 | * bash: 300 [kernel] page_fault | ||
| 40 | * bash: 300 [kernel] sys_perf_event_open | ||
| 41 | */ | ||
| 42 | struct machine *setup_fake_machine(struct machines *machines); | ||
| 43 | |||
| 44 | #endif /* __PERF_TESTS__HISTS_COMMON_H__ */ | ||
diff --git a/tools/perf/tests/hists_filter.c b/tools/perf/tests/hists_filter.c new file mode 100644 index 000000000000..23dc2f4d12c3 --- /dev/null +++ b/tools/perf/tests/hists_filter.c | |||
| @@ -0,0 +1,315 @@ | |||
| 1 | #include "perf.h" | ||
| 2 | #include "util/debug.h" | ||
| 3 | #include "util/symbol.h" | ||
| 4 | #include "util/sort.h" | ||
| 5 | #include "util/evsel.h" | ||
| 6 | #include "util/evlist.h" | ||
| 7 | #include "util/machine.h" | ||
| 8 | #include "util/thread.h" | ||
| 9 | #include "util/parse-events.h" | ||
| 10 | #include "tests/tests.h" | ||
| 11 | #include "tests/hists_common.h" | ||
| 12 | |||
| 13 | struct sample { | ||
| 14 | u32 pid; | ||
| 15 | u64 ip; | ||
| 16 | struct thread *thread; | ||
| 17 | struct map *map; | ||
| 18 | struct symbol *sym; | ||
| 19 | }; | ||
| 20 | |||
| 21 | /* For the numbers, see hists_common.c */ | ||
| 22 | static struct sample fake_samples[] = { | ||
| 23 | /* perf [kernel] schedule() */ | ||
| 24 | { .pid = 100, .ip = 0xf0000 + 700, }, | ||
| 25 | /* perf [perf] main() */ | ||
| 26 | { .pid = 100, .ip = 0x40000 + 700, }, | ||
| 27 | /* perf [libc] malloc() */ | ||
| 28 | { .pid = 100, .ip = 0x50000 + 700, }, | ||
| 29 | /* perf [perf] main() */ | ||
| 30 | { .pid = 200, .ip = 0x40000 + 700, }, /* will be merged */ | ||
| 31 | /* perf [perf] cmd_record() */ | ||
| 32 | { .pid = 200, .ip = 0x40000 + 900, }, | ||
| 33 | /* perf [kernel] page_fault() */ | ||
| 34 | { .pid = 200, .ip = 0xf0000 + 800, }, | ||
| 35 | /* bash [bash] main() */ | ||
| 36 | { .pid = 300, .ip = 0x40000 + 700, }, | ||
| 37 | /* bash [bash] xmalloc() */ | ||
| 38 | { .pid = 300, .ip = 0x40000 + 800, }, | ||
| 39 | /* bash [libc] malloc() */ | ||
| 40 | { .pid = 300, .ip = 0x50000 + 700, }, | ||
| 41 | /* bash [kernel] page_fault() */ | ||
| 42 | { .pid = 300, .ip = 0xf0000 + 800, }, | ||
| 43 | }; | ||
| 44 | |||
| 45 | static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | ||
| 46 | { | ||
| 47 | struct perf_evsel *evsel; | ||
| 48 | struct addr_location al; | ||
| 49 | struct hist_entry *he; | ||
| 50 | struct perf_sample sample = { .cpu = 0, }; | ||
| 51 | size_t i; | ||
| 52 | |||
| 53 | /* | ||
| 54 | * each evsel will have 10 samples but the 4th sample | ||
| 55 | * (perf [perf] main) will be collapsed to an existing entry | ||
| 56 | * so total 9 entries will be in the tree. | ||
| 57 | */ | ||
| 58 | evlist__for_each(evlist, evsel) { | ||
| 59 | for (i = 0; i < ARRAY_SIZE(fake_samples); i++) { | ||
| 60 | const union perf_event event = { | ||
| 61 | .header = { | ||
| 62 | .misc = PERF_RECORD_MISC_USER, | ||
| 63 | }, | ||
| 64 | }; | ||
| 65 | |||
| 66 | /* make sure it has no filter at first */ | ||
| 67 | evsel->hists.thread_filter = NULL; | ||
| 68 | evsel->hists.dso_filter = NULL; | ||
| 69 | evsel->hists.symbol_filter_str = NULL; | ||
| 70 | |||
| 71 | sample.pid = fake_samples[i].pid; | ||
| 72 | sample.ip = fake_samples[i].ip; | ||
| 73 | |||
| 74 | if (perf_event__preprocess_sample(&event, machine, &al, | ||
| 75 | &sample) < 0) | ||
| 76 | goto out; | ||
| 77 | |||
| 78 | he = __hists__add_entry(&evsel->hists, &al, NULL, | ||
| 79 | NULL, NULL, 100, 1, 0); | ||
| 80 | if (he == NULL) | ||
| 81 | goto out; | ||
| 82 | |||
| 83 | fake_samples[i].thread = al.thread; | ||
| 84 | fake_samples[i].map = al.map; | ||
| 85 | fake_samples[i].sym = al.sym; | ||
| 86 | |||
| 87 | hists__inc_nr_events(he->hists, PERF_RECORD_SAMPLE); | ||
| 88 | if (!he->filtered) | ||
| 89 | he->hists->stats.nr_non_filtered_samples++; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | return 0; | ||
| 94 | |||
| 95 | out: | ||
| 96 | pr_debug("Not enough memory for adding a hist entry\n"); | ||
| 97 | return TEST_FAIL; | ||
| 98 | } | ||
| 99 | |||
| 100 | static void print_hists(struct hists *hists) | ||
| 101 | { | ||
| 102 | int i = 0; | ||
| 103 | struct rb_root *root; | ||
| 104 | struct rb_node *node; | ||
| 105 | |||
| 106 | root = &hists->entries; | ||
| 107 | |||
| 108 | pr_info("----- %s --------\n", __func__); | ||
| 109 | node = rb_first(root); | ||
| 110 | while (node) { | ||
| 111 | struct hist_entry *he; | ||
| 112 | |||
| 113 | he = rb_entry(node, struct hist_entry, rb_node); | ||
| 114 | |||
| 115 | if (!he->filtered) { | ||
| 116 | pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n", | ||
| 117 | i, thread__comm_str(he->thread), | ||
| 118 | he->ms.map->dso->short_name, | ||
| 119 | he->ms.sym->name, he->stat.period); | ||
| 120 | } | ||
| 121 | |||
| 122 | i++; | ||
| 123 | node = rb_next(node); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | int test__hists_filter(void) | ||
| 128 | { | ||
| 129 | int err = TEST_FAIL; | ||
| 130 | struct machines machines; | ||
| 131 | struct machine *machine; | ||
| 132 | struct perf_evsel *evsel; | ||
| 133 | struct perf_evlist *evlist = perf_evlist__new(); | ||
| 134 | |||
| 135 | TEST_ASSERT_VAL("No memory", evlist); | ||
| 136 | |||
| 137 | err = parse_events(evlist, "cpu-clock"); | ||
| 138 | if (err) | ||
| 139 | goto out; | ||
| 140 | err = parse_events(evlist, "task-clock"); | ||
| 141 | if (err) | ||
| 142 | goto out; | ||
| 143 | |||
| 144 | /* default sort order (comm,dso,sym) will be used */ | ||
| 145 | if (setup_sorting() < 0) | ||
| 146 | goto out; | ||
| 147 | |||
| 148 | machines__init(&machines); | ||
| 149 | |||
| 150 | /* setup threads/dso/map/symbols also */ | ||
| 151 | machine = setup_fake_machine(&machines); | ||
| 152 | if (!machine) | ||
| 153 | goto out; | ||
| 154 | |||
| 155 | if (verbose > 1) | ||
| 156 | machine__fprintf(machine, stderr); | ||
| 157 | |||
| 158 | /* process sample events */ | ||
| 159 | err = add_hist_entries(evlist, machine); | ||
| 160 | if (err < 0) | ||
| 161 | goto out; | ||
| 162 | |||
| 163 | evlist__for_each(evlist, evsel) { | ||
| 164 | struct hists *hists = &evsel->hists; | ||
| 165 | |||
| 166 | hists__collapse_resort(hists, NULL); | ||
| 167 | hists__output_resort(hists); | ||
| 168 | |||
| 169 | if (verbose > 2) { | ||
| 170 | pr_info("Normal histogram\n"); | ||
| 171 | print_hists(hists); | ||
| 172 | } | ||
| 173 | |||
| 174 | TEST_ASSERT_VAL("Invalid nr samples", | ||
| 175 | hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10); | ||
| 176 | TEST_ASSERT_VAL("Invalid nr hist entries", | ||
| 177 | hists->nr_entries == 9); | ||
| 178 | TEST_ASSERT_VAL("Invalid total period", | ||
| 179 | hists->stats.total_period == 1000); | ||
| 180 | TEST_ASSERT_VAL("Unmatched nr samples", | ||
| 181 | hists->stats.nr_events[PERF_RECORD_SAMPLE] == | ||
| 182 | hists->stats.nr_non_filtered_samples); | ||
| 183 | TEST_ASSERT_VAL("Unmatched nr hist entries", | ||
| 184 | hists->nr_entries == hists->nr_non_filtered_entries); | ||
| 185 | TEST_ASSERT_VAL("Unmatched total period", | ||
| 186 | hists->stats.total_period == | ||
| 187 | hists->stats.total_non_filtered_period); | ||
| 188 | |||
| 189 | /* now applying thread filter for 'bash' */ | ||
| 190 | evsel->hists.thread_filter = fake_samples[9].thread; | ||
| 191 | hists__filter_by_thread(hists); | ||
| 192 | |||
| 193 | if (verbose > 2) { | ||
| 194 | pr_info("Histogram for thread filter\n"); | ||
| 195 | print_hists(hists); | ||
| 196 | } | ||
| 197 | |||
| 198 | /* normal stats should be invariant */ | ||
| 199 | TEST_ASSERT_VAL("Invalid nr samples", | ||
| 200 | hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10); | ||
| 201 | TEST_ASSERT_VAL("Invalid nr hist entries", | ||
| 202 | hists->nr_entries == 9); | ||
| 203 | TEST_ASSERT_VAL("Invalid total period", | ||
| 204 | hists->stats.total_period == 1000); | ||
| 205 | |||
| 206 | /* but filter stats are changed */ | ||
| 207 | TEST_ASSERT_VAL("Unmatched nr samples for thread filter", | ||
| 208 | hists->stats.nr_non_filtered_samples == 4); | ||
| 209 | TEST_ASSERT_VAL("Unmatched nr hist entries for thread filter", | ||
| 210 | hists->nr_non_filtered_entries == 4); | ||
| 211 | TEST_ASSERT_VAL("Unmatched total period for thread filter", | ||
| 212 | hists->stats.total_non_filtered_period == 400); | ||
| 213 | |||
| 214 | /* remove thread filter first */ | ||
| 215 | evsel->hists.thread_filter = NULL; | ||
| 216 | hists__filter_by_thread(hists); | ||
| 217 | |||
| 218 | /* now applying dso filter for 'kernel' */ | ||
| 219 | evsel->hists.dso_filter = fake_samples[0].map->dso; | ||
| 220 | hists__filter_by_dso(hists); | ||
| 221 | |||
| 222 | if (verbose > 2) { | ||
| 223 | pr_info("Histogram for dso filter\n"); | ||
| 224 | print_hists(hists); | ||
| 225 | } | ||
| 226 | |||
| 227 | /* normal stats should be invariant */ | ||
| 228 | TEST_ASSERT_VAL("Invalid nr samples", | ||
| 229 | hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10); | ||
| 230 | TEST_ASSERT_VAL("Invalid nr hist entries", | ||
| 231 | hists->nr_entries == 9); | ||
| 232 | TEST_ASSERT_VAL("Invalid total period", | ||
| 233 | hists->stats.total_period == 1000); | ||
| 234 | |||
| 235 | /* but filter stats are changed */ | ||
| 236 | TEST_ASSERT_VAL("Unmatched nr samples for dso filter", | ||
| 237 | hists->stats.nr_non_filtered_samples == 3); | ||
| 238 | TEST_ASSERT_VAL("Unmatched nr hist entries for dso filter", | ||
| 239 | hists->nr_non_filtered_entries == 3); | ||
| 240 | TEST_ASSERT_VAL("Unmatched total period for dso filter", | ||
| 241 | hists->stats.total_non_filtered_period == 300); | ||
| 242 | |||
| 243 | /* remove dso filter first */ | ||
| 244 | evsel->hists.dso_filter = NULL; | ||
| 245 | hists__filter_by_dso(hists); | ||
| 246 | |||
| 247 | /* | ||
| 248 | * now applying symbol filter for 'main'. Also note that | ||
| 249 | * there's 3 samples that have 'main' symbol but the 4th | ||
| 250 | * entry of fake_samples was collapsed already so it won't | ||
| 251 | * be counted as a separate entry but the sample count and | ||
| 252 | * total period will be remained. | ||
| 253 | */ | ||
| 254 | evsel->hists.symbol_filter_str = "main"; | ||
| 255 | hists__filter_by_symbol(hists); | ||
| 256 | |||
| 257 | if (verbose > 2) { | ||
| 258 | pr_info("Histogram for symbol filter\n"); | ||
| 259 | print_hists(hists); | ||
| 260 | } | ||
| 261 | |||
| 262 | /* normal stats should be invariant */ | ||
| 263 | TEST_ASSERT_VAL("Invalid nr samples", | ||
| 264 | hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10); | ||
| 265 | TEST_ASSERT_VAL("Invalid nr hist entries", | ||
| 266 | hists->nr_entries == 9); | ||
| 267 | TEST_ASSERT_VAL("Invalid total period", | ||
| 268 | hists->stats.total_period == 1000); | ||
| 269 | |||
| 270 | /* but filter stats are changed */ | ||
| 271 | TEST_ASSERT_VAL("Unmatched nr samples for symbol filter", | ||
| 272 | hists->stats.nr_non_filtered_samples == 3); | ||
| 273 | TEST_ASSERT_VAL("Unmatched nr hist entries for symbol filter", | ||
| 274 | hists->nr_non_filtered_entries == 2); | ||
| 275 | TEST_ASSERT_VAL("Unmatched total period for symbol filter", | ||
| 276 | hists->stats.total_non_filtered_period == 300); | ||
| 277 | |||
| 278 | /* now applying all filters at once. */ | ||
| 279 | evsel->hists.thread_filter = fake_samples[1].thread; | ||
| 280 | evsel->hists.dso_filter = fake_samples[1].map->dso; | ||
| 281 | hists__filter_by_thread(hists); | ||
| 282 | hists__filter_by_dso(hists); | ||
| 283 | |||
| 284 | if (verbose > 2) { | ||
| 285 | pr_info("Histogram for all filters\n"); | ||
| 286 | print_hists(hists); | ||
| 287 | } | ||
| 288 | |||
| 289 | /* normal stats should be invariant */ | ||
| 290 | TEST_ASSERT_VAL("Invalid nr samples", | ||
| 291 | hists->stats.nr_events[PERF_RECORD_SAMPLE] == 10); | ||
| 292 | TEST_ASSERT_VAL("Invalid nr hist entries", | ||
| 293 | hists->nr_entries == 9); | ||
| 294 | TEST_ASSERT_VAL("Invalid total period", | ||
| 295 | hists->stats.total_period == 1000); | ||
| 296 | |||
| 297 | /* but filter stats are changed */ | ||
| 298 | TEST_ASSERT_VAL("Unmatched nr samples for all filter", | ||
| 299 | hists->stats.nr_non_filtered_samples == 2); | ||
| 300 | TEST_ASSERT_VAL("Unmatched nr hist entries for all filter", | ||
| 301 | hists->nr_non_filtered_entries == 1); | ||
| 302 | TEST_ASSERT_VAL("Unmatched total period for all filter", | ||
| 303 | hists->stats.total_non_filtered_period == 200); | ||
| 304 | } | ||
| 305 | |||
| 306 | |||
| 307 | err = TEST_OK; | ||
| 308 | |||
| 309 | out: | ||
| 310 | /* tear down everything */ | ||
| 311 | perf_evlist__delete(evlist); | ||
| 312 | machines__exit(&machines); | ||
| 313 | |||
| 314 | return err; | ||
| 315 | } | ||
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 7ccbc7b6ae77..e42d6790811a 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c | |||
| @@ -8,145 +8,7 @@ | |||
| 8 | #include "machine.h" | 8 | #include "machine.h" |
| 9 | #include "thread.h" | 9 | #include "thread.h" |
| 10 | #include "parse-events.h" | 10 | #include "parse-events.h" |
| 11 | 11 | #include "hists_common.h" | |
| 12 | static struct { | ||
| 13 | u32 pid; | ||
| 14 | const char *comm; | ||
| 15 | } fake_threads[] = { | ||
| 16 | { 100, "perf" }, | ||
| 17 | { 200, "perf" }, | ||
| 18 | { 300, "bash" }, | ||
| 19 | }; | ||
| 20 | |||
| 21 | static struct { | ||
| 22 | u32 pid; | ||
| 23 | u64 start; | ||
| 24 | const char *filename; | ||
| 25 | } fake_mmap_info[] = { | ||
| 26 | { 100, 0x40000, "perf" }, | ||
| 27 | { 100, 0x50000, "libc" }, | ||
| 28 | { 100, 0xf0000, "[kernel]" }, | ||
| 29 | { 200, 0x40000, "perf" }, | ||
| 30 | { 200, 0x50000, "libc" }, | ||
| 31 | { 200, 0xf0000, "[kernel]" }, | ||
| 32 | { 300, 0x40000, "bash" }, | ||
| 33 | { 300, 0x50000, "libc" }, | ||
| 34 | { 300, 0xf0000, "[kernel]" }, | ||
| 35 | }; | ||
| 36 | |||
| 37 | struct fake_sym { | ||
| 38 | u64 start; | ||
| 39 | u64 length; | ||
| 40 | const char *name; | ||
| 41 | }; | ||
| 42 | |||
| 43 | static struct fake_sym perf_syms[] = { | ||
| 44 | { 700, 100, "main" }, | ||
| 45 | { 800, 100, "run_command" }, | ||
| 46 | { 900, 100, "cmd_record" }, | ||
| 47 | }; | ||
| 48 | |||
| 49 | static struct fake_sym bash_syms[] = { | ||
| 50 | { 700, 100, "main" }, | ||
| 51 | { 800, 100, "xmalloc" }, | ||
| 52 | { 900, 100, "xfree" }, | ||
| 53 | }; | ||
| 54 | |||
| 55 | static struct fake_sym libc_syms[] = { | ||
| 56 | { 700, 100, "malloc" }, | ||
| 57 | { 800, 100, "free" }, | ||
| 58 | { 900, 100, "realloc" }, | ||
| 59 | }; | ||
| 60 | |||
| 61 | static struct fake_sym kernel_syms[] = { | ||
| 62 | { 700, 100, "schedule" }, | ||
| 63 | { 800, 100, "page_fault" }, | ||
| 64 | { 900, 100, "sys_perf_event_open" }, | ||
| 65 | }; | ||
| 66 | |||
| 67 | static struct { | ||
| 68 | const char *dso_name; | ||
| 69 | struct fake_sym *syms; | ||
| 70 | size_t nr_syms; | ||
| 71 | } fake_symbols[] = { | ||
| 72 | { "perf", perf_syms, ARRAY_SIZE(perf_syms) }, | ||
| 73 | { "bash", bash_syms, ARRAY_SIZE(bash_syms) }, | ||
| 74 | { "libc", libc_syms, ARRAY_SIZE(libc_syms) }, | ||
| 75 | { "[kernel]", kernel_syms, ARRAY_SIZE(kernel_syms) }, | ||
| 76 | }; | ||
| 77 | |||
| 78 | static struct machine *setup_fake_machine(struct machines *machines) | ||
| 79 | { | ||
| 80 | struct machine *machine = machines__find(machines, HOST_KERNEL_ID); | ||
| 81 | size_t i; | ||
| 82 | |||
| 83 | if (machine == NULL) { | ||
| 84 | pr_debug("Not enough memory for machine setup\n"); | ||
| 85 | return NULL; | ||
| 86 | } | ||
| 87 | |||
| 88 | for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { | ||
| 89 | struct thread *thread; | ||
| 90 | |||
| 91 | thread = machine__findnew_thread(machine, fake_threads[i].pid, | ||
| 92 | fake_threads[i].pid); | ||
| 93 | if (thread == NULL) | ||
| 94 | goto out; | ||
| 95 | |||
| 96 | thread__set_comm(thread, fake_threads[i].comm, 0); | ||
| 97 | } | ||
| 98 | |||
| 99 | for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) { | ||
| 100 | union perf_event fake_mmap_event = { | ||
| 101 | .mmap = { | ||
| 102 | .header = { .misc = PERF_RECORD_MISC_USER, }, | ||
| 103 | .pid = fake_mmap_info[i].pid, | ||
| 104 | .tid = fake_mmap_info[i].pid, | ||
| 105 | .start = fake_mmap_info[i].start, | ||
| 106 | .len = 0x1000ULL, | ||
| 107 | .pgoff = 0ULL, | ||
| 108 | }, | ||
| 109 | }; | ||
| 110 | |||
| 111 | strcpy(fake_mmap_event.mmap.filename, | ||
| 112 | fake_mmap_info[i].filename); | ||
| 113 | |||
| 114 | machine__process_mmap_event(machine, &fake_mmap_event, NULL); | ||
| 115 | } | ||
| 116 | |||
| 117 | for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) { | ||
| 118 | size_t k; | ||
| 119 | struct dso *dso; | ||
| 120 | |||
| 121 | dso = __dsos__findnew(&machine->user_dsos, | ||
| 122 | fake_symbols[i].dso_name); | ||
| 123 | if (dso == NULL) | ||
| 124 | goto out; | ||
| 125 | |||
| 126 | /* emulate dso__load() */ | ||
| 127 | dso__set_loaded(dso, MAP__FUNCTION); | ||
| 128 | |||
| 129 | for (k = 0; k < fake_symbols[i].nr_syms; k++) { | ||
| 130 | struct symbol *sym; | ||
| 131 | struct fake_sym *fsym = &fake_symbols[i].syms[k]; | ||
| 132 | |||
| 133 | sym = symbol__new(fsym->start, fsym->length, | ||
| 134 | STB_GLOBAL, fsym->name); | ||
| 135 | if (sym == NULL) | ||
| 136 | goto out; | ||
| 137 | |||
| 138 | symbols__insert(&dso->symbols[MAP__FUNCTION], sym); | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | return machine; | ||
| 143 | |||
| 144 | out: | ||
| 145 | pr_debug("Not enough memory for machine setup\n"); | ||
| 146 | machine__delete_threads(machine); | ||
| 147 | machine__delete(machine); | ||
| 148 | return NULL; | ||
| 149 | } | ||
| 150 | 12 | ||
| 151 | struct sample { | 13 | struct sample { |
| 152 | u32 pid; | 14 | u32 pid; |
| @@ -156,6 +18,7 @@ struct sample { | |||
| 156 | struct symbol *sym; | 18 | struct symbol *sym; |
| 157 | }; | 19 | }; |
| 158 | 20 | ||
| 21 | /* For the numbers, see hists_common.c */ | ||
| 159 | static struct sample fake_common_samples[] = { | 22 | static struct sample fake_common_samples[] = { |
| 160 | /* perf [kernel] schedule() */ | 23 | /* perf [kernel] schedule() */ |
| 161 | { .pid = 100, .ip = 0xf0000 + 700, }, | 24 | { .pid = 100, .ip = 0xf0000 + 700, }, |
diff --git a/tools/perf/tests/mmap-thread-lookup.c b/tools/perf/tests/mmap-thread-lookup.c new file mode 100644 index 000000000000..4a456fef66ca --- /dev/null +++ b/tools/perf/tests/mmap-thread-lookup.c | |||
| @@ -0,0 +1,233 @@ | |||
| 1 | #include <unistd.h> | ||
| 2 | #include <sys/syscall.h> | ||
| 3 | #include <sys/types.h> | ||
| 4 | #include <sys/mman.h> | ||
| 5 | #include <pthread.h> | ||
| 6 | #include <stdlib.h> | ||
| 7 | #include <stdio.h> | ||
| 8 | #include "debug.h" | ||
| 9 | #include "tests.h" | ||
| 10 | #include "machine.h" | ||
| 11 | #include "thread_map.h" | ||
| 12 | #include "symbol.h" | ||
| 13 | #include "thread.h" | ||
| 14 | |||
| 15 | #define THREADS 4 | ||
| 16 | |||
| 17 | static int go_away; | ||
| 18 | |||
| 19 | struct thread_data { | ||
| 20 | pthread_t pt; | ||
| 21 | pid_t tid; | ||
| 22 | void *map; | ||
| 23 | int ready[2]; | ||
| 24 | }; | ||
| 25 | |||
| 26 | static struct thread_data threads[THREADS]; | ||
| 27 | |||
| 28 | static int thread_init(struct thread_data *td) | ||
| 29 | { | ||
| 30 | void *map; | ||
| 31 | |||
| 32 | map = mmap(NULL, page_size, | ||
| 33 | PROT_READ|PROT_WRITE|PROT_EXEC, | ||
| 34 | MAP_SHARED|MAP_ANONYMOUS, -1, 0); | ||
| 35 | |||
| 36 | if (map == MAP_FAILED) { | ||
| 37 | perror("mmap failed"); | ||
| 38 | return -1; | ||
| 39 | } | ||
| 40 | |||
| 41 | td->map = map; | ||
| 42 | td->tid = syscall(SYS_gettid); | ||
| 43 | |||
| 44 | pr_debug("tid = %d, map = %p\n", td->tid, map); | ||
| 45 | return 0; | ||
| 46 | } | ||
| 47 | |||
| 48 | static void *thread_fn(void *arg) | ||
| 49 | { | ||
| 50 | struct thread_data *td = arg; | ||
| 51 | ssize_t ret; | ||
| 52 | int go; | ||
| 53 | |||
| 54 | if (thread_init(td)) | ||
| 55 | return NULL; | ||
| 56 | |||
| 57 | /* Signal thread_create thread is initialized. */ | ||
| 58 | ret = write(td->ready[1], &go, sizeof(int)); | ||
| 59 | if (ret != sizeof(int)) { | ||
| 60 | pr_err("failed to notify\n"); | ||
| 61 | return NULL; | ||
| 62 | } | ||
| 63 | |||
| 64 | while (!go_away) { | ||
| 65 | /* Waiting for main thread to kill us. */ | ||
| 66 | usleep(100); | ||
| 67 | } | ||
| 68 | |||
| 69 | munmap(td->map, page_size); | ||
| 70 | return NULL; | ||
| 71 | } | ||
| 72 | |||
| 73 | static int thread_create(int i) | ||
| 74 | { | ||
| 75 | struct thread_data *td = &threads[i]; | ||
| 76 | int err, go; | ||
| 77 | |||
| 78 | if (pipe(td->ready)) | ||
| 79 | return -1; | ||
| 80 | |||
| 81 | err = pthread_create(&td->pt, NULL, thread_fn, td); | ||
| 82 | if (!err) { | ||
| 83 | /* Wait for thread initialization. */ | ||
| 84 | ssize_t ret = read(td->ready[0], &go, sizeof(int)); | ||
| 85 | err = ret != sizeof(int); | ||
| 86 | } | ||
| 87 | |||
| 88 | close(td->ready[0]); | ||
| 89 | close(td->ready[1]); | ||
| 90 | return err; | ||
| 91 | } | ||
| 92 | |||
| 93 | static int threads_create(void) | ||
| 94 | { | ||
| 95 | struct thread_data *td0 = &threads[0]; | ||
| 96 | int i, err = 0; | ||
| 97 | |||
| 98 | go_away = 0; | ||
| 99 | |||
| 100 | /* 0 is main thread */ | ||
| 101 | if (thread_init(td0)) | ||
| 102 | return -1; | ||
| 103 | |||
| 104 | for (i = 1; !err && i < THREADS; i++) | ||
| 105 | err = thread_create(i); | ||
| 106 | |||
| 107 | return err; | ||
| 108 | } | ||
| 109 | |||
| 110 | static int threads_destroy(void) | ||
| 111 | { | ||
| 112 | struct thread_data *td0 = &threads[0]; | ||
| 113 | int i, err = 0; | ||
| 114 | |||
| 115 | /* cleanup the main thread */ | ||
| 116 | munmap(td0->map, page_size); | ||
| 117 | |||
| 118 | go_away = 1; | ||
| 119 | |||
| 120 | for (i = 1; !err && i < THREADS; i++) | ||
| 121 | err = pthread_join(threads[i].pt, NULL); | ||
| 122 | |||
| 123 | return err; | ||
| 124 | } | ||
| 125 | |||
| 126 | typedef int (*synth_cb)(struct machine *machine); | ||
| 127 | |||
| 128 | static int synth_all(struct machine *machine) | ||
| 129 | { | ||
| 130 | return perf_event__synthesize_threads(NULL, | ||
| 131 | perf_event__process, | ||
| 132 | machine, 0); | ||
| 133 | } | ||
| 134 | |||
| 135 | static int synth_process(struct machine *machine) | ||
| 136 | { | ||
| 137 | struct thread_map *map; | ||
| 138 | int err; | ||
| 139 | |||
| 140 | map = thread_map__new_by_pid(getpid()); | ||
| 141 | |||
| 142 | err = perf_event__synthesize_thread_map(NULL, map, | ||
| 143 | perf_event__process, | ||
| 144 | machine, 0); | ||
| 145 | |||
| 146 | thread_map__delete(map); | ||
| 147 | return err; | ||
| 148 | } | ||
| 149 | |||
| 150 | static int mmap_events(synth_cb synth) | ||
| 151 | { | ||
| 152 | struct machines machines; | ||
| 153 | struct machine *machine; | ||
| 154 | int err, i; | ||
| 155 | |||
| 156 | /* | ||
| 157 | * The threads_create will not return before all threads | ||
| 158 | * are spawned and all created memory map. | ||
| 159 | * | ||
| 160 | * They will loop until threads_destroy is called, so we | ||
| 161 | * can safely run synthesizing function. | ||
| 162 | */ | ||
| 163 | TEST_ASSERT_VAL("failed to create threads", !threads_create()); | ||
| 164 | |||
| 165 | machines__init(&machines); | ||
| 166 | machine = &machines.host; | ||
| 167 | |||
| 168 | dump_trace = verbose > 1 ? 1 : 0; | ||
| 169 | |||
| 170 | err = synth(machine); | ||
| 171 | |||
| 172 | dump_trace = 0; | ||
| 173 | |||
| 174 | TEST_ASSERT_VAL("failed to destroy threads", !threads_destroy()); | ||
| 175 | TEST_ASSERT_VAL("failed to synthesize maps", !err); | ||
| 176 | |||
| 177 | /* | ||
| 178 | * All data is synthesized, try to find map for each | ||
| 179 | * thread object. | ||
| 180 | */ | ||
| 181 | for (i = 0; i < THREADS; i++) { | ||
| 182 | struct thread_data *td = &threads[i]; | ||
| 183 | struct addr_location al; | ||
| 184 | struct thread *thread; | ||
| 185 | |||
| 186 | thread = machine__findnew_thread(machine, getpid(), td->tid); | ||
| 187 | |||
| 188 | pr_debug("looking for map %p\n", td->map); | ||
| 189 | |||
| 190 | thread__find_addr_map(thread, machine, | ||
| 191 | PERF_RECORD_MISC_USER, MAP__FUNCTION, | ||
| 192 | (unsigned long) (td->map + 1), &al); | ||
| 193 | |||
| 194 | if (!al.map) { | ||
| 195 | pr_debug("failed, couldn't find map\n"); | ||
| 196 | err = -1; | ||
| 197 | break; | ||
| 198 | } | ||
| 199 | |||
| 200 | pr_debug("map %p, addr %" PRIx64 "\n", al.map, al.map->start); | ||
| 201 | } | ||
| 202 | |||
| 203 | machine__delete_threads(machine); | ||
| 204 | machines__exit(&machines); | ||
| 205 | return err; | ||
| 206 | } | ||
| 207 | |||
| 208 | /* | ||
| 209 | * This test creates 'THREADS' number of threads (including | ||
| 210 | * main thread) and each thread creates memory map. | ||
| 211 | * | ||
| 212 | * When threads are created, we synthesize them with both | ||
| 213 | * (separate tests): | ||
| 214 | * perf_event__synthesize_thread_map (process based) | ||
| 215 | * perf_event__synthesize_threads (global) | ||
| 216 | * | ||
| 217 | * We test we can find all memory maps via: | ||
| 218 | * thread__find_addr_map | ||
| 219 | * | ||
| 220 | * by using all thread objects. | ||
| 221 | */ | ||
| 222 | int test__mmap_thread_lookup(void) | ||
| 223 | { | ||
| 224 | /* perf_event__synthesize_threads synthesize */ | ||
| 225 | TEST_ASSERT_VAL("failed with sythesizing all", | ||
| 226 | !mmap_events(synth_all)); | ||
| 227 | |||
| 228 | /* perf_event__synthesize_thread_map synthesize */ | ||
| 229 | TEST_ASSERT_VAL("failed with sythesizing process", | ||
| 230 | !mmap_events(synth_process)); | ||
| 231 | |||
| 232 | return 0; | ||
| 233 | } | ||
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index a24795ca002d..a9d7cb019f9e 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
| @@ -41,6 +41,9 @@ int test__sample_parsing(void); | |||
| 41 | int test__keep_tracking(void); | 41 | int test__keep_tracking(void); |
| 42 | int test__parse_no_sample_id_all(void); | 42 | int test__parse_no_sample_id_all(void); |
| 43 | int test__dwarf_unwind(void); | 43 | int test__dwarf_unwind(void); |
| 44 | int test__hists_filter(void); | ||
| 45 | int test__mmap_thread_lookup(void); | ||
| 46 | int test__thread_mg_share(void); | ||
| 44 | 47 | ||
| 45 | #if defined(__x86_64__) || defined(__i386__) | 48 | #if defined(__x86_64__) || defined(__i386__) |
| 46 | #ifdef HAVE_DWARF_UNWIND_SUPPORT | 49 | #ifdef HAVE_DWARF_UNWIND_SUPPORT |
diff --git a/tools/perf/tests/thread-mg-share.c b/tools/perf/tests/thread-mg-share.c new file mode 100644 index 000000000000..2b2e0dbe114f --- /dev/null +++ b/tools/perf/tests/thread-mg-share.c | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | #include "tests.h" | ||
| 2 | #include "machine.h" | ||
| 3 | #include "thread.h" | ||
| 4 | #include "map.h" | ||
| 5 | |||
| 6 | int test__thread_mg_share(void) | ||
| 7 | { | ||
| 8 | struct machines machines; | ||
| 9 | struct machine *machine; | ||
| 10 | |||
| 11 | /* thread group */ | ||
| 12 | struct thread *leader; | ||
| 13 | struct thread *t1, *t2, *t3; | ||
| 14 | struct map_groups *mg; | ||
| 15 | |||
| 16 | /* other process */ | ||
| 17 | struct thread *other, *other_leader; | ||
| 18 | struct map_groups *other_mg; | ||
| 19 | |||
| 20 | /* | ||
| 21 | * This test create 2 processes abstractions (struct thread) | ||
| 22 | * with several threads and checks they properly share and | ||
| 23 | * maintain map groups info (struct map_groups). | ||
| 24 | * | ||
| 25 | * thread group (pid: 0, tids: 0, 1, 2, 3) | ||
| 26 | * other group (pid: 4, tids: 4, 5) | ||
| 27 | */ | ||
| 28 | |||
| 29 | machines__init(&machines); | ||
| 30 | machine = &machines.host; | ||
| 31 | |||
| 32 | /* create process with 4 threads */ | ||
| 33 | leader = machine__findnew_thread(machine, 0, 0); | ||
| 34 | t1 = machine__findnew_thread(machine, 0, 1); | ||
| 35 | t2 = machine__findnew_thread(machine, 0, 2); | ||
| 36 | t3 = machine__findnew_thread(machine, 0, 3); | ||
| 37 | |||
| 38 | /* and create 1 separated process, without thread leader */ | ||
| 39 | other = machine__findnew_thread(machine, 4, 5); | ||
| 40 | |||
| 41 | TEST_ASSERT_VAL("failed to create threads", | ||
| 42 | leader && t1 && t2 && t3 && other); | ||
| 43 | |||
| 44 | mg = leader->mg; | ||
| 45 | TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 4); | ||
| 46 | |||
| 47 | /* test the map groups pointer is shared */ | ||
| 48 | TEST_ASSERT_VAL("map groups don't match", mg == t1->mg); | ||
| 49 | TEST_ASSERT_VAL("map groups don't match", mg == t2->mg); | ||
| 50 | TEST_ASSERT_VAL("map groups don't match", mg == t3->mg); | ||
| 51 | |||
| 52 | /* | ||
| 53 | * Verify the other leader was created by previous call. | ||
| 54 | * It should have shared map groups with no change in | ||
| 55 | * refcnt. | ||
| 56 | */ | ||
| 57 | other_leader = machine__find_thread(machine, 4, 4); | ||
| 58 | TEST_ASSERT_VAL("failed to find other leader", other_leader); | ||
| 59 | |||
| 60 | other_mg = other->mg; | ||
| 61 | TEST_ASSERT_VAL("wrong refcnt", other_mg->refcnt == 2); | ||
| 62 | |||
| 63 | TEST_ASSERT_VAL("map groups don't match", other_mg == other_leader->mg); | ||
| 64 | |||
| 65 | /* release thread group */ | ||
| 66 | thread__delete(leader); | ||
| 67 | TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 3); | ||
| 68 | |||
| 69 | thread__delete(t1); | ||
| 70 | TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 2); | ||
| 71 | |||
| 72 | thread__delete(t2); | ||
| 73 | TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 1); | ||
| 74 | |||
| 75 | thread__delete(t3); | ||
| 76 | |||
| 77 | /* release other group */ | ||
| 78 | thread__delete(other_leader); | ||
| 79 | TEST_ASSERT_VAL("wrong refcnt", other_mg->refcnt == 1); | ||
| 80 | |||
| 81 | thread__delete(other); | ||
| 82 | |||
| 83 | /* | ||
| 84 | * Cannot call machine__delete_threads(machine) now, | ||
| 85 | * because we've already released all the threads. | ||
| 86 | */ | ||
| 87 | |||
| 88 | machines__exit(&machines); | ||
| 89 | return 0; | ||
| 90 | } | ||
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index d59893edf031..9eccf7f4f367 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c | |||
| @@ -495,7 +495,7 @@ print_entries: | |||
| 495 | break; | 495 | break; |
| 496 | 496 | ||
| 497 | if (h->ms.map == NULL && verbose > 1) { | 497 | if (h->ms.map == NULL && verbose > 1) { |
| 498 | __map_groups__fprintf_maps(&h->thread->mg, | 498 | __map_groups__fprintf_maps(h->thread->mg, |
| 499 | MAP__FUNCTION, verbose, fp); | 499 | MAP__FUNCTION, verbose, fp); |
| 500 | fprintf(fp, "%.10s end\n", graph_dotted_line); | 500 | fprintf(fp, "%.10s end\n", graph_dotted_line); |
| 501 | } | 501 | } |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 9d12aa6dd485..dbcaea1a8180 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
| @@ -699,7 +699,7 @@ void thread__find_addr_map(struct thread *thread, | |||
| 699 | enum map_type type, u64 addr, | 699 | enum map_type type, u64 addr, |
| 700 | struct addr_location *al) | 700 | struct addr_location *al) |
| 701 | { | 701 | { |
| 702 | struct map_groups *mg = &thread->mg; | 702 | struct map_groups *mg = thread->mg; |
| 703 | bool load_map = false; | 703 | bool load_map = false; |
| 704 | 704 | ||
| 705 | al->machine = machine; | 705 | al->machine = machine; |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index a53cd0b8c151..98ec56dc890b 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
| @@ -316,6 +316,17 @@ static struct thread *__machine__findnew_thread(struct machine *machine, | |||
| 316 | rb_link_node(&th->rb_node, parent, p); | 316 | rb_link_node(&th->rb_node, parent, p); |
| 317 | rb_insert_color(&th->rb_node, &machine->threads); | 317 | rb_insert_color(&th->rb_node, &machine->threads); |
| 318 | machine->last_match = th; | 318 | machine->last_match = th; |
| 319 | |||
| 320 | /* | ||
| 321 | * We have to initialize map_groups separately | ||
| 322 | * after rb tree is updated. | ||
| 323 | * | ||
| 324 | * The reason is that we call machine__findnew_thread | ||
| 325 | * within thread__init_map_groups to find the thread | ||
| 326 | * leader and that would screwed the rb tree. | ||
| 327 | */ | ||
| 328 | if (thread__init_map_groups(th, machine)) | ||
| 329 | return NULL; | ||
| 319 | } | 330 | } |
| 320 | 331 | ||
| 321 | return th; | 332 | return th; |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 39cd2d0faff6..ba5f5c0c838b 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
| @@ -323,6 +323,7 @@ void map_groups__init(struct map_groups *mg) | |||
| 323 | INIT_LIST_HEAD(&mg->removed_maps[i]); | 323 | INIT_LIST_HEAD(&mg->removed_maps[i]); |
| 324 | } | 324 | } |
| 325 | mg->machine = NULL; | 325 | mg->machine = NULL; |
| 326 | mg->refcnt = 1; | ||
| 326 | } | 327 | } |
| 327 | 328 | ||
| 328 | static void maps__delete(struct rb_root *maps) | 329 | static void maps__delete(struct rb_root *maps) |
| @@ -358,6 +359,28 @@ void map_groups__exit(struct map_groups *mg) | |||
| 358 | } | 359 | } |
| 359 | } | 360 | } |
| 360 | 361 | ||
| 362 | struct map_groups *map_groups__new(void) | ||
| 363 | { | ||
| 364 | struct map_groups *mg = malloc(sizeof(*mg)); | ||
| 365 | |||
| 366 | if (mg != NULL) | ||
| 367 | map_groups__init(mg); | ||
| 368 | |||
| 369 | return mg; | ||
| 370 | } | ||
| 371 | |||
| 372 | void map_groups__delete(struct map_groups *mg) | ||
| 373 | { | ||
| 374 | map_groups__exit(mg); | ||
| 375 | free(mg); | ||
| 376 | } | ||
| 377 | |||
| 378 | void map_groups__put(struct map_groups *mg) | ||
| 379 | { | ||
| 380 | if (--mg->refcnt == 0) | ||
| 381 | map_groups__delete(mg); | ||
| 382 | } | ||
| 383 | |||
| 361 | void map_groups__flush(struct map_groups *mg) | 384 | void map_groups__flush(struct map_groups *mg) |
| 362 | { | 385 | { |
| 363 | int type; | 386 | int type; |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index f00f058afb3b..d6445b27d672 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
| @@ -59,8 +59,20 @@ struct map_groups { | |||
| 59 | struct rb_root maps[MAP__NR_TYPES]; | 59 | struct rb_root maps[MAP__NR_TYPES]; |
| 60 | struct list_head removed_maps[MAP__NR_TYPES]; | 60 | struct list_head removed_maps[MAP__NR_TYPES]; |
| 61 | struct machine *machine; | 61 | struct machine *machine; |
| 62 | int refcnt; | ||
| 62 | }; | 63 | }; |
| 63 | 64 | ||
| 65 | struct map_groups *map_groups__new(void); | ||
| 66 | void map_groups__delete(struct map_groups *mg); | ||
| 67 | |||
| 68 | static inline struct map_groups *map_groups__get(struct map_groups *mg) | ||
| 69 | { | ||
| 70 | ++mg->refcnt; | ||
| 71 | return mg; | ||
| 72 | } | ||
| 73 | |||
| 74 | void map_groups__put(struct map_groups *mg); | ||
| 75 | |||
| 64 | static inline struct kmap *map__kmap(struct map *map) | 76 | static inline struct kmap *map__kmap(struct map *map) |
| 65 | { | 77 | { |
| 66 | return (struct kmap *)(map + 1); | 78 | return (struct kmap *)(map + 1); |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 3ce0498bdae6..2fde0d5e40b5 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
| @@ -8,6 +8,22 @@ | |||
| 8 | #include "debug.h" | 8 | #include "debug.h" |
| 9 | #include "comm.h" | 9 | #include "comm.h" |
| 10 | 10 | ||
| 11 | int thread__init_map_groups(struct thread *thread, struct machine *machine) | ||
| 12 | { | ||
| 13 | struct thread *leader; | ||
| 14 | pid_t pid = thread->pid_; | ||
| 15 | |||
| 16 | if (pid == thread->tid) { | ||
| 17 | thread->mg = map_groups__new(); | ||
| 18 | } else { | ||
| 19 | leader = machine__findnew_thread(machine, pid, pid); | ||
| 20 | if (leader) | ||
| 21 | thread->mg = map_groups__get(leader->mg); | ||
| 22 | } | ||
| 23 | |||
| 24 | return thread->mg ? 0 : -1; | ||
| 25 | } | ||
| 26 | |||
| 11 | struct thread *thread__new(pid_t pid, pid_t tid) | 27 | struct thread *thread__new(pid_t pid, pid_t tid) |
| 12 | { | 28 | { |
| 13 | char *comm_str; | 29 | char *comm_str; |
| @@ -15,7 +31,6 @@ struct thread *thread__new(pid_t pid, pid_t tid) | |||
| 15 | struct thread *thread = zalloc(sizeof(*thread)); | 31 | struct thread *thread = zalloc(sizeof(*thread)); |
| 16 | 32 | ||
| 17 | if (thread != NULL) { | 33 | if (thread != NULL) { |
| 18 | map_groups__init(&thread->mg); | ||
| 19 | thread->pid_ = pid; | 34 | thread->pid_ = pid; |
| 20 | thread->tid = tid; | 35 | thread->tid = tid; |
| 21 | thread->ppid = -1; | 36 | thread->ppid = -1; |
| @@ -45,7 +60,8 @@ void thread__delete(struct thread *thread) | |||
| 45 | { | 60 | { |
| 46 | struct comm *comm, *tmp; | 61 | struct comm *comm, *tmp; |
| 47 | 62 | ||
| 48 | map_groups__exit(&thread->mg); | 63 | map_groups__put(thread->mg); |
| 64 | thread->mg = NULL; | ||
| 49 | list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { | 65 | list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { |
| 50 | list_del(&comm->list); | 66 | list_del(&comm->list); |
| 51 | comm__free(comm); | 67 | comm__free(comm); |
| @@ -111,18 +127,35 @@ int thread__comm_len(struct thread *thread) | |||
| 111 | size_t thread__fprintf(struct thread *thread, FILE *fp) | 127 | size_t thread__fprintf(struct thread *thread, FILE *fp) |
| 112 | { | 128 | { |
| 113 | return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) + | 129 | return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) + |
| 114 | map_groups__fprintf(&thread->mg, verbose, fp); | 130 | map_groups__fprintf(thread->mg, verbose, fp); |
| 115 | } | 131 | } |
| 116 | 132 | ||
| 117 | void thread__insert_map(struct thread *thread, struct map *map) | 133 | void thread__insert_map(struct thread *thread, struct map *map) |
| 118 | { | 134 | { |
| 119 | map_groups__fixup_overlappings(&thread->mg, map, verbose, stderr); | 135 | map_groups__fixup_overlappings(thread->mg, map, verbose, stderr); |
| 120 | map_groups__insert(&thread->mg, map); | 136 | map_groups__insert(thread->mg, map); |
| 137 | } | ||
| 138 | |||
| 139 | static int thread__clone_map_groups(struct thread *thread, | ||
| 140 | struct thread *parent) | ||
| 141 | { | ||
| 142 | int i; | ||
| 143 | |||
| 144 | /* This is new thread, we share map groups for process. */ | ||
| 145 | if (thread->pid_ == parent->pid_) | ||
| 146 | return 0; | ||
| 147 | |||
| 148 | /* But this one is new process, copy maps. */ | ||
| 149 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
| 150 | if (map_groups__clone(thread->mg, parent->mg, i) < 0) | ||
| 151 | return -ENOMEM; | ||
| 152 | |||
| 153 | return 0; | ||
| 121 | } | 154 | } |
| 122 | 155 | ||
| 123 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) | 156 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) |
| 124 | { | 157 | { |
| 125 | int i, err; | 158 | int err; |
| 126 | 159 | ||
| 127 | if (parent->comm_set) { | 160 | if (parent->comm_set) { |
| 128 | const char *comm = thread__comm_str(parent); | 161 | const char *comm = thread__comm_str(parent); |
| @@ -134,13 +167,8 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) | |||
| 134 | thread->comm_set = true; | 167 | thread->comm_set = true; |
| 135 | } | 168 | } |
| 136 | 169 | ||
| 137 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
| 138 | if (map_groups__clone(&thread->mg, &parent->mg, i) < 0) | ||
| 139 | return -ENOMEM; | ||
| 140 | |||
| 141 | thread->ppid = parent->tid; | 170 | thread->ppid = parent->tid; |
| 142 | 171 | return thread__clone_map_groups(thread, parent); | |
| 143 | return 0; | ||
| 144 | } | 172 | } |
| 145 | 173 | ||
| 146 | void thread__find_cpumode_addr_location(struct thread *thread, | 174 | void thread__find_cpumode_addr_location(struct thread *thread, |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 9b29f085aede..3c0c2724f82c 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
| @@ -13,7 +13,7 @@ struct thread { | |||
| 13 | struct rb_node rb_node; | 13 | struct rb_node rb_node; |
| 14 | struct list_head node; | 14 | struct list_head node; |
| 15 | }; | 15 | }; |
| 16 | struct map_groups mg; | 16 | struct map_groups *mg; |
| 17 | pid_t pid_; /* Not all tools update this */ | 17 | pid_t pid_; /* Not all tools update this */ |
| 18 | pid_t tid; | 18 | pid_t tid; |
| 19 | pid_t ppid; | 19 | pid_t ppid; |
| @@ -30,6 +30,7 @@ struct machine; | |||
| 30 | struct comm; | 30 | struct comm; |
| 31 | 31 | ||
| 32 | struct thread *thread__new(pid_t pid, pid_t tid); | 32 | struct thread *thread__new(pid_t pid, pid_t tid); |
| 33 | int thread__init_map_groups(struct thread *thread, struct machine *machine); | ||
| 33 | void thread__delete(struct thread *thread); | 34 | void thread__delete(struct thread *thread); |
| 34 | static inline void thread__exited(struct thread *thread) | 35 | static inline void thread__exited(struct thread *thread) |
| 35 | { | 36 | { |
