aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2014-04-29 02:41:21 -0400
committerIngo Molnar <mingo@kernel.org>2014-04-29 02:41:21 -0400
commit201131998fbf074b03679afedcc29948e63331ef (patch)
treefbfa52100f795fd9f048d7c08050f4f733bd7fa1
parent2933d7813d8618f18632a7dc7f4e7f1f7d17383a (diff)
parentfabf01238289e9ae009499594fc54642f5802a24 (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.perf4
-rw-r--r--tools/perf/arch/x86/tests/dwarf-unwind.c2
-rw-r--r--tools/perf/perf.h6
-rw-r--r--tools/perf/tests/builtin-test.c12
-rw-r--r--tools/perf/tests/hists_common.c148
-rw-r--r--tools/perf/tests/hists_common.h44
-rw-r--r--tools/perf/tests/hists_filter.c315
-rw-r--r--tools/perf/tests/hists_link.c141
-rw-r--r--tools/perf/tests/mmap-thread-lookup.c233
-rw-r--r--tools/perf/tests/tests.h3
-rw-r--r--tools/perf/tests/thread-mg-share.c90
-rw-r--r--tools/perf/ui/stdio/hist.c2
-rw-r--r--tools/perf/util/event.c2
-rw-r--r--tools/perf/util/machine.c11
-rw-r--r--tools/perf/util/map.c23
-rw-r--r--tools/perf/util/map.h12
-rw-r--r--tools/perf/util/thread.c52
-rw-r--r--tools/perf/util/thread.h3
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
397LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o 397LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o
398LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o 398LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o
399LIB_OBJS += $(OUTPUT)tests/pmu.o 399LIB_OBJS += $(OUTPUT)tests/pmu.o
400LIB_OBJS += $(OUTPUT)tests/hists_common.o
400LIB_OBJS += $(OUTPUT)tests/hists_link.o 401LIB_OBJS += $(OUTPUT)tests/hists_link.o
402LIB_OBJS += $(OUTPUT)tests/hists_filter.o
401LIB_OBJS += $(OUTPUT)tests/python-use.o 403LIB_OBJS += $(OUTPUT)tests/python-use.o
402LIB_OBJS += $(OUTPUT)tests/bp_signal.o 404LIB_OBJS += $(OUTPUT)tests/bp_signal.o
403LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o 405LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o
@@ -414,6 +416,8 @@ ifeq ($(ARCH),x86)
414LIB_OBJS += $(OUTPUT)tests/dwarf-unwind.o 416LIB_OBJS += $(OUTPUT)tests/dwarf-unwind.o
415endif 417endif
416endif 418endif
419LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o
420LIB_OBJS += $(OUTPUT)tests/thread-mg-share.o
417 421
418BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o 422BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
419BUILTIN_OBJS += $(OUTPUT)builtin-bench.o 423BUILTIN_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
11static struct {
12 u32 pid;
13 const char *comm;
14} fake_threads[] = {
15 { 100, "perf" },
16 { 200, "perf" },
17 { 300, "bash" },
18};
19
20static 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
36struct fake_sym {
37 u64 start;
38 u64 length;
39 const char *name;
40};
41
42static struct fake_sym perf_syms[] = {
43 { 700, 100, "main" },
44 { 800, 100, "run_command" },
45 { 900, 100, "cmd_record" },
46};
47
48static struct fake_sym bash_syms[] = {
49 { 700, 100, "main" },
50 { 800, 100, "xmalloc" },
51 { 900, 100, "xfree" },
52};
53
54static struct fake_sym libc_syms[] = {
55 { 700, 100, "malloc" },
56 { 800, 100, "free" },
57 { 900, 100, "realloc" },
58};
59
60static struct fake_sym kernel_syms[] = {
61 { 700, 100, "schedule" },
62 { 800, 100, "page_fault" },
63 { 900, 100, "sys_perf_event_open" },
64};
65
66static 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
77struct 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
143out:
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
4struct machine;
5struct 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 */
42struct 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
13struct 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 */
22static 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
45static 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
95out:
96 pr_debug("Not enough memory for adding a hist entry\n");
97 return TEST_FAIL;
98}
99
100static 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
127int 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
309out:
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"
12static struct {
13 u32 pid;
14 const char *comm;
15} fake_threads[] = {
16 { 100, "perf" },
17 { 200, "perf" },
18 { 300, "bash" },
19};
20
21static 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
37struct fake_sym {
38 u64 start;
39 u64 length;
40 const char *name;
41};
42
43static struct fake_sym perf_syms[] = {
44 { 700, 100, "main" },
45 { 800, 100, "run_command" },
46 { 900, 100, "cmd_record" },
47};
48
49static struct fake_sym bash_syms[] = {
50 { 700, 100, "main" },
51 { 800, 100, "xmalloc" },
52 { 900, 100, "xfree" },
53};
54
55static struct fake_sym libc_syms[] = {
56 { 700, 100, "malloc" },
57 { 800, 100, "free" },
58 { 900, 100, "realloc" },
59};
60
61static struct fake_sym kernel_syms[] = {
62 { 700, 100, "schedule" },
63 { 800, 100, "page_fault" },
64 { 900, 100, "sys_perf_event_open" },
65};
66
67static 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
78static 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
144out:
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
151struct sample { 13struct 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 */
159static struct sample fake_common_samples[] = { 22static 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
17static int go_away;
18
19struct thread_data {
20 pthread_t pt;
21 pid_t tid;
22 void *map;
23 int ready[2];
24};
25
26static struct thread_data threads[THREADS];
27
28static 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
48static 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
73static 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
93static 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
110static 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
126typedef int (*synth_cb)(struct machine *machine);
127
128static int synth_all(struct machine *machine)
129{
130 return perf_event__synthesize_threads(NULL,
131 perf_event__process,
132 machine, 0);
133}
134
135static 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
150static 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 */
222int 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);
41int test__keep_tracking(void); 41int test__keep_tracking(void);
42int test__parse_no_sample_id_all(void); 42int test__parse_no_sample_id_all(void);
43int test__dwarf_unwind(void); 43int test__dwarf_unwind(void);
44int test__hists_filter(void);
45int test__mmap_thread_lookup(void);
46int 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
6int 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
328static void maps__delete(struct rb_root *maps) 329static 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
362struct 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
372void map_groups__delete(struct map_groups *mg)
373{
374 map_groups__exit(mg);
375 free(mg);
376}
377
378void map_groups__put(struct map_groups *mg)
379{
380 if (--mg->refcnt == 0)
381 map_groups__delete(mg);
382}
383
361void map_groups__flush(struct map_groups *mg) 384void 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
65struct map_groups *map_groups__new(void);
66void map_groups__delete(struct map_groups *mg);
67
68static inline struct map_groups *map_groups__get(struct map_groups *mg)
69{
70 ++mg->refcnt;
71 return mg;
72}
73
74void map_groups__put(struct map_groups *mg);
75
64static inline struct kmap *map__kmap(struct map *map) 76static 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
11int 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
11struct thread *thread__new(pid_t pid, pid_t tid) 27struct 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)
111size_t thread__fprintf(struct thread *thread, FILE *fp) 127size_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
117void thread__insert_map(struct thread *thread, struct map *map) 133void 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
139static 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
123int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) 156int 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
146void thread__find_cpumode_addr_location(struct thread *thread, 174void 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;
30struct comm; 30struct comm;
31 31
32struct thread *thread__new(pid_t pid, pid_t tid); 32struct thread *thread__new(pid_t pid, pid_t tid);
33int thread__init_map_groups(struct thread *thread, struct machine *machine);
33void thread__delete(struct thread *thread); 34void thread__delete(struct thread *thread);
34static inline void thread__exited(struct thread *thread) 35static inline void thread__exited(struct thread *thread)
35{ 36{