diff options
author | Ingo Molnar <mingo@kernel.org> | 2014-04-29 02:41:21 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2014-04-29 02:41:21 -0400 |
commit | 201131998fbf074b03679afedcc29948e63331ef (patch) | |
tree | fbfa52100f795fd9f048d7c08050f4f733bd7fa1 | |
parent | 2933d7813d8618f18632a7dc7f4e7f1f7d17383a (diff) | |
parent | fabf01238289e9ae009499594fc54642f5802a24 (diff) |
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/jolsa/perf into perf/core
Pull perf/core improvements and fixes from Jiri Olsa:
* Add a test case for hists filtering (Namhyung Kim)
* Share map_groups among threads of the same group (Arnaldo Carvalho de Melo, Jiri Olsa)
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-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 | { |