diff options
36 files changed, 1647 insertions, 1022 deletions
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 86b797a35aa6..fcc51fe0195c 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt | |||
@@ -73,6 +73,10 @@ OPTIONS | |||
73 | (Only for --vars) Show external defined variables in addition to local | 73 | (Only for --vars) Show external defined variables in addition to local |
74 | variables. | 74 | variables. |
75 | 75 | ||
76 | -F:: | ||
77 | --funcs:: | ||
78 | Show available functions in given module or kernel. | ||
79 | |||
76 | -f:: | 80 | -f:: |
77 | --force:: | 81 | --force:: |
78 | Forcibly add events with existing name. | 82 | Forcibly add events with existing name. |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 7141c42e1469..638e8e146bb9 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -402,6 +402,7 @@ LIB_H += util/debug.h | |||
402 | LIB_H += util/debugfs.h | 402 | LIB_H += util/debugfs.h |
403 | LIB_H += util/event.h | 403 | LIB_H += util/event.h |
404 | LIB_H += util/evsel.h | 404 | LIB_H += util/evsel.h |
405 | LIB_H += util/evlist.h | ||
405 | LIB_H += util/exec_cmd.h | 406 | LIB_H += util/exec_cmd.h |
406 | LIB_H += util/types.h | 407 | LIB_H += util/types.h |
407 | LIB_H += util/levenshtein.h | 408 | LIB_H += util/levenshtein.h |
@@ -425,6 +426,7 @@ LIB_H += util/values.h | |||
425 | LIB_H += util/sort.h | 426 | LIB_H += util/sort.h |
426 | LIB_H += util/hist.h | 427 | LIB_H += util/hist.h |
427 | LIB_H += util/thread.h | 428 | LIB_H += util/thread.h |
429 | LIB_H += util/thread_map.h | ||
428 | LIB_H += util/trace-event.h | 430 | LIB_H += util/trace-event.h |
429 | LIB_H += util/probe-finder.h | 431 | LIB_H += util/probe-finder.h |
430 | LIB_H += util/probe-event.h | 432 | LIB_H += util/probe-event.h |
@@ -440,6 +442,7 @@ LIB_OBJS += $(OUTPUT)util/ctype.o | |||
440 | LIB_OBJS += $(OUTPUT)util/debugfs.o | 442 | LIB_OBJS += $(OUTPUT)util/debugfs.o |
441 | LIB_OBJS += $(OUTPUT)util/environment.o | 443 | LIB_OBJS += $(OUTPUT)util/environment.o |
442 | LIB_OBJS += $(OUTPUT)util/event.o | 444 | LIB_OBJS += $(OUTPUT)util/event.o |
445 | LIB_OBJS += $(OUTPUT)util/evlist.o | ||
443 | LIB_OBJS += $(OUTPUT)util/evsel.o | 446 | LIB_OBJS += $(OUTPUT)util/evsel.o |
444 | LIB_OBJS += $(OUTPUT)util/exec_cmd.o | 447 | LIB_OBJS += $(OUTPUT)util/exec_cmd.o |
445 | LIB_OBJS += $(OUTPUT)util/help.o | 448 | LIB_OBJS += $(OUTPUT)util/help.o |
@@ -469,6 +472,7 @@ LIB_OBJS += $(OUTPUT)util/map.o | |||
469 | LIB_OBJS += $(OUTPUT)util/pstack.o | 472 | LIB_OBJS += $(OUTPUT)util/pstack.o |
470 | LIB_OBJS += $(OUTPUT)util/session.o | 473 | LIB_OBJS += $(OUTPUT)util/session.o |
471 | LIB_OBJS += $(OUTPUT)util/thread.o | 474 | LIB_OBJS += $(OUTPUT)util/thread.o |
475 | LIB_OBJS += $(OUTPUT)util/thread_map.o | ||
472 | LIB_OBJS += $(OUTPUT)util/trace-event-parse.o | 476 | LIB_OBJS += $(OUTPUT)util/trace-event-parse.o |
473 | LIB_OBJS += $(OUTPUT)util/trace-event-read.o | 477 | LIB_OBJS += $(OUTPUT)util/trace-event-read.o |
474 | LIB_OBJS += $(OUTPUT)util/trace-event-info.o | 478 | LIB_OBJS += $(OUTPUT)util/trace-event-info.o |
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index add163c9f0e7..6cf708aba7c9 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
@@ -52,6 +52,7 @@ static struct { | |||
52 | bool show_lines; | 52 | bool show_lines; |
53 | bool show_vars; | 53 | bool show_vars; |
54 | bool show_ext_vars; | 54 | bool show_ext_vars; |
55 | bool show_funcs; | ||
55 | bool mod_events; | 56 | bool mod_events; |
56 | int nevents; | 57 | int nevents; |
57 | struct perf_probe_event events[MAX_PROBES]; | 58 | struct perf_probe_event events[MAX_PROBES]; |
@@ -221,6 +222,8 @@ static const struct option options[] = { | |||
221 | OPT__DRY_RUN(&probe_event_dry_run), | 222 | OPT__DRY_RUN(&probe_event_dry_run), |
222 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, | 223 | OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, |
223 | "Set how many probe points can be found for a probe."), | 224 | "Set how many probe points can be found for a probe."), |
225 | OPT_BOOLEAN('F', "funcs", ¶ms.show_funcs, | ||
226 | "Show potential probe-able functions."), | ||
224 | OPT_END() | 227 | OPT_END() |
225 | }; | 228 | }; |
226 | 229 | ||
@@ -246,7 +249,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
246 | params.max_probe_points = MAX_PROBES; | 249 | params.max_probe_points = MAX_PROBES; |
247 | 250 | ||
248 | if ((!params.nevents && !params.dellist && !params.list_events && | 251 | if ((!params.nevents && !params.dellist && !params.list_events && |
249 | !params.show_lines)) | 252 | !params.show_lines && !params.show_funcs)) |
250 | usage_with_options(probe_usage, options); | 253 | usage_with_options(probe_usage, options); |
251 | 254 | ||
252 | /* | 255 | /* |
@@ -267,12 +270,36 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
267 | pr_err(" Error: Don't use --list with --vars.\n"); | 270 | pr_err(" Error: Don't use --list with --vars.\n"); |
268 | usage_with_options(probe_usage, options); | 271 | usage_with_options(probe_usage, options); |
269 | } | 272 | } |
273 | if (params.show_funcs) { | ||
274 | pr_err(" Error: Don't use --list with --funcs.\n"); | ||
275 | usage_with_options(probe_usage, options); | ||
276 | } | ||
270 | ret = show_perf_probe_events(); | 277 | ret = show_perf_probe_events(); |
271 | if (ret < 0) | 278 | if (ret < 0) |
272 | pr_err(" Error: Failed to show event list. (%d)\n", | 279 | pr_err(" Error: Failed to show event list. (%d)\n", |
273 | ret); | 280 | ret); |
274 | return ret; | 281 | return ret; |
275 | } | 282 | } |
283 | if (params.show_funcs) { | ||
284 | if (params.nevents != 0 || params.dellist) { | ||
285 | pr_err(" Error: Don't use --funcs with" | ||
286 | " --add/--del.\n"); | ||
287 | usage_with_options(probe_usage, options); | ||
288 | } | ||
289 | if (params.show_lines) { | ||
290 | pr_err(" Error: Don't use --funcs with --line.\n"); | ||
291 | usage_with_options(probe_usage, options); | ||
292 | } | ||
293 | if (params.show_vars) { | ||
294 | pr_err(" Error: Don't use --funcs with --vars.\n"); | ||
295 | usage_with_options(probe_usage, options); | ||
296 | } | ||
297 | ret = show_available_funcs(params.target_module); | ||
298 | if (ret < 0) | ||
299 | pr_err(" Error: Failed to show functions." | ||
300 | " (%d)\n", ret); | ||
301 | return ret; | ||
302 | } | ||
276 | 303 | ||
277 | #ifdef DWARF_SUPPORT | 304 | #ifdef DWARF_SUPPORT |
278 | if (params.show_lines) { | 305 | if (params.show_lines) { |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index b2f729fdb317..d7886307f6f4 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -18,17 +18,20 @@ | |||
18 | 18 | ||
19 | #include "util/header.h" | 19 | #include "util/header.h" |
20 | #include "util/event.h" | 20 | #include "util/event.h" |
21 | #include "util/evlist.h" | ||
21 | #include "util/evsel.h" | 22 | #include "util/evsel.h" |
22 | #include "util/debug.h" | 23 | #include "util/debug.h" |
23 | #include "util/session.h" | 24 | #include "util/session.h" |
24 | #include "util/symbol.h" | 25 | #include "util/symbol.h" |
25 | #include "util/cpumap.h" | 26 | #include "util/cpumap.h" |
27 | #include "util/thread_map.h" | ||
26 | 28 | ||
27 | #include <unistd.h> | 29 | #include <unistd.h> |
28 | #include <sched.h> | 30 | #include <sched.h> |
29 | #include <sys/mman.h> | 31 | #include <sys/mman.h> |
30 | 32 | ||
31 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 33 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
34 | #define SID(e, x, y) xyarray__entry(e->id, x, y) | ||
32 | 35 | ||
33 | enum write_mode_t { | 36 | enum write_mode_t { |
34 | WRITE_FORCE, | 37 | WRITE_FORCE, |
@@ -46,7 +49,7 @@ static unsigned int user_freq = UINT_MAX; | |||
46 | static int freq = 1000; | 49 | static int freq = 1000; |
47 | static int output; | 50 | static int output; |
48 | static int pipe_output = 0; | 51 | static int pipe_output = 0; |
49 | static const char *output_name = "perf.data"; | 52 | static const char *output_name = NULL; |
50 | static int group = 0; | 53 | static int group = 0; |
51 | static int realtime_prio = 0; | 54 | static int realtime_prio = 0; |
52 | static bool nodelay = false; | 55 | static bool nodelay = false; |
@@ -66,51 +69,17 @@ static bool sample_address = false; | |||
66 | static bool sample_time = false; | 69 | static bool sample_time = false; |
67 | static bool no_buildid = false; | 70 | static bool no_buildid = false; |
68 | static bool no_buildid_cache = false; | 71 | static bool no_buildid_cache = false; |
72 | static struct perf_evlist *evsel_list; | ||
69 | 73 | ||
70 | static long samples = 0; | 74 | static long samples = 0; |
71 | static u64 bytes_written = 0; | 75 | static u64 bytes_written = 0; |
72 | 76 | ||
73 | static struct pollfd *event_array; | ||
74 | |||
75 | static int nr_poll = 0; | ||
76 | static int nr_cpu = 0; | ||
77 | |||
78 | static int file_new = 1; | 77 | static int file_new = 1; |
79 | static off_t post_processing_offset; | 78 | static off_t post_processing_offset; |
80 | 79 | ||
81 | static struct perf_session *session; | 80 | static struct perf_session *session; |
82 | static const char *cpu_list; | 81 | static const char *cpu_list; |
83 | 82 | ||
84 | struct mmap_data { | ||
85 | void *base; | ||
86 | unsigned int mask; | ||
87 | unsigned int prev; | ||
88 | }; | ||
89 | |||
90 | static struct mmap_data mmap_array[MAX_NR_CPUS]; | ||
91 | |||
92 | static unsigned long mmap_read_head(struct mmap_data *md) | ||
93 | { | ||
94 | struct perf_event_mmap_page *pc = md->base; | ||
95 | long head; | ||
96 | |||
97 | head = pc->data_head; | ||
98 | rmb(); | ||
99 | |||
100 | return head; | ||
101 | } | ||
102 | |||
103 | static void mmap_write_tail(struct mmap_data *md, unsigned long tail) | ||
104 | { | ||
105 | struct perf_event_mmap_page *pc = md->base; | ||
106 | |||
107 | /* | ||
108 | * ensure all reads are done before we write the tail out. | ||
109 | */ | ||
110 | /* mb(); */ | ||
111 | pc->data_tail = tail; | ||
112 | } | ||
113 | |||
114 | static void advance_output(size_t size) | 83 | static void advance_output(size_t size) |
115 | { | 84 | { |
116 | bytes_written += size; | 85 | bytes_written += size; |
@@ -139,9 +108,9 @@ static int process_synthesized_event(event_t *event, | |||
139 | return 0; | 108 | return 0; |
140 | } | 109 | } |
141 | 110 | ||
142 | static void mmap_read(struct mmap_data *md) | 111 | static void mmap_read(struct perf_mmap *md) |
143 | { | 112 | { |
144 | unsigned int head = mmap_read_head(md); | 113 | unsigned int head = perf_mmap__read_head(md); |
145 | unsigned int old = md->prev; | 114 | unsigned int old = md->prev; |
146 | unsigned char *data = md->base + page_size; | 115 | unsigned char *data = md->base + page_size; |
147 | unsigned long size; | 116 | unsigned long size; |
@@ -185,7 +154,7 @@ static void mmap_read(struct mmap_data *md) | |||
185 | write_output(buf, size); | 154 | write_output(buf, size); |
186 | 155 | ||
187 | md->prev = old; | 156 | md->prev = old; |
188 | mmap_write_tail(md, old); | 157 | perf_mmap__write_tail(md, old); |
189 | } | 158 | } |
190 | 159 | ||
191 | static volatile int done = 0; | 160 | static volatile int done = 0; |
@@ -209,8 +178,6 @@ static void sig_atexit(void) | |||
209 | kill(getpid(), signr); | 178 | kill(getpid(), signr); |
210 | } | 179 | } |
211 | 180 | ||
212 | static int group_fd; | ||
213 | |||
214 | static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) | 181 | static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) |
215 | { | 182 | { |
216 | struct perf_header_attr *h_attr; | 183 | struct perf_header_attr *h_attr; |
@@ -234,28 +201,47 @@ static void create_counter(struct perf_evsel *evsel, int cpu) | |||
234 | char *filter = evsel->filter; | 201 | char *filter = evsel->filter; |
235 | struct perf_event_attr *attr = &evsel->attr; | 202 | struct perf_event_attr *attr = &evsel->attr; |
236 | struct perf_header_attr *h_attr; | 203 | struct perf_header_attr *h_attr; |
237 | int track = !evsel->idx; /* only the first counter needs these */ | 204 | struct perf_sample_id *sid; |
238 | int thread_index; | 205 | int thread_index; |
239 | int ret; | 206 | int ret; |
240 | struct { | 207 | |
241 | u64 count; | 208 | for (thread_index = 0; thread_index < threads->nr; thread_index++) { |
242 | u64 time_enabled; | 209 | h_attr = get_header_attr(attr, evsel->idx); |
243 | u64 time_running; | 210 | if (h_attr == NULL) |
244 | u64 id; | 211 | die("nomem\n"); |
245 | } read_data; | 212 | |
246 | /* | 213 | if (!file_new) { |
247 | * Check if parse_single_tracepoint_event has already asked for | 214 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { |
248 | * PERF_SAMPLE_TIME. | 215 | fprintf(stderr, "incompatible append\n"); |
249 | * | 216 | exit(-1); |
250 | * XXX this is kludgy but short term fix for problems introduced by | 217 | } |
251 | * eac23d1c that broke 'perf script' by having different sample_types | 218 | } |
252 | * when using multiple tracepoint events when we use a perf binary | 219 | |
253 | * that tries to use sample_id_all on an older kernel. | 220 | sid = SID(evsel, cpu, thread_index); |
254 | * | 221 | if (perf_header_attr__add_id(h_attr, sid->id) < 0) { |
255 | * We need to move counter creation to perf_session, support | 222 | pr_warning("Not enough memory to add id\n"); |
256 | * different sample_types, etc. | 223 | exit(-1); |
257 | */ | 224 | } |
258 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | 225 | |
226 | if (filter != NULL) { | ||
227 | ret = ioctl(FD(evsel, cpu, thread_index), | ||
228 | PERF_EVENT_IOC_SET_FILTER, filter); | ||
229 | if (ret) { | ||
230 | error("failed to set filter with %d (%s)\n", errno, | ||
231 | strerror(errno)); | ||
232 | exit(-1); | ||
233 | } | ||
234 | } | ||
235 | } | ||
236 | |||
237 | if (!sample_type) | ||
238 | sample_type = attr->sample_type; | ||
239 | } | ||
240 | |||
241 | static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist) | ||
242 | { | ||
243 | struct perf_event_attr *attr = &evsel->attr; | ||
244 | int track = !evsel->idx; /* only the first counter needs these */ | ||
259 | 245 | ||
260 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 246 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
261 | PERF_FORMAT_TOTAL_TIME_RUNNING | | 247 | PERF_FORMAT_TOTAL_TIME_RUNNING | |
@@ -263,7 +249,7 @@ static void create_counter(struct perf_evsel *evsel, int cpu) | |||
263 | 249 | ||
264 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 250 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
265 | 251 | ||
266 | if (nr_counters > 1) | 252 | if (evlist->nr_entries > 1) |
267 | attr->sample_type |= PERF_SAMPLE_ID; | 253 | attr->sample_type |= PERF_SAMPLE_ID; |
268 | 254 | ||
269 | /* | 255 | /* |
@@ -315,19 +301,39 @@ static void create_counter(struct perf_evsel *evsel, int cpu) | |||
315 | 301 | ||
316 | attr->mmap = track; | 302 | attr->mmap = track; |
317 | attr->comm = track; | 303 | attr->comm = track; |
318 | attr->inherit = !no_inherit; | 304 | |
319 | if (target_pid == -1 && target_tid == -1 && !system_wide) { | 305 | if (target_pid == -1 && target_tid == -1 && !system_wide) { |
320 | attr->disabled = 1; | 306 | attr->disabled = 1; |
321 | attr->enable_on_exec = 1; | 307 | attr->enable_on_exec = 1; |
322 | } | 308 | } |
323 | retry_sample_id: | 309 | } |
324 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | ||
325 | 310 | ||
326 | for (thread_index = 0; thread_index < threads->nr; thread_index++) { | 311 | static void open_counters(struct perf_evlist *evlist) |
327 | try_again: | 312 | { |
328 | FD(evsel, nr_cpu, thread_index) = sys_perf_event_open(attr, threads->map[thread_index], cpu, group_fd, 0); | 313 | struct perf_evsel *pos; |
314 | int cpu; | ||
329 | 315 | ||
330 | if (FD(evsel, nr_cpu, thread_index) < 0) { | 316 | list_for_each_entry(pos, &evlist->entries, node) { |
317 | struct perf_event_attr *attr = &pos->attr; | ||
318 | /* | ||
319 | * Check if parse_single_tracepoint_event has already asked for | ||
320 | * PERF_SAMPLE_TIME. | ||
321 | * | ||
322 | * XXX this is kludgy but short term fix for problems introduced by | ||
323 | * eac23d1c that broke 'perf script' by having different sample_types | ||
324 | * when using multiple tracepoint events when we use a perf binary | ||
325 | * that tries to use sample_id_all on an older kernel. | ||
326 | * | ||
327 | * We need to move counter creation to perf_session, support | ||
328 | * different sample_types, etc. | ||
329 | */ | ||
330 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | ||
331 | |||
332 | config_attr(pos, evlist); | ||
333 | retry_sample_id: | ||
334 | attr->sample_id_all = sample_id_all_avail ? 1 : 0; | ||
335 | try_again: | ||
336 | if (perf_evsel__open(pos, cpus, threads, group, !no_inherit) < 0) { | ||
331 | int err = errno; | 337 | int err = errno; |
332 | 338 | ||
333 | if (err == EPERM || err == EACCES) | 339 | if (err == EPERM || err == EACCES) |
@@ -364,7 +370,7 @@ try_again: | |||
364 | } | 370 | } |
365 | printf("\n"); | 371 | printf("\n"); |
366 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", | 372 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", |
367 | FD(evsel, nr_cpu, thread_index), strerror(err)); | 373 | err, strerror(err)); |
368 | 374 | ||
369 | #if defined(__i386__) || defined(__x86_64__) | 375 | #if defined(__i386__) || defined(__x86_64__) |
370 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) | 376 | if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP) |
@@ -375,90 +381,16 @@ try_again: | |||
375 | #endif | 381 | #endif |
376 | 382 | ||
377 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | 383 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); |
378 | exit(-1); | ||
379 | } | ||
380 | |||
381 | h_attr = get_header_attr(attr, evsel->idx); | ||
382 | if (h_attr == NULL) | ||
383 | die("nomem\n"); | ||
384 | |||
385 | if (!file_new) { | ||
386 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { | ||
387 | fprintf(stderr, "incompatible append\n"); | ||
388 | exit(-1); | ||
389 | } | ||
390 | } | ||
391 | |||
392 | if (read(FD(evsel, nr_cpu, thread_index), &read_data, sizeof(read_data)) == -1) { | ||
393 | perror("Unable to read perf file descriptor"); | ||
394 | exit(-1); | ||
395 | } | ||
396 | |||
397 | if (perf_header_attr__add_id(h_attr, read_data.id) < 0) { | ||
398 | pr_warning("Not enough memory to add id\n"); | ||
399 | exit(-1); | ||
400 | } | ||
401 | |||
402 | assert(FD(evsel, nr_cpu, thread_index) >= 0); | ||
403 | fcntl(FD(evsel, nr_cpu, thread_index), F_SETFL, O_NONBLOCK); | ||
404 | |||
405 | /* | ||
406 | * First counter acts as the group leader: | ||
407 | */ | ||
408 | if (group && group_fd == -1) | ||
409 | group_fd = FD(evsel, nr_cpu, thread_index); | ||
410 | |||
411 | if (evsel->idx || thread_index) { | ||
412 | struct perf_evsel *first; | ||
413 | first = list_entry(evsel_list.next, struct perf_evsel, node); | ||
414 | ret = ioctl(FD(evsel, nr_cpu, thread_index), | ||
415 | PERF_EVENT_IOC_SET_OUTPUT, | ||
416 | FD(first, nr_cpu, 0)); | ||
417 | if (ret) { | ||
418 | error("failed to set output: %d (%s)\n", errno, | ||
419 | strerror(errno)); | ||
420 | exit(-1); | ||
421 | } | ||
422 | } else { | ||
423 | mmap_array[nr_cpu].prev = 0; | ||
424 | mmap_array[nr_cpu].mask = mmap_pages*page_size - 1; | ||
425 | mmap_array[nr_cpu].base = mmap(NULL, (mmap_pages+1)*page_size, | ||
426 | PROT_READ | PROT_WRITE, MAP_SHARED, FD(evsel, nr_cpu, thread_index), 0); | ||
427 | if (mmap_array[nr_cpu].base == MAP_FAILED) { | ||
428 | error("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
429 | exit(-1); | ||
430 | } | ||
431 | |||
432 | event_array[nr_poll].fd = FD(evsel, nr_cpu, thread_index); | ||
433 | event_array[nr_poll].events = POLLIN; | ||
434 | nr_poll++; | ||
435 | } | ||
436 | |||
437 | if (filter != NULL) { | ||
438 | ret = ioctl(FD(evsel, nr_cpu, thread_index), | ||
439 | PERF_EVENT_IOC_SET_FILTER, filter); | ||
440 | if (ret) { | ||
441 | error("failed to set filter with %d (%s)\n", errno, | ||
442 | strerror(errno)); | ||
443 | exit(-1); | ||
444 | } | ||
445 | } | 384 | } |
446 | } | 385 | } |
447 | 386 | ||
448 | if (!sample_type) | 387 | if (perf_evlist__mmap(evlist, cpus, threads, mmap_pages, false) < 0) |
449 | sample_type = attr->sample_type; | 388 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); |
450 | } | ||
451 | |||
452 | static void open_counters(int cpu) | ||
453 | { | ||
454 | struct perf_evsel *pos; | ||
455 | |||
456 | group_fd = -1; | ||
457 | |||
458 | list_for_each_entry(pos, &evsel_list, node) | ||
459 | create_counter(pos, cpu); | ||
460 | 389 | ||
461 | nr_cpu++; | 390 | for (cpu = 0; cpu < cpus->nr; ++cpu) { |
391 | list_for_each_entry(pos, &evlist->entries, node) | ||
392 | create_counter(pos, cpu); | ||
393 | } | ||
462 | } | 394 | } |
463 | 395 | ||
464 | static int process_buildids(void) | 396 | static int process_buildids(void) |
@@ -481,9 +413,9 @@ static void atexit_header(void) | |||
481 | 413 | ||
482 | if (!no_buildid) | 414 | if (!no_buildid) |
483 | process_buildids(); | 415 | process_buildids(); |
484 | perf_header__write(&session->header, output, true); | 416 | perf_header__write(&session->header, evsel_list, output, true); |
485 | perf_session__delete(session); | 417 | perf_session__delete(session); |
486 | perf_evsel_list__delete(); | 418 | perf_evlist__delete(evsel_list); |
487 | symbol__exit(); | 419 | symbol__exit(); |
488 | } | 420 | } |
489 | } | 421 | } |
@@ -533,9 +465,9 @@ static void mmap_read_all(void) | |||
533 | { | 465 | { |
534 | int i; | 466 | int i; |
535 | 467 | ||
536 | for (i = 0; i < nr_cpu; i++) { | 468 | for (i = 0; i < cpus->nr; i++) { |
537 | if (mmap_array[i].base) | 469 | if (evsel_list->mmap[i].base) |
538 | mmap_read(&mmap_array[i]); | 470 | mmap_read(&evsel_list->mmap[i]); |
539 | } | 471 | } |
540 | 472 | ||
541 | if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO)) | 473 | if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO)) |
@@ -566,18 +498,26 @@ static int __cmd_record(int argc, const char **argv) | |||
566 | exit(-1); | 498 | exit(-1); |
567 | } | 499 | } |
568 | 500 | ||
569 | if (!strcmp(output_name, "-")) | 501 | if (!output_name) { |
570 | pipe_output = 1; | 502 | if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) |
571 | else if (!stat(output_name, &st) && st.st_size) { | 503 | pipe_output = 1; |
572 | if (write_mode == WRITE_FORCE) { | 504 | else |
573 | char oldname[PATH_MAX]; | 505 | output_name = "perf.data"; |
574 | snprintf(oldname, sizeof(oldname), "%s.old", | 506 | } |
575 | output_name); | 507 | if (output_name) { |
576 | unlink(oldname); | 508 | if (!strcmp(output_name, "-")) |
577 | rename(output_name, oldname); | 509 | pipe_output = 1; |
510 | else if (!stat(output_name, &st) && st.st_size) { | ||
511 | if (write_mode == WRITE_FORCE) { | ||
512 | char oldname[PATH_MAX]; | ||
513 | snprintf(oldname, sizeof(oldname), "%s.old", | ||
514 | output_name); | ||
515 | unlink(oldname); | ||
516 | rename(output_name, oldname); | ||
517 | } | ||
518 | } else if (write_mode == WRITE_APPEND) { | ||
519 | write_mode = WRITE_FORCE; | ||
578 | } | 520 | } |
579 | } else if (write_mode == WRITE_APPEND) { | ||
580 | write_mode = WRITE_FORCE; | ||
581 | } | 521 | } |
582 | 522 | ||
583 | flags = O_CREAT|O_RDWR; | 523 | flags = O_CREAT|O_RDWR; |
@@ -611,7 +551,7 @@ static int __cmd_record(int argc, const char **argv) | |||
611 | goto out_delete_session; | 551 | goto out_delete_session; |
612 | } | 552 | } |
613 | 553 | ||
614 | if (have_tracepoints(&evsel_list)) | 554 | if (have_tracepoints(&evsel_list->entries)) |
615 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); | 555 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); |
616 | 556 | ||
617 | /* | 557 | /* |
@@ -673,12 +613,7 @@ static int __cmd_record(int argc, const char **argv) | |||
673 | close(child_ready_pipe[0]); | 613 | close(child_ready_pipe[0]); |
674 | } | 614 | } |
675 | 615 | ||
676 | if (!system_wide && no_inherit && !cpu_list) { | 616 | open_counters(evsel_list); |
677 | open_counters(-1); | ||
678 | } else { | ||
679 | for (i = 0; i < cpus->nr; i++) | ||
680 | open_counters(cpus->map[i]); | ||
681 | } | ||
682 | 617 | ||
683 | perf_session__set_sample_type(session, sample_type); | 618 | perf_session__set_sample_type(session, sample_type); |
684 | 619 | ||
@@ -687,7 +622,8 @@ static int __cmd_record(int argc, const char **argv) | |||
687 | if (err < 0) | 622 | if (err < 0) |
688 | return err; | 623 | return err; |
689 | } else if (file_new) { | 624 | } else if (file_new) { |
690 | err = perf_header__write(&session->header, output, false); | 625 | err = perf_header__write(&session->header, evsel_list, |
626 | output, false); | ||
691 | if (err < 0) | 627 | if (err < 0) |
692 | return err; | 628 | return err; |
693 | } | 629 | } |
@@ -712,7 +648,7 @@ static int __cmd_record(int argc, const char **argv) | |||
712 | return err; | 648 | return err; |
713 | } | 649 | } |
714 | 650 | ||
715 | if (have_tracepoints(&evsel_list)) { | 651 | if (have_tracepoints(&evsel_list->entries)) { |
716 | /* | 652 | /* |
717 | * FIXME err <= 0 here actually means that | 653 | * FIXME err <= 0 here actually means that |
718 | * there were no tracepoints so its not really | 654 | * there were no tracepoints so its not really |
@@ -721,7 +657,7 @@ static int __cmd_record(int argc, const char **argv) | |||
721 | * return this more properly and also | 657 | * return this more properly and also |
722 | * propagate errors that now are calling die() | 658 | * propagate errors that now are calling die() |
723 | */ | 659 | */ |
724 | err = event__synthesize_tracing_data(output, &evsel_list, | 660 | err = event__synthesize_tracing_data(output, evsel_list, |
725 | process_synthesized_event, | 661 | process_synthesized_event, |
726 | session); | 662 | session); |
727 | if (err <= 0) { | 663 | if (err <= 0) { |
@@ -789,15 +725,15 @@ static int __cmd_record(int argc, const char **argv) | |||
789 | if (hits == samples) { | 725 | if (hits == samples) { |
790 | if (done) | 726 | if (done) |
791 | break; | 727 | break; |
792 | err = poll(event_array, nr_poll, -1); | 728 | err = poll(evsel_list->pollfd, evsel_list->nr_fds, -1); |
793 | waking++; | 729 | waking++; |
794 | } | 730 | } |
795 | 731 | ||
796 | if (done) { | 732 | if (done) { |
797 | for (i = 0; i < nr_cpu; i++) { | 733 | for (i = 0; i < cpus->nr; i++) { |
798 | struct perf_evsel *pos; | 734 | struct perf_evsel *pos; |
799 | 735 | ||
800 | list_for_each_entry(pos, &evsel_list, node) { | 736 | list_for_each_entry(pos, &evsel_list->entries, node) { |
801 | for (thread = 0; | 737 | for (thread = 0; |
802 | thread < threads->nr; | 738 | thread < threads->nr; |
803 | thread++) | 739 | thread++) |
@@ -838,10 +774,10 @@ static const char * const record_usage[] = { | |||
838 | static bool force, append_file; | 774 | static bool force, append_file; |
839 | 775 | ||
840 | const struct option record_options[] = { | 776 | const struct option record_options[] = { |
841 | OPT_CALLBACK('e', "event", NULL, "event", | 777 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
842 | "event selector. use 'perf list' to list available events", | 778 | "event selector. use 'perf list' to list available events", |
843 | parse_events), | 779 | parse_events), |
844 | OPT_CALLBACK(0, "filter", NULL, "filter", | 780 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", |
845 | "event filter", parse_filter), | 781 | "event filter", parse_filter), |
846 | OPT_INTEGER('p', "pid", &target_pid, | 782 | OPT_INTEGER('p', "pid", &target_pid, |
847 | "record events on existing process id"), | 783 | "record events on existing process id"), |
@@ -892,6 +828,10 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
892 | int err = -ENOMEM; | 828 | int err = -ENOMEM; |
893 | struct perf_evsel *pos; | 829 | struct perf_evsel *pos; |
894 | 830 | ||
831 | evsel_list = perf_evlist__new(); | ||
832 | if (evsel_list == NULL) | ||
833 | return -ENOMEM; | ||
834 | |||
895 | argc = parse_options(argc, argv, record_options, record_usage, | 835 | argc = parse_options(argc, argv, record_options, record_usage, |
896 | PARSE_OPT_STOP_AT_NON_OPTION); | 836 | PARSE_OPT_STOP_AT_NON_OPTION); |
897 | if (!argc && target_pid == -1 && target_tid == -1 && | 837 | if (!argc && target_pid == -1 && target_tid == -1 && |
@@ -913,7 +853,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
913 | if (no_buildid_cache || no_buildid) | 853 | if (no_buildid_cache || no_buildid) |
914 | disable_buildid_cache(); | 854 | disable_buildid_cache(); |
915 | 855 | ||
916 | if (list_empty(&evsel_list) && perf_evsel_list__create_default() < 0) { | 856 | if (evsel_list->nr_entries == 0 && |
857 | perf_evlist__add_default(evsel_list) < 0) { | ||
917 | pr_err("Not enough memory for event selector list\n"); | 858 | pr_err("Not enough memory for event selector list\n"); |
918 | goto out_symbol_exit; | 859 | goto out_symbol_exit; |
919 | } | 860 | } |
@@ -927,21 +868,22 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
927 | usage_with_options(record_usage, record_options); | 868 | usage_with_options(record_usage, record_options); |
928 | } | 869 | } |
929 | 870 | ||
930 | cpus = cpu_map__new(cpu_list); | 871 | if (target_tid != -1) |
931 | if (cpus == NULL) { | 872 | cpus = cpu_map__dummy_new(); |
932 | perror("failed to parse CPUs map"); | 873 | else |
933 | return -1; | 874 | cpus = cpu_map__new(cpu_list); |
934 | } | ||
935 | 875 | ||
936 | list_for_each_entry(pos, &evsel_list, node) { | 876 | if (cpus == NULL) |
877 | usage_with_options(record_usage, record_options); | ||
878 | |||
879 | list_for_each_entry(pos, &evsel_list->entries, node) { | ||
937 | if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) | 880 | if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) |
938 | goto out_free_fd; | 881 | goto out_free_fd; |
939 | if (perf_header__push_event(pos->attr.config, event_name(pos))) | 882 | if (perf_header__push_event(pos->attr.config, event_name(pos))) |
940 | goto out_free_fd; | 883 | goto out_free_fd; |
941 | } | 884 | } |
942 | event_array = malloc((sizeof(struct pollfd) * MAX_NR_CPUS * | 885 | |
943 | MAX_COUNTERS * threads->nr)); | 886 | if (perf_evlist__alloc_pollfd(evsel_list, cpus->nr, threads->nr) < 0) |
944 | if (!event_array) | ||
945 | goto out_free_fd; | 887 | goto out_free_fd; |
946 | 888 | ||
947 | if (user_interval != ULLONG_MAX) | 889 | if (user_interval != ULLONG_MAX) |
@@ -959,13 +901,11 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
959 | } else { | 901 | } else { |
960 | fprintf(stderr, "frequency and count are zero, aborting\n"); | 902 | fprintf(stderr, "frequency and count are zero, aborting\n"); |
961 | err = -EINVAL; | 903 | err = -EINVAL; |
962 | goto out_free_event_array; | 904 | goto out_free_fd; |
963 | } | 905 | } |
964 | 906 | ||
965 | err = __cmd_record(argc, argv); | 907 | err = __cmd_record(argc, argv); |
966 | 908 | ||
967 | out_free_event_array: | ||
968 | free(event_array); | ||
969 | out_free_fd: | 909 | out_free_fd: |
970 | thread_map__delete(threads); | 910 | thread_map__delete(threads); |
971 | threads = NULL; | 911 | threads = NULL; |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index c27e31f289e6..f6a43493d1d0 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -81,18 +81,17 @@ static int perf_session__add_hist_entry(struct perf_session *self, | |||
81 | struct addr_location *al, | 81 | struct addr_location *al, |
82 | struct sample_data *data) | 82 | struct sample_data *data) |
83 | { | 83 | { |
84 | struct map_symbol *syms = NULL; | ||
85 | struct symbol *parent = NULL; | 84 | struct symbol *parent = NULL; |
86 | int err = -ENOMEM; | 85 | int err = 0; |
87 | struct hist_entry *he; | 86 | struct hist_entry *he; |
88 | struct hists *hists; | 87 | struct hists *hists; |
89 | struct perf_event_attr *attr; | 88 | struct perf_event_attr *attr; |
90 | 89 | ||
91 | if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) { | 90 | if ((sort__has_parent || symbol_conf.use_callchain) && data->callchain) { |
92 | syms = perf_session__resolve_callchain(self, al->thread, | 91 | err = perf_session__resolve_callchain(self, al->thread, |
93 | data->callchain, &parent); | 92 | data->callchain, &parent); |
94 | if (syms == NULL) | 93 | if (err) |
95 | return -ENOMEM; | 94 | return err; |
96 | } | 95 | } |
97 | 96 | ||
98 | attr = perf_header__find_attr(data->id, &self->header); | 97 | attr = perf_header__find_attr(data->id, &self->header); |
@@ -101,16 +100,17 @@ static int perf_session__add_hist_entry(struct perf_session *self, | |||
101 | else | 100 | else |
102 | hists = perf_session__hists_findnew(self, data->id, 0, 0); | 101 | hists = perf_session__hists_findnew(self, data->id, 0, 0); |
103 | if (hists == NULL) | 102 | if (hists == NULL) |
104 | goto out_free_syms; | 103 | return -ENOMEM; |
104 | |||
105 | he = __hists__add_entry(hists, al, parent, data->period); | 105 | he = __hists__add_entry(hists, al, parent, data->period); |
106 | if (he == NULL) | 106 | if (he == NULL) |
107 | goto out_free_syms; | 107 | return -ENOMEM; |
108 | err = 0; | 108 | |
109 | if (symbol_conf.use_callchain) { | 109 | if (symbol_conf.use_callchain) { |
110 | err = callchain_append(he->callchain, data->callchain, syms, | 110 | err = callchain_append(he->callchain, &self->callchain_cursor, |
111 | data->period); | 111 | data->period); |
112 | if (err) | 112 | if (err) |
113 | goto out_free_syms; | 113 | return err; |
114 | } | 114 | } |
115 | /* | 115 | /* |
116 | * Only in the newt browser we are doing integrated annotation, | 116 | * Only in the newt browser we are doing integrated annotation, |
@@ -119,8 +119,7 @@ static int perf_session__add_hist_entry(struct perf_session *self, | |||
119 | */ | 119 | */ |
120 | if (use_browser > 0) | 120 | if (use_browser > 0) |
121 | err = hist_entry__inc_addr_samples(he, al->addr); | 121 | err = hist_entry__inc_addr_samples(he, al->addr); |
122 | out_free_syms: | 122 | |
123 | free(syms); | ||
124 | return err; | 123 | return err; |
125 | } | 124 | } |
126 | 125 | ||
@@ -222,7 +221,7 @@ static int perf_session__setup_sample_type(struct perf_session *self) | |||
222 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && | 221 | } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE && |
223 | !symbol_conf.use_callchain) { | 222 | !symbol_conf.use_callchain) { |
224 | symbol_conf.use_callchain = true; | 223 | symbol_conf.use_callchain = true; |
225 | if (register_callchain_param(&callchain_param) < 0) { | 224 | if (callchain_register_param(&callchain_param) < 0) { |
226 | fprintf(stderr, "Can't register callchain" | 225 | fprintf(stderr, "Can't register callchain" |
227 | " params\n"); | 226 | " params\n"); |
228 | return -EINVAL; | 227 | return -EINVAL; |
@@ -424,7 +423,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg, | |||
424 | if (tok2) | 423 | if (tok2) |
425 | callchain_param.print_limit = strtod(tok2, &endptr); | 424 | callchain_param.print_limit = strtod(tok2, &endptr); |
426 | setup: | 425 | setup: |
427 | if (register_callchain_param(&callchain_param) < 0) { | 426 | if (callchain_register_param(&callchain_param) < 0) { |
428 | fprintf(stderr, "Can't register callchain params\n"); | 427 | fprintf(stderr, "Can't register callchain params\n"); |
429 | return -1; | 428 | return -1; |
430 | } | 429 | } |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index a482a191a0ca..8906adfdbd8e 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -43,11 +43,13 @@ | |||
43 | #include "util/parse-options.h" | 43 | #include "util/parse-options.h" |
44 | #include "util/parse-events.h" | 44 | #include "util/parse-events.h" |
45 | #include "util/event.h" | 45 | #include "util/event.h" |
46 | #include "util/evlist.h" | ||
46 | #include "util/evsel.h" | 47 | #include "util/evsel.h" |
47 | #include "util/debug.h" | 48 | #include "util/debug.h" |
48 | #include "util/header.h" | 49 | #include "util/header.h" |
49 | #include "util/cpumap.h" | 50 | #include "util/cpumap.h" |
50 | #include "util/thread.h" | 51 | #include "util/thread.h" |
52 | #include "util/thread_map.h" | ||
51 | 53 | ||
52 | #include <sys/prctl.h> | 54 | #include <sys/prctl.h> |
53 | #include <math.h> | 55 | #include <math.h> |
@@ -71,6 +73,8 @@ static struct perf_event_attr default_attrs[] = { | |||
71 | 73 | ||
72 | }; | 74 | }; |
73 | 75 | ||
76 | struct perf_evlist *evsel_list; | ||
77 | |||
74 | static bool system_wide = false; | 78 | static bool system_wide = false; |
75 | static struct cpu_map *cpus; | 79 | static struct cpu_map *cpus; |
76 | static int run_idx = 0; | 80 | static int run_idx = 0; |
@@ -166,7 +170,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) | |||
166 | PERF_FORMAT_TOTAL_TIME_RUNNING; | 170 | PERF_FORMAT_TOTAL_TIME_RUNNING; |
167 | 171 | ||
168 | if (system_wide) | 172 | if (system_wide) |
169 | return perf_evsel__open_per_cpu(evsel, cpus); | 173 | return perf_evsel__open_per_cpu(evsel, cpus, false, false); |
170 | 174 | ||
171 | attr->inherit = !no_inherit; | 175 | attr->inherit = !no_inherit; |
172 | if (target_pid == -1 && target_tid == -1) { | 176 | if (target_pid == -1 && target_tid == -1) { |
@@ -174,7 +178,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) | |||
174 | attr->enable_on_exec = 1; | 178 | attr->enable_on_exec = 1; |
175 | } | 179 | } |
176 | 180 | ||
177 | return perf_evsel__open_per_thread(evsel, threads); | 181 | return perf_evsel__open_per_thread(evsel, threads, false, false); |
178 | } | 182 | } |
179 | 183 | ||
180 | /* | 184 | /* |
@@ -309,7 +313,7 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
309 | close(child_ready_pipe[0]); | 313 | close(child_ready_pipe[0]); |
310 | } | 314 | } |
311 | 315 | ||
312 | list_for_each_entry(counter, &evsel_list, node) { | 316 | list_for_each_entry(counter, &evsel_list->entries, node) { |
313 | if (create_perf_stat_counter(counter) < 0) { | 317 | if (create_perf_stat_counter(counter) < 0) { |
314 | if (errno == -EPERM || errno == -EACCES) { | 318 | if (errno == -EPERM || errno == -EACCES) { |
315 | error("You may not have permission to collect %sstats.\n" | 319 | error("You may not have permission to collect %sstats.\n" |
@@ -347,12 +351,12 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
347 | update_stats(&walltime_nsecs_stats, t1 - t0); | 351 | update_stats(&walltime_nsecs_stats, t1 - t0); |
348 | 352 | ||
349 | if (no_aggr) { | 353 | if (no_aggr) { |
350 | list_for_each_entry(counter, &evsel_list, node) { | 354 | list_for_each_entry(counter, &evsel_list->entries, node) { |
351 | read_counter(counter); | 355 | read_counter(counter); |
352 | perf_evsel__close_fd(counter, cpus->nr, 1); | 356 | perf_evsel__close_fd(counter, cpus->nr, 1); |
353 | } | 357 | } |
354 | } else { | 358 | } else { |
355 | list_for_each_entry(counter, &evsel_list, node) { | 359 | list_for_each_entry(counter, &evsel_list->entries, node) { |
356 | read_counter_aggr(counter); | 360 | read_counter_aggr(counter); |
357 | perf_evsel__close_fd(counter, cpus->nr, threads->nr); | 361 | perf_evsel__close_fd(counter, cpus->nr, threads->nr); |
358 | } | 362 | } |
@@ -555,10 +559,10 @@ static void print_stat(int argc, const char **argv) | |||
555 | } | 559 | } |
556 | 560 | ||
557 | if (no_aggr) { | 561 | if (no_aggr) { |
558 | list_for_each_entry(counter, &evsel_list, node) | 562 | list_for_each_entry(counter, &evsel_list->entries, node) |
559 | print_counter(counter); | 563 | print_counter(counter); |
560 | } else { | 564 | } else { |
561 | list_for_each_entry(counter, &evsel_list, node) | 565 | list_for_each_entry(counter, &evsel_list->entries, node) |
562 | print_counter_aggr(counter); | 566 | print_counter_aggr(counter); |
563 | } | 567 | } |
564 | 568 | ||
@@ -610,7 +614,7 @@ static int stat__set_big_num(const struct option *opt __used, | |||
610 | } | 614 | } |
611 | 615 | ||
612 | static const struct option options[] = { | 616 | static const struct option options[] = { |
613 | OPT_CALLBACK('e', "event", NULL, "event", | 617 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
614 | "event selector. use 'perf list' to list available events", | 618 | "event selector. use 'perf list' to list available events", |
615 | parse_events), | 619 | parse_events), |
616 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, | 620 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, |
@@ -648,6 +652,10 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
648 | 652 | ||
649 | setlocale(LC_ALL, ""); | 653 | setlocale(LC_ALL, ""); |
650 | 654 | ||
655 | evsel_list = perf_evlist__new(); | ||
656 | if (evsel_list == NULL) | ||
657 | return -ENOMEM; | ||
658 | |||
651 | argc = parse_options(argc, argv, options, stat_usage, | 659 | argc = parse_options(argc, argv, options, stat_usage, |
652 | PARSE_OPT_STOP_AT_NON_OPTION); | 660 | PARSE_OPT_STOP_AT_NON_OPTION); |
653 | 661 | ||
@@ -679,17 +687,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
679 | usage_with_options(stat_usage, options); | 687 | usage_with_options(stat_usage, options); |
680 | 688 | ||
681 | /* Set attrs and nr_counters if no event is selected and !null_run */ | 689 | /* Set attrs and nr_counters if no event is selected and !null_run */ |
682 | if (!null_run && !nr_counters) { | 690 | if (!null_run && !evsel_list->nr_entries) { |
683 | size_t c; | 691 | size_t c; |
684 | 692 | ||
685 | nr_counters = ARRAY_SIZE(default_attrs); | ||
686 | |||
687 | for (c = 0; c < ARRAY_SIZE(default_attrs); ++c) { | 693 | for (c = 0; c < ARRAY_SIZE(default_attrs); ++c) { |
688 | pos = perf_evsel__new(&default_attrs[c], | 694 | pos = perf_evsel__new(&default_attrs[c], c); |
689 | nr_counters); | ||
690 | if (pos == NULL) | 695 | if (pos == NULL) |
691 | goto out; | 696 | goto out; |
692 | list_add(&pos->node, &evsel_list); | 697 | perf_evlist__add(evsel_list, pos); |
693 | } | 698 | } |
694 | } | 699 | } |
695 | 700 | ||
@@ -713,7 +718,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
713 | return -1; | 718 | return -1; |
714 | } | 719 | } |
715 | 720 | ||
716 | list_for_each_entry(pos, &evsel_list, node) { | 721 | list_for_each_entry(pos, &evsel_list->entries, node) { |
717 | if (perf_evsel__alloc_stat_priv(pos) < 0 || | 722 | if (perf_evsel__alloc_stat_priv(pos) < 0 || |
718 | perf_evsel__alloc_counts(pos, cpus->nr) < 0 || | 723 | perf_evsel__alloc_counts(pos, cpus->nr) < 0 || |
719 | perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) | 724 | perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) |
@@ -741,9 +746,9 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
741 | if (status != -1) | 746 | if (status != -1) |
742 | print_stat(argc, argv); | 747 | print_stat(argc, argv); |
743 | out_free_fd: | 748 | out_free_fd: |
744 | list_for_each_entry(pos, &evsel_list, node) | 749 | list_for_each_entry(pos, &evsel_list->entries, node) |
745 | perf_evsel__free_stat_priv(pos); | 750 | perf_evsel__free_stat_priv(pos); |
746 | perf_evsel_list__delete(); | 751 | perf_evlist__delete(evsel_list); |
747 | out: | 752 | out: |
748 | thread_map__delete(threads); | 753 | thread_map__delete(threads); |
749 | threads = NULL; | 754 | threads = NULL; |
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 5dcdba653d70..231e3e21810c 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c | |||
@@ -7,10 +7,11 @@ | |||
7 | 7 | ||
8 | #include "util/cache.h" | 8 | #include "util/cache.h" |
9 | #include "util/debug.h" | 9 | #include "util/debug.h" |
10 | #include "util/evlist.h" | ||
10 | #include "util/parse-options.h" | 11 | #include "util/parse-options.h" |
11 | #include "util/session.h" | 12 | #include "util/parse-events.h" |
12 | #include "util/symbol.h" | 13 | #include "util/symbol.h" |
13 | #include "util/thread.h" | 14 | #include "util/thread_map.h" |
14 | 15 | ||
15 | static long page_size; | 16 | static long page_size; |
16 | 17 | ||
@@ -238,14 +239,14 @@ out: | |||
238 | #include "util/evsel.h" | 239 | #include "util/evsel.h" |
239 | #include <sys/types.h> | 240 | #include <sys/types.h> |
240 | 241 | ||
241 | static int trace_event__id(const char *event_name) | 242 | static int trace_event__id(const char *evname) |
242 | { | 243 | { |
243 | char *filename; | 244 | char *filename; |
244 | int err = -1, fd; | 245 | int err = -1, fd; |
245 | 246 | ||
246 | if (asprintf(&filename, | 247 | if (asprintf(&filename, |
247 | "/sys/kernel/debug/tracing/events/syscalls/%s/id", | 248 | "/sys/kernel/debug/tracing/events/syscalls/%s/id", |
248 | event_name) < 0) | 249 | evname) < 0) |
249 | return -1; | 250 | return -1; |
250 | 251 | ||
251 | fd = open(filename, O_RDONLY); | 252 | fd = open(filename, O_RDONLY); |
@@ -289,7 +290,7 @@ static int test__open_syscall_event(void) | |||
289 | goto out_thread_map_delete; | 290 | goto out_thread_map_delete; |
290 | } | 291 | } |
291 | 292 | ||
292 | if (perf_evsel__open_per_thread(evsel, threads) < 0) { | 293 | if (perf_evsel__open_per_thread(evsel, threads, false, false) < 0) { |
293 | pr_debug("failed to open counter: %s, " | 294 | pr_debug("failed to open counter: %s, " |
294 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | 295 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", |
295 | strerror(errno)); | 296 | strerror(errno)); |
@@ -347,9 +348,9 @@ static int test__open_syscall_event_on_all_cpus(void) | |||
347 | } | 348 | } |
348 | 349 | ||
349 | cpus = cpu_map__new(NULL); | 350 | cpus = cpu_map__new(NULL); |
350 | if (threads == NULL) { | 351 | if (cpus == NULL) { |
351 | pr_debug("thread_map__new\n"); | 352 | pr_debug("cpu_map__new\n"); |
352 | return -1; | 353 | goto out_thread_map_delete; |
353 | } | 354 | } |
354 | 355 | ||
355 | 356 | ||
@@ -364,7 +365,7 @@ static int test__open_syscall_event_on_all_cpus(void) | |||
364 | goto out_thread_map_delete; | 365 | goto out_thread_map_delete; |
365 | } | 366 | } |
366 | 367 | ||
367 | if (perf_evsel__open(evsel, cpus, threads) < 0) { | 368 | if (perf_evsel__open(evsel, cpus, threads, false, false) < 0) { |
368 | pr_debug("failed to open counter: %s, " | 369 | pr_debug("failed to open counter: %s, " |
369 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | 370 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", |
370 | strerror(errno)); | 371 | strerror(errno)); |
@@ -408,6 +409,8 @@ static int test__open_syscall_event_on_all_cpus(void) | |||
408 | goto out_close_fd; | 409 | goto out_close_fd; |
409 | } | 410 | } |
410 | 411 | ||
412 | err = 0; | ||
413 | |||
411 | for (cpu = 0; cpu < cpus->nr; ++cpu) { | 414 | for (cpu = 0; cpu < cpus->nr; ++cpu) { |
412 | unsigned int expected; | 415 | unsigned int expected; |
413 | 416 | ||
@@ -416,18 +419,18 @@ static int test__open_syscall_event_on_all_cpus(void) | |||
416 | 419 | ||
417 | if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) { | 420 | if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) { |
418 | pr_debug("perf_evsel__open_read_on_cpu\n"); | 421 | pr_debug("perf_evsel__open_read_on_cpu\n"); |
419 | goto out_close_fd; | 422 | err = -1; |
423 | break; | ||
420 | } | 424 | } |
421 | 425 | ||
422 | expected = nr_open_calls + cpu; | 426 | expected = nr_open_calls + cpu; |
423 | if (evsel->counts->cpu[cpu].val != expected) { | 427 | if (evsel->counts->cpu[cpu].val != expected) { |
424 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n", | 428 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n", |
425 | expected, cpus->map[cpu], evsel->counts->cpu[cpu].val); | 429 | expected, cpus->map[cpu], evsel->counts->cpu[cpu].val); |
426 | goto out_close_fd; | 430 | err = -1; |
427 | } | 431 | } |
428 | } | 432 | } |
429 | 433 | ||
430 | err = 0; | ||
431 | out_close_fd: | 434 | out_close_fd: |
432 | perf_evsel__close_fd(evsel, 1, threads->nr); | 435 | perf_evsel__close_fd(evsel, 1, threads->nr); |
433 | out_evsel_delete: | 436 | out_evsel_delete: |
@@ -437,6 +440,159 @@ out_thread_map_delete: | |||
437 | return err; | 440 | return err; |
438 | } | 441 | } |
439 | 442 | ||
443 | /* | ||
444 | * This test will generate random numbers of calls to some getpid syscalls, | ||
445 | * then establish an mmap for a group of events that are created to monitor | ||
446 | * the syscalls. | ||
447 | * | ||
448 | * It will receive the events, using mmap, use its PERF_SAMPLE_ID generated | ||
449 | * sample.id field to map back to its respective perf_evsel instance. | ||
450 | * | ||
451 | * Then it checks if the number of syscalls reported as perf events by | ||
452 | * the kernel corresponds to the number of syscalls made. | ||
453 | */ | ||
454 | static int test__basic_mmap(void) | ||
455 | { | ||
456 | int err = -1; | ||
457 | event_t *event; | ||
458 | struct thread_map *threads; | ||
459 | struct cpu_map *cpus; | ||
460 | struct perf_evlist *evlist; | ||
461 | struct perf_event_attr attr = { | ||
462 | .type = PERF_TYPE_TRACEPOINT, | ||
463 | .read_format = PERF_FORMAT_ID, | ||
464 | .sample_type = PERF_SAMPLE_ID, | ||
465 | .watermark = 0, | ||
466 | }; | ||
467 | cpu_set_t cpu_set; | ||
468 | const char *syscall_names[] = { "getsid", "getppid", "getpgrp", | ||
469 | "getpgid", }; | ||
470 | pid_t (*syscalls[])(void) = { (void *)getsid, getppid, getpgrp, | ||
471 | (void*)getpgid }; | ||
472 | #define nsyscalls ARRAY_SIZE(syscall_names) | ||
473 | int ids[nsyscalls]; | ||
474 | unsigned int nr_events[nsyscalls], | ||
475 | expected_nr_events[nsyscalls], i, j; | ||
476 | struct perf_evsel *evsels[nsyscalls], *evsel; | ||
477 | |||
478 | for (i = 0; i < nsyscalls; ++i) { | ||
479 | char name[64]; | ||
480 | |||
481 | snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]); | ||
482 | ids[i] = trace_event__id(name); | ||
483 | if (ids[i] < 0) { | ||
484 | pr_debug("Is debugfs mounted on /sys/kernel/debug?\n"); | ||
485 | return -1; | ||
486 | } | ||
487 | nr_events[i] = 0; | ||
488 | expected_nr_events[i] = random() % 257; | ||
489 | } | ||
490 | |||
491 | threads = thread_map__new(-1, getpid()); | ||
492 | if (threads == NULL) { | ||
493 | pr_debug("thread_map__new\n"); | ||
494 | return -1; | ||
495 | } | ||
496 | |||
497 | cpus = cpu_map__new(NULL); | ||
498 | if (threads == NULL) { | ||
499 | pr_debug("thread_map__new\n"); | ||
500 | goto out_free_threads; | ||
501 | } | ||
502 | |||
503 | CPU_ZERO(&cpu_set); | ||
504 | CPU_SET(cpus->map[0], &cpu_set); | ||
505 | sched_setaffinity(0, sizeof(cpu_set), &cpu_set); | ||
506 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { | ||
507 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", | ||
508 | cpus->map[0], strerror(errno)); | ||
509 | goto out_free_cpus; | ||
510 | } | ||
511 | |||
512 | evlist = perf_evlist__new(); | ||
513 | if (threads == NULL) { | ||
514 | pr_debug("perf_evlist__new\n"); | ||
515 | goto out_free_cpus; | ||
516 | } | ||
517 | |||
518 | /* anonymous union fields, can't be initialized above */ | ||
519 | attr.wakeup_events = 1; | ||
520 | attr.sample_period = 1; | ||
521 | |||
522 | for (i = 0; i < nsyscalls; ++i) { | ||
523 | attr.config = ids[i]; | ||
524 | evsels[i] = perf_evsel__new(&attr, i); | ||
525 | if (evsels[i] == NULL) { | ||
526 | pr_debug("perf_evsel__new\n"); | ||
527 | goto out_free_evlist; | ||
528 | } | ||
529 | |||
530 | perf_evlist__add(evlist, evsels[i]); | ||
531 | |||
532 | if (perf_evsel__open(evsels[i], cpus, threads, false, false) < 0) { | ||
533 | pr_debug("failed to open counter: %s, " | ||
534 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
535 | strerror(errno)); | ||
536 | goto out_close_fd; | ||
537 | } | ||
538 | } | ||
539 | |||
540 | if (perf_evlist__mmap(evlist, cpus, threads, 128, true) < 0) { | ||
541 | pr_debug("failed to mmap events: %d (%s)\n", errno, | ||
542 | strerror(errno)); | ||
543 | goto out_close_fd; | ||
544 | } | ||
545 | |||
546 | for (i = 0; i < nsyscalls; ++i) | ||
547 | for (j = 0; j < expected_nr_events[i]; ++j) { | ||
548 | int foo = syscalls[i](); | ||
549 | ++foo; | ||
550 | } | ||
551 | |||
552 | while ((event = perf_evlist__read_on_cpu(evlist, 0)) != NULL) { | ||
553 | struct sample_data sample; | ||
554 | |||
555 | if (event->header.type != PERF_RECORD_SAMPLE) { | ||
556 | pr_debug("unexpected %s event\n", | ||
557 | event__get_event_name(event->header.type)); | ||
558 | goto out_munmap; | ||
559 | } | ||
560 | |||
561 | event__parse_sample(event, attr.sample_type, false, &sample); | ||
562 | evsel = perf_evlist__id2evsel(evlist, sample.id); | ||
563 | if (evsel == NULL) { | ||
564 | pr_debug("event with id %" PRIu64 | ||
565 | " doesn't map to an evsel\n", sample.id); | ||
566 | goto out_munmap; | ||
567 | } | ||
568 | nr_events[evsel->idx]++; | ||
569 | } | ||
570 | |||
571 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
572 | if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) { | ||
573 | pr_debug("expected %d %s events, got %d\n", | ||
574 | expected_nr_events[evsel->idx], | ||
575 | event_name(evsel), nr_events[evsel->idx]); | ||
576 | goto out_munmap; | ||
577 | } | ||
578 | } | ||
579 | |||
580 | err = 0; | ||
581 | out_munmap: | ||
582 | perf_evlist__munmap(evlist, 1); | ||
583 | out_close_fd: | ||
584 | for (i = 0; i < nsyscalls; ++i) | ||
585 | perf_evsel__close_fd(evsels[i], 1, threads->nr); | ||
586 | out_free_evlist: | ||
587 | perf_evlist__delete(evlist); | ||
588 | out_free_cpus: | ||
589 | cpu_map__delete(cpus); | ||
590 | out_free_threads: | ||
591 | thread_map__delete(threads); | ||
592 | return err; | ||
593 | #undef nsyscalls | ||
594 | } | ||
595 | |||
440 | static struct test { | 596 | static struct test { |
441 | const char *desc; | 597 | const char *desc; |
442 | int (*func)(void); | 598 | int (*func)(void); |
@@ -454,6 +610,10 @@ static struct test { | |||
454 | .func = test__open_syscall_event_on_all_cpus, | 610 | .func = test__open_syscall_event_on_all_cpus, |
455 | }, | 611 | }, |
456 | { | 612 | { |
613 | .desc = "read samples using the mmap interface", | ||
614 | .func = test__basic_mmap, | ||
615 | }, | ||
616 | { | ||
457 | .func = NULL, | 617 | .func = NULL, |
458 | }, | 618 | }, |
459 | }; | 619 | }; |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index b6998e055767..ce2e50c891c7 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -21,10 +21,12 @@ | |||
21 | #include "perf.h" | 21 | #include "perf.h" |
22 | 22 | ||
23 | #include "util/color.h" | 23 | #include "util/color.h" |
24 | #include "util/evlist.h" | ||
24 | #include "util/evsel.h" | 25 | #include "util/evsel.h" |
25 | #include "util/session.h" | 26 | #include "util/session.h" |
26 | #include "util/symbol.h" | 27 | #include "util/symbol.h" |
27 | #include "util/thread.h" | 28 | #include "util/thread.h" |
29 | #include "util/thread_map.h" | ||
28 | #include "util/util.h" | 30 | #include "util/util.h" |
29 | #include <linux/rbtree.h> | 31 | #include <linux/rbtree.h> |
30 | #include "util/parse-options.h" | 32 | #include "util/parse-options.h" |
@@ -60,6 +62,8 @@ | |||
60 | 62 | ||
61 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 63 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
62 | 64 | ||
65 | struct perf_evlist *evsel_list; | ||
66 | |||
63 | static bool system_wide = false; | 67 | static bool system_wide = false; |
64 | 68 | ||
65 | static int default_interval = 0; | 69 | static int default_interval = 0; |
@@ -75,7 +79,7 @@ static struct cpu_map *cpus; | |||
75 | static int realtime_prio = 0; | 79 | static int realtime_prio = 0; |
76 | static bool group = false; | 80 | static bool group = false; |
77 | static unsigned int page_size; | 81 | static unsigned int page_size; |
78 | static unsigned int mmap_pages = 16; | 82 | static unsigned int mmap_pages = 128; |
79 | static int freq = 1000; /* 1 KHz */ | 83 | static int freq = 1000; /* 1 KHz */ |
80 | 84 | ||
81 | static int delay_secs = 2; | 85 | static int delay_secs = 2; |
@@ -267,7 +271,7 @@ static void __zero_source_counters(struct sym_entry *syme) | |||
267 | 271 | ||
268 | line = syme->src->lines; | 272 | line = syme->src->lines; |
269 | while (line) { | 273 | while (line) { |
270 | for (i = 0; i < nr_counters; i++) | 274 | for (i = 0; i < evsel_list->nr_entries; i++) |
271 | line->count[i] = 0; | 275 | line->count[i] = 0; |
272 | line = line->next; | 276 | line = line->next; |
273 | } | 277 | } |
@@ -414,7 +418,7 @@ static double sym_weight(const struct sym_entry *sym) | |||
414 | if (!display_weighted) | 418 | if (!display_weighted) |
415 | return weight; | 419 | return weight; |
416 | 420 | ||
417 | for (counter = 1; counter < nr_counters-1; counter++) | 421 | for (counter = 1; counter < evsel_list->nr_entries - 1; counter++) |
418 | weight *= sym->count[counter]; | 422 | weight *= sym->count[counter]; |
419 | 423 | ||
420 | weight /= (sym->count[counter] + 1); | 424 | weight /= (sym->count[counter] + 1); |
@@ -501,7 +505,7 @@ static void print_sym_table(void) | |||
501 | rb_insert_active_sym(&tmp, syme); | 505 | rb_insert_active_sym(&tmp, syme); |
502 | sum_ksamples += syme->snap_count; | 506 | sum_ksamples += syme->snap_count; |
503 | 507 | ||
504 | for (j = 0; j < nr_counters; j++) | 508 | for (j = 0; j < evsel_list->nr_entries; j++) |
505 | syme->count[j] = zero ? 0 : syme->count[j] * 7 / 8; | 509 | syme->count[j] = zero ? 0 : syme->count[j] * 7 / 8; |
506 | } else | 510 | } else |
507 | list_remove_active_sym(syme); | 511 | list_remove_active_sym(syme); |
@@ -535,9 +539,9 @@ static void print_sym_table(void) | |||
535 | esamples_percent); | 539 | esamples_percent); |
536 | } | 540 | } |
537 | 541 | ||
538 | if (nr_counters == 1 || !display_weighted) { | 542 | if (evsel_list->nr_entries == 1 || !display_weighted) { |
539 | struct perf_evsel *first; | 543 | struct perf_evsel *first; |
540 | first = list_entry(evsel_list.next, struct perf_evsel, node); | 544 | first = list_entry(evsel_list->entries.next, struct perf_evsel, node); |
541 | printf("%" PRIu64, (uint64_t)first->attr.sample_period); | 545 | printf("%" PRIu64, (uint64_t)first->attr.sample_period); |
542 | if (freq) | 546 | if (freq) |
543 | printf("Hz "); | 547 | printf("Hz "); |
@@ -547,7 +551,7 @@ static void print_sym_table(void) | |||
547 | 551 | ||
548 | if (!display_weighted) | 552 | if (!display_weighted) |
549 | printf("%s", event_name(sym_evsel)); | 553 | printf("%s", event_name(sym_evsel)); |
550 | else list_for_each_entry(counter, &evsel_list, node) { | 554 | else list_for_each_entry(counter, &evsel_list->entries, node) { |
551 | if (counter->idx) | 555 | if (counter->idx) |
552 | printf("/"); | 556 | printf("/"); |
553 | 557 | ||
@@ -606,7 +610,7 @@ static void print_sym_table(void) | |||
606 | sym_width = winsize.ws_col - dso_width - 29; | 610 | sym_width = winsize.ws_col - dso_width - 29; |
607 | } | 611 | } |
608 | putchar('\n'); | 612 | putchar('\n'); |
609 | if (nr_counters == 1) | 613 | if (evsel_list->nr_entries == 1) |
610 | printf(" samples pcnt"); | 614 | printf(" samples pcnt"); |
611 | else | 615 | else |
612 | printf(" weight samples pcnt"); | 616 | printf(" weight samples pcnt"); |
@@ -615,7 +619,7 @@ static void print_sym_table(void) | |||
615 | printf(" RIP "); | 619 | printf(" RIP "); |
616 | printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); | 620 | printf(" %-*.*s DSO\n", sym_width, sym_width, "function"); |
617 | printf(" %s _______ _____", | 621 | printf(" %s _______ _____", |
618 | nr_counters == 1 ? " " : "______"); | 622 | evsel_list->nr_entries == 1 ? " " : "______"); |
619 | if (verbose) | 623 | if (verbose) |
620 | printf(" ________________"); | 624 | printf(" ________________"); |
621 | printf(" %-*.*s", sym_width, sym_width, graph_line); | 625 | printf(" %-*.*s", sym_width, sym_width, graph_line); |
@@ -634,7 +638,7 @@ static void print_sym_table(void) | |||
634 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / | 638 | pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) / |
635 | sum_ksamples)); | 639 | sum_ksamples)); |
636 | 640 | ||
637 | if (nr_counters == 1 || !display_weighted) | 641 | if (evsel_list->nr_entries == 1 || !display_weighted) |
638 | printf("%20.2f ", syme->weight); | 642 | printf("%20.2f ", syme->weight); |
639 | else | 643 | else |
640 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); | 644 | printf("%9.1f %10ld ", syme->weight, syme->snap_count); |
@@ -744,7 +748,7 @@ static void print_mapped_keys(void) | |||
744 | fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs); | 748 | fprintf(stdout, "\t[d] display refresh delay. \t(%d)\n", delay_secs); |
745 | fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries); | 749 | fprintf(stdout, "\t[e] display entries (lines). \t(%d)\n", print_entries); |
746 | 750 | ||
747 | if (nr_counters > 1) | 751 | if (evsel_list->nr_entries > 1) |
748 | fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_evsel)); | 752 | fprintf(stdout, "\t[E] active event counter. \t(%s)\n", event_name(sym_evsel)); |
749 | 753 | ||
750 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); | 754 | fprintf(stdout, "\t[f] profile display filter (count). \t(%d)\n", count_filter); |
@@ -753,7 +757,7 @@ static void print_mapped_keys(void) | |||
753 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); | 757 | fprintf(stdout, "\t[s] annotate symbol. \t(%s)\n", name?: "NULL"); |
754 | fprintf(stdout, "\t[S] stop annotation.\n"); | 758 | fprintf(stdout, "\t[S] stop annotation.\n"); |
755 | 759 | ||
756 | if (nr_counters > 1) | 760 | if (evsel_list->nr_entries > 1) |
757 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); | 761 | fprintf(stdout, "\t[w] toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0); |
758 | 762 | ||
759 | fprintf(stdout, | 763 | fprintf(stdout, |
@@ -783,7 +787,7 @@ static int key_mapped(int c) | |||
783 | return 1; | 787 | return 1; |
784 | case 'E': | 788 | case 'E': |
785 | case 'w': | 789 | case 'w': |
786 | return nr_counters > 1 ? 1 : 0; | 790 | return evsel_list->nr_entries > 1 ? 1 : 0; |
787 | default: | 791 | default: |
788 | break; | 792 | break; |
789 | } | 793 | } |
@@ -831,22 +835,22 @@ static void handle_keypress(struct perf_session *session, int c) | |||
831 | signal(SIGWINCH, SIG_DFL); | 835 | signal(SIGWINCH, SIG_DFL); |
832 | break; | 836 | break; |
833 | case 'E': | 837 | case 'E': |
834 | if (nr_counters > 1) { | 838 | if (evsel_list->nr_entries > 1) { |
835 | fprintf(stderr, "\nAvailable events:"); | 839 | fprintf(stderr, "\nAvailable events:"); |
836 | 840 | ||
837 | list_for_each_entry(sym_evsel, &evsel_list, node) | 841 | list_for_each_entry(sym_evsel, &evsel_list->entries, node) |
838 | fprintf(stderr, "\n\t%d %s", sym_evsel->idx, event_name(sym_evsel)); | 842 | fprintf(stderr, "\n\t%d %s", sym_evsel->idx, event_name(sym_evsel)); |
839 | 843 | ||
840 | prompt_integer(&sym_counter, "Enter details event counter"); | 844 | prompt_integer(&sym_counter, "Enter details event counter"); |
841 | 845 | ||
842 | if (sym_counter >= nr_counters) { | 846 | if (sym_counter >= evsel_list->nr_entries) { |
843 | sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node); | 847 | sym_evsel = list_entry(evsel_list->entries.next, struct perf_evsel, node); |
844 | sym_counter = 0; | 848 | sym_counter = 0; |
845 | fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(sym_evsel)); | 849 | fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(sym_evsel)); |
846 | sleep(1); | 850 | sleep(1); |
847 | break; | 851 | break; |
848 | } | 852 | } |
849 | list_for_each_entry(sym_evsel, &evsel_list, node) | 853 | list_for_each_entry(sym_evsel, &evsel_list->entries, node) |
850 | if (sym_evsel->idx == sym_counter) | 854 | if (sym_evsel->idx == sym_counter) |
851 | break; | 855 | break; |
852 | } else sym_counter = 0; | 856 | } else sym_counter = 0; |
@@ -930,6 +934,7 @@ repeat: | |||
930 | /* Tag samples to be skipped. */ | 934 | /* Tag samples to be skipped. */ |
931 | static const char *skip_symbols[] = { | 935 | static const char *skip_symbols[] = { |
932 | "default_idle", | 936 | "default_idle", |
937 | "native_safe_halt", | ||
933 | "cpu_idle", | 938 | "cpu_idle", |
934 | "enter_idle", | 939 | "enter_idle", |
935 | "exit_idle", | 940 | "exit_idle", |
@@ -988,8 +993,7 @@ static int symbol_filter(struct map *map, struct symbol *sym) | |||
988 | 993 | ||
989 | static void event__process_sample(const event_t *self, | 994 | static void event__process_sample(const event_t *self, |
990 | struct sample_data *sample, | 995 | struct sample_data *sample, |
991 | struct perf_session *session, | 996 | struct perf_session *session) |
992 | struct perf_evsel *evsel) | ||
993 | { | 997 | { |
994 | u64 ip = self->ip.ip; | 998 | u64 ip = self->ip.ip; |
995 | struct sym_entry *syme; | 999 | struct sym_entry *syme; |
@@ -1082,8 +1086,12 @@ static void event__process_sample(const event_t *self, | |||
1082 | 1086 | ||
1083 | syme = symbol__priv(al.sym); | 1087 | syme = symbol__priv(al.sym); |
1084 | if (!syme->skip) { | 1088 | if (!syme->skip) { |
1085 | syme->count[evsel->idx]++; | 1089 | struct perf_evsel *evsel; |
1090 | |||
1086 | syme->origin = origin; | 1091 | syme->origin = origin; |
1092 | evsel = perf_evlist__id2evsel(evsel_list, sample->id); | ||
1093 | assert(evsel != NULL); | ||
1094 | syme->count[evsel->idx]++; | ||
1087 | record_precise_ip(syme, evsel->idx, ip); | 1095 | record_precise_ip(syme, evsel->idx, ip); |
1088 | pthread_mutex_lock(&active_symbols_lock); | 1096 | pthread_mutex_lock(&active_symbols_lock); |
1089 | if (list_empty(&syme->node) || !syme->node.next) | 1097 | if (list_empty(&syme->node) || !syme->node.next) |
@@ -1092,156 +1100,52 @@ static void event__process_sample(const event_t *self, | |||
1092 | } | 1100 | } |
1093 | } | 1101 | } |
1094 | 1102 | ||
1095 | struct mmap_data { | 1103 | static void perf_session__mmap_read_cpu(struct perf_session *self, int cpu) |
1096 | void *base; | ||
1097 | int mask; | ||
1098 | unsigned int prev; | ||
1099 | }; | ||
1100 | |||
1101 | static int perf_evsel__alloc_mmap_per_thread(struct perf_evsel *evsel, | ||
1102 | int ncpus, int nthreads) | ||
1103 | { | ||
1104 | evsel->priv = xyarray__new(ncpus, nthreads, sizeof(struct mmap_data)); | ||
1105 | return evsel->priv != NULL ? 0 : -ENOMEM; | ||
1106 | } | ||
1107 | |||
1108 | static void perf_evsel__free_mmap(struct perf_evsel *evsel) | ||
1109 | { | ||
1110 | xyarray__delete(evsel->priv); | ||
1111 | evsel->priv = NULL; | ||
1112 | } | ||
1113 | |||
1114 | static unsigned int mmap_read_head(struct mmap_data *md) | ||
1115 | { | ||
1116 | struct perf_event_mmap_page *pc = md->base; | ||
1117 | int head; | ||
1118 | |||
1119 | head = pc->data_head; | ||
1120 | rmb(); | ||
1121 | |||
1122 | return head; | ||
1123 | } | ||
1124 | |||
1125 | static void perf_session__mmap_read_counter(struct perf_session *self, | ||
1126 | struct perf_evsel *evsel, | ||
1127 | int cpu, int thread_idx) | ||
1128 | { | 1104 | { |
1129 | struct xyarray *mmap_array = evsel->priv; | ||
1130 | struct mmap_data *md = xyarray__entry(mmap_array, cpu, thread_idx); | ||
1131 | unsigned int head = mmap_read_head(md); | ||
1132 | unsigned int old = md->prev; | ||
1133 | unsigned char *data = md->base + page_size; | ||
1134 | struct sample_data sample; | 1105 | struct sample_data sample; |
1135 | int diff; | 1106 | event_t *event; |
1136 | |||
1137 | /* | ||
1138 | * If we're further behind than half the buffer, there's a chance | ||
1139 | * the writer will bite our tail and mess up the samples under us. | ||
1140 | * | ||
1141 | * If we somehow ended up ahead of the head, we got messed up. | ||
1142 | * | ||
1143 | * In either case, truncate and restart at head. | ||
1144 | */ | ||
1145 | diff = head - old; | ||
1146 | if (diff > md->mask / 2 || diff < 0) { | ||
1147 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); | ||
1148 | |||
1149 | /* | ||
1150 | * head points to a known good entry, start there. | ||
1151 | */ | ||
1152 | old = head; | ||
1153 | } | ||
1154 | |||
1155 | for (; old != head;) { | ||
1156 | event_t *event = (event_t *)&data[old & md->mask]; | ||
1157 | 1107 | ||
1158 | event_t event_copy; | 1108 | while ((event = perf_evlist__read_on_cpu(evsel_list, cpu)) != NULL) { |
1159 | 1109 | perf_session__parse_sample(self, event, &sample); | |
1160 | size_t size = event->header.size; | ||
1161 | |||
1162 | /* | ||
1163 | * Event straddles the mmap boundary -- header should always | ||
1164 | * be inside due to u64 alignment of output. | ||
1165 | */ | ||
1166 | if ((old & md->mask) + size != ((old + size) & md->mask)) { | ||
1167 | unsigned int offset = old; | ||
1168 | unsigned int len = min(sizeof(*event), size), cpy; | ||
1169 | void *dst = &event_copy; | ||
1170 | |||
1171 | do { | ||
1172 | cpy = min(md->mask + 1 - (offset & md->mask), len); | ||
1173 | memcpy(dst, &data[offset & md->mask], cpy); | ||
1174 | offset += cpy; | ||
1175 | dst += cpy; | ||
1176 | len -= cpy; | ||
1177 | } while (len); | ||
1178 | |||
1179 | event = &event_copy; | ||
1180 | } | ||
1181 | 1110 | ||
1182 | event__parse_sample(event, self, &sample); | ||
1183 | if (event->header.type == PERF_RECORD_SAMPLE) | 1111 | if (event->header.type == PERF_RECORD_SAMPLE) |
1184 | event__process_sample(event, &sample, self, evsel); | 1112 | event__process_sample(event, &sample, self); |
1185 | else | 1113 | else |
1186 | event__process(event, &sample, self); | 1114 | event__process(event, &sample, self); |
1187 | old += size; | ||
1188 | } | 1115 | } |
1189 | |||
1190 | md->prev = old; | ||
1191 | } | 1116 | } |
1192 | 1117 | ||
1193 | static struct pollfd *event_array; | ||
1194 | |||
1195 | static void perf_session__mmap_read(struct perf_session *self) | 1118 | static void perf_session__mmap_read(struct perf_session *self) |
1196 | { | 1119 | { |
1197 | struct perf_evsel *counter; | 1120 | int i; |
1198 | int i, thread_index; | ||
1199 | |||
1200 | for (i = 0; i < cpus->nr; i++) { | ||
1201 | list_for_each_entry(counter, &evsel_list, node) { | ||
1202 | for (thread_index = 0; | ||
1203 | thread_index < threads->nr; | ||
1204 | thread_index++) { | ||
1205 | perf_session__mmap_read_counter(self, | ||
1206 | counter, i, thread_index); | ||
1207 | } | ||
1208 | } | ||
1209 | } | ||
1210 | } | ||
1211 | 1121 | ||
1212 | int nr_poll; | 1122 | for (i = 0; i < cpus->nr; i++) |
1213 | int group_fd; | 1123 | perf_session__mmap_read_cpu(self, i); |
1124 | } | ||
1214 | 1125 | ||
1215 | static void start_counter(int i, struct perf_evsel *evsel) | 1126 | static void start_counters(struct perf_evlist *evlist) |
1216 | { | 1127 | { |
1217 | struct xyarray *mmap_array = evsel->priv; | 1128 | struct perf_evsel *counter; |
1218 | struct mmap_data *mm; | ||
1219 | struct perf_event_attr *attr; | ||
1220 | int cpu = -1; | ||
1221 | int thread_index; | ||
1222 | |||
1223 | if (target_tid == -1) | ||
1224 | cpu = cpus->map[i]; | ||
1225 | 1129 | ||
1226 | attr = &evsel->attr; | 1130 | list_for_each_entry(counter, &evlist->entries, node) { |
1131 | struct perf_event_attr *attr = &counter->attr; | ||
1227 | 1132 | ||
1228 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 1133 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
1229 | 1134 | ||
1230 | if (freq) { | 1135 | if (freq) { |
1231 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 1136 | attr->sample_type |= PERF_SAMPLE_PERIOD; |
1232 | attr->freq = 1; | 1137 | attr->freq = 1; |
1233 | attr->sample_freq = freq; | 1138 | attr->sample_freq = freq; |
1234 | } | 1139 | } |
1235 | 1140 | ||
1236 | attr->inherit = (cpu < 0) && inherit; | 1141 | if (evlist->nr_entries > 1) { |
1237 | attr->mmap = 1; | 1142 | attr->sample_type |= PERF_SAMPLE_ID; |
1143 | attr->read_format |= PERF_FORMAT_ID; | ||
1144 | } | ||
1238 | 1145 | ||
1239 | for (thread_index = 0; thread_index < threads->nr; thread_index++) { | 1146 | attr->mmap = 1; |
1240 | try_again: | 1147 | try_again: |
1241 | FD(evsel, i, thread_index) = sys_perf_event_open(attr, | 1148 | if (perf_evsel__open(counter, cpus, threads, group, inherit) < 0) { |
1242 | threads->map[thread_index], cpu, group_fd, 0); | ||
1243 | |||
1244 | if (FD(evsel, i, thread_index) < 0) { | ||
1245 | int err = errno; | 1149 | int err = errno; |
1246 | 1150 | ||
1247 | if (err == EPERM || err == EACCES) | 1151 | if (err == EPERM || err == EACCES) |
@@ -1253,8 +1157,8 @@ try_again: | |||
1253 | * based cpu-clock-tick sw counter, which | 1157 | * based cpu-clock-tick sw counter, which |
1254 | * is always available even if no PMU support: | 1158 | * is always available even if no PMU support: |
1255 | */ | 1159 | */ |
1256 | if (attr->type == PERF_TYPE_HARDWARE | 1160 | if (attr->type == PERF_TYPE_HARDWARE && |
1257 | && attr->config == PERF_COUNT_HW_CPU_CYCLES) { | 1161 | attr->config == PERF_COUNT_HW_CPU_CYCLES) { |
1258 | 1162 | ||
1259 | if (verbose) | 1163 | if (verbose) |
1260 | warning(" ... trying to fall back to cpu-clock-ticks\n"); | 1164 | warning(" ... trying to fall back to cpu-clock-ticks\n"); |
@@ -1264,39 +1168,23 @@ try_again: | |||
1264 | goto try_again; | 1168 | goto try_again; |
1265 | } | 1169 | } |
1266 | printf("\n"); | 1170 | printf("\n"); |
1267 | error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n", | 1171 | error("sys_perf_event_open() syscall returned with %d " |
1268 | FD(evsel, i, thread_index), strerror(err)); | 1172 | "(%s). /bin/dmesg may provide additional information.\n", |
1173 | err, strerror(err)); | ||
1269 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | 1174 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); |
1270 | exit(-1); | 1175 | exit(-1); |
1271 | } | 1176 | } |
1272 | assert(FD(evsel, i, thread_index) >= 0); | ||
1273 | fcntl(FD(evsel, i, thread_index), F_SETFL, O_NONBLOCK); | ||
1274 | |||
1275 | /* | ||
1276 | * First counter acts as the group leader: | ||
1277 | */ | ||
1278 | if (group && group_fd == -1) | ||
1279 | group_fd = FD(evsel, i, thread_index); | ||
1280 | |||
1281 | event_array[nr_poll].fd = FD(evsel, i, thread_index); | ||
1282 | event_array[nr_poll].events = POLLIN; | ||
1283 | nr_poll++; | ||
1284 | |||
1285 | mm = xyarray__entry(mmap_array, i, thread_index); | ||
1286 | mm->prev = 0; | ||
1287 | mm->mask = mmap_pages*page_size - 1; | ||
1288 | mm->base = mmap(NULL, (mmap_pages+1)*page_size, | ||
1289 | PROT_READ, MAP_SHARED, FD(evsel, i, thread_index), 0); | ||
1290 | if (mm->base == MAP_FAILED) | ||
1291 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
1292 | } | 1177 | } |
1178 | |||
1179 | if (perf_evlist__mmap(evlist, cpus, threads, mmap_pages, true) < 0) | ||
1180 | die("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
1293 | } | 1181 | } |
1294 | 1182 | ||
1295 | static int __cmd_top(void) | 1183 | static int __cmd_top(void) |
1296 | { | 1184 | { |
1297 | pthread_t thread; | 1185 | pthread_t thread; |
1298 | struct perf_evsel *counter; | 1186 | struct perf_evsel *first; |
1299 | int i, ret; | 1187 | int ret; |
1300 | /* | 1188 | /* |
1301 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this | 1189 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this |
1302 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. | 1190 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. |
@@ -1310,14 +1198,12 @@ static int __cmd_top(void) | |||
1310 | else | 1198 | else |
1311 | event__synthesize_threads(event__process, session); | 1199 | event__synthesize_threads(event__process, session); |
1312 | 1200 | ||
1313 | for (i = 0; i < cpus->nr; i++) { | 1201 | start_counters(evsel_list); |
1314 | group_fd = -1; | 1202 | first = list_entry(evsel_list->entries.next, struct perf_evsel, node); |
1315 | list_for_each_entry(counter, &evsel_list, node) | 1203 | perf_session__set_sample_type(session, first->attr.sample_type); |
1316 | start_counter(i, counter); | ||
1317 | } | ||
1318 | 1204 | ||
1319 | /* Wait for a minimal set of events before starting the snapshot */ | 1205 | /* Wait for a minimal set of events before starting the snapshot */ |
1320 | poll(&event_array[0], nr_poll, 100); | 1206 | poll(evsel_list->pollfd, evsel_list->nr_fds, 100); |
1321 | 1207 | ||
1322 | perf_session__mmap_read(session); | 1208 | perf_session__mmap_read(session); |
1323 | 1209 | ||
@@ -1342,7 +1228,7 @@ static int __cmd_top(void) | |||
1342 | perf_session__mmap_read(session); | 1228 | perf_session__mmap_read(session); |
1343 | 1229 | ||
1344 | if (hits == samples) | 1230 | if (hits == samples) |
1345 | ret = poll(event_array, nr_poll, 100); | 1231 | ret = poll(evsel_list->pollfd, evsel_list->nr_fds, 100); |
1346 | } | 1232 | } |
1347 | 1233 | ||
1348 | return 0; | 1234 | return 0; |
@@ -1354,7 +1240,7 @@ static const char * const top_usage[] = { | |||
1354 | }; | 1240 | }; |
1355 | 1241 | ||
1356 | static const struct option options[] = { | 1242 | static const struct option options[] = { |
1357 | OPT_CALLBACK('e', "event", NULL, "event", | 1243 | OPT_CALLBACK('e', "event", &evsel_list, "event", |
1358 | "event selector. use 'perf list' to list available events", | 1244 | "event selector. use 'perf list' to list available events", |
1359 | parse_events), | 1245 | parse_events), |
1360 | OPT_INTEGER('c', "count", &default_interval, | 1246 | OPT_INTEGER('c', "count", &default_interval, |
@@ -1404,6 +1290,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1404 | struct perf_evsel *pos; | 1290 | struct perf_evsel *pos; |
1405 | int status = -ENOMEM; | 1291 | int status = -ENOMEM; |
1406 | 1292 | ||
1293 | evsel_list = perf_evlist__new(); | ||
1294 | if (evsel_list == NULL) | ||
1295 | return -ENOMEM; | ||
1296 | |||
1407 | page_size = sysconf(_SC_PAGE_SIZE); | 1297 | page_size = sysconf(_SC_PAGE_SIZE); |
1408 | 1298 | ||
1409 | argc = parse_options(argc, argv, options, top_usage, 0); | 1299 | argc = parse_options(argc, argv, options, top_usage, 0); |
@@ -1419,11 +1309,6 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1419 | usage_with_options(top_usage, options); | 1309 | usage_with_options(top_usage, options); |
1420 | } | 1310 | } |
1421 | 1311 | ||
1422 | event_array = malloc((sizeof(struct pollfd) * | ||
1423 | MAX_NR_CPUS * MAX_COUNTERS * threads->nr)); | ||
1424 | if (!event_array) | ||
1425 | return -ENOMEM; | ||
1426 | |||
1427 | /* CPU and PID are mutually exclusive */ | 1312 | /* CPU and PID are mutually exclusive */ |
1428 | if (target_tid > 0 && cpu_list) { | 1313 | if (target_tid > 0 && cpu_list) { |
1429 | printf("WARNING: PID switch overriding CPU\n"); | 1314 | printf("WARNING: PID switch overriding CPU\n"); |
@@ -1431,7 +1316,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1431 | cpu_list = NULL; | 1316 | cpu_list = NULL; |
1432 | } | 1317 | } |
1433 | 1318 | ||
1434 | if (!nr_counters && perf_evsel_list__create_default() < 0) { | 1319 | if (!evsel_list->nr_entries && |
1320 | perf_evlist__add_default(evsel_list) < 0) { | ||
1435 | pr_err("Not enough memory for event selector list\n"); | 1321 | pr_err("Not enough memory for event selector list\n"); |
1436 | return -ENOMEM; | 1322 | return -ENOMEM; |
1437 | } | 1323 | } |
@@ -1459,9 +1345,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1459 | if (cpus == NULL) | 1345 | if (cpus == NULL) |
1460 | usage_with_options(top_usage, options); | 1346 | usage_with_options(top_usage, options); |
1461 | 1347 | ||
1462 | list_for_each_entry(pos, &evsel_list, node) { | 1348 | list_for_each_entry(pos, &evsel_list->entries, node) { |
1463 | if (perf_evsel__alloc_mmap_per_thread(pos, cpus->nr, threads->nr) < 0 || | 1349 | if (perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) |
1464 | perf_evsel__alloc_fd(pos, cpus->nr, threads->nr) < 0) | ||
1465 | goto out_free_fd; | 1350 | goto out_free_fd; |
1466 | /* | 1351 | /* |
1467 | * Fill in the ones not specifically initialized via -c: | 1352 | * Fill in the ones not specifically initialized via -c: |
@@ -1472,10 +1357,14 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1472 | pos->attr.sample_period = default_interval; | 1357 | pos->attr.sample_period = default_interval; |
1473 | } | 1358 | } |
1474 | 1359 | ||
1475 | sym_evsel = list_entry(evsel_list.next, struct perf_evsel, node); | 1360 | if (perf_evlist__alloc_pollfd(evsel_list, cpus->nr, threads->nr) < 0 || |
1361 | perf_evlist__alloc_mmap(evsel_list, cpus->nr) < 0) | ||
1362 | goto out_free_fd; | ||
1363 | |||
1364 | sym_evsel = list_entry(evsel_list->entries.next, struct perf_evsel, node); | ||
1476 | 1365 | ||
1477 | symbol_conf.priv_size = (sizeof(struct sym_entry) + | 1366 | symbol_conf.priv_size = (sizeof(struct sym_entry) + |
1478 | (nr_counters + 1) * sizeof(unsigned long)); | 1367 | (evsel_list->nr_entries + 1) * sizeof(unsigned long)); |
1479 | 1368 | ||
1480 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); | 1369 | symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL); |
1481 | if (symbol__init() < 0) | 1370 | if (symbol__init() < 0) |
@@ -1489,9 +1378,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1489 | 1378 | ||
1490 | status = __cmd_top(); | 1379 | status = __cmd_top(); |
1491 | out_free_fd: | 1380 | out_free_fd: |
1492 | list_for_each_entry(pos, &evsel_list, node) | 1381 | perf_evlist__delete(evsel_list); |
1493 | perf_evsel__free_mmap(pos); | ||
1494 | perf_evsel_list__delete(); | ||
1495 | 1382 | ||
1496 | return status; | 1383 | return status; |
1497 | } | 1384 | } |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 95aaf565c704..a5fc660c1f12 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -94,6 +94,32 @@ void get_term_dimensions(struct winsize *ws); | |||
94 | #include "util/types.h" | 94 | #include "util/types.h" |
95 | #include <stdbool.h> | 95 | #include <stdbool.h> |
96 | 96 | ||
97 | struct perf_mmap { | ||
98 | void *base; | ||
99 | int mask; | ||
100 | unsigned int prev; | ||
101 | }; | ||
102 | |||
103 | static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) | ||
104 | { | ||
105 | struct perf_event_mmap_page *pc = mm->base; | ||
106 | int head = pc->data_head; | ||
107 | rmb(); | ||
108 | return head; | ||
109 | } | ||
110 | |||
111 | static inline void perf_mmap__write_tail(struct perf_mmap *md, | ||
112 | unsigned long tail) | ||
113 | { | ||
114 | struct perf_event_mmap_page *pc = md->base; | ||
115 | |||
116 | /* | ||
117 | * ensure all reads are done before we write the tail out. | ||
118 | */ | ||
119 | /* mb(); */ | ||
120 | pc->data_tail = tail; | ||
121 | } | ||
122 | |||
97 | /* | 123 | /* |
98 | * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all | 124 | * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all |
99 | * counters in the current task. | 125 | * counters in the current task. |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index e12d539417b2..f8c66d1435e0 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2009-2010, Frederic Weisbecker <fweisbec@gmail.com> | 2 | * Copyright (C) 2009-2011, Frederic Weisbecker <fweisbec@gmail.com> |
3 | * | 3 | * |
4 | * Handle the callchains from the stream in an ad-hoc radix tree and then | 4 | * Handle the callchains from the stream in an ad-hoc radix tree and then |
5 | * sort them in an rbtree. | 5 | * sort them in an rbtree. |
@@ -26,10 +26,10 @@ bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event) | |||
26 | } | 26 | } |
27 | 27 | ||
28 | #define chain_for_each_child(child, parent) \ | 28 | #define chain_for_each_child(child, parent) \ |
29 | list_for_each_entry(child, &parent->children, brothers) | 29 | list_for_each_entry(child, &parent->children, siblings) |
30 | 30 | ||
31 | #define chain_for_each_child_safe(child, next, parent) \ | 31 | #define chain_for_each_child_safe(child, next, parent) \ |
32 | list_for_each_entry_safe(child, next, &parent->children, brothers) | 32 | list_for_each_entry_safe(child, next, &parent->children, siblings) |
33 | 33 | ||
34 | static void | 34 | static void |
35 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | 35 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, |
@@ -38,14 +38,14 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | |||
38 | struct rb_node **p = &root->rb_node; | 38 | struct rb_node **p = &root->rb_node; |
39 | struct rb_node *parent = NULL; | 39 | struct rb_node *parent = NULL; |
40 | struct callchain_node *rnode; | 40 | struct callchain_node *rnode; |
41 | u64 chain_cumul = cumul_hits(chain); | 41 | u64 chain_cumul = callchain_cumul_hits(chain); |
42 | 42 | ||
43 | while (*p) { | 43 | while (*p) { |
44 | u64 rnode_cumul; | 44 | u64 rnode_cumul; |
45 | 45 | ||
46 | parent = *p; | 46 | parent = *p; |
47 | rnode = rb_entry(parent, struct callchain_node, rb_node); | 47 | rnode = rb_entry(parent, struct callchain_node, rb_node); |
48 | rnode_cumul = cumul_hits(rnode); | 48 | rnode_cumul = callchain_cumul_hits(rnode); |
49 | 49 | ||
50 | switch (mode) { | 50 | switch (mode) { |
51 | case CHAIN_FLAT: | 51 | case CHAIN_FLAT: |
@@ -104,7 +104,7 @@ static void __sort_chain_graph_abs(struct callchain_node *node, | |||
104 | 104 | ||
105 | chain_for_each_child(child, node) { | 105 | chain_for_each_child(child, node) { |
106 | __sort_chain_graph_abs(child, min_hit); | 106 | __sort_chain_graph_abs(child, min_hit); |
107 | if (cumul_hits(child) >= min_hit) | 107 | if (callchain_cumul_hits(child) >= min_hit) |
108 | rb_insert_callchain(&node->rb_root, child, | 108 | rb_insert_callchain(&node->rb_root, child, |
109 | CHAIN_GRAPH_ABS); | 109 | CHAIN_GRAPH_ABS); |
110 | } | 110 | } |
@@ -129,7 +129,7 @@ static void __sort_chain_graph_rel(struct callchain_node *node, | |||
129 | 129 | ||
130 | chain_for_each_child(child, node) { | 130 | chain_for_each_child(child, node) { |
131 | __sort_chain_graph_rel(child, min_percent); | 131 | __sort_chain_graph_rel(child, min_percent); |
132 | if (cumul_hits(child) >= min_hit) | 132 | if (callchain_cumul_hits(child) >= min_hit) |
133 | rb_insert_callchain(&node->rb_root, child, | 133 | rb_insert_callchain(&node->rb_root, child, |
134 | CHAIN_GRAPH_REL); | 134 | CHAIN_GRAPH_REL); |
135 | } | 135 | } |
@@ -143,7 +143,7 @@ sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root, | |||
143 | rb_root->rb_node = chain_root->node.rb_root.rb_node; | 143 | rb_root->rb_node = chain_root->node.rb_root.rb_node; |
144 | } | 144 | } |
145 | 145 | ||
146 | int register_callchain_param(struct callchain_param *param) | 146 | int callchain_register_param(struct callchain_param *param) |
147 | { | 147 | { |
148 | switch (param->mode) { | 148 | switch (param->mode) { |
149 | case CHAIN_GRAPH_ABS: | 149 | case CHAIN_GRAPH_ABS: |
@@ -189,32 +189,27 @@ create_child(struct callchain_node *parent, bool inherit_children) | |||
189 | chain_for_each_child(next, new) | 189 | chain_for_each_child(next, new) |
190 | next->parent = new; | 190 | next->parent = new; |
191 | } | 191 | } |
192 | list_add_tail(&new->brothers, &parent->children); | 192 | list_add_tail(&new->siblings, &parent->children); |
193 | 193 | ||
194 | return new; | 194 | return new; |
195 | } | 195 | } |
196 | 196 | ||
197 | 197 | ||
198 | struct resolved_ip { | ||
199 | u64 ip; | ||
200 | struct map_symbol ms; | ||
201 | }; | ||
202 | |||
203 | struct resolved_chain { | ||
204 | u64 nr; | ||
205 | struct resolved_ip ips[0]; | ||
206 | }; | ||
207 | |||
208 | |||
209 | /* | 198 | /* |
210 | * Fill the node with callchain values | 199 | * Fill the node with callchain values |
211 | */ | 200 | */ |
212 | static void | 201 | static void |
213 | fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) | 202 | fill_node(struct callchain_node *node, struct callchain_cursor *cursor) |
214 | { | 203 | { |
215 | unsigned int i; | 204 | struct callchain_cursor_node *cursor_node; |
205 | |||
206 | node->val_nr = cursor->nr - cursor->pos; | ||
207 | if (!node->val_nr) | ||
208 | pr_warning("Warning: empty node in callchain tree\n"); | ||
216 | 209 | ||
217 | for (i = start; i < chain->nr; i++) { | 210 | cursor_node = callchain_cursor_current(cursor); |
211 | |||
212 | while (cursor_node) { | ||
218 | struct callchain_list *call; | 213 | struct callchain_list *call; |
219 | 214 | ||
220 | call = zalloc(sizeof(*call)); | 215 | call = zalloc(sizeof(*call)); |
@@ -222,23 +217,25 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) | |||
222 | perror("not enough memory for the code path tree"); | 217 | perror("not enough memory for the code path tree"); |
223 | return; | 218 | return; |
224 | } | 219 | } |
225 | call->ip = chain->ips[i].ip; | 220 | call->ip = cursor_node->ip; |
226 | call->ms = chain->ips[i].ms; | 221 | call->ms.sym = cursor_node->sym; |
222 | call->ms.map = cursor_node->map; | ||
227 | list_add_tail(&call->list, &node->val); | 223 | list_add_tail(&call->list, &node->val); |
224 | |||
225 | callchain_cursor_advance(cursor); | ||
226 | cursor_node = callchain_cursor_current(cursor); | ||
228 | } | 227 | } |
229 | node->val_nr = chain->nr - start; | ||
230 | if (!node->val_nr) | ||
231 | pr_warning("Warning: empty node in callchain tree\n"); | ||
232 | } | 228 | } |
233 | 229 | ||
234 | static void | 230 | static void |
235 | add_child(struct callchain_node *parent, struct resolved_chain *chain, | 231 | add_child(struct callchain_node *parent, |
236 | int start, u64 period) | 232 | struct callchain_cursor *cursor, |
233 | u64 period) | ||
237 | { | 234 | { |
238 | struct callchain_node *new; | 235 | struct callchain_node *new; |
239 | 236 | ||
240 | new = create_child(parent, false); | 237 | new = create_child(parent, false); |
241 | fill_node(new, chain, start); | 238 | fill_node(new, cursor); |
242 | 239 | ||
243 | new->children_hit = 0; | 240 | new->children_hit = 0; |
244 | new->hit = period; | 241 | new->hit = period; |
@@ -250,9 +247,10 @@ add_child(struct callchain_node *parent, struct resolved_chain *chain, | |||
250 | * Then create another child to host the given callchain of new branch | 247 | * Then create another child to host the given callchain of new branch |
251 | */ | 248 | */ |
252 | static void | 249 | static void |
253 | split_add_child(struct callchain_node *parent, struct resolved_chain *chain, | 250 | split_add_child(struct callchain_node *parent, |
254 | struct callchain_list *to_split, int idx_parents, int idx_local, | 251 | struct callchain_cursor *cursor, |
255 | u64 period) | 252 | struct callchain_list *to_split, |
253 | u64 idx_parents, u64 idx_local, u64 period) | ||
256 | { | 254 | { |
257 | struct callchain_node *new; | 255 | struct callchain_node *new; |
258 | struct list_head *old_tail; | 256 | struct list_head *old_tail; |
@@ -272,14 +270,14 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain, | |||
272 | /* split the hits */ | 270 | /* split the hits */ |
273 | new->hit = parent->hit; | 271 | new->hit = parent->hit; |
274 | new->children_hit = parent->children_hit; | 272 | new->children_hit = parent->children_hit; |
275 | parent->children_hit = cumul_hits(new); | 273 | parent->children_hit = callchain_cumul_hits(new); |
276 | new->val_nr = parent->val_nr - idx_local; | 274 | new->val_nr = parent->val_nr - idx_local; |
277 | parent->val_nr = idx_local; | 275 | parent->val_nr = idx_local; |
278 | 276 | ||
279 | /* create a new child for the new branch if any */ | 277 | /* create a new child for the new branch if any */ |
280 | if (idx_total < chain->nr) { | 278 | if (idx_total < cursor->nr) { |
281 | parent->hit = 0; | 279 | parent->hit = 0; |
282 | add_child(parent, chain, idx_total, period); | 280 | add_child(parent, cursor, period); |
283 | parent->children_hit += period; | 281 | parent->children_hit += period; |
284 | } else { | 282 | } else { |
285 | parent->hit = period; | 283 | parent->hit = period; |
@@ -287,36 +285,41 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain, | |||
287 | } | 285 | } |
288 | 286 | ||
289 | static int | 287 | static int |
290 | append_chain(struct callchain_node *root, struct resolved_chain *chain, | 288 | append_chain(struct callchain_node *root, |
291 | unsigned int start, u64 period); | 289 | struct callchain_cursor *cursor, |
290 | u64 period); | ||
292 | 291 | ||
293 | static void | 292 | static void |
294 | append_chain_children(struct callchain_node *root, struct resolved_chain *chain, | 293 | append_chain_children(struct callchain_node *root, |
295 | unsigned int start, u64 period) | 294 | struct callchain_cursor *cursor, |
295 | u64 period) | ||
296 | { | 296 | { |
297 | struct callchain_node *rnode; | 297 | struct callchain_node *rnode; |
298 | 298 | ||
299 | /* lookup in childrens */ | 299 | /* lookup in childrens */ |
300 | chain_for_each_child(rnode, root) { | 300 | chain_for_each_child(rnode, root) { |
301 | unsigned int ret = append_chain(rnode, chain, start, period); | 301 | unsigned int ret = append_chain(rnode, cursor, period); |
302 | 302 | ||
303 | if (!ret) | 303 | if (!ret) |
304 | goto inc_children_hit; | 304 | goto inc_children_hit; |
305 | } | 305 | } |
306 | /* nothing in children, add to the current node */ | 306 | /* nothing in children, add to the current node */ |
307 | add_child(root, chain, start, period); | 307 | add_child(root, cursor, period); |
308 | 308 | ||
309 | inc_children_hit: | 309 | inc_children_hit: |
310 | root->children_hit += period; | 310 | root->children_hit += period; |
311 | } | 311 | } |
312 | 312 | ||
313 | static int | 313 | static int |
314 | append_chain(struct callchain_node *root, struct resolved_chain *chain, | 314 | append_chain(struct callchain_node *root, |
315 | unsigned int start, u64 period) | 315 | struct callchain_cursor *cursor, |
316 | u64 period) | ||
316 | { | 317 | { |
318 | struct callchain_cursor_node *curr_snap = cursor->curr; | ||
317 | struct callchain_list *cnode; | 319 | struct callchain_list *cnode; |
318 | unsigned int i = start; | 320 | u64 start = cursor->pos; |
319 | bool found = false; | 321 | bool found = false; |
322 | u64 matches; | ||
320 | 323 | ||
321 | /* | 324 | /* |
322 | * Lookup in the current node | 325 | * Lookup in the current node |
@@ -324,141 +327,134 @@ append_chain(struct callchain_node *root, struct resolved_chain *chain, | |||
324 | * anywhere inside a function. | 327 | * anywhere inside a function. |
325 | */ | 328 | */ |
326 | list_for_each_entry(cnode, &root->val, list) { | 329 | list_for_each_entry(cnode, &root->val, list) { |
330 | struct callchain_cursor_node *node; | ||
327 | struct symbol *sym; | 331 | struct symbol *sym; |
328 | 332 | ||
329 | if (i == chain->nr) | 333 | node = callchain_cursor_current(cursor); |
334 | if (!node) | ||
330 | break; | 335 | break; |
331 | 336 | ||
332 | sym = chain->ips[i].ms.sym; | 337 | sym = node->sym; |
333 | 338 | ||
334 | if (cnode->ms.sym && sym) { | 339 | if (cnode->ms.sym && sym) { |
335 | if (cnode->ms.sym->start != sym->start) | 340 | if (cnode->ms.sym->start != sym->start) |
336 | break; | 341 | break; |
337 | } else if (cnode->ip != chain->ips[i].ip) | 342 | } else if (cnode->ip != node->ip) |
338 | break; | 343 | break; |
339 | 344 | ||
340 | if (!found) | 345 | if (!found) |
341 | found = true; | 346 | found = true; |
342 | i++; | 347 | |
348 | callchain_cursor_advance(cursor); | ||
343 | } | 349 | } |
344 | 350 | ||
345 | /* matches not, relay on the parent */ | 351 | /* matches not, relay on the parent */ |
346 | if (!found) | 352 | if (!found) { |
353 | cursor->curr = curr_snap; | ||
354 | cursor->pos = start; | ||
347 | return -1; | 355 | return -1; |
356 | } | ||
357 | |||
358 | matches = cursor->pos - start; | ||
348 | 359 | ||
349 | /* we match only a part of the node. Split it and add the new chain */ | 360 | /* we match only a part of the node. Split it and add the new chain */ |
350 | if (i - start < root->val_nr) { | 361 | if (matches < root->val_nr) { |
351 | split_add_child(root, chain, cnode, start, i - start, period); | 362 | split_add_child(root, cursor, cnode, start, matches, period); |
352 | return 0; | 363 | return 0; |
353 | } | 364 | } |
354 | 365 | ||
355 | /* we match 100% of the path, increment the hit */ | 366 | /* we match 100% of the path, increment the hit */ |
356 | if (i - start == root->val_nr && i == chain->nr) { | 367 | if (matches == root->val_nr && cursor->pos == cursor->nr) { |
357 | root->hit += period; | 368 | root->hit += period; |
358 | return 0; | 369 | return 0; |
359 | } | 370 | } |
360 | 371 | ||
361 | /* We match the node and still have a part remaining */ | 372 | /* We match the node and still have a part remaining */ |
362 | append_chain_children(root, chain, i, period); | 373 | append_chain_children(root, cursor, period); |
363 | 374 | ||
364 | return 0; | 375 | return 0; |
365 | } | 376 | } |
366 | 377 | ||
367 | static void filter_context(struct ip_callchain *old, struct resolved_chain *new, | 378 | int callchain_append(struct callchain_root *root, |
368 | struct map_symbol *syms) | 379 | struct callchain_cursor *cursor, |
369 | { | 380 | u64 period) |
370 | int i, j = 0; | ||
371 | |||
372 | for (i = 0; i < (int)old->nr; i++) { | ||
373 | if (old->ips[i] >= PERF_CONTEXT_MAX) | ||
374 | continue; | ||
375 | |||
376 | new->ips[j].ip = old->ips[i]; | ||
377 | new->ips[j].ms = syms[i]; | ||
378 | j++; | ||
379 | } | ||
380 | |||
381 | new->nr = j; | ||
382 | } | ||
383 | |||
384 | |||
385 | int callchain_append(struct callchain_root *root, struct ip_callchain *chain, | ||
386 | struct map_symbol *syms, u64 period) | ||
387 | { | 381 | { |
388 | struct resolved_chain *filtered; | 382 | if (!cursor->nr) |
389 | |||
390 | if (!chain->nr) | ||
391 | return 0; | 383 | return 0; |
392 | 384 | ||
393 | filtered = zalloc(sizeof(*filtered) + | 385 | callchain_cursor_commit(cursor); |
394 | chain->nr * sizeof(struct resolved_ip)); | ||
395 | if (!filtered) | ||
396 | return -ENOMEM; | ||
397 | |||
398 | filter_context(chain, filtered, syms); | ||
399 | |||
400 | if (!filtered->nr) | ||
401 | goto end; | ||
402 | 386 | ||
403 | append_chain_children(&root->node, filtered, 0, period); | 387 | append_chain_children(&root->node, cursor, period); |
404 | 388 | ||
405 | if (filtered->nr > root->max_depth) | 389 | if (cursor->nr > root->max_depth) |
406 | root->max_depth = filtered->nr; | 390 | root->max_depth = cursor->nr; |
407 | end: | ||
408 | free(filtered); | ||
409 | 391 | ||
410 | return 0; | 392 | return 0; |
411 | } | 393 | } |
412 | 394 | ||
413 | static int | 395 | static int |
414 | merge_chain_branch(struct callchain_node *dst, struct callchain_node *src, | 396 | merge_chain_branch(struct callchain_cursor *cursor, |
415 | struct resolved_chain *chain) | 397 | struct callchain_node *dst, struct callchain_node *src) |
416 | { | 398 | { |
399 | struct callchain_cursor_node **old_last = cursor->last; | ||
417 | struct callchain_node *child, *next_child; | 400 | struct callchain_node *child, *next_child; |
418 | struct callchain_list *list, *next_list; | 401 | struct callchain_list *list, *next_list; |
419 | int old_pos = chain->nr; | 402 | int old_pos = cursor->nr; |
420 | int err = 0; | 403 | int err = 0; |
421 | 404 | ||
422 | list_for_each_entry_safe(list, next_list, &src->val, list) { | 405 | list_for_each_entry_safe(list, next_list, &src->val, list) { |
423 | chain->ips[chain->nr].ip = list->ip; | 406 | callchain_cursor_append(cursor, list->ip, |
424 | chain->ips[chain->nr].ms = list->ms; | 407 | list->ms.map, list->ms.sym); |
425 | chain->nr++; | ||
426 | list_del(&list->list); | 408 | list_del(&list->list); |
427 | free(list); | 409 | free(list); |
428 | } | 410 | } |
429 | 411 | ||
430 | if (src->hit) | 412 | if (src->hit) { |
431 | append_chain_children(dst, chain, 0, src->hit); | 413 | callchain_cursor_commit(cursor); |
414 | append_chain_children(dst, cursor, src->hit); | ||
415 | } | ||
432 | 416 | ||
433 | chain_for_each_child_safe(child, next_child, src) { | 417 | chain_for_each_child_safe(child, next_child, src) { |
434 | err = merge_chain_branch(dst, child, chain); | 418 | err = merge_chain_branch(cursor, dst, child); |
435 | if (err) | 419 | if (err) |
436 | break; | 420 | break; |
437 | 421 | ||
438 | list_del(&child->brothers); | 422 | list_del(&child->siblings); |
439 | free(child); | 423 | free(child); |
440 | } | 424 | } |
441 | 425 | ||
442 | chain->nr = old_pos; | 426 | cursor->nr = old_pos; |
427 | cursor->last = old_last; | ||
443 | 428 | ||
444 | return err; | 429 | return err; |
445 | } | 430 | } |
446 | 431 | ||
447 | int callchain_merge(struct callchain_root *dst, struct callchain_root *src) | 432 | int callchain_merge(struct callchain_cursor *cursor, |
433 | struct callchain_root *dst, struct callchain_root *src) | ||
434 | { | ||
435 | return merge_chain_branch(cursor, &dst->node, &src->node); | ||
436 | } | ||
437 | |||
438 | int callchain_cursor_append(struct callchain_cursor *cursor, | ||
439 | u64 ip, struct map *map, struct symbol *sym) | ||
448 | { | 440 | { |
449 | struct resolved_chain *chain; | 441 | struct callchain_cursor_node *node = *cursor->last; |
450 | int err; | ||
451 | 442 | ||
452 | chain = malloc(sizeof(*chain) + | 443 | if (!node) { |
453 | src->max_depth * sizeof(struct resolved_ip)); | 444 | node = calloc(sizeof(*node), 1); |
454 | if (!chain) | 445 | if (!node) |
455 | return -ENOMEM; | 446 | return -ENOMEM; |
456 | 447 | ||
457 | chain->nr = 0; | 448 | *cursor->last = node; |
449 | } | ||
458 | 450 | ||
459 | err = merge_chain_branch(&dst->node, &src->node, chain); | 451 | node->ip = ip; |
452 | node->map = map; | ||
453 | node->sym = sym; | ||
460 | 454 | ||
461 | free(chain); | 455 | cursor->nr++; |
462 | 456 | ||
463 | return err; | 457 | cursor->last = &node->next; |
458 | |||
459 | return 0; | ||
464 | } | 460 | } |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index c15fb8c24ad2..67137256a1cd 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -16,7 +16,7 @@ enum chain_mode { | |||
16 | 16 | ||
17 | struct callchain_node { | 17 | struct callchain_node { |
18 | struct callchain_node *parent; | 18 | struct callchain_node *parent; |
19 | struct list_head brothers; | 19 | struct list_head siblings; |
20 | struct list_head children; | 20 | struct list_head children; |
21 | struct list_head val; | 21 | struct list_head val; |
22 | struct rb_node rb_node; /* to sort nodes in an rbtree */ | 22 | struct rb_node rb_node; /* to sort nodes in an rbtree */ |
@@ -49,9 +49,30 @@ struct callchain_list { | |||
49 | struct list_head list; | 49 | struct list_head list; |
50 | }; | 50 | }; |
51 | 51 | ||
52 | /* | ||
53 | * A callchain cursor is a single linked list that | ||
54 | * let one feed a callchain progressively. | ||
55 | * It keeps persitent allocated entries to minimize | ||
56 | * allocations. | ||
57 | */ | ||
58 | struct callchain_cursor_node { | ||
59 | u64 ip; | ||
60 | struct map *map; | ||
61 | struct symbol *sym; | ||
62 | struct callchain_cursor_node *next; | ||
63 | }; | ||
64 | |||
65 | struct callchain_cursor { | ||
66 | u64 nr; | ||
67 | struct callchain_cursor_node *first; | ||
68 | struct callchain_cursor_node **last; | ||
69 | u64 pos; | ||
70 | struct callchain_cursor_node *curr; | ||
71 | }; | ||
72 | |||
52 | static inline void callchain_init(struct callchain_root *root) | 73 | static inline void callchain_init(struct callchain_root *root) |
53 | { | 74 | { |
54 | INIT_LIST_HEAD(&root->node.brothers); | 75 | INIT_LIST_HEAD(&root->node.siblings); |
55 | INIT_LIST_HEAD(&root->node.children); | 76 | INIT_LIST_HEAD(&root->node.children); |
56 | INIT_LIST_HEAD(&root->node.val); | 77 | INIT_LIST_HEAD(&root->node.val); |
57 | 78 | ||
@@ -61,15 +82,54 @@ static inline void callchain_init(struct callchain_root *root) | |||
61 | root->max_depth = 0; | 82 | root->max_depth = 0; |
62 | } | 83 | } |
63 | 84 | ||
64 | static inline u64 cumul_hits(struct callchain_node *node) | 85 | static inline u64 callchain_cumul_hits(struct callchain_node *node) |
65 | { | 86 | { |
66 | return node->hit + node->children_hit; | 87 | return node->hit + node->children_hit; |
67 | } | 88 | } |
68 | 89 | ||
69 | int register_callchain_param(struct callchain_param *param); | 90 | int callchain_register_param(struct callchain_param *param); |
70 | int callchain_append(struct callchain_root *root, struct ip_callchain *chain, | 91 | int callchain_append(struct callchain_root *root, |
71 | struct map_symbol *syms, u64 period); | 92 | struct callchain_cursor *cursor, |
72 | int callchain_merge(struct callchain_root *dst, struct callchain_root *src); | 93 | u64 period); |
94 | |||
95 | int callchain_merge(struct callchain_cursor *cursor, | ||
96 | struct callchain_root *dst, struct callchain_root *src); | ||
73 | 97 | ||
74 | bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); | 98 | bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); |
99 | |||
100 | /* | ||
101 | * Initialize a cursor before adding entries inside, but keep | ||
102 | * the previously allocated entries as a cache. | ||
103 | */ | ||
104 | static inline void callchain_cursor_reset(struct callchain_cursor *cursor) | ||
105 | { | ||
106 | cursor->nr = 0; | ||
107 | cursor->last = &cursor->first; | ||
108 | } | ||
109 | |||
110 | int callchain_cursor_append(struct callchain_cursor *cursor, u64 ip, | ||
111 | struct map *map, struct symbol *sym); | ||
112 | |||
113 | /* Close a cursor writing session. Initialize for the reader */ | ||
114 | static inline void callchain_cursor_commit(struct callchain_cursor *cursor) | ||
115 | { | ||
116 | cursor->curr = cursor->first; | ||
117 | cursor->pos = 0; | ||
118 | } | ||
119 | |||
120 | /* Cursor reading iteration helpers */ | ||
121 | static inline struct callchain_cursor_node * | ||
122 | callchain_cursor_current(struct callchain_cursor *cursor) | ||
123 | { | ||
124 | if (cursor->pos == cursor->nr) | ||
125 | return NULL; | ||
126 | |||
127 | return cursor->curr; | ||
128 | } | ||
129 | |||
130 | static inline void callchain_cursor_advance(struct callchain_cursor *cursor) | ||
131 | { | ||
132 | cursor->curr = cursor->curr->next; | ||
133 | cursor->pos++; | ||
134 | } | ||
75 | #endif /* __PERF_CALLCHAIN_H */ | 135 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 3ccaa1043383..6893eec693ab 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
@@ -177,3 +177,8 @@ struct cpu_map *cpu_map__dummy_new(void) | |||
177 | 177 | ||
178 | return cpus; | 178 | return cpus; |
179 | } | 179 | } |
180 | |||
181 | void cpu_map__delete(struct cpu_map *map) | ||
182 | { | ||
183 | free(map); | ||
184 | } | ||
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index f7a4f42f6307..072c0a374794 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
@@ -8,6 +8,6 @@ struct cpu_map { | |||
8 | 8 | ||
9 | struct cpu_map *cpu_map__new(const char *cpu_list); | 9 | struct cpu_map *cpu_map__new(const char *cpu_list); |
10 | struct cpu_map *cpu_map__dummy_new(void); | 10 | struct cpu_map *cpu_map__dummy_new(void); |
11 | void *cpu_map__delete(struct cpu_map *map); | 11 | void cpu_map__delete(struct cpu_map *map); |
12 | 12 | ||
13 | #endif /* __PERF_CPUMAP_H */ | 13 | #endif /* __PERF_CPUMAP_H */ |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 1478ab4ee222..e4db8b888546 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -826,128 +826,3 @@ out_filtered: | |||
826 | al->filtered = true; | 826 | al->filtered = true; |
827 | return 0; | 827 | return 0; |
828 | } | 828 | } |
829 | |||
830 | static int event__parse_id_sample(const event_t *event, | ||
831 | struct perf_session *session, | ||
832 | struct sample_data *sample) | ||
833 | { | ||
834 | const u64 *array; | ||
835 | u64 type; | ||
836 | |||
837 | sample->cpu = sample->pid = sample->tid = -1; | ||
838 | sample->stream_id = sample->id = sample->time = -1ULL; | ||
839 | |||
840 | if (!session->sample_id_all) | ||
841 | return 0; | ||
842 | |||
843 | array = event->sample.array; | ||
844 | array += ((event->header.size - | ||
845 | sizeof(event->header)) / sizeof(u64)) - 1; | ||
846 | type = session->sample_type; | ||
847 | |||
848 | if (type & PERF_SAMPLE_CPU) { | ||
849 | u32 *p = (u32 *)array; | ||
850 | sample->cpu = *p; | ||
851 | array--; | ||
852 | } | ||
853 | |||
854 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
855 | sample->stream_id = *array; | ||
856 | array--; | ||
857 | } | ||
858 | |||
859 | if (type & PERF_SAMPLE_ID) { | ||
860 | sample->id = *array; | ||
861 | array--; | ||
862 | } | ||
863 | |||
864 | if (type & PERF_SAMPLE_TIME) { | ||
865 | sample->time = *array; | ||
866 | array--; | ||
867 | } | ||
868 | |||
869 | if (type & PERF_SAMPLE_TID) { | ||
870 | u32 *p = (u32 *)array; | ||
871 | sample->pid = p[0]; | ||
872 | sample->tid = p[1]; | ||
873 | } | ||
874 | |||
875 | return 0; | ||
876 | } | ||
877 | |||
878 | int event__parse_sample(const event_t *event, struct perf_session *session, | ||
879 | struct sample_data *data) | ||
880 | { | ||
881 | const u64 *array; | ||
882 | u64 type; | ||
883 | |||
884 | if (event->header.type != PERF_RECORD_SAMPLE) | ||
885 | return event__parse_id_sample(event, session, data); | ||
886 | |||
887 | array = event->sample.array; | ||
888 | type = session->sample_type; | ||
889 | |||
890 | if (type & PERF_SAMPLE_IP) { | ||
891 | data->ip = event->ip.ip; | ||
892 | array++; | ||
893 | } | ||
894 | |||
895 | if (type & PERF_SAMPLE_TID) { | ||
896 | u32 *p = (u32 *)array; | ||
897 | data->pid = p[0]; | ||
898 | data->tid = p[1]; | ||
899 | array++; | ||
900 | } | ||
901 | |||
902 | if (type & PERF_SAMPLE_TIME) { | ||
903 | data->time = *array; | ||
904 | array++; | ||
905 | } | ||
906 | |||
907 | if (type & PERF_SAMPLE_ADDR) { | ||
908 | data->addr = *array; | ||
909 | array++; | ||
910 | } | ||
911 | |||
912 | data->id = -1ULL; | ||
913 | if (type & PERF_SAMPLE_ID) { | ||
914 | data->id = *array; | ||
915 | array++; | ||
916 | } | ||
917 | |||
918 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
919 | data->stream_id = *array; | ||
920 | array++; | ||
921 | } | ||
922 | |||
923 | if (type & PERF_SAMPLE_CPU) { | ||
924 | u32 *p = (u32 *)array; | ||
925 | data->cpu = *p; | ||
926 | array++; | ||
927 | } else | ||
928 | data->cpu = -1; | ||
929 | |||
930 | if (type & PERF_SAMPLE_PERIOD) { | ||
931 | data->period = *array; | ||
932 | array++; | ||
933 | } | ||
934 | |||
935 | if (type & PERF_SAMPLE_READ) { | ||
936 | pr_debug("PERF_SAMPLE_READ is unsuported for now\n"); | ||
937 | return -1; | ||
938 | } | ||
939 | |||
940 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
941 | data->callchain = (struct ip_callchain *)array; | ||
942 | array += 1 + data->callchain->nr; | ||
943 | } | ||
944 | |||
945 | if (type & PERF_SAMPLE_RAW) { | ||
946 | u32 *p = (u32 *)array; | ||
947 | data->raw_size = *p; | ||
948 | p++; | ||
949 | data->raw_data = p; | ||
950 | } | ||
951 | |||
952 | return 0; | ||
953 | } | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 2b7e91902f10..d79e4edd82f9 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -169,9 +169,10 @@ struct addr_location; | |||
169 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | 169 | int event__preprocess_sample(const event_t *self, struct perf_session *session, |
170 | struct addr_location *al, struct sample_data *data, | 170 | struct addr_location *al, struct sample_data *data, |
171 | symbol_filter_t filter); | 171 | symbol_filter_t filter); |
172 | int event__parse_sample(const event_t *event, struct perf_session *session, | ||
173 | struct sample_data *sample); | ||
174 | 172 | ||
175 | const char *event__get_event_name(unsigned int id); | 173 | const char *event__get_event_name(unsigned int id); |
176 | 174 | ||
175 | int event__parse_sample(const event_t *event, u64 type, bool sample_id_all, | ||
176 | struct sample_data *sample); | ||
177 | |||
177 | #endif /* __PERF_RECORD_H */ | 178 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c new file mode 100644 index 000000000000..df0610e9c61b --- /dev/null +++ b/tools/perf/util/evlist.c | |||
@@ -0,0 +1,170 @@ | |||
1 | #include <poll.h> | ||
2 | #include "evlist.h" | ||
3 | #include "evsel.h" | ||
4 | #include "util.h" | ||
5 | |||
6 | #include <linux/bitops.h> | ||
7 | #include <linux/hash.h> | ||
8 | |||
9 | void perf_evlist__init(struct perf_evlist *evlist) | ||
10 | { | ||
11 | int i; | ||
12 | |||
13 | for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i) | ||
14 | INIT_HLIST_HEAD(&evlist->heads[i]); | ||
15 | INIT_LIST_HEAD(&evlist->entries); | ||
16 | } | ||
17 | |||
18 | struct perf_evlist *perf_evlist__new(void) | ||
19 | { | ||
20 | struct perf_evlist *evlist = zalloc(sizeof(*evlist)); | ||
21 | |||
22 | if (evlist != NULL) | ||
23 | perf_evlist__init(evlist); | ||
24 | |||
25 | return evlist; | ||
26 | } | ||
27 | |||
28 | static void perf_evlist__purge(struct perf_evlist *evlist) | ||
29 | { | ||
30 | struct perf_evsel *pos, *n; | ||
31 | |||
32 | list_for_each_entry_safe(pos, n, &evlist->entries, node) { | ||
33 | list_del_init(&pos->node); | ||
34 | perf_evsel__delete(pos); | ||
35 | } | ||
36 | |||
37 | evlist->nr_entries = 0; | ||
38 | } | ||
39 | |||
40 | void perf_evlist__exit(struct perf_evlist *evlist) | ||
41 | { | ||
42 | free(evlist->mmap); | ||
43 | free(evlist->pollfd); | ||
44 | evlist->mmap = NULL; | ||
45 | evlist->pollfd = NULL; | ||
46 | } | ||
47 | |||
48 | void perf_evlist__delete(struct perf_evlist *evlist) | ||
49 | { | ||
50 | perf_evlist__purge(evlist); | ||
51 | perf_evlist__exit(evlist); | ||
52 | free(evlist); | ||
53 | } | ||
54 | |||
55 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | ||
56 | { | ||
57 | list_add_tail(&entry->node, &evlist->entries); | ||
58 | ++evlist->nr_entries; | ||
59 | } | ||
60 | |||
61 | int perf_evlist__add_default(struct perf_evlist *evlist) | ||
62 | { | ||
63 | struct perf_event_attr attr = { | ||
64 | .type = PERF_TYPE_HARDWARE, | ||
65 | .config = PERF_COUNT_HW_CPU_CYCLES, | ||
66 | }; | ||
67 | struct perf_evsel *evsel = perf_evsel__new(&attr, 0); | ||
68 | |||
69 | if (evsel == NULL) | ||
70 | return -ENOMEM; | ||
71 | |||
72 | perf_evlist__add(evlist, evsel); | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist, int ncpus, int nthreads) | ||
77 | { | ||
78 | int nfds = ncpus * nthreads * evlist->nr_entries; | ||
79 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); | ||
80 | return evlist->pollfd != NULL ? 0 : -ENOMEM; | ||
81 | } | ||
82 | |||
83 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) | ||
84 | { | ||
85 | fcntl(fd, F_SETFL, O_NONBLOCK); | ||
86 | evlist->pollfd[evlist->nr_fds].fd = fd; | ||
87 | evlist->pollfd[evlist->nr_fds].events = POLLIN; | ||
88 | evlist->nr_fds++; | ||
89 | } | ||
90 | |||
91 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | ||
92 | { | ||
93 | struct hlist_head *head; | ||
94 | struct hlist_node *pos; | ||
95 | struct perf_sample_id *sid; | ||
96 | int hash; | ||
97 | |||
98 | if (evlist->nr_entries == 1) | ||
99 | return list_entry(evlist->entries.next, struct perf_evsel, node); | ||
100 | |||
101 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); | ||
102 | head = &evlist->heads[hash]; | ||
103 | |||
104 | hlist_for_each_entry(sid, pos, head, node) | ||
105 | if (sid->id == id) | ||
106 | return sid->evsel; | ||
107 | return NULL; | ||
108 | } | ||
109 | |||
110 | event_t *perf_evlist__read_on_cpu(struct perf_evlist *evlist, int cpu) | ||
111 | { | ||
112 | /* XXX Move this to perf.c, making it generally available */ | ||
113 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | ||
114 | struct perf_mmap *md = &evlist->mmap[cpu]; | ||
115 | unsigned int head = perf_mmap__read_head(md); | ||
116 | unsigned int old = md->prev; | ||
117 | unsigned char *data = md->base + page_size; | ||
118 | event_t *event = NULL; | ||
119 | int diff; | ||
120 | |||
121 | /* | ||
122 | * If we're further behind than half the buffer, there's a chance | ||
123 | * the writer will bite our tail and mess up the samples under us. | ||
124 | * | ||
125 | * If we somehow ended up ahead of the head, we got messed up. | ||
126 | * | ||
127 | * In either case, truncate and restart at head. | ||
128 | */ | ||
129 | diff = head - old; | ||
130 | if (diff > md->mask / 2 || diff < 0) { | ||
131 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); | ||
132 | |||
133 | /* | ||
134 | * head points to a known good entry, start there. | ||
135 | */ | ||
136 | old = head; | ||
137 | } | ||
138 | |||
139 | if (old != head) { | ||
140 | size_t size; | ||
141 | |||
142 | event = (event_t *)&data[old & md->mask]; | ||
143 | size = event->header.size; | ||
144 | |||
145 | /* | ||
146 | * Event straddles the mmap boundary -- header should always | ||
147 | * be inside due to u64 alignment of output. | ||
148 | */ | ||
149 | if ((old & md->mask) + size != ((old + size) & md->mask)) { | ||
150 | unsigned int offset = old; | ||
151 | unsigned int len = min(sizeof(*event), size), cpy; | ||
152 | void *dst = &evlist->event_copy; | ||
153 | |||
154 | do { | ||
155 | cpy = min(md->mask + 1 - (offset & md->mask), len); | ||
156 | memcpy(dst, &data[offset & md->mask], cpy); | ||
157 | offset += cpy; | ||
158 | dst += cpy; | ||
159 | len -= cpy; | ||
160 | } while (len); | ||
161 | |||
162 | event = &evlist->event_copy; | ||
163 | } | ||
164 | |||
165 | old += size; | ||
166 | } | ||
167 | |||
168 | md->prev = old; | ||
169 | return event; | ||
170 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h new file mode 100644 index 000000000000..acbe48eac608 --- /dev/null +++ b/tools/perf/util/evlist.h | |||
@@ -0,0 +1,41 @@ | |||
1 | #ifndef __PERF_EVLIST_H | ||
2 | #define __PERF_EVLIST_H 1 | ||
3 | |||
4 | #include <linux/list.h> | ||
5 | #include "../perf.h" | ||
6 | #include "event.h" | ||
7 | |||
8 | struct pollfd; | ||
9 | |||
10 | #define PERF_EVLIST__HLIST_BITS 8 | ||
11 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) | ||
12 | |||
13 | struct perf_evlist { | ||
14 | struct list_head entries; | ||
15 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; | ||
16 | int nr_entries; | ||
17 | int nr_fds; | ||
18 | int mmap_len; | ||
19 | event_t event_copy; | ||
20 | struct perf_mmap *mmap; | ||
21 | struct pollfd *pollfd; | ||
22 | }; | ||
23 | |||
24 | struct perf_evsel; | ||
25 | |||
26 | struct perf_evlist *perf_evlist__new(void); | ||
27 | void perf_evlist__init(struct perf_evlist *evlist); | ||
28 | void perf_evlist__exit(struct perf_evlist *evlist); | ||
29 | void perf_evlist__delete(struct perf_evlist *evlist); | ||
30 | |||
31 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry); | ||
32 | int perf_evlist__add_default(struct perf_evlist *evlist); | ||
33 | |||
34 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist, int ncpus, int nthreads); | ||
35 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); | ||
36 | |||
37 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | ||
38 | |||
39 | event_t *perf_evlist__read_on_cpu(struct perf_evlist *self, int cpu); | ||
40 | |||
41 | #endif /* __PERF_EVLIST_H */ | ||
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f5cfed60af98..76ab553637d6 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -1,20 +1,33 @@ | |||
1 | #include "evsel.h" | 1 | #include "evsel.h" |
2 | #include "evlist.h" | ||
2 | #include "../perf.h" | 3 | #include "../perf.h" |
3 | #include "util.h" | 4 | #include "util.h" |
4 | #include "cpumap.h" | 5 | #include "cpumap.h" |
5 | #include "thread.h" | 6 | #include "thread_map.h" |
7 | |||
8 | #include <unistd.h> | ||
9 | #include <sys/mman.h> | ||
10 | |||
11 | #include <linux/bitops.h> | ||
12 | #include <linux/hash.h> | ||
6 | 13 | ||
7 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 14 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
15 | #define SID(e, x, y) xyarray__entry(e->id, x, y) | ||
16 | |||
17 | void perf_evsel__init(struct perf_evsel *evsel, | ||
18 | struct perf_event_attr *attr, int idx) | ||
19 | { | ||
20 | evsel->idx = idx; | ||
21 | evsel->attr = *attr; | ||
22 | INIT_LIST_HEAD(&evsel->node); | ||
23 | } | ||
8 | 24 | ||
9 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | 25 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) |
10 | { | 26 | { |
11 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | 27 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); |
12 | 28 | ||
13 | if (evsel != NULL) { | 29 | if (evsel != NULL) |
14 | evsel->idx = idx; | 30 | perf_evsel__init(evsel, attr, idx); |
15 | evsel->attr = *attr; | ||
16 | INIT_LIST_HEAD(&evsel->node); | ||
17 | } | ||
18 | 31 | ||
19 | return evsel; | 32 | return evsel; |
20 | } | 33 | } |
@@ -25,6 +38,12 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
25 | return evsel->fd != NULL ? 0 : -ENOMEM; | 38 | return evsel->fd != NULL ? 0 : -ENOMEM; |
26 | } | 39 | } |
27 | 40 | ||
41 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | ||
42 | { | ||
43 | evsel->id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); | ||
44 | return evsel->id != NULL ? 0 : -ENOMEM; | ||
45 | } | ||
46 | |||
28 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) | 47 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) |
29 | { | 48 | { |
30 | evsel->counts = zalloc((sizeof(*evsel->counts) + | 49 | evsel->counts = zalloc((sizeof(*evsel->counts) + |
@@ -38,6 +57,12 @@ void perf_evsel__free_fd(struct perf_evsel *evsel) | |||
38 | evsel->fd = NULL; | 57 | evsel->fd = NULL; |
39 | } | 58 | } |
40 | 59 | ||
60 | void perf_evsel__free_id(struct perf_evsel *evsel) | ||
61 | { | ||
62 | xyarray__delete(evsel->id); | ||
63 | evsel->id = NULL; | ||
64 | } | ||
65 | |||
41 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 66 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
42 | { | 67 | { |
43 | int cpu, thread; | 68 | int cpu, thread; |
@@ -49,10 +74,34 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
49 | } | 74 | } |
50 | } | 75 | } |
51 | 76 | ||
52 | void perf_evsel__delete(struct perf_evsel *evsel) | 77 | void perf_evlist__munmap(struct perf_evlist *evlist, int ncpus) |
78 | { | ||
79 | int cpu; | ||
80 | |||
81 | for (cpu = 0; cpu < ncpus; cpu++) { | ||
82 | if (evlist->mmap[cpu].base != NULL) { | ||
83 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | ||
84 | evlist->mmap[cpu].base = NULL; | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | |||
89 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist, int ncpus) | ||
90 | { | ||
91 | evlist->mmap = zalloc(ncpus * sizeof(struct perf_mmap)); | ||
92 | return evlist->mmap != NULL ? 0 : -ENOMEM; | ||
93 | } | ||
94 | |||
95 | void perf_evsel__exit(struct perf_evsel *evsel) | ||
53 | { | 96 | { |
54 | assert(list_empty(&evsel->node)); | 97 | assert(list_empty(&evsel->node)); |
55 | xyarray__delete(evsel->fd); | 98 | xyarray__delete(evsel->fd); |
99 | xyarray__delete(evsel->id); | ||
100 | } | ||
101 | |||
102 | void perf_evsel__delete(struct perf_evsel *evsel) | ||
103 | { | ||
104 | perf_evsel__exit(evsel); | ||
56 | free(evsel); | 105 | free(evsel); |
57 | } | 106 | } |
58 | 107 | ||
@@ -128,7 +177,7 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
128 | } | 177 | } |
129 | 178 | ||
130 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 179 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
131 | struct thread_map *threads) | 180 | struct thread_map *threads, bool group, bool inherit) |
132 | { | 181 | { |
133 | int cpu, thread; | 182 | int cpu, thread; |
134 | 183 | ||
@@ -137,12 +186,20 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
137 | return -1; | 186 | return -1; |
138 | 187 | ||
139 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 188 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
189 | int group_fd = -1; | ||
190 | |||
191 | evsel->attr.inherit = (cpus->map[cpu] < 0) && inherit; | ||
192 | |||
140 | for (thread = 0; thread < threads->nr; thread++) { | 193 | for (thread = 0; thread < threads->nr; thread++) { |
141 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, | 194 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, |
142 | threads->map[thread], | 195 | threads->map[thread], |
143 | cpus->map[cpu], -1, 0); | 196 | cpus->map[cpu], |
197 | group_fd, 0); | ||
144 | if (FD(evsel, cpu, thread) < 0) | 198 | if (FD(evsel, cpu, thread) < 0) |
145 | goto out_close; | 199 | goto out_close; |
200 | |||
201 | if (group && group_fd == -1) | ||
202 | group_fd = FD(evsel, cpu, thread); | ||
146 | } | 203 | } |
147 | } | 204 | } |
148 | 205 | ||
@@ -175,10 +232,9 @@ static struct { | |||
175 | .threads = { -1, }, | 232 | .threads = { -1, }, |
176 | }; | 233 | }; |
177 | 234 | ||
178 | int perf_evsel__open(struct perf_evsel *evsel, | 235 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
179 | struct cpu_map *cpus, struct thread_map *threads) | 236 | struct thread_map *threads, bool group, bool inherit) |
180 | { | 237 | { |
181 | |||
182 | if (cpus == NULL) { | 238 | if (cpus == NULL) { |
183 | /* Work around old compiler warnings about strict aliasing */ | 239 | /* Work around old compiler warnings about strict aliasing */ |
184 | cpus = &empty_cpu_map.map; | 240 | cpus = &empty_cpu_map.map; |
@@ -187,15 +243,243 @@ int perf_evsel__open(struct perf_evsel *evsel, | |||
187 | if (threads == NULL) | 243 | if (threads == NULL) |
188 | threads = &empty_thread_map.map; | 244 | threads = &empty_thread_map.map; |
189 | 245 | ||
190 | return __perf_evsel__open(evsel, cpus, threads); | 246 | return __perf_evsel__open(evsel, cpus, threads, group, inherit); |
247 | } | ||
248 | |||
249 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, | ||
250 | struct cpu_map *cpus, bool group, bool inherit) | ||
251 | { | ||
252 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, inherit); | ||
253 | } | ||
254 | |||
255 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, | ||
256 | struct thread_map *threads, bool group, bool inherit) | ||
257 | { | ||
258 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, inherit); | ||
259 | } | ||
260 | |||
261 | static int __perf_evlist__mmap(struct perf_evlist *evlist, int cpu, int prot, | ||
262 | int mask, int fd) | ||
263 | { | ||
264 | evlist->mmap[cpu].prev = 0; | ||
265 | evlist->mmap[cpu].mask = mask; | ||
266 | evlist->mmap[cpu].base = mmap(NULL, evlist->mmap_len, prot, | ||
267 | MAP_SHARED, fd, 0); | ||
268 | if (evlist->mmap[cpu].base == MAP_FAILED) | ||
269 | return -1; | ||
270 | |||
271 | perf_evlist__add_pollfd(evlist, fd); | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static int perf_evlist__id_hash(struct perf_evlist *evlist, struct perf_evsel *evsel, | ||
276 | int cpu, int thread, int fd) | ||
277 | { | ||
278 | struct perf_sample_id *sid; | ||
279 | u64 read_data[4] = { 0, }; | ||
280 | int hash, id_idx = 1; /* The first entry is the counter value */ | ||
281 | |||
282 | if (!(evsel->attr.read_format & PERF_FORMAT_ID) || | ||
283 | read(fd, &read_data, sizeof(read_data)) == -1) | ||
284 | return -1; | ||
285 | |||
286 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | ||
287 | ++id_idx; | ||
288 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | ||
289 | ++id_idx; | ||
290 | |||
291 | sid = SID(evsel, cpu, thread); | ||
292 | sid->id = read_data[id_idx]; | ||
293 | sid->evsel = evsel; | ||
294 | hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS); | ||
295 | hlist_add_head(&sid->node, &evlist->heads[hash]); | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | /** perf_evlist__mmap - Create per cpu maps to receive events | ||
300 | * | ||
301 | * @evlist - list of events | ||
302 | * @cpus - cpu map being monitored | ||
303 | * @threads - threads map being monitored | ||
304 | * @pages - map length in pages | ||
305 | * @overwrite - overwrite older events? | ||
306 | * | ||
307 | * If overwrite is false the user needs to signal event consuption using: | ||
308 | * | ||
309 | * struct perf_mmap *m = &evlist->mmap[cpu]; | ||
310 | * unsigned int head = perf_mmap__read_head(m); | ||
311 | * | ||
312 | * perf_mmap__write_tail(m, head) | ||
313 | */ | ||
314 | int perf_evlist__mmap(struct perf_evlist *evlist, struct cpu_map *cpus, | ||
315 | struct thread_map *threads, int pages, bool overwrite) | ||
316 | { | ||
317 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | ||
318 | int mask = pages * page_size - 1, cpu; | ||
319 | struct perf_evsel *first_evsel, *evsel; | ||
320 | int thread, prot = PROT_READ | (overwrite ? 0 : PROT_WRITE); | ||
321 | |||
322 | if (evlist->mmap == NULL && | ||
323 | perf_evlist__alloc_mmap(evlist, cpus->nr) < 0) | ||
324 | return -ENOMEM; | ||
325 | |||
326 | if (evlist->pollfd == NULL && | ||
327 | perf_evlist__alloc_pollfd(evlist, cpus->nr, threads->nr) < 0) | ||
328 | return -ENOMEM; | ||
329 | |||
330 | evlist->mmap_len = (pages + 1) * page_size; | ||
331 | first_evsel = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
332 | |||
333 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
334 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
335 | evsel->id == NULL && | ||
336 | perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0) | ||
337 | return -ENOMEM; | ||
338 | |||
339 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
340 | for (thread = 0; thread < threads->nr; thread++) { | ||
341 | int fd = FD(evsel, cpu, thread); | ||
342 | |||
343 | if (evsel->idx || thread) { | ||
344 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, | ||
345 | FD(first_evsel, cpu, 0)) != 0) | ||
346 | goto out_unmap; | ||
347 | } else if (__perf_evlist__mmap(evlist, cpu, prot, mask, fd) < 0) | ||
348 | goto out_unmap; | ||
349 | |||
350 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
351 | perf_evlist__id_hash(evlist, evsel, cpu, thread, fd) < 0) | ||
352 | goto out_unmap; | ||
353 | } | ||
354 | } | ||
355 | } | ||
356 | |||
357 | return 0; | ||
358 | |||
359 | out_unmap: | ||
360 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
361 | if (evlist->mmap[cpu].base != NULL) { | ||
362 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | ||
363 | evlist->mmap[cpu].base = NULL; | ||
364 | } | ||
365 | } | ||
366 | return -1; | ||
191 | } | 367 | } |
192 | 368 | ||
193 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) | 369 | static int event__parse_id_sample(const event_t *event, u64 type, |
370 | struct sample_data *sample) | ||
194 | { | 371 | { |
195 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); | 372 | const u64 *array = event->sample.array; |
373 | |||
374 | array += ((event->header.size - | ||
375 | sizeof(event->header)) / sizeof(u64)) - 1; | ||
376 | |||
377 | if (type & PERF_SAMPLE_CPU) { | ||
378 | u32 *p = (u32 *)array; | ||
379 | sample->cpu = *p; | ||
380 | array--; | ||
381 | } | ||
382 | |||
383 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
384 | sample->stream_id = *array; | ||
385 | array--; | ||
386 | } | ||
387 | |||
388 | if (type & PERF_SAMPLE_ID) { | ||
389 | sample->id = *array; | ||
390 | array--; | ||
391 | } | ||
392 | |||
393 | if (type & PERF_SAMPLE_TIME) { | ||
394 | sample->time = *array; | ||
395 | array--; | ||
396 | } | ||
397 | |||
398 | if (type & PERF_SAMPLE_TID) { | ||
399 | u32 *p = (u32 *)array; | ||
400 | sample->pid = p[0]; | ||
401 | sample->tid = p[1]; | ||
402 | } | ||
403 | |||
404 | return 0; | ||
196 | } | 405 | } |
197 | 406 | ||
198 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) | 407 | int event__parse_sample(const event_t *event, u64 type, bool sample_id_all, |
408 | struct sample_data *data) | ||
199 | { | 409 | { |
200 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); | 410 | const u64 *array; |
411 | |||
412 | data->cpu = data->pid = data->tid = -1; | ||
413 | data->stream_id = data->id = data->time = -1ULL; | ||
414 | |||
415 | if (event->header.type != PERF_RECORD_SAMPLE) { | ||
416 | if (!sample_id_all) | ||
417 | return 0; | ||
418 | return event__parse_id_sample(event, type, data); | ||
419 | } | ||
420 | |||
421 | array = event->sample.array; | ||
422 | |||
423 | if (type & PERF_SAMPLE_IP) { | ||
424 | data->ip = event->ip.ip; | ||
425 | array++; | ||
426 | } | ||
427 | |||
428 | if (type & PERF_SAMPLE_TID) { | ||
429 | u32 *p = (u32 *)array; | ||
430 | data->pid = p[0]; | ||
431 | data->tid = p[1]; | ||
432 | array++; | ||
433 | } | ||
434 | |||
435 | if (type & PERF_SAMPLE_TIME) { | ||
436 | data->time = *array; | ||
437 | array++; | ||
438 | } | ||
439 | |||
440 | if (type & PERF_SAMPLE_ADDR) { | ||
441 | data->addr = *array; | ||
442 | array++; | ||
443 | } | ||
444 | |||
445 | data->id = -1ULL; | ||
446 | if (type & PERF_SAMPLE_ID) { | ||
447 | data->id = *array; | ||
448 | array++; | ||
449 | } | ||
450 | |||
451 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
452 | data->stream_id = *array; | ||
453 | array++; | ||
454 | } | ||
455 | |||
456 | if (type & PERF_SAMPLE_CPU) { | ||
457 | u32 *p = (u32 *)array; | ||
458 | data->cpu = *p; | ||
459 | array++; | ||
460 | } | ||
461 | |||
462 | if (type & PERF_SAMPLE_PERIOD) { | ||
463 | data->period = *array; | ||
464 | array++; | ||
465 | } | ||
466 | |||
467 | if (type & PERF_SAMPLE_READ) { | ||
468 | fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n"); | ||
469 | return -1; | ||
470 | } | ||
471 | |||
472 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
473 | data->callchain = (struct ip_callchain *)array; | ||
474 | array += 1 + data->callchain->nr; | ||
475 | } | ||
476 | |||
477 | if (type & PERF_SAMPLE_RAW) { | ||
478 | u32 *p = (u32 *)array; | ||
479 | data->raw_size = *p; | ||
480 | p++; | ||
481 | data->raw_data = p; | ||
482 | } | ||
483 | |||
484 | return 0; | ||
201 | } | 485 | } |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index b2d755fe88a5..7962e7587dea 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -24,11 +24,24 @@ struct perf_counts { | |||
24 | struct perf_counts_values cpu[]; | 24 | struct perf_counts_values cpu[]; |
25 | }; | 25 | }; |
26 | 26 | ||
27 | struct perf_evsel; | ||
28 | |||
29 | /* | ||
30 | * Per fd, to map back from PERF_SAMPLE_ID to evsel, only used when there are | ||
31 | * more than one entry in the evlist. | ||
32 | */ | ||
33 | struct perf_sample_id { | ||
34 | struct hlist_node node; | ||
35 | u64 id; | ||
36 | struct perf_evsel *evsel; | ||
37 | }; | ||
38 | |||
27 | struct perf_evsel { | 39 | struct perf_evsel { |
28 | struct list_head node; | 40 | struct list_head node; |
29 | struct perf_event_attr attr; | 41 | struct perf_event_attr attr; |
30 | char *filter; | 42 | char *filter; |
31 | struct xyarray *fd; | 43 | struct xyarray *fd; |
44 | struct xyarray *id; | ||
32 | struct perf_counts *counts; | 45 | struct perf_counts *counts; |
33 | int idx; | 46 | int idx; |
34 | void *priv; | 47 | void *priv; |
@@ -36,19 +49,31 @@ struct perf_evsel { | |||
36 | 49 | ||
37 | struct cpu_map; | 50 | struct cpu_map; |
38 | struct thread_map; | 51 | struct thread_map; |
52 | struct perf_evlist; | ||
39 | 53 | ||
40 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); | 54 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); |
55 | void perf_evsel__init(struct perf_evsel *evsel, | ||
56 | struct perf_event_attr *attr, int idx); | ||
57 | void perf_evsel__exit(struct perf_evsel *evsel); | ||
41 | void perf_evsel__delete(struct perf_evsel *evsel); | 58 | void perf_evsel__delete(struct perf_evsel *evsel); |
42 | 59 | ||
43 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 60 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
61 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); | ||
44 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); | 62 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); |
63 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist, int ncpus); | ||
45 | void perf_evsel__free_fd(struct perf_evsel *evsel); | 64 | void perf_evsel__free_fd(struct perf_evsel *evsel); |
65 | void perf_evsel__free_id(struct perf_evsel *evsel); | ||
46 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 66 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
47 | 67 | ||
48 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus); | 68 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, |
49 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads); | 69 | struct cpu_map *cpus, bool group, bool inherit); |
50 | int perf_evsel__open(struct perf_evsel *evsel, | 70 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, |
51 | struct cpu_map *cpus, struct thread_map *threads); | 71 | struct thread_map *threads, bool group, bool inherit); |
72 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | ||
73 | struct thread_map *threads, bool group, bool inherit); | ||
74 | int perf_evlist__mmap(struct perf_evlist *evlist, struct cpu_map *cpus, | ||
75 | struct thread_map *threads, int pages, bool overwrite); | ||
76 | void perf_evlist__munmap(struct perf_evlist *evlist, int ncpus); | ||
52 | 77 | ||
53 | #define perf_evsel__match(evsel, t, c) \ | 78 | #define perf_evsel__match(evsel, t, c) \ |
54 | (evsel->attr.type == PERF_TYPE_##t && \ | 79 | (evsel->attr.type == PERF_TYPE_##t && \ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index f6a929e74981..f0138d472339 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -8,6 +8,7 @@ | |||
8 | #include <linux/list.h> | 8 | #include <linux/list.h> |
9 | #include <linux/kernel.h> | 9 | #include <linux/kernel.h> |
10 | 10 | ||
11 | #include "evlist.h" | ||
11 | #include "util.h" | 12 | #include "util.h" |
12 | #include "header.h" | 13 | #include "header.h" |
13 | #include "../perf.h" | 14 | #include "../perf.h" |
@@ -428,7 +429,8 @@ static bool perf_session__read_build_ids(struct perf_session *self, bool with_hi | |||
428 | return ret; | 429 | return ret; |
429 | } | 430 | } |
430 | 431 | ||
431 | static int perf_header__adds_write(struct perf_header *self, int fd) | 432 | static int perf_header__adds_write(struct perf_header *self, |
433 | struct perf_evlist *evlist, int fd) | ||
432 | { | 434 | { |
433 | int nr_sections; | 435 | int nr_sections; |
434 | struct perf_session *session; | 436 | struct perf_session *session; |
@@ -463,7 +465,7 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
463 | 465 | ||
464 | /* Write trace info */ | 466 | /* Write trace info */ |
465 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); | 467 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); |
466 | read_tracing_data(fd, &evsel_list); | 468 | read_tracing_data(fd, &evlist->entries); |
467 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; | 469 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; |
468 | } | 470 | } |
469 | 471 | ||
@@ -513,7 +515,8 @@ int perf_header__write_pipe(int fd) | |||
513 | return 0; | 515 | return 0; |
514 | } | 516 | } |
515 | 517 | ||
516 | int perf_header__write(struct perf_header *self, int fd, bool at_exit) | 518 | int perf_header__write(struct perf_header *self, struct perf_evlist *evlist, |
519 | int fd, bool at_exit) | ||
517 | { | 520 | { |
518 | struct perf_file_header f_header; | 521 | struct perf_file_header f_header; |
519 | struct perf_file_attr f_attr; | 522 | struct perf_file_attr f_attr; |
@@ -566,7 +569,7 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) | |||
566 | self->data_offset = lseek(fd, 0, SEEK_CUR); | 569 | self->data_offset = lseek(fd, 0, SEEK_CUR); |
567 | 570 | ||
568 | if (at_exit) { | 571 | if (at_exit) { |
569 | err = perf_header__adds_write(self, fd); | 572 | err = perf_header__adds_write(self, evlist, fd); |
570 | if (err < 0) | 573 | if (err < 0) |
571 | return err; | 574 | return err; |
572 | } | 575 | } |
@@ -1133,7 +1136,7 @@ int event__process_event_type(event_t *self, | |||
1133 | return 0; | 1136 | return 0; |
1134 | } | 1137 | } |
1135 | 1138 | ||
1136 | int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | 1139 | int event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, |
1137 | event__handler_t process, | 1140 | event__handler_t process, |
1138 | struct perf_session *session __unused) | 1141 | struct perf_session *session __unused) |
1139 | { | 1142 | { |
@@ -1144,7 +1147,7 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | |||
1144 | memset(&ev, 0, sizeof(ev)); | 1147 | memset(&ev, 0, sizeof(ev)); |
1145 | 1148 | ||
1146 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; | 1149 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; |
1147 | size = read_tracing_data_size(fd, pattrs); | 1150 | size = read_tracing_data_size(fd, &evlist->entries); |
1148 | if (size <= 0) | 1151 | if (size <= 0) |
1149 | return size; | 1152 | return size; |
1150 | aligned_size = ALIGN(size, sizeof(u64)); | 1153 | aligned_size = ALIGN(size, sizeof(u64)); |
@@ -1154,7 +1157,7 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | |||
1154 | 1157 | ||
1155 | process(&ev, NULL, session); | 1158 | process(&ev, NULL, session); |
1156 | 1159 | ||
1157 | err = read_tracing_data(fd, pattrs); | 1160 | err = read_tracing_data(fd, &evlist->entries); |
1158 | write_padded(fd, NULL, 0, padding); | 1161 | write_padded(fd, NULL, 0, padding); |
1159 | 1162 | ||
1160 | return aligned_size; | 1163 | return aligned_size; |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 33f16be7b72f..65afd7f74e0d 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -65,8 +65,11 @@ struct perf_header { | |||
65 | int perf_header__init(struct perf_header *self); | 65 | int perf_header__init(struct perf_header *self); |
66 | void perf_header__exit(struct perf_header *self); | 66 | void perf_header__exit(struct perf_header *self); |
67 | 67 | ||
68 | struct perf_evlist; | ||
69 | |||
68 | int perf_header__read(struct perf_session *session, int fd); | 70 | int perf_header__read(struct perf_session *session, int fd); |
69 | int perf_header__write(struct perf_header *self, int fd, bool at_exit); | 71 | int perf_header__write(struct perf_header *self, struct perf_evlist *evlist, |
72 | int fd, bool at_exit); | ||
70 | int perf_header__write_pipe(int fd); | 73 | int perf_header__write_pipe(int fd); |
71 | 74 | ||
72 | int perf_header__add_attr(struct perf_header *self, | 75 | int perf_header__add_attr(struct perf_header *self, |
@@ -113,7 +116,7 @@ int event__synthesize_event_types(event__handler_t process, | |||
113 | int event__process_event_type(event_t *self, | 116 | int event__process_event_type(event_t *self, |
114 | struct perf_session *session); | 117 | struct perf_session *session); |
115 | 118 | ||
116 | int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | 119 | int event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, |
117 | event__handler_t process, | 120 | event__handler_t process, |
118 | struct perf_session *session); | 121 | struct perf_session *session); |
119 | int event__process_tracing_data(event_t *self, | 122 | int event__process_tracing_data(event_t *self, |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 32f4f1f2f6e4..02ed318d7312 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -211,7 +211,9 @@ void hist_entry__free(struct hist_entry *he) | |||
211 | * collapse the histogram | 211 | * collapse the histogram |
212 | */ | 212 | */ |
213 | 213 | ||
214 | static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | 214 | static bool hists__collapse_insert_entry(struct hists *self, |
215 | struct rb_root *root, | ||
216 | struct hist_entry *he) | ||
215 | { | 217 | { |
216 | struct rb_node **p = &root->rb_node; | 218 | struct rb_node **p = &root->rb_node; |
217 | struct rb_node *parent = NULL; | 219 | struct rb_node *parent = NULL; |
@@ -226,8 +228,11 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | |||
226 | 228 | ||
227 | if (!cmp) { | 229 | if (!cmp) { |
228 | iter->period += he->period; | 230 | iter->period += he->period; |
229 | if (symbol_conf.use_callchain) | 231 | if (symbol_conf.use_callchain) { |
230 | callchain_merge(iter->callchain, he->callchain); | 232 | callchain_cursor_reset(&self->callchain_cursor); |
233 | callchain_merge(&self->callchain_cursor, iter->callchain, | ||
234 | he->callchain); | ||
235 | } | ||
231 | hist_entry__free(he); | 236 | hist_entry__free(he); |
232 | return false; | 237 | return false; |
233 | } | 238 | } |
@@ -262,7 +267,7 @@ void hists__collapse_resort(struct hists *self) | |||
262 | next = rb_next(&n->rb_node); | 267 | next = rb_next(&n->rb_node); |
263 | 268 | ||
264 | rb_erase(&n->rb_node, &self->entries); | 269 | rb_erase(&n->rb_node, &self->entries); |
265 | if (collapse__insert_entry(&tmp, n)) | 270 | if (hists__collapse_insert_entry(self, &tmp, n)) |
266 | hists__inc_nr_entries(self, n); | 271 | hists__inc_nr_entries(self, n); |
267 | } | 272 | } |
268 | 273 | ||
@@ -425,7 +430,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
425 | u64 cumul; | 430 | u64 cumul; |
426 | 431 | ||
427 | child = rb_entry(node, struct callchain_node, rb_node); | 432 | child = rb_entry(node, struct callchain_node, rb_node); |
428 | cumul = cumul_hits(child); | 433 | cumul = callchain_cumul_hits(child); |
429 | remaining -= cumul; | 434 | remaining -= cumul; |
430 | 435 | ||
431 | /* | 436 | /* |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index ee789856a8c9..889559b86492 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -77,6 +77,8 @@ struct hists { | |||
77 | u64 event_stream; | 77 | u64 event_stream; |
78 | u32 type; | 78 | u32 type; |
79 | u16 col_len[HISTC_NR_COLS]; | 79 | u16 col_len[HISTC_NR_COLS]; |
80 | /* Best would be to reuse the session callchain cursor */ | ||
81 | struct callchain_cursor callchain_cursor; | ||
80 | }; | 82 | }; |
81 | 83 | ||
82 | struct hist_entry *__hists__add_entry(struct hists *self, | 84 | struct hist_entry *__hists__add_entry(struct hists *self, |
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h index f5ca26e53fbb..356c7e467b83 100644 --- a/tools/perf/util/include/linux/list.h +++ b/tools/perf/util/include/linux/list.h | |||
@@ -1,3 +1,4 @@ | |||
1 | #include <linux/kernel.h> | ||
1 | #include "../../../../include/linux/list.h" | 2 | #include "../../../../include/linux/list.h" |
2 | 3 | ||
3 | #ifndef PERF_LIST_H | 4 | #ifndef PERF_LIST_H |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 135f69baf966..cf082daa43e3 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include "../../../include/linux/hw_breakpoint.h" | 1 | #include "../../../include/linux/hw_breakpoint.h" |
2 | #include "util.h" | 2 | #include "util.h" |
3 | #include "../perf.h" | 3 | #include "../perf.h" |
4 | #include "evlist.h" | ||
4 | #include "evsel.h" | 5 | #include "evsel.h" |
5 | #include "parse-options.h" | 6 | #include "parse-options.h" |
6 | #include "parse-events.h" | 7 | #include "parse-events.h" |
@@ -11,10 +12,6 @@ | |||
11 | #include "header.h" | 12 | #include "header.h" |
12 | #include "debugfs.h" | 13 | #include "debugfs.h" |
13 | 14 | ||
14 | int nr_counters; | ||
15 | |||
16 | LIST_HEAD(evsel_list); | ||
17 | |||
18 | struct event_symbol { | 15 | struct event_symbol { |
19 | u8 type; | 16 | u8 type; |
20 | u64 config; | 17 | u64 config; |
@@ -449,8 +446,8 @@ parse_single_tracepoint_event(char *sys_name, | |||
449 | /* sys + ':' + event + ':' + flags*/ | 446 | /* sys + ':' + event + ':' + flags*/ |
450 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 447 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) |
451 | static enum event_result | 448 | static enum event_result |
452 | parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, | 449 | parse_multiple_tracepoint_event(const struct option *opt, char *sys_name, |
453 | char *flags) | 450 | const char *evt_exp, char *flags) |
454 | { | 451 | { |
455 | char evt_path[MAXPATHLEN]; | 452 | char evt_path[MAXPATHLEN]; |
456 | struct dirent *evt_ent; | 453 | struct dirent *evt_ent; |
@@ -483,15 +480,16 @@ parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, | |||
483 | if (len < 0) | 480 | if (len < 0) |
484 | return EVT_FAILED; | 481 | return EVT_FAILED; |
485 | 482 | ||
486 | if (parse_events(NULL, event_opt, 0)) | 483 | if (parse_events(opt, event_opt, 0)) |
487 | return EVT_FAILED; | 484 | return EVT_FAILED; |
488 | } | 485 | } |
489 | 486 | ||
490 | return EVT_HANDLED_ALL; | 487 | return EVT_HANDLED_ALL; |
491 | } | 488 | } |
492 | 489 | ||
493 | static enum event_result parse_tracepoint_event(const char **strp, | 490 | static enum event_result |
494 | struct perf_event_attr *attr) | 491 | parse_tracepoint_event(const struct option *opt, const char **strp, |
492 | struct perf_event_attr *attr) | ||
495 | { | 493 | { |
496 | const char *evt_name; | 494 | const char *evt_name; |
497 | char *flags = NULL, *comma_loc; | 495 | char *flags = NULL, *comma_loc; |
@@ -530,7 +528,7 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
530 | return EVT_FAILED; | 528 | return EVT_FAILED; |
531 | if (strpbrk(evt_name, "*?")) { | 529 | if (strpbrk(evt_name, "*?")) { |
532 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ | 530 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ |
533 | return parse_multiple_tracepoint_event(sys_name, evt_name, | 531 | return parse_multiple_tracepoint_event(opt, sys_name, evt_name, |
534 | flags); | 532 | flags); |
535 | } else { | 533 | } else { |
536 | return parse_single_tracepoint_event(sys_name, evt_name, | 534 | return parse_single_tracepoint_event(sys_name, evt_name, |
@@ -740,11 +738,12 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) | |||
740 | * Symbolic names are (almost) exactly matched. | 738 | * Symbolic names are (almost) exactly matched. |
741 | */ | 739 | */ |
742 | static enum event_result | 740 | static enum event_result |
743 | parse_event_symbols(const char **str, struct perf_event_attr *attr) | 741 | parse_event_symbols(const struct option *opt, const char **str, |
742 | struct perf_event_attr *attr) | ||
744 | { | 743 | { |
745 | enum event_result ret; | 744 | enum event_result ret; |
746 | 745 | ||
747 | ret = parse_tracepoint_event(str, attr); | 746 | ret = parse_tracepoint_event(opt, str, attr); |
748 | if (ret != EVT_FAILED) | 747 | if (ret != EVT_FAILED) |
749 | goto modifier; | 748 | goto modifier; |
750 | 749 | ||
@@ -778,14 +777,15 @@ modifier: | |||
778 | return ret; | 777 | return ret; |
779 | } | 778 | } |
780 | 779 | ||
781 | int parse_events(const struct option *opt __used, const char *str, int unset __used) | 780 | int parse_events(const struct option *opt, const char *str, int unset __used) |
782 | { | 781 | { |
782 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
783 | struct perf_event_attr attr; | 783 | struct perf_event_attr attr; |
784 | enum event_result ret; | 784 | enum event_result ret; |
785 | 785 | ||
786 | for (;;) { | 786 | for (;;) { |
787 | memset(&attr, 0, sizeof(attr)); | 787 | memset(&attr, 0, sizeof(attr)); |
788 | ret = parse_event_symbols(&str, &attr); | 788 | ret = parse_event_symbols(opt, &str, &attr); |
789 | if (ret == EVT_FAILED) | 789 | if (ret == EVT_FAILED) |
790 | return -1; | 790 | return -1; |
791 | 791 | ||
@@ -794,12 +794,10 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
794 | 794 | ||
795 | if (ret != EVT_HANDLED_ALL) { | 795 | if (ret != EVT_HANDLED_ALL) { |
796 | struct perf_evsel *evsel; | 796 | struct perf_evsel *evsel; |
797 | evsel = perf_evsel__new(&attr, | 797 | evsel = perf_evsel__new(&attr, evlist->nr_entries); |
798 | nr_counters); | ||
799 | if (evsel == NULL) | 798 | if (evsel == NULL) |
800 | return -1; | 799 | return -1; |
801 | list_add_tail(&evsel->node, &evsel_list); | 800 | perf_evlist__add(evlist, evsel); |
802 | ++nr_counters; | ||
803 | } | 801 | } |
804 | 802 | ||
805 | if (*str == 0) | 803 | if (*str == 0) |
@@ -813,13 +811,14 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
813 | return 0; | 811 | return 0; |
814 | } | 812 | } |
815 | 813 | ||
816 | int parse_filter(const struct option *opt __used, const char *str, | 814 | int parse_filter(const struct option *opt, const char *str, |
817 | int unset __used) | 815 | int unset __used) |
818 | { | 816 | { |
817 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
819 | struct perf_evsel *last = NULL; | 818 | struct perf_evsel *last = NULL; |
820 | 819 | ||
821 | if (!list_empty(&evsel_list)) | 820 | if (evlist->nr_entries > 0) |
822 | last = list_entry(evsel_list.prev, struct perf_evsel, node); | 821 | last = list_entry(evlist->entries.prev, struct perf_evsel, node); |
823 | 822 | ||
824 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { | 823 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { |
825 | fprintf(stderr, | 824 | fprintf(stderr, |
@@ -981,33 +980,3 @@ void print_events(void) | |||
981 | 980 | ||
982 | exit(129); | 981 | exit(129); |
983 | } | 982 | } |
984 | |||
985 | int perf_evsel_list__create_default(void) | ||
986 | { | ||
987 | struct perf_evsel *evsel; | ||
988 | struct perf_event_attr attr; | ||
989 | |||
990 | memset(&attr, 0, sizeof(attr)); | ||
991 | attr.type = PERF_TYPE_HARDWARE; | ||
992 | attr.config = PERF_COUNT_HW_CPU_CYCLES; | ||
993 | |||
994 | evsel = perf_evsel__new(&attr, 0); | ||
995 | |||
996 | if (evsel == NULL) | ||
997 | return -ENOMEM; | ||
998 | |||
999 | list_add(&evsel->node, &evsel_list); | ||
1000 | ++nr_counters; | ||
1001 | return 0; | ||
1002 | } | ||
1003 | |||
1004 | void perf_evsel_list__delete(void) | ||
1005 | { | ||
1006 | struct perf_evsel *pos, *n; | ||
1007 | |||
1008 | list_for_each_entry_safe(pos, n, &evsel_list, node) { | ||
1009 | list_del_init(&pos->node); | ||
1010 | perf_evsel__delete(pos); | ||
1011 | } | ||
1012 | nr_counters = 0; | ||
1013 | } | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 458e3ecf17af..cf7e94abb676 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -9,11 +9,6 @@ | |||
9 | struct list_head; | 9 | struct list_head; |
10 | struct perf_evsel; | 10 | struct perf_evsel; |
11 | 11 | ||
12 | extern struct list_head evsel_list; | ||
13 | |||
14 | int perf_evsel_list__create_default(void); | ||
15 | void perf_evsel_list__delete(void); | ||
16 | |||
17 | struct option; | 12 | struct option; |
18 | 13 | ||
19 | struct tracepoint_path { | 14 | struct tracepoint_path { |
@@ -25,8 +20,6 @@ struct tracepoint_path { | |||
25 | extern struct tracepoint_path *tracepoint_id_to_path(u64 config); | 20 | extern struct tracepoint_path *tracepoint_id_to_path(u64 config); |
26 | extern bool have_tracepoints(struct list_head *evlist); | 21 | extern bool have_tracepoints(struct list_head *evlist); |
27 | 22 | ||
28 | extern int nr_counters; | ||
29 | |||
30 | const char *event_name(struct perf_evsel *event); | 23 | const char *event_name(struct perf_evsel *event); |
31 | extern const char *__event_name(int type, u64 config); | 24 | extern const char *__event_name(int type, u64 config); |
32 | 25 | ||
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 6e29d9c9dccc..859d377a3df3 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <string.h> | 31 | #include <string.h> |
32 | #include <stdarg.h> | 32 | #include <stdarg.h> |
33 | #include <limits.h> | 33 | #include <limits.h> |
34 | #include <elf.h> | ||
34 | 35 | ||
35 | #undef _GNU_SOURCE | 36 | #undef _GNU_SOURCE |
36 | #include "util.h" | 37 | #include "util.h" |
@@ -111,7 +112,25 @@ static struct symbol *__find_kernel_function_by_name(const char *name, | |||
111 | NULL); | 112 | NULL); |
112 | } | 113 | } |
113 | 114 | ||
114 | const char *kernel_get_module_path(const char *module) | 115 | static struct map *kernel_get_module_map(const char *module) |
116 | { | ||
117 | struct rb_node *nd; | ||
118 | struct map_groups *grp = &machine.kmaps; | ||
119 | |||
120 | if (!module) | ||
121 | module = "kernel"; | ||
122 | |||
123 | for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { | ||
124 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
125 | if (strncmp(pos->dso->short_name + 1, module, | ||
126 | pos->dso->short_name_len - 2) == 0) { | ||
127 | return pos; | ||
128 | } | ||
129 | } | ||
130 | return NULL; | ||
131 | } | ||
132 | |||
133 | static struct dso *kernel_get_module_dso(const char *module) | ||
115 | { | 134 | { |
116 | struct dso *dso; | 135 | struct dso *dso; |
117 | struct map *map; | 136 | struct map *map; |
@@ -141,7 +160,13 @@ const char *kernel_get_module_path(const char *module) | |||
141 | } | 160 | } |
142 | } | 161 | } |
143 | found: | 162 | found: |
144 | return dso->long_name; | 163 | return dso; |
164 | } | ||
165 | |||
166 | const char *kernel_get_module_path(const char *module) | ||
167 | { | ||
168 | struct dso *dso = kernel_get_module_dso(module); | ||
169 | return (dso) ? dso->long_name : NULL; | ||
145 | } | 170 | } |
146 | 171 | ||
147 | #ifdef DWARF_SUPPORT | 172 | #ifdef DWARF_SUPPORT |
@@ -1913,3 +1938,42 @@ int del_perf_probe_events(struct strlist *dellist) | |||
1913 | return ret; | 1938 | return ret; |
1914 | } | 1939 | } |
1915 | 1940 | ||
1941 | /* | ||
1942 | * If a symbol corresponds to a function with global binding return 0. | ||
1943 | * For all others return 1. | ||
1944 | */ | ||
1945 | static int filter_non_global_functions(struct map *map __unused, | ||
1946 | struct symbol *sym) | ||
1947 | { | ||
1948 | if (sym->binding != STB_GLOBAL) | ||
1949 | return 1; | ||
1950 | |||
1951 | return 0; | ||
1952 | } | ||
1953 | |||
1954 | int show_available_funcs(const char *module) | ||
1955 | { | ||
1956 | struct map *map; | ||
1957 | int ret; | ||
1958 | |||
1959 | setup_pager(); | ||
1960 | |||
1961 | ret = init_vmlinux(); | ||
1962 | if (ret < 0) | ||
1963 | return ret; | ||
1964 | |||
1965 | map = kernel_get_module_map(module); | ||
1966 | if (!map) { | ||
1967 | pr_err("Failed to find %s map.\n", (module) ? : "kernel"); | ||
1968 | return -EINVAL; | ||
1969 | } | ||
1970 | if (map__load(map, filter_non_global_functions)) { | ||
1971 | pr_err("Failed to load map.\n"); | ||
1972 | return -EINVAL; | ||
1973 | } | ||
1974 | if (!dso__sorted_by_name(map->dso, map->type)) | ||
1975 | dso__sort_by_name(map->dso, map->type); | ||
1976 | |||
1977 | dso__fprintf_symbols_by_name(map->dso, map->type, stdout); | ||
1978 | return 0; | ||
1979 | } | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 5accbedfea37..1fb4f18337d3 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -127,6 +127,7 @@ extern int show_line_range(struct line_range *lr, const char *module); | |||
127 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, | 127 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, |
128 | int max_probe_points, const char *module, | 128 | int max_probe_points, const char *module, |
129 | bool externs); | 129 | bool externs); |
130 | extern int show_available_funcs(const char *module); | ||
130 | 131 | ||
131 | 132 | ||
132 | /* Maximum index number of event-name postfix */ | 133 | /* Maximum index number of event-name postfix */ |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index ab83b6ac5d65..69215bff17e9 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -280,6 +280,19 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | |||
280 | return name ? (strcmp(tname, name) == 0) : false; | 280 | return name ? (strcmp(tname, name) == 0) : false; |
281 | } | 281 | } |
282 | 282 | ||
283 | /* Get callsite line number of inline-function instance */ | ||
284 | static int die_get_call_lineno(Dwarf_Die *in_die) | ||
285 | { | ||
286 | Dwarf_Attribute attr; | ||
287 | Dwarf_Word ret; | ||
288 | |||
289 | if (!dwarf_attr(in_die, DW_AT_call_line, &attr)) | ||
290 | return -ENOENT; | ||
291 | |||
292 | dwarf_formudata(&attr, &ret); | ||
293 | return (int)ret; | ||
294 | } | ||
295 | |||
283 | /* Get type die */ | 296 | /* Get type die */ |
284 | static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | 297 | static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) |
285 | { | 298 | { |
@@ -458,6 +471,151 @@ static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | |||
458 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); | 471 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); |
459 | } | 472 | } |
460 | 473 | ||
474 | /* Walker on lines (Note: line number will not be sorted) */ | ||
475 | typedef int (* line_walk_handler_t) (const char *fname, int lineno, | ||
476 | Dwarf_Addr addr, void *data); | ||
477 | |||
478 | struct __line_walk_param { | ||
479 | const char *fname; | ||
480 | line_walk_handler_t handler; | ||
481 | void *data; | ||
482 | int retval; | ||
483 | }; | ||
484 | |||
485 | static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) | ||
486 | { | ||
487 | struct __line_walk_param *lw = data; | ||
488 | Dwarf_Addr addr; | ||
489 | int lineno; | ||
490 | |||
491 | if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { | ||
492 | lineno = die_get_call_lineno(in_die); | ||
493 | if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { | ||
494 | lw->retval = lw->handler(lw->fname, lineno, addr, | ||
495 | lw->data); | ||
496 | if (lw->retval != 0) | ||
497 | return DIE_FIND_CB_FOUND; | ||
498 | } | ||
499 | } | ||
500 | return DIE_FIND_CB_SIBLING; | ||
501 | } | ||
502 | |||
503 | /* Walk on lines of blocks included in given DIE */ | ||
504 | static int __die_walk_funclines(Dwarf_Die *sp_die, | ||
505 | line_walk_handler_t handler, void *data) | ||
506 | { | ||
507 | struct __line_walk_param lw = { | ||
508 | .handler = handler, | ||
509 | .data = data, | ||
510 | .retval = 0, | ||
511 | }; | ||
512 | Dwarf_Die die_mem; | ||
513 | Dwarf_Addr addr; | ||
514 | int lineno; | ||
515 | |||
516 | /* Handle function declaration line */ | ||
517 | lw.fname = dwarf_decl_file(sp_die); | ||
518 | if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
519 | dwarf_entrypc(sp_die, &addr) == 0) { | ||
520 | lw.retval = handler(lw.fname, lineno, addr, data); | ||
521 | if (lw.retval != 0) | ||
522 | goto done; | ||
523 | } | ||
524 | die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem); | ||
525 | done: | ||
526 | return lw.retval; | ||
527 | } | ||
528 | |||
529 | static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) | ||
530 | { | ||
531 | struct __line_walk_param *lw = data; | ||
532 | |||
533 | lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data); | ||
534 | if (lw->retval != 0) | ||
535 | return DWARF_CB_ABORT; | ||
536 | |||
537 | return DWARF_CB_OK; | ||
538 | } | ||
539 | |||
540 | /* | ||
541 | * Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on | ||
542 | * the lines inside the subprogram, otherwise PDIE must be a CU DIE. | ||
543 | */ | ||
544 | static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler, | ||
545 | void *data) | ||
546 | { | ||
547 | Dwarf_Lines *lines; | ||
548 | Dwarf_Line *line; | ||
549 | Dwarf_Addr addr; | ||
550 | const char *fname; | ||
551 | int lineno, ret = 0; | ||
552 | Dwarf_Die die_mem, *cu_die; | ||
553 | size_t nlines, i; | ||
554 | |||
555 | /* Get the CU die */ | ||
556 | if (dwarf_tag(pdie) == DW_TAG_subprogram) | ||
557 | cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL); | ||
558 | else | ||
559 | cu_die = pdie; | ||
560 | if (!cu_die) { | ||
561 | pr_debug2("Failed to get CU from subprogram\n"); | ||
562 | return -EINVAL; | ||
563 | } | ||
564 | |||
565 | /* Get lines list in the CU */ | ||
566 | if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { | ||
567 | pr_debug2("Failed to get source lines on this CU.\n"); | ||
568 | return -ENOENT; | ||
569 | } | ||
570 | pr_debug2("Get %zd lines from this CU\n", nlines); | ||
571 | |||
572 | /* Walk on the lines on lines list */ | ||
573 | for (i = 0; i < nlines; i++) { | ||
574 | line = dwarf_onesrcline(lines, i); | ||
575 | if (line == NULL || | ||
576 | dwarf_lineno(line, &lineno) != 0 || | ||
577 | dwarf_lineaddr(line, &addr) != 0) { | ||
578 | pr_debug2("Failed to get line info. " | ||
579 | "Possible error in debuginfo.\n"); | ||
580 | continue; | ||
581 | } | ||
582 | /* Filter lines based on address */ | ||
583 | if (pdie != cu_die) | ||
584 | /* | ||
585 | * Address filtering | ||
586 | * The line is included in given function, and | ||
587 | * no inline block includes it. | ||
588 | */ | ||
589 | if (!dwarf_haspc(pdie, addr) || | ||
590 | die_find_inlinefunc(pdie, addr, &die_mem)) | ||
591 | continue; | ||
592 | /* Get source line */ | ||
593 | fname = dwarf_linesrc(line, NULL, NULL); | ||
594 | |||
595 | ret = handler(fname, lineno, addr, data); | ||
596 | if (ret != 0) | ||
597 | return ret; | ||
598 | } | ||
599 | |||
600 | /* | ||
601 | * Dwarf lines doesn't include function declarations and inlined | ||
602 | * subroutines. We have to check functions list or given function. | ||
603 | */ | ||
604 | if (pdie != cu_die) | ||
605 | ret = __die_walk_funclines(pdie, handler, data); | ||
606 | else { | ||
607 | struct __line_walk_param param = { | ||
608 | .handler = handler, | ||
609 | .data = data, | ||
610 | .retval = 0, | ||
611 | }; | ||
612 | dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); | ||
613 | ret = param.retval; | ||
614 | } | ||
615 | |||
616 | return ret; | ||
617 | } | ||
618 | |||
461 | struct __find_variable_param { | 619 | struct __find_variable_param { |
462 | const char *name; | 620 | const char *name; |
463 | Dwarf_Addr addr; | 621 | Dwarf_Addr addr; |
@@ -1050,43 +1208,26 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1050 | return ret; | 1208 | return ret; |
1051 | } | 1209 | } |
1052 | 1210 | ||
1053 | /* Find probe point from its line number */ | 1211 | static int probe_point_line_walker(const char *fname, int lineno, |
1054 | static int find_probe_point_by_line(struct probe_finder *pf) | 1212 | Dwarf_Addr addr, void *data) |
1055 | { | 1213 | { |
1056 | Dwarf_Lines *lines; | 1214 | struct probe_finder *pf = data; |
1057 | Dwarf_Line *line; | 1215 | int ret; |
1058 | size_t nlines, i; | ||
1059 | Dwarf_Addr addr; | ||
1060 | int lineno; | ||
1061 | int ret = 0; | ||
1062 | |||
1063 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { | ||
1064 | pr_warning("No source lines found.\n"); | ||
1065 | return -ENOENT; | ||
1066 | } | ||
1067 | 1216 | ||
1068 | for (i = 0; i < nlines && ret == 0; i++) { | 1217 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) |
1069 | line = dwarf_onesrcline(lines, i); | 1218 | return 0; |
1070 | if (dwarf_lineno(line, &lineno) != 0 || | ||
1071 | lineno != pf->lno) | ||
1072 | continue; | ||
1073 | 1219 | ||
1074 | /* TODO: Get fileno from line, but how? */ | 1220 | pf->addr = addr; |
1075 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | 1221 | ret = call_probe_finder(NULL, pf); |
1076 | continue; | ||
1077 | 1222 | ||
1078 | if (dwarf_lineaddr(line, &addr) != 0) { | 1223 | /* Continue if no error, because the line will be in inline function */ |
1079 | pr_warning("Failed to get the address of the line.\n"); | 1224 | return ret < 0 ?: 0; |
1080 | return -ENOENT; | 1225 | } |
1081 | } | ||
1082 | pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", | ||
1083 | (int)i, lineno, (uintmax_t)addr); | ||
1084 | pf->addr = addr; | ||
1085 | 1226 | ||
1086 | ret = call_probe_finder(NULL, pf); | 1227 | /* Find probe point from its line number */ |
1087 | /* Continuing, because target line might be inlined. */ | 1228 | static int find_probe_point_by_line(struct probe_finder *pf) |
1088 | } | 1229 | { |
1089 | return ret; | 1230 | return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf); |
1090 | } | 1231 | } |
1091 | 1232 | ||
1092 | /* Find lines which match lazy pattern */ | 1233 | /* Find lines which match lazy pattern */ |
@@ -1140,15 +1281,31 @@ out_close: | |||
1140 | return nlines; | 1281 | return nlines; |
1141 | } | 1282 | } |
1142 | 1283 | ||
1284 | static int probe_point_lazy_walker(const char *fname, int lineno, | ||
1285 | Dwarf_Addr addr, void *data) | ||
1286 | { | ||
1287 | struct probe_finder *pf = data; | ||
1288 | int ret; | ||
1289 | |||
1290 | if (!line_list__has_line(&pf->lcache, lineno) || | ||
1291 | strtailcmp(fname, pf->fname) != 0) | ||
1292 | return 0; | ||
1293 | |||
1294 | pr_debug("Probe line found: line:%d addr:0x%llx\n", | ||
1295 | lineno, (unsigned long long)addr); | ||
1296 | pf->addr = addr; | ||
1297 | ret = call_probe_finder(NULL, pf); | ||
1298 | |||
1299 | /* | ||
1300 | * Continue if no error, because the lazy pattern will match | ||
1301 | * to other lines | ||
1302 | */ | ||
1303 | return ret < 0 ?: 0; | ||
1304 | } | ||
1305 | |||
1143 | /* Find probe points from lazy pattern */ | 1306 | /* Find probe points from lazy pattern */ |
1144 | static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | 1307 | static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) |
1145 | { | 1308 | { |
1146 | Dwarf_Lines *lines; | ||
1147 | Dwarf_Line *line; | ||
1148 | size_t nlines, i; | ||
1149 | Dwarf_Addr addr; | ||
1150 | Dwarf_Die die_mem; | ||
1151 | int lineno; | ||
1152 | int ret = 0; | 1309 | int ret = 0; |
1153 | 1310 | ||
1154 | if (list_empty(&pf->lcache)) { | 1311 | if (list_empty(&pf->lcache)) { |
@@ -1162,45 +1319,7 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1162 | return ret; | 1319 | return ret; |
1163 | } | 1320 | } |
1164 | 1321 | ||
1165 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { | 1322 | return die_walk_lines(sp_die, probe_point_lazy_walker, pf); |
1166 | pr_warning("No source lines found.\n"); | ||
1167 | return -ENOENT; | ||
1168 | } | ||
1169 | |||
1170 | for (i = 0; i < nlines && ret >= 0; i++) { | ||
1171 | line = dwarf_onesrcline(lines, i); | ||
1172 | |||
1173 | if (dwarf_lineno(line, &lineno) != 0 || | ||
1174 | !line_list__has_line(&pf->lcache, lineno)) | ||
1175 | continue; | ||
1176 | |||
1177 | /* TODO: Get fileno from line, but how? */ | ||
1178 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | ||
1179 | continue; | ||
1180 | |||
1181 | if (dwarf_lineaddr(line, &addr) != 0) { | ||
1182 | pr_debug("Failed to get the address of line %d.\n", | ||
1183 | lineno); | ||
1184 | continue; | ||
1185 | } | ||
1186 | if (sp_die) { | ||
1187 | /* Address filtering 1: does sp_die include addr? */ | ||
1188 | if (!dwarf_haspc(sp_die, addr)) | ||
1189 | continue; | ||
1190 | /* Address filtering 2: No child include addr? */ | ||
1191 | if (die_find_inlinefunc(sp_die, addr, &die_mem)) | ||
1192 | continue; | ||
1193 | } | ||
1194 | |||
1195 | pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n", | ||
1196 | (int)i, lineno, (unsigned long long)addr); | ||
1197 | pf->addr = addr; | ||
1198 | |||
1199 | ret = call_probe_finder(sp_die, pf); | ||
1200 | /* Continuing, because target line might be inlined. */ | ||
1201 | } | ||
1202 | /* TODO: deallocate lines, but how? */ | ||
1203 | return ret; | ||
1204 | } | 1323 | } |
1205 | 1324 | ||
1206 | /* Callback parameter with return value */ | 1325 | /* Callback parameter with return value */ |
@@ -1644,91 +1763,28 @@ static int line_range_add_line(const char *src, unsigned int lineno, | |||
1644 | return line_list__add_line(&lr->line_list, lineno); | 1763 | return line_list__add_line(&lr->line_list, lineno); |
1645 | } | 1764 | } |
1646 | 1765 | ||
1647 | /* Search function declaration lines */ | 1766 | static int line_range_walk_cb(const char *fname, int lineno, |
1648 | static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) | 1767 | Dwarf_Addr addr __used, |
1768 | void *data) | ||
1649 | { | 1769 | { |
1650 | struct dwarf_callback_param *param = data; | 1770 | struct line_finder *lf = data; |
1651 | struct line_finder *lf = param->data; | ||
1652 | const char *src; | ||
1653 | int lineno; | ||
1654 | 1771 | ||
1655 | src = dwarf_decl_file(sp_die); | 1772 | if ((strtailcmp(fname, lf->fname) != 0) || |
1656 | if (src && strtailcmp(src, lf->fname) != 0) | ||
1657 | return DWARF_CB_OK; | ||
1658 | |||
1659 | if (dwarf_decl_line(sp_die, &lineno) != 0 || | ||
1660 | (lf->lno_s > lineno || lf->lno_e < lineno)) | 1773 | (lf->lno_s > lineno || lf->lno_e < lineno)) |
1661 | return DWARF_CB_OK; | 1774 | return 0; |
1662 | 1775 | ||
1663 | param->retval = line_range_add_line(src, lineno, lf->lr); | 1776 | if (line_range_add_line(fname, lineno, lf->lr) < 0) |
1664 | if (param->retval < 0) | 1777 | return -EINVAL; |
1665 | return DWARF_CB_ABORT; | ||
1666 | return DWARF_CB_OK; | ||
1667 | } | ||
1668 | 1778 | ||
1669 | static int find_line_range_func_decl_lines(struct line_finder *lf) | 1779 | return 0; |
1670 | { | ||
1671 | struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0}; | ||
1672 | dwarf_getfuncs(&lf->cu_die, line_range_funcdecl_cb, ¶m, 0); | ||
1673 | return param.retval; | ||
1674 | } | 1780 | } |
1675 | 1781 | ||
1676 | /* Find line range from its line number */ | 1782 | /* Find line range from its line number */ |
1677 | static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | 1783 | static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) |
1678 | { | 1784 | { |
1679 | Dwarf_Lines *lines; | 1785 | int ret; |
1680 | Dwarf_Line *line; | ||
1681 | size_t nlines, i; | ||
1682 | Dwarf_Addr addr; | ||
1683 | int lineno, ret = 0; | ||
1684 | const char *src; | ||
1685 | Dwarf_Die die_mem; | ||
1686 | |||
1687 | line_list__init(&lf->lr->line_list); | ||
1688 | if (dwarf_getsrclines(&lf->cu_die, &lines, &nlines) != 0) { | ||
1689 | pr_warning("No source lines found.\n"); | ||
1690 | return -ENOENT; | ||
1691 | } | ||
1692 | |||
1693 | /* Search probable lines on lines list */ | ||
1694 | for (i = 0; i < nlines; i++) { | ||
1695 | line = dwarf_onesrcline(lines, i); | ||
1696 | if (dwarf_lineno(line, &lineno) != 0 || | ||
1697 | (lf->lno_s > lineno || lf->lno_e < lineno)) | ||
1698 | continue; | ||
1699 | |||
1700 | if (sp_die) { | ||
1701 | /* Address filtering 1: does sp_die include addr? */ | ||
1702 | if (dwarf_lineaddr(line, &addr) != 0 || | ||
1703 | !dwarf_haspc(sp_die, addr)) | ||
1704 | continue; | ||
1705 | |||
1706 | /* Address filtering 2: No child include addr? */ | ||
1707 | if (die_find_inlinefunc(sp_die, addr, &die_mem)) | ||
1708 | continue; | ||
1709 | } | ||
1710 | |||
1711 | /* TODO: Get fileno from line, but how? */ | ||
1712 | src = dwarf_linesrc(line, NULL, NULL); | ||
1713 | if (strtailcmp(src, lf->fname) != 0) | ||
1714 | continue; | ||
1715 | |||
1716 | ret = line_range_add_line(src, lineno, lf->lr); | ||
1717 | if (ret < 0) | ||
1718 | return ret; | ||
1719 | } | ||
1720 | 1786 | ||
1721 | /* | 1787 | ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf); |
1722 | * Dwarf lines doesn't include function declarations. We have to | ||
1723 | * check functions list or given function. | ||
1724 | */ | ||
1725 | if (sp_die) { | ||
1726 | src = dwarf_decl_file(sp_die); | ||
1727 | if (src && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
1728 | (lf->lno_s <= lineno && lf->lno_e >= lineno)) | ||
1729 | ret = line_range_add_line(src, lineno, lf->lr); | ||
1730 | } else | ||
1731 | ret = find_line_range_func_decl_lines(lf); | ||
1732 | 1788 | ||
1733 | /* Update status */ | 1789 | /* Update status */ |
1734 | if (ret >= 0) | 1790 | if (ret >= 0) |
@@ -1758,9 +1814,6 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | |||
1758 | struct line_finder *lf = param->data; | 1814 | struct line_finder *lf = param->data; |
1759 | struct line_range *lr = lf->lr; | 1815 | struct line_range *lr = lf->lr; |
1760 | 1816 | ||
1761 | pr_debug("find (%llx) %s\n", | ||
1762 | (unsigned long long)dwarf_dieoffset(sp_die), | ||
1763 | dwarf_diename(sp_die)); | ||
1764 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | 1817 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && |
1765 | die_compare_name(sp_die, lr->function)) { | 1818 | die_compare_name(sp_die, lr->function)) { |
1766 | lf->fname = dwarf_decl_file(sp_die); | 1819 | lf->fname = dwarf_decl_file(sp_die); |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 105f00bfd555..e6a07408669e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -242,17 +242,16 @@ static bool symbol__match_parent_regex(struct symbol *sym) | |||
242 | return 0; | 242 | return 0; |
243 | } | 243 | } |
244 | 244 | ||
245 | struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | 245 | int perf_session__resolve_callchain(struct perf_session *self, |
246 | struct thread *thread, | 246 | struct thread *thread, |
247 | struct ip_callchain *chain, | 247 | struct ip_callchain *chain, |
248 | struct symbol **parent) | 248 | struct symbol **parent) |
249 | { | 249 | { |
250 | u8 cpumode = PERF_RECORD_MISC_USER; | 250 | u8 cpumode = PERF_RECORD_MISC_USER; |
251 | unsigned int i; | 251 | unsigned int i; |
252 | struct map_symbol *syms = calloc(chain->nr, sizeof(*syms)); | 252 | int err; |
253 | 253 | ||
254 | if (!syms) | 254 | callchain_cursor_reset(&self->callchain_cursor); |
255 | return NULL; | ||
256 | 255 | ||
257 | for (i = 0; i < chain->nr; i++) { | 256 | for (i = 0; i < chain->nr; i++) { |
258 | u64 ip = chain->ips[i]; | 257 | u64 ip = chain->ips[i]; |
@@ -281,12 +280,15 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | |||
281 | *parent = al.sym; | 280 | *parent = al.sym; |
282 | if (!symbol_conf.use_callchain) | 281 | if (!symbol_conf.use_callchain) |
283 | break; | 282 | break; |
284 | syms[i].map = al.map; | ||
285 | syms[i].sym = al.sym; | ||
286 | } | 283 | } |
284 | |||
285 | err = callchain_cursor_append(&self->callchain_cursor, | ||
286 | ip, al.map, al.sym); | ||
287 | if (err) | ||
288 | return err; | ||
287 | } | 289 | } |
288 | 290 | ||
289 | return syms; | 291 | return 0; |
290 | } | 292 | } |
291 | 293 | ||
292 | static int process_event_synth_stub(event_t *event __used, | 294 | static int process_event_synth_stub(event_t *event __used, |
@@ -494,7 +496,7 @@ static void flush_sample_queue(struct perf_session *s, | |||
494 | if (iter->timestamp > limit) | 496 | if (iter->timestamp > limit) |
495 | break; | 497 | break; |
496 | 498 | ||
497 | event__parse_sample(iter->event, s, &sample); | 499 | perf_session__parse_sample(s, iter->event, &sample); |
498 | perf_session_deliver_event(s, iter->event, &sample, ops, | 500 | perf_session_deliver_event(s, iter->event, &sample, ops, |
499 | iter->file_offset); | 501 | iter->file_offset); |
500 | 502 | ||
@@ -804,7 +806,7 @@ static int perf_session__process_event(struct perf_session *session, | |||
804 | /* | 806 | /* |
805 | * For all kernel events we get the sample data | 807 | * For all kernel events we get the sample data |
806 | */ | 808 | */ |
807 | event__parse_sample(event, session, &sample); | 809 | perf_session__parse_sample(session, event, &sample); |
808 | 810 | ||
809 | /* Preprocess sample records - precheck callchains */ | 811 | /* Preprocess sample records - precheck callchains */ |
810 | if (perf_session__preprocess_sample(session, event, &sample)) | 812 | if (perf_session__preprocess_sample(session, event, &sample)) |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index decd83f274fd..78239767011e 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -51,7 +51,8 @@ struct perf_session { | |||
51 | int cwdlen; | 51 | int cwdlen; |
52 | char *cwd; | 52 | char *cwd; |
53 | struct ordered_samples ordered_samples; | 53 | struct ordered_samples ordered_samples; |
54 | char filename[0]; | 54 | struct callchain_cursor callchain_cursor; |
55 | char filename[0]; | ||
55 | }; | 56 | }; |
56 | 57 | ||
57 | struct perf_event_ops; | 58 | struct perf_event_ops; |
@@ -94,10 +95,10 @@ int __perf_session__process_events(struct perf_session *self, | |||
94 | int perf_session__process_events(struct perf_session *self, | 95 | int perf_session__process_events(struct perf_session *self, |
95 | struct perf_event_ops *event_ops); | 96 | struct perf_event_ops *event_ops); |
96 | 97 | ||
97 | struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | 98 | int perf_session__resolve_callchain(struct perf_session *self, |
98 | struct thread *thread, | 99 | struct thread *thread, |
99 | struct ip_callchain *chain, | 100 | struct ip_callchain *chain, |
100 | struct symbol **parent); | 101 | struct symbol **parent); |
101 | 102 | ||
102 | bool perf_session__has_traces(struct perf_session *self, const char *msg); | 103 | bool perf_session__has_traces(struct perf_session *self, const char *msg); |
103 | 104 | ||
@@ -154,4 +155,13 @@ size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp) | |||
154 | { | 155 | { |
155 | return hists__fprintf_nr_events(&self->hists, fp); | 156 | return hists__fprintf_nr_events(&self->hists, fp); |
156 | } | 157 | } |
158 | |||
159 | static inline int perf_session__parse_sample(struct perf_session *session, | ||
160 | const event_t *event, | ||
161 | struct sample_data *sample) | ||
162 | { | ||
163 | return event__parse_sample(event, session->sample_type, | ||
164 | session->sample_id_all, sample); | ||
165 | } | ||
166 | |||
157 | #endif /* __PERF_SESSION_H */ | 167 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 00f4eade2e3e..d5d3b22250f3 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -7,61 +7,6 @@ | |||
7 | #include "util.h" | 7 | #include "util.h" |
8 | #include "debug.h" | 8 | #include "debug.h" |
9 | 9 | ||
10 | /* Skip "." and ".." directories */ | ||
11 | static int filter(const struct dirent *dir) | ||
12 | { | ||
13 | if (dir->d_name[0] == '.') | ||
14 | return 0; | ||
15 | else | ||
16 | return 1; | ||
17 | } | ||
18 | |||
19 | struct thread_map *thread_map__new_by_pid(pid_t pid) | ||
20 | { | ||
21 | struct thread_map *threads; | ||
22 | char name[256]; | ||
23 | int items; | ||
24 | struct dirent **namelist = NULL; | ||
25 | int i; | ||
26 | |||
27 | sprintf(name, "/proc/%d/task", pid); | ||
28 | items = scandir(name, &namelist, filter, NULL); | ||
29 | if (items <= 0) | ||
30 | return NULL; | ||
31 | |||
32 | threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); | ||
33 | if (threads != NULL) { | ||
34 | for (i = 0; i < items; i++) | ||
35 | threads->map[i] = atoi(namelist[i]->d_name); | ||
36 | threads->nr = items; | ||
37 | } | ||
38 | |||
39 | for (i=0; i<items; i++) | ||
40 | free(namelist[i]); | ||
41 | free(namelist); | ||
42 | |||
43 | return threads; | ||
44 | } | ||
45 | |||
46 | struct thread_map *thread_map__new_by_tid(pid_t tid) | ||
47 | { | ||
48 | struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t)); | ||
49 | |||
50 | if (threads != NULL) { | ||
51 | threads->map[0] = tid; | ||
52 | threads->nr = 1; | ||
53 | } | ||
54 | |||
55 | return threads; | ||
56 | } | ||
57 | |||
58 | struct thread_map *thread_map__new(pid_t pid, pid_t tid) | ||
59 | { | ||
60 | if (pid != -1) | ||
61 | return thread_map__new_by_pid(pid); | ||
62 | return thread_map__new_by_tid(tid); | ||
63 | } | ||
64 | |||
65 | static struct thread *thread__new(pid_t pid) | 10 | static struct thread *thread__new(pid_t pid) |
66 | { | 11 | { |
67 | struct thread *self = zalloc(sizeof(*self)); | 12 | struct thread *self = zalloc(sizeof(*self)); |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index d7574101054a..e5f2401c1b5e 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -18,24 +18,10 @@ struct thread { | |||
18 | int comm_len; | 18 | int comm_len; |
19 | }; | 19 | }; |
20 | 20 | ||
21 | struct thread_map { | ||
22 | int nr; | ||
23 | int map[]; | ||
24 | }; | ||
25 | |||
26 | struct perf_session; | 21 | struct perf_session; |
27 | 22 | ||
28 | void thread__delete(struct thread *self); | 23 | void thread__delete(struct thread *self); |
29 | 24 | ||
30 | struct thread_map *thread_map__new_by_pid(pid_t pid); | ||
31 | struct thread_map *thread_map__new_by_tid(pid_t tid); | ||
32 | struct thread_map *thread_map__new(pid_t pid, pid_t tid); | ||
33 | |||
34 | static inline void thread_map__delete(struct thread_map *threads) | ||
35 | { | ||
36 | free(threads); | ||
37 | } | ||
38 | |||
39 | int thread__set_comm(struct thread *self, const char *comm); | 25 | int thread__set_comm(struct thread *self, const char *comm); |
40 | int thread__comm_len(struct thread *self); | 26 | int thread__comm_len(struct thread *self); |
41 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); | 27 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); |
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c new file mode 100644 index 000000000000..a5df131b77c3 --- /dev/null +++ b/tools/perf/util/thread_map.c | |||
@@ -0,0 +1,64 @@ | |||
1 | #include <dirent.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <stdio.h> | ||
4 | #include "thread_map.h" | ||
5 | |||
6 | /* Skip "." and ".." directories */ | ||
7 | static int filter(const struct dirent *dir) | ||
8 | { | ||
9 | if (dir->d_name[0] == '.') | ||
10 | return 0; | ||
11 | else | ||
12 | return 1; | ||
13 | } | ||
14 | |||
15 | struct thread_map *thread_map__new_by_pid(pid_t pid) | ||
16 | { | ||
17 | struct thread_map *threads; | ||
18 | char name[256]; | ||
19 | int items; | ||
20 | struct dirent **namelist = NULL; | ||
21 | int i; | ||
22 | |||
23 | sprintf(name, "/proc/%d/task", pid); | ||
24 | items = scandir(name, &namelist, filter, NULL); | ||
25 | if (items <= 0) | ||
26 | return NULL; | ||
27 | |||
28 | threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); | ||
29 | if (threads != NULL) { | ||
30 | for (i = 0; i < items; i++) | ||
31 | threads->map[i] = atoi(namelist[i]->d_name); | ||
32 | threads->nr = items; | ||
33 | } | ||
34 | |||
35 | for (i=0; i<items; i++) | ||
36 | free(namelist[i]); | ||
37 | free(namelist); | ||
38 | |||
39 | return threads; | ||
40 | } | ||
41 | |||
42 | struct thread_map *thread_map__new_by_tid(pid_t tid) | ||
43 | { | ||
44 | struct thread_map *threads = malloc(sizeof(*threads) + sizeof(pid_t)); | ||
45 | |||
46 | if (threads != NULL) { | ||
47 | threads->map[0] = tid; | ||
48 | threads->nr = 1; | ||
49 | } | ||
50 | |||
51 | return threads; | ||
52 | } | ||
53 | |||
54 | struct thread_map *thread_map__new(pid_t pid, pid_t tid) | ||
55 | { | ||
56 | if (pid != -1) | ||
57 | return thread_map__new_by_pid(pid); | ||
58 | return thread_map__new_by_tid(tid); | ||
59 | } | ||
60 | |||
61 | void thread_map__delete(struct thread_map *threads) | ||
62 | { | ||
63 | free(threads); | ||
64 | } | ||
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h new file mode 100644 index 000000000000..3cb907311409 --- /dev/null +++ b/tools/perf/util/thread_map.h | |||
@@ -0,0 +1,15 @@ | |||
1 | #ifndef __PERF_THREAD_MAP_H | ||
2 | #define __PERF_THREAD_MAP_H | ||
3 | |||
4 | #include <sys/types.h> | ||
5 | |||
6 | struct thread_map { | ||
7 | int nr; | ||
8 | int map[]; | ||
9 | }; | ||
10 | |||
11 | struct thread_map *thread_map__new_by_pid(pid_t pid); | ||
12 | struct thread_map *thread_map__new_by_tid(pid_t tid); | ||
13 | struct thread_map *thread_map__new(pid_t pid, pid_t tid); | ||
14 | void thread_map__delete(struct thread_map *threads); | ||
15 | #endif /* __PERF_THREAD_MAP_H */ | ||
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 60c463c16028..86428239fa65 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
@@ -377,7 +377,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, | |||
377 | while (node) { | 377 | while (node) { |
378 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | 378 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); |
379 | struct rb_node *next = rb_next(node); | 379 | struct rb_node *next = rb_next(node); |
380 | u64 cumul = cumul_hits(child); | 380 | u64 cumul = callchain_cumul_hits(child); |
381 | struct callchain_list *chain; | 381 | struct callchain_list *chain; |
382 | char folded_sign = ' '; | 382 | char folded_sign = ' '; |
383 | int first = true; | 383 | int first = true; |