diff options
Diffstat (limited to 'tools/perf/util')
67 files changed, 5564 insertions, 2143 deletions
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index 97d76562a1a0..26d4d3fd6deb 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN | |||
@@ -23,10 +23,10 @@ if test -d ../../.git -o -f ../../.git && | |||
23 | then | 23 | then |
24 | VN=$(echo "$VN" | sed -e 's/-/./g'); | 24 | VN=$(echo "$VN" | sed -e 's/-/./g'); |
25 | else | 25 | else |
26 | eval `grep '^VERSION\s*=' ../../Makefile|tr -d ' '` | 26 | eval $(grep '^VERSION[[:space:]]*=' ../../Makefile|tr -d ' ') |
27 | eval `grep '^PATCHLEVEL\s*=' ../../Makefile|tr -d ' '` | 27 | eval $(grep '^PATCHLEVEL[[:space:]]*=' ../../Makefile|tr -d ' ') |
28 | eval `grep '^SUBLEVEL\s*=' ../../Makefile|tr -d ' '` | 28 | eval $(grep '^SUBLEVEL[[:space:]]*=' ../../Makefile|tr -d ' ') |
29 | eval `grep '^EXTRAVERSION\s*=' ../../Makefile|tr -d ' '` | 29 | eval $(grep '^EXTRAVERSION[[:space:]]*=' ../../Makefile|tr -d ' ') |
30 | 30 | ||
31 | VN="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}" | 31 | VN="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}" |
32 | fi | 32 | fi |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c new file mode 100644 index 000000000000..e01af2b1a469 --- /dev/null +++ b/tools/perf/util/annotate.c | |||
@@ -0,0 +1,605 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Parts came from builtin-annotate.c, see those files for further | ||
5 | * copyright notes. | ||
6 | * | ||
7 | * Released under the GPL v2. (and only v2, not any later version) | ||
8 | */ | ||
9 | |||
10 | #include "util.h" | ||
11 | #include "build-id.h" | ||
12 | #include "color.h" | ||
13 | #include "cache.h" | ||
14 | #include "symbol.h" | ||
15 | #include "debug.h" | ||
16 | #include "annotate.h" | ||
17 | #include <pthread.h> | ||
18 | |||
19 | int symbol__annotate_init(struct map *map __used, struct symbol *sym) | ||
20 | { | ||
21 | struct annotation *notes = symbol__annotation(sym); | ||
22 | pthread_mutex_init(¬es->lock, NULL); | ||
23 | return 0; | ||
24 | } | ||
25 | |||
26 | int symbol__alloc_hist(struct symbol *sym, int nevents) | ||
27 | { | ||
28 | struct annotation *notes = symbol__annotation(sym); | ||
29 | size_t sizeof_sym_hist = (sizeof(struct sym_hist) + | ||
30 | (sym->end - sym->start) * sizeof(u64)); | ||
31 | |||
32 | notes->src = zalloc(sizeof(*notes->src) + nevents * sizeof_sym_hist); | ||
33 | if (notes->src == NULL) | ||
34 | return -1; | ||
35 | notes->src->sizeof_sym_hist = sizeof_sym_hist; | ||
36 | notes->src->nr_histograms = nevents; | ||
37 | INIT_LIST_HEAD(¬es->src->source); | ||
38 | return 0; | ||
39 | } | ||
40 | |||
41 | void symbol__annotate_zero_histograms(struct symbol *sym) | ||
42 | { | ||
43 | struct annotation *notes = symbol__annotation(sym); | ||
44 | |||
45 | pthread_mutex_lock(¬es->lock); | ||
46 | if (notes->src != NULL) | ||
47 | memset(notes->src->histograms, 0, | ||
48 | notes->src->nr_histograms * notes->src->sizeof_sym_hist); | ||
49 | pthread_mutex_unlock(¬es->lock); | ||
50 | } | ||
51 | |||
52 | int symbol__inc_addr_samples(struct symbol *sym, struct map *map, | ||
53 | int evidx, u64 addr) | ||
54 | { | ||
55 | unsigned offset; | ||
56 | struct annotation *notes; | ||
57 | struct sym_hist *h; | ||
58 | |||
59 | notes = symbol__annotation(sym); | ||
60 | if (notes->src == NULL) | ||
61 | return -ENOMEM; | ||
62 | |||
63 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); | ||
64 | |||
65 | if (addr >= sym->end) | ||
66 | return 0; | ||
67 | |||
68 | offset = addr - sym->start; | ||
69 | h = annotation__histogram(notes, evidx); | ||
70 | h->sum++; | ||
71 | h->addr[offset]++; | ||
72 | |||
73 | pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64 | ||
74 | ", evidx=%d] => %" PRIu64 "\n", sym->start, sym->name, | ||
75 | addr, addr - sym->start, evidx, h->addr[offset]); | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) | ||
80 | { | ||
81 | struct objdump_line *self = malloc(sizeof(*self) + privsize); | ||
82 | |||
83 | if (self != NULL) { | ||
84 | self->offset = offset; | ||
85 | self->line = line; | ||
86 | } | ||
87 | |||
88 | return self; | ||
89 | } | ||
90 | |||
91 | void objdump_line__free(struct objdump_line *self) | ||
92 | { | ||
93 | free(self->line); | ||
94 | free(self); | ||
95 | } | ||
96 | |||
97 | static void objdump__add_line(struct list_head *head, struct objdump_line *line) | ||
98 | { | ||
99 | list_add_tail(&line->node, head); | ||
100 | } | ||
101 | |||
102 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
103 | struct objdump_line *pos) | ||
104 | { | ||
105 | list_for_each_entry_continue(pos, head, node) | ||
106 | if (pos->offset >= 0) | ||
107 | return pos; | ||
108 | |||
109 | return NULL; | ||
110 | } | ||
111 | |||
112 | static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, | ||
113 | int evidx, u64 len, int min_pcnt, | ||
114 | int printed, int max_lines, | ||
115 | struct objdump_line *queue) | ||
116 | { | ||
117 | static const char *prev_line; | ||
118 | static const char *prev_color; | ||
119 | |||
120 | if (oline->offset != -1) { | ||
121 | const char *path = NULL; | ||
122 | unsigned int hits = 0; | ||
123 | double percent = 0.0; | ||
124 | const char *color; | ||
125 | struct annotation *notes = symbol__annotation(sym); | ||
126 | struct source_line *src_line = notes->src->lines; | ||
127 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
128 | s64 offset = oline->offset; | ||
129 | struct objdump_line *next; | ||
130 | |||
131 | next = objdump__get_next_ip_line(¬es->src->source, oline); | ||
132 | |||
133 | while (offset < (s64)len && | ||
134 | (next == NULL || offset < next->offset)) { | ||
135 | if (src_line) { | ||
136 | if (path == NULL) | ||
137 | path = src_line[offset].path; | ||
138 | percent += src_line[offset].percent; | ||
139 | } else | ||
140 | hits += h->addr[offset]; | ||
141 | |||
142 | ++offset; | ||
143 | } | ||
144 | |||
145 | if (src_line == NULL && h->sum) | ||
146 | percent = 100.0 * hits / h->sum; | ||
147 | |||
148 | if (percent < min_pcnt) | ||
149 | return -1; | ||
150 | |||
151 | if (max_lines && printed >= max_lines) | ||
152 | return 1; | ||
153 | |||
154 | if (queue != NULL) { | ||
155 | list_for_each_entry_from(queue, ¬es->src->source, node) { | ||
156 | if (queue == oline) | ||
157 | break; | ||
158 | objdump_line__print(queue, sym, evidx, len, | ||
159 | 0, 0, 1, NULL); | ||
160 | } | ||
161 | } | ||
162 | |||
163 | color = get_percent_color(percent); | ||
164 | |||
165 | /* | ||
166 | * Also color the filename and line if needed, with | ||
167 | * the same color than the percentage. Don't print it | ||
168 | * twice for close colored addr with the same filename:line | ||
169 | */ | ||
170 | if (path) { | ||
171 | if (!prev_line || strcmp(prev_line, path) | ||
172 | || color != prev_color) { | ||
173 | color_fprintf(stdout, color, " %s", path); | ||
174 | prev_line = path; | ||
175 | prev_color = color; | ||
176 | } | ||
177 | } | ||
178 | |||
179 | color_fprintf(stdout, color, " %7.2f", percent); | ||
180 | printf(" : "); | ||
181 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", oline->line); | ||
182 | } else if (max_lines && printed >= max_lines) | ||
183 | return 1; | ||
184 | else { | ||
185 | if (queue) | ||
186 | return -1; | ||
187 | |||
188 | if (!*oline->line) | ||
189 | printf(" :\n"); | ||
190 | else | ||
191 | printf(" : %s\n", oline->line); | ||
192 | } | ||
193 | |||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | ||
198 | FILE *file, size_t privsize) | ||
199 | { | ||
200 | struct annotation *notes = symbol__annotation(sym); | ||
201 | struct objdump_line *objdump_line; | ||
202 | char *line = NULL, *tmp, *tmp2, *c; | ||
203 | size_t line_len; | ||
204 | s64 line_ip, offset = -1; | ||
205 | |||
206 | if (getline(&line, &line_len, file) < 0) | ||
207 | return -1; | ||
208 | |||
209 | if (!line) | ||
210 | return -1; | ||
211 | |||
212 | while (line_len != 0 && isspace(line[line_len - 1])) | ||
213 | line[--line_len] = '\0'; | ||
214 | |||
215 | c = strchr(line, '\n'); | ||
216 | if (c) | ||
217 | *c = 0; | ||
218 | |||
219 | line_ip = -1; | ||
220 | |||
221 | /* | ||
222 | * Strip leading spaces: | ||
223 | */ | ||
224 | tmp = line; | ||
225 | while (*tmp) { | ||
226 | if (*tmp != ' ') | ||
227 | break; | ||
228 | tmp++; | ||
229 | } | ||
230 | |||
231 | if (*tmp) { | ||
232 | /* | ||
233 | * Parse hexa addresses followed by ':' | ||
234 | */ | ||
235 | line_ip = strtoull(tmp, &tmp2, 16); | ||
236 | if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') | ||
237 | line_ip = -1; | ||
238 | } | ||
239 | |||
240 | if (line_ip != -1) { | ||
241 | u64 start = map__rip_2objdump(map, sym->start), | ||
242 | end = map__rip_2objdump(map, sym->end); | ||
243 | |||
244 | offset = line_ip - start; | ||
245 | if (offset < 0 || (u64)line_ip > end) | ||
246 | offset = -1; | ||
247 | } | ||
248 | |||
249 | objdump_line = objdump_line__new(offset, line, privsize); | ||
250 | if (objdump_line == NULL) { | ||
251 | free(line); | ||
252 | return -1; | ||
253 | } | ||
254 | objdump__add_line(¬es->src->source, objdump_line); | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) | ||
260 | { | ||
261 | struct dso *dso = map->dso; | ||
262 | char *filename = dso__build_id_filename(dso, NULL, 0); | ||
263 | bool free_filename = true; | ||
264 | char command[PATH_MAX * 2]; | ||
265 | FILE *file; | ||
266 | int err = 0; | ||
267 | char symfs_filename[PATH_MAX]; | ||
268 | |||
269 | if (filename) { | ||
270 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||
271 | symbol_conf.symfs, filename); | ||
272 | } | ||
273 | |||
274 | if (filename == NULL) { | ||
275 | if (dso->has_build_id) { | ||
276 | pr_err("Can't annotate %s: not enough memory\n", | ||
277 | sym->name); | ||
278 | return -ENOMEM; | ||
279 | } | ||
280 | goto fallback; | ||
281 | } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || | ||
282 | strstr(command, "[kernel.kallsyms]") || | ||
283 | access(symfs_filename, R_OK)) { | ||
284 | free(filename); | ||
285 | fallback: | ||
286 | /* | ||
287 | * If we don't have build-ids or the build-id file isn't in the | ||
288 | * cache, or is just a kallsyms file, well, lets hope that this | ||
289 | * DSO is the same as when 'perf record' ran. | ||
290 | */ | ||
291 | filename = dso->long_name; | ||
292 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||
293 | symbol_conf.symfs, filename); | ||
294 | free_filename = false; | ||
295 | } | ||
296 | |||
297 | if (dso->symtab_type == SYMTAB__KALLSYMS) { | ||
298 | char bf[BUILD_ID_SIZE * 2 + 16] = " with build id "; | ||
299 | char *build_id_msg = NULL; | ||
300 | |||
301 | if (dso->annotate_warned) | ||
302 | goto out_free_filename; | ||
303 | |||
304 | if (dso->has_build_id) { | ||
305 | build_id__sprintf(dso->build_id, | ||
306 | sizeof(dso->build_id), bf + 15); | ||
307 | build_id_msg = bf; | ||
308 | } | ||
309 | err = -ENOENT; | ||
310 | dso->annotate_warned = 1; | ||
311 | pr_err("Can't annotate %s: No vmlinux file%s was found in the " | ||
312 | "path.\nPlease use 'perf buildid-cache -av vmlinux' or " | ||
313 | "--vmlinux vmlinux.\n", | ||
314 | sym->name, build_id_msg ?: ""); | ||
315 | goto out_free_filename; | ||
316 | } | ||
317 | |||
318 | pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, | ||
319 | filename, sym->name, map->unmap_ip(map, sym->start), | ||
320 | map->unmap_ip(map, sym->end)); | ||
321 | |||
322 | pr_debug("annotating [%p] %30s : [%p] %30s\n", | ||
323 | dso, dso->long_name, sym, sym->name); | ||
324 | |||
325 | snprintf(command, sizeof(command), | ||
326 | "objdump --start-address=0x%016" PRIx64 | ||
327 | " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand", | ||
328 | map__rip_2objdump(map, sym->start), | ||
329 | map__rip_2objdump(map, sym->end), | ||
330 | symfs_filename, filename); | ||
331 | |||
332 | pr_debug("Executing: %s\n", command); | ||
333 | |||
334 | file = popen(command, "r"); | ||
335 | if (!file) | ||
336 | goto out_free_filename; | ||
337 | |||
338 | while (!feof(file)) | ||
339 | if (symbol__parse_objdump_line(sym, map, file, privsize) < 0) | ||
340 | break; | ||
341 | |||
342 | pclose(file); | ||
343 | out_free_filename: | ||
344 | if (free_filename) | ||
345 | free(filename); | ||
346 | return err; | ||
347 | } | ||
348 | |||
349 | static void insert_source_line(struct rb_root *root, struct source_line *src_line) | ||
350 | { | ||
351 | struct source_line *iter; | ||
352 | struct rb_node **p = &root->rb_node; | ||
353 | struct rb_node *parent = NULL; | ||
354 | |||
355 | while (*p != NULL) { | ||
356 | parent = *p; | ||
357 | iter = rb_entry(parent, struct source_line, node); | ||
358 | |||
359 | if (src_line->percent > iter->percent) | ||
360 | p = &(*p)->rb_left; | ||
361 | else | ||
362 | p = &(*p)->rb_right; | ||
363 | } | ||
364 | |||
365 | rb_link_node(&src_line->node, parent, p); | ||
366 | rb_insert_color(&src_line->node, root); | ||
367 | } | ||
368 | |||
369 | static void symbol__free_source_line(struct symbol *sym, int len) | ||
370 | { | ||
371 | struct annotation *notes = symbol__annotation(sym); | ||
372 | struct source_line *src_line = notes->src->lines; | ||
373 | int i; | ||
374 | |||
375 | for (i = 0; i < len; i++) | ||
376 | free(src_line[i].path); | ||
377 | |||
378 | free(src_line); | ||
379 | notes->src->lines = NULL; | ||
380 | } | ||
381 | |||
382 | /* Get the filename:line for the colored entries */ | ||
383 | static int symbol__get_source_line(struct symbol *sym, struct map *map, | ||
384 | int evidx, struct rb_root *root, int len, | ||
385 | const char *filename) | ||
386 | { | ||
387 | u64 start; | ||
388 | int i; | ||
389 | char cmd[PATH_MAX * 2]; | ||
390 | struct source_line *src_line; | ||
391 | struct annotation *notes = symbol__annotation(sym); | ||
392 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
393 | |||
394 | if (!h->sum) | ||
395 | return 0; | ||
396 | |||
397 | src_line = notes->src->lines = calloc(len, sizeof(struct source_line)); | ||
398 | if (!notes->src->lines) | ||
399 | return -1; | ||
400 | |||
401 | start = map->unmap_ip(map, sym->start); | ||
402 | |||
403 | for (i = 0; i < len; i++) { | ||
404 | char *path = NULL; | ||
405 | size_t line_len; | ||
406 | u64 offset; | ||
407 | FILE *fp; | ||
408 | |||
409 | src_line[i].percent = 100.0 * h->addr[i] / h->sum; | ||
410 | if (src_line[i].percent <= 0.5) | ||
411 | continue; | ||
412 | |||
413 | offset = start + i; | ||
414 | sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset); | ||
415 | fp = popen(cmd, "r"); | ||
416 | if (!fp) | ||
417 | continue; | ||
418 | |||
419 | if (getline(&path, &line_len, fp) < 0 || !line_len) | ||
420 | goto next; | ||
421 | |||
422 | src_line[i].path = malloc(sizeof(char) * line_len + 1); | ||
423 | if (!src_line[i].path) | ||
424 | goto next; | ||
425 | |||
426 | strcpy(src_line[i].path, path); | ||
427 | insert_source_line(root, &src_line[i]); | ||
428 | |||
429 | next: | ||
430 | pclose(fp); | ||
431 | } | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | static void print_summary(struct rb_root *root, const char *filename) | ||
437 | { | ||
438 | struct source_line *src_line; | ||
439 | struct rb_node *node; | ||
440 | |||
441 | printf("\nSorted summary for file %s\n", filename); | ||
442 | printf("----------------------------------------------\n\n"); | ||
443 | |||
444 | if (RB_EMPTY_ROOT(root)) { | ||
445 | printf(" Nothing higher than %1.1f%%\n", MIN_GREEN); | ||
446 | return; | ||
447 | } | ||
448 | |||
449 | node = rb_first(root); | ||
450 | while (node) { | ||
451 | double percent; | ||
452 | const char *color; | ||
453 | char *path; | ||
454 | |||
455 | src_line = rb_entry(node, struct source_line, node); | ||
456 | percent = src_line->percent; | ||
457 | color = get_percent_color(percent); | ||
458 | path = src_line->path; | ||
459 | |||
460 | color_fprintf(stdout, color, " %7.2f %s", percent, path); | ||
461 | node = rb_next(node); | ||
462 | } | ||
463 | } | ||
464 | |||
465 | static void symbol__annotate_hits(struct symbol *sym, int evidx) | ||
466 | { | ||
467 | struct annotation *notes = symbol__annotation(sym); | ||
468 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
469 | u64 len = sym->end - sym->start, offset; | ||
470 | |||
471 | for (offset = 0; offset < len; ++offset) | ||
472 | if (h->addr[offset] != 0) | ||
473 | printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2, | ||
474 | sym->start + offset, h->addr[offset]); | ||
475 | printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); | ||
476 | } | ||
477 | |||
478 | int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | ||
479 | bool full_paths, int min_pcnt, int max_lines, | ||
480 | int context) | ||
481 | { | ||
482 | struct dso *dso = map->dso; | ||
483 | const char *filename = dso->long_name, *d_filename; | ||
484 | struct annotation *notes = symbol__annotation(sym); | ||
485 | struct objdump_line *pos, *queue = NULL; | ||
486 | int printed = 2, queue_len = 0; | ||
487 | int more = 0; | ||
488 | u64 len; | ||
489 | |||
490 | if (full_paths) | ||
491 | d_filename = filename; | ||
492 | else | ||
493 | d_filename = basename(filename); | ||
494 | |||
495 | len = sym->end - sym->start; | ||
496 | |||
497 | printf(" Percent | Source code & Disassembly of %s\n", d_filename); | ||
498 | printf("------------------------------------------------\n"); | ||
499 | |||
500 | if (verbose) | ||
501 | symbol__annotate_hits(sym, evidx); | ||
502 | |||
503 | list_for_each_entry(pos, ¬es->src->source, node) { | ||
504 | if (context && queue == NULL) { | ||
505 | queue = pos; | ||
506 | queue_len = 0; | ||
507 | } | ||
508 | |||
509 | switch (objdump_line__print(pos, sym, evidx, len, min_pcnt, | ||
510 | printed, max_lines, queue)) { | ||
511 | case 0: | ||
512 | ++printed; | ||
513 | if (context) { | ||
514 | printed += queue_len; | ||
515 | queue = NULL; | ||
516 | queue_len = 0; | ||
517 | } | ||
518 | break; | ||
519 | case 1: | ||
520 | /* filtered by max_lines */ | ||
521 | ++more; | ||
522 | break; | ||
523 | case -1: | ||
524 | default: | ||
525 | /* | ||
526 | * Filtered by min_pcnt or non IP lines when | ||
527 | * context != 0 | ||
528 | */ | ||
529 | if (!context) | ||
530 | break; | ||
531 | if (queue_len == context) | ||
532 | queue = list_entry(queue->node.next, typeof(*queue), node); | ||
533 | else | ||
534 | ++queue_len; | ||
535 | break; | ||
536 | } | ||
537 | } | ||
538 | |||
539 | return more; | ||
540 | } | ||
541 | |||
542 | void symbol__annotate_zero_histogram(struct symbol *sym, int evidx) | ||
543 | { | ||
544 | struct annotation *notes = symbol__annotation(sym); | ||
545 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
546 | |||
547 | memset(h, 0, notes->src->sizeof_sym_hist); | ||
548 | } | ||
549 | |||
550 | void symbol__annotate_decay_histogram(struct symbol *sym, int evidx) | ||
551 | { | ||
552 | struct annotation *notes = symbol__annotation(sym); | ||
553 | struct sym_hist *h = annotation__histogram(notes, evidx); | ||
554 | struct objdump_line *pos; | ||
555 | int len = sym->end - sym->start; | ||
556 | |||
557 | h->sum = 0; | ||
558 | |||
559 | list_for_each_entry(pos, ¬es->src->source, node) { | ||
560 | if (pos->offset != -1 && pos->offset < len) { | ||
561 | h->addr[pos->offset] = h->addr[pos->offset] * 7 / 8; | ||
562 | h->sum += h->addr[pos->offset]; | ||
563 | } | ||
564 | } | ||
565 | } | ||
566 | |||
567 | void objdump_line_list__purge(struct list_head *head) | ||
568 | { | ||
569 | struct objdump_line *pos, *n; | ||
570 | |||
571 | list_for_each_entry_safe(pos, n, head, node) { | ||
572 | list_del(&pos->node); | ||
573 | objdump_line__free(pos); | ||
574 | } | ||
575 | } | ||
576 | |||
577 | int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | ||
578 | bool print_lines, bool full_paths, int min_pcnt, | ||
579 | int max_lines) | ||
580 | { | ||
581 | struct dso *dso = map->dso; | ||
582 | const char *filename = dso->long_name; | ||
583 | struct rb_root source_line = RB_ROOT; | ||
584 | u64 len; | ||
585 | |||
586 | if (symbol__annotate(sym, map, 0) < 0) | ||
587 | return -1; | ||
588 | |||
589 | len = sym->end - sym->start; | ||
590 | |||
591 | if (print_lines) { | ||
592 | symbol__get_source_line(sym, map, evidx, &source_line, | ||
593 | len, filename); | ||
594 | print_summary(&source_line, filename); | ||
595 | } | ||
596 | |||
597 | symbol__annotate_printf(sym, map, evidx, full_paths, | ||
598 | min_pcnt, max_lines, 0); | ||
599 | if (print_lines) | ||
600 | symbol__free_source_line(sym, len); | ||
601 | |||
602 | objdump_line_list__purge(&symbol__annotation(sym)->src->source); | ||
603 | |||
604 | return 0; | ||
605 | } | ||
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h new file mode 100644 index 000000000000..c2c286896801 --- /dev/null +++ b/tools/perf/util/annotate.h | |||
@@ -0,0 +1,103 @@ | |||
1 | #ifndef __PERF_ANNOTATE_H | ||
2 | #define __PERF_ANNOTATE_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include "types.h" | ||
6 | #include "symbol.h" | ||
7 | #include <linux/list.h> | ||
8 | #include <linux/rbtree.h> | ||
9 | |||
10 | struct objdump_line { | ||
11 | struct list_head node; | ||
12 | s64 offset; | ||
13 | char *line; | ||
14 | }; | ||
15 | |||
16 | void objdump_line__free(struct objdump_line *self); | ||
17 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
18 | struct objdump_line *pos); | ||
19 | |||
20 | struct sym_hist { | ||
21 | u64 sum; | ||
22 | u64 addr[0]; | ||
23 | }; | ||
24 | |||
25 | struct source_line { | ||
26 | struct rb_node node; | ||
27 | double percent; | ||
28 | char *path; | ||
29 | }; | ||
30 | |||
31 | /** struct annotated_source - symbols with hits have this attached as in sannotation | ||
32 | * | ||
33 | * @histogram: Array of addr hit histograms per event being monitored | ||
34 | * @lines: If 'print_lines' is specified, per source code line percentages | ||
35 | * @source: source parsed from objdump -dS | ||
36 | * | ||
37 | * lines is allocated, percentages calculated and all sorted by percentage | ||
38 | * when the annotation is about to be presented, so the percentages are for | ||
39 | * one of the entries in the histogram array, i.e. for the event/counter being | ||
40 | * presented. It is deallocated right after symbol__{tui,tty,etc}_annotate | ||
41 | * returns. | ||
42 | */ | ||
43 | struct annotated_source { | ||
44 | struct list_head source; | ||
45 | struct source_line *lines; | ||
46 | int nr_histograms; | ||
47 | int sizeof_sym_hist; | ||
48 | struct sym_hist histograms[0]; | ||
49 | }; | ||
50 | |||
51 | struct annotation { | ||
52 | pthread_mutex_t lock; | ||
53 | struct annotated_source *src; | ||
54 | }; | ||
55 | |||
56 | struct sannotation { | ||
57 | struct annotation annotation; | ||
58 | struct symbol symbol; | ||
59 | }; | ||
60 | |||
61 | static inline struct sym_hist *annotation__histogram(struct annotation *notes, int idx) | ||
62 | { | ||
63 | return (((void *)¬es->src->histograms) + | ||
64 | (notes->src->sizeof_sym_hist * idx)); | ||
65 | } | ||
66 | |||
67 | static inline struct annotation *symbol__annotation(struct symbol *sym) | ||
68 | { | ||
69 | struct sannotation *a = container_of(sym, struct sannotation, symbol); | ||
70 | return &a->annotation; | ||
71 | } | ||
72 | |||
73 | int symbol__inc_addr_samples(struct symbol *sym, struct map *map, | ||
74 | int evidx, u64 addr); | ||
75 | int symbol__alloc_hist(struct symbol *sym, int nevents); | ||
76 | void symbol__annotate_zero_histograms(struct symbol *sym); | ||
77 | |||
78 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); | ||
79 | int symbol__annotate_init(struct map *map __used, struct symbol *sym); | ||
80 | int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, | ||
81 | bool full_paths, int min_pcnt, int max_lines, | ||
82 | int context); | ||
83 | void symbol__annotate_zero_histogram(struct symbol *sym, int evidx); | ||
84 | void symbol__annotate_decay_histogram(struct symbol *sym, int evidx); | ||
85 | void objdump_line_list__purge(struct list_head *head); | ||
86 | |||
87 | int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | ||
88 | bool print_lines, bool full_paths, int min_pcnt, | ||
89 | int max_lines); | ||
90 | |||
91 | #ifdef NO_NEWT_SUPPORT | ||
92 | static inline int symbol__tui_annotate(struct symbol *sym __used, | ||
93 | struct map *map __used, | ||
94 | int evidx __used, int refresh __used) | ||
95 | { | ||
96 | return 0; | ||
97 | } | ||
98 | #else | ||
99 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | ||
100 | int refresh); | ||
101 | #endif | ||
102 | |||
103 | #endif /* __PERF_ANNOTATE_H */ | ||
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index deffb8c96071..a91cd99f26ea 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -14,8 +14,9 @@ | |||
14 | #include <linux/kernel.h> | 14 | #include <linux/kernel.h> |
15 | #include "debug.h" | 15 | #include "debug.h" |
16 | 16 | ||
17 | static int build_id__mark_dso_hit(event_t *event, | 17 | static int build_id__mark_dso_hit(union perf_event *event, |
18 | struct sample_data *sample __used, | 18 | struct perf_sample *sample __used, |
19 | struct perf_evsel *evsel __used, | ||
19 | struct perf_session *session) | 20 | struct perf_session *session) |
20 | { | 21 | { |
21 | struct addr_location al; | 22 | struct addr_location al; |
@@ -37,13 +38,14 @@ static int build_id__mark_dso_hit(event_t *event, | |||
37 | return 0; | 38 | return 0; |
38 | } | 39 | } |
39 | 40 | ||
40 | static int event__exit_del_thread(event_t *self, struct sample_data *sample __used, | 41 | static int perf_event__exit_del_thread(union perf_event *event, |
41 | struct perf_session *session) | 42 | struct perf_sample *sample __used, |
43 | struct perf_session *session) | ||
42 | { | 44 | { |
43 | struct thread *thread = perf_session__findnew(session, self->fork.tid); | 45 | struct thread *thread = perf_session__findnew(session, event->fork.tid); |
44 | 46 | ||
45 | dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, | 47 | dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, |
46 | self->fork.ppid, self->fork.ptid); | 48 | event->fork.ppid, event->fork.ptid); |
47 | 49 | ||
48 | if (thread) { | 50 | if (thread) { |
49 | rb_erase(&thread->rb_node, &session->threads); | 51 | rb_erase(&thread->rb_node, &session->threads); |
@@ -56,9 +58,9 @@ static int event__exit_del_thread(event_t *self, struct sample_data *sample __us | |||
56 | 58 | ||
57 | struct perf_event_ops build_id__mark_dso_hit_ops = { | 59 | struct perf_event_ops build_id__mark_dso_hit_ops = { |
58 | .sample = build_id__mark_dso_hit, | 60 | .sample = build_id__mark_dso_hit, |
59 | .mmap = event__process_mmap, | 61 | .mmap = perf_event__process_mmap, |
60 | .fork = event__process_task, | 62 | .fork = perf_event__process_task, |
61 | .exit = event__exit_del_thread, | 63 | .exit = perf_event__exit_del_thread, |
62 | }; | 64 | }; |
63 | 65 | ||
64 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size) | 66 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size) |
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index a7729797fd96..fc5e5a09d5b9 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -34,13 +34,14 @@ extern int pager_use_color; | |||
34 | extern int use_browser; | 34 | extern int use_browser; |
35 | 35 | ||
36 | #ifdef NO_NEWT_SUPPORT | 36 | #ifdef NO_NEWT_SUPPORT |
37 | static inline void setup_browser(void) | 37 | static inline void setup_browser(bool fallback_to_pager) |
38 | { | 38 | { |
39 | setup_pager(); | 39 | if (fallback_to_pager) |
40 | setup_pager(); | ||
40 | } | 41 | } |
41 | static inline void exit_browser(bool wait_for_ok __used) {} | 42 | static inline void exit_browser(bool wait_for_ok __used) {} |
42 | #else | 43 | #else |
43 | void setup_browser(void); | 44 | void setup_browser(bool fallback_to_pager); |
44 | void exit_browser(bool wait_for_ok); | 45 | void exit_browser(bool wait_for_ok); |
45 | #endif | 46 | #endif |
46 | 47 | ||
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index e12d539417b2..9f7106a8d9a4 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. |
@@ -18,7 +18,8 @@ | |||
18 | #include "util.h" | 18 | #include "util.h" |
19 | #include "callchain.h" | 19 | #include "callchain.h" |
20 | 20 | ||
21 | bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event) | 21 | bool ip_callchain__valid(struct ip_callchain *chain, |
22 | const union perf_event *event) | ||
22 | { | 23 | { |
23 | unsigned int chain_size = event->header.size; | 24 | unsigned int chain_size = event->header.size; |
24 | chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; | 25 | chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; |
@@ -26,10 +27,10 @@ bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event) | |||
26 | } | 27 | } |
27 | 28 | ||
28 | #define chain_for_each_child(child, parent) \ | 29 | #define chain_for_each_child(child, parent) \ |
29 | list_for_each_entry(child, &parent->children, brothers) | 30 | list_for_each_entry(child, &parent->children, siblings) |
30 | 31 | ||
31 | #define chain_for_each_child_safe(child, next, parent) \ | 32 | #define chain_for_each_child_safe(child, next, parent) \ |
32 | list_for_each_entry_safe(child, next, &parent->children, brothers) | 33 | list_for_each_entry_safe(child, next, &parent->children, siblings) |
33 | 34 | ||
34 | static void | 35 | static void |
35 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | 36 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, |
@@ -38,14 +39,14 @@ rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | |||
38 | struct rb_node **p = &root->rb_node; | 39 | struct rb_node **p = &root->rb_node; |
39 | struct rb_node *parent = NULL; | 40 | struct rb_node *parent = NULL; |
40 | struct callchain_node *rnode; | 41 | struct callchain_node *rnode; |
41 | u64 chain_cumul = cumul_hits(chain); | 42 | u64 chain_cumul = callchain_cumul_hits(chain); |
42 | 43 | ||
43 | while (*p) { | 44 | while (*p) { |
44 | u64 rnode_cumul; | 45 | u64 rnode_cumul; |
45 | 46 | ||
46 | parent = *p; | 47 | parent = *p; |
47 | rnode = rb_entry(parent, struct callchain_node, rb_node); | 48 | rnode = rb_entry(parent, struct callchain_node, rb_node); |
48 | rnode_cumul = cumul_hits(rnode); | 49 | rnode_cumul = callchain_cumul_hits(rnode); |
49 | 50 | ||
50 | switch (mode) { | 51 | switch (mode) { |
51 | case CHAIN_FLAT: | 52 | case CHAIN_FLAT: |
@@ -104,7 +105,7 @@ static void __sort_chain_graph_abs(struct callchain_node *node, | |||
104 | 105 | ||
105 | chain_for_each_child(child, node) { | 106 | chain_for_each_child(child, node) { |
106 | __sort_chain_graph_abs(child, min_hit); | 107 | __sort_chain_graph_abs(child, min_hit); |
107 | if (cumul_hits(child) >= min_hit) | 108 | if (callchain_cumul_hits(child) >= min_hit) |
108 | rb_insert_callchain(&node->rb_root, child, | 109 | rb_insert_callchain(&node->rb_root, child, |
109 | CHAIN_GRAPH_ABS); | 110 | CHAIN_GRAPH_ABS); |
110 | } | 111 | } |
@@ -129,7 +130,7 @@ static void __sort_chain_graph_rel(struct callchain_node *node, | |||
129 | 130 | ||
130 | chain_for_each_child(child, node) { | 131 | chain_for_each_child(child, node) { |
131 | __sort_chain_graph_rel(child, min_percent); | 132 | __sort_chain_graph_rel(child, min_percent); |
132 | if (cumul_hits(child) >= min_hit) | 133 | if (callchain_cumul_hits(child) >= min_hit) |
133 | rb_insert_callchain(&node->rb_root, child, | 134 | rb_insert_callchain(&node->rb_root, child, |
134 | CHAIN_GRAPH_REL); | 135 | CHAIN_GRAPH_REL); |
135 | } | 136 | } |
@@ -143,7 +144,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; | 144 | rb_root->rb_node = chain_root->node.rb_root.rb_node; |
144 | } | 145 | } |
145 | 146 | ||
146 | int register_callchain_param(struct callchain_param *param) | 147 | int callchain_register_param(struct callchain_param *param) |
147 | { | 148 | { |
148 | switch (param->mode) { | 149 | switch (param->mode) { |
149 | case CHAIN_GRAPH_ABS: | 150 | case CHAIN_GRAPH_ABS: |
@@ -189,32 +190,27 @@ create_child(struct callchain_node *parent, bool inherit_children) | |||
189 | chain_for_each_child(next, new) | 190 | chain_for_each_child(next, new) |
190 | next->parent = new; | 191 | next->parent = new; |
191 | } | 192 | } |
192 | list_add_tail(&new->brothers, &parent->children); | 193 | list_add_tail(&new->siblings, &parent->children); |
193 | 194 | ||
194 | return new; | 195 | return new; |
195 | } | 196 | } |
196 | 197 | ||
197 | 198 | ||
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 | /* | 199 | /* |
210 | * Fill the node with callchain values | 200 | * Fill the node with callchain values |
211 | */ | 201 | */ |
212 | static void | 202 | static void |
213 | fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) | 203 | fill_node(struct callchain_node *node, struct callchain_cursor *cursor) |
214 | { | 204 | { |
215 | unsigned int i; | 205 | struct callchain_cursor_node *cursor_node; |
206 | |||
207 | node->val_nr = cursor->nr - cursor->pos; | ||
208 | if (!node->val_nr) | ||
209 | pr_warning("Warning: empty node in callchain tree\n"); | ||
216 | 210 | ||
217 | for (i = start; i < chain->nr; i++) { | 211 | cursor_node = callchain_cursor_current(cursor); |
212 | |||
213 | while (cursor_node) { | ||
218 | struct callchain_list *call; | 214 | struct callchain_list *call; |
219 | 215 | ||
220 | call = zalloc(sizeof(*call)); | 216 | call = zalloc(sizeof(*call)); |
@@ -222,23 +218,25 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) | |||
222 | perror("not enough memory for the code path tree"); | 218 | perror("not enough memory for the code path tree"); |
223 | return; | 219 | return; |
224 | } | 220 | } |
225 | call->ip = chain->ips[i].ip; | 221 | call->ip = cursor_node->ip; |
226 | call->ms = chain->ips[i].ms; | 222 | call->ms.sym = cursor_node->sym; |
223 | call->ms.map = cursor_node->map; | ||
227 | list_add_tail(&call->list, &node->val); | 224 | list_add_tail(&call->list, &node->val); |
225 | |||
226 | callchain_cursor_advance(cursor); | ||
227 | cursor_node = callchain_cursor_current(cursor); | ||
228 | } | 228 | } |
229 | node->val_nr = chain->nr - start; | ||
230 | if (!node->val_nr) | ||
231 | pr_warning("Warning: empty node in callchain tree\n"); | ||
232 | } | 229 | } |
233 | 230 | ||
234 | static void | 231 | static void |
235 | add_child(struct callchain_node *parent, struct resolved_chain *chain, | 232 | add_child(struct callchain_node *parent, |
236 | int start, u64 period) | 233 | struct callchain_cursor *cursor, |
234 | u64 period) | ||
237 | { | 235 | { |
238 | struct callchain_node *new; | 236 | struct callchain_node *new; |
239 | 237 | ||
240 | new = create_child(parent, false); | 238 | new = create_child(parent, false); |
241 | fill_node(new, chain, start); | 239 | fill_node(new, cursor); |
242 | 240 | ||
243 | new->children_hit = 0; | 241 | new->children_hit = 0; |
244 | new->hit = period; | 242 | new->hit = period; |
@@ -250,9 +248,10 @@ add_child(struct callchain_node *parent, struct resolved_chain *chain, | |||
250 | * Then create another child to host the given callchain of new branch | 248 | * Then create another child to host the given callchain of new branch |
251 | */ | 249 | */ |
252 | static void | 250 | static void |
253 | split_add_child(struct callchain_node *parent, struct resolved_chain *chain, | 251 | split_add_child(struct callchain_node *parent, |
254 | struct callchain_list *to_split, int idx_parents, int idx_local, | 252 | struct callchain_cursor *cursor, |
255 | u64 period) | 253 | struct callchain_list *to_split, |
254 | u64 idx_parents, u64 idx_local, u64 period) | ||
256 | { | 255 | { |
257 | struct callchain_node *new; | 256 | struct callchain_node *new; |
258 | struct list_head *old_tail; | 257 | struct list_head *old_tail; |
@@ -272,14 +271,14 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain, | |||
272 | /* split the hits */ | 271 | /* split the hits */ |
273 | new->hit = parent->hit; | 272 | new->hit = parent->hit; |
274 | new->children_hit = parent->children_hit; | 273 | new->children_hit = parent->children_hit; |
275 | parent->children_hit = cumul_hits(new); | 274 | parent->children_hit = callchain_cumul_hits(new); |
276 | new->val_nr = parent->val_nr - idx_local; | 275 | new->val_nr = parent->val_nr - idx_local; |
277 | parent->val_nr = idx_local; | 276 | parent->val_nr = idx_local; |
278 | 277 | ||
279 | /* create a new child for the new branch if any */ | 278 | /* create a new child for the new branch if any */ |
280 | if (idx_total < chain->nr) { | 279 | if (idx_total < cursor->nr) { |
281 | parent->hit = 0; | 280 | parent->hit = 0; |
282 | add_child(parent, chain, idx_total, period); | 281 | add_child(parent, cursor, period); |
283 | parent->children_hit += period; | 282 | parent->children_hit += period; |
284 | } else { | 283 | } else { |
285 | parent->hit = period; | 284 | parent->hit = period; |
@@ -287,36 +286,41 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain, | |||
287 | } | 286 | } |
288 | 287 | ||
289 | static int | 288 | static int |
290 | append_chain(struct callchain_node *root, struct resolved_chain *chain, | 289 | append_chain(struct callchain_node *root, |
291 | unsigned int start, u64 period); | 290 | struct callchain_cursor *cursor, |
291 | u64 period); | ||
292 | 292 | ||
293 | static void | 293 | static void |
294 | append_chain_children(struct callchain_node *root, struct resolved_chain *chain, | 294 | append_chain_children(struct callchain_node *root, |
295 | unsigned int start, u64 period) | 295 | struct callchain_cursor *cursor, |
296 | u64 period) | ||
296 | { | 297 | { |
297 | struct callchain_node *rnode; | 298 | struct callchain_node *rnode; |
298 | 299 | ||
299 | /* lookup in childrens */ | 300 | /* lookup in childrens */ |
300 | chain_for_each_child(rnode, root) { | 301 | chain_for_each_child(rnode, root) { |
301 | unsigned int ret = append_chain(rnode, chain, start, period); | 302 | unsigned int ret = append_chain(rnode, cursor, period); |
302 | 303 | ||
303 | if (!ret) | 304 | if (!ret) |
304 | goto inc_children_hit; | 305 | goto inc_children_hit; |
305 | } | 306 | } |
306 | /* nothing in children, add to the current node */ | 307 | /* nothing in children, add to the current node */ |
307 | add_child(root, chain, start, period); | 308 | add_child(root, cursor, period); |
308 | 309 | ||
309 | inc_children_hit: | 310 | inc_children_hit: |
310 | root->children_hit += period; | 311 | root->children_hit += period; |
311 | } | 312 | } |
312 | 313 | ||
313 | static int | 314 | static int |
314 | append_chain(struct callchain_node *root, struct resolved_chain *chain, | 315 | append_chain(struct callchain_node *root, |
315 | unsigned int start, u64 period) | 316 | struct callchain_cursor *cursor, |
317 | u64 period) | ||
316 | { | 318 | { |
319 | struct callchain_cursor_node *curr_snap = cursor->curr; | ||
317 | struct callchain_list *cnode; | 320 | struct callchain_list *cnode; |
318 | unsigned int i = start; | 321 | u64 start = cursor->pos; |
319 | bool found = false; | 322 | bool found = false; |
323 | u64 matches; | ||
320 | 324 | ||
321 | /* | 325 | /* |
322 | * Lookup in the current node | 326 | * Lookup in the current node |
@@ -324,141 +328,134 @@ append_chain(struct callchain_node *root, struct resolved_chain *chain, | |||
324 | * anywhere inside a function. | 328 | * anywhere inside a function. |
325 | */ | 329 | */ |
326 | list_for_each_entry(cnode, &root->val, list) { | 330 | list_for_each_entry(cnode, &root->val, list) { |
331 | struct callchain_cursor_node *node; | ||
327 | struct symbol *sym; | 332 | struct symbol *sym; |
328 | 333 | ||
329 | if (i == chain->nr) | 334 | node = callchain_cursor_current(cursor); |
335 | if (!node) | ||
330 | break; | 336 | break; |
331 | 337 | ||
332 | sym = chain->ips[i].ms.sym; | 338 | sym = node->sym; |
333 | 339 | ||
334 | if (cnode->ms.sym && sym) { | 340 | if (cnode->ms.sym && sym) { |
335 | if (cnode->ms.sym->start != sym->start) | 341 | if (cnode->ms.sym->start != sym->start) |
336 | break; | 342 | break; |
337 | } else if (cnode->ip != chain->ips[i].ip) | 343 | } else if (cnode->ip != node->ip) |
338 | break; | 344 | break; |
339 | 345 | ||
340 | if (!found) | 346 | if (!found) |
341 | found = true; | 347 | found = true; |
342 | i++; | 348 | |
349 | callchain_cursor_advance(cursor); | ||
343 | } | 350 | } |
344 | 351 | ||
345 | /* matches not, relay on the parent */ | 352 | /* matches not, relay on the parent */ |
346 | if (!found) | 353 | if (!found) { |
354 | cursor->curr = curr_snap; | ||
355 | cursor->pos = start; | ||
347 | return -1; | 356 | return -1; |
357 | } | ||
358 | |||
359 | matches = cursor->pos - start; | ||
348 | 360 | ||
349 | /* we match only a part of the node. Split it and add the new chain */ | 361 | /* we match only a part of the node. Split it and add the new chain */ |
350 | if (i - start < root->val_nr) { | 362 | if (matches < root->val_nr) { |
351 | split_add_child(root, chain, cnode, start, i - start, period); | 363 | split_add_child(root, cursor, cnode, start, matches, period); |
352 | return 0; | 364 | return 0; |
353 | } | 365 | } |
354 | 366 | ||
355 | /* we match 100% of the path, increment the hit */ | 367 | /* we match 100% of the path, increment the hit */ |
356 | if (i - start == root->val_nr && i == chain->nr) { | 368 | if (matches == root->val_nr && cursor->pos == cursor->nr) { |
357 | root->hit += period; | 369 | root->hit += period; |
358 | return 0; | 370 | return 0; |
359 | } | 371 | } |
360 | 372 | ||
361 | /* We match the node and still have a part remaining */ | 373 | /* We match the node and still have a part remaining */ |
362 | append_chain_children(root, chain, i, period); | 374 | append_chain_children(root, cursor, period); |
363 | 375 | ||
364 | return 0; | 376 | return 0; |
365 | } | 377 | } |
366 | 378 | ||
367 | static void filter_context(struct ip_callchain *old, struct resolved_chain *new, | 379 | int callchain_append(struct callchain_root *root, |
368 | struct map_symbol *syms) | 380 | struct callchain_cursor *cursor, |
369 | { | 381 | 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 | { | 382 | { |
388 | struct resolved_chain *filtered; | 383 | if (!cursor->nr) |
389 | |||
390 | if (!chain->nr) | ||
391 | return 0; | 384 | return 0; |
392 | 385 | ||
393 | filtered = zalloc(sizeof(*filtered) + | 386 | 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 | 387 | ||
403 | append_chain_children(&root->node, filtered, 0, period); | 388 | append_chain_children(&root->node, cursor, period); |
404 | 389 | ||
405 | if (filtered->nr > root->max_depth) | 390 | if (cursor->nr > root->max_depth) |
406 | root->max_depth = filtered->nr; | 391 | root->max_depth = cursor->nr; |
407 | end: | ||
408 | free(filtered); | ||
409 | 392 | ||
410 | return 0; | 393 | return 0; |
411 | } | 394 | } |
412 | 395 | ||
413 | static int | 396 | static int |
414 | merge_chain_branch(struct callchain_node *dst, struct callchain_node *src, | 397 | merge_chain_branch(struct callchain_cursor *cursor, |
415 | struct resolved_chain *chain) | 398 | struct callchain_node *dst, struct callchain_node *src) |
416 | { | 399 | { |
400 | struct callchain_cursor_node **old_last = cursor->last; | ||
417 | struct callchain_node *child, *next_child; | 401 | struct callchain_node *child, *next_child; |
418 | struct callchain_list *list, *next_list; | 402 | struct callchain_list *list, *next_list; |
419 | int old_pos = chain->nr; | 403 | int old_pos = cursor->nr; |
420 | int err = 0; | 404 | int err = 0; |
421 | 405 | ||
422 | list_for_each_entry_safe(list, next_list, &src->val, list) { | 406 | list_for_each_entry_safe(list, next_list, &src->val, list) { |
423 | chain->ips[chain->nr].ip = list->ip; | 407 | callchain_cursor_append(cursor, list->ip, |
424 | chain->ips[chain->nr].ms = list->ms; | 408 | list->ms.map, list->ms.sym); |
425 | chain->nr++; | ||
426 | list_del(&list->list); | 409 | list_del(&list->list); |
427 | free(list); | 410 | free(list); |
428 | } | 411 | } |
429 | 412 | ||
430 | if (src->hit) | 413 | if (src->hit) { |
431 | append_chain_children(dst, chain, 0, src->hit); | 414 | callchain_cursor_commit(cursor); |
415 | append_chain_children(dst, cursor, src->hit); | ||
416 | } | ||
432 | 417 | ||
433 | chain_for_each_child_safe(child, next_child, src) { | 418 | chain_for_each_child_safe(child, next_child, src) { |
434 | err = merge_chain_branch(dst, child, chain); | 419 | err = merge_chain_branch(cursor, dst, child); |
435 | if (err) | 420 | if (err) |
436 | break; | 421 | break; |
437 | 422 | ||
438 | list_del(&child->brothers); | 423 | list_del(&child->siblings); |
439 | free(child); | 424 | free(child); |
440 | } | 425 | } |
441 | 426 | ||
442 | chain->nr = old_pos; | 427 | cursor->nr = old_pos; |
428 | cursor->last = old_last; | ||
443 | 429 | ||
444 | return err; | 430 | return err; |
445 | } | 431 | } |
446 | 432 | ||
447 | int callchain_merge(struct callchain_root *dst, struct callchain_root *src) | 433 | int callchain_merge(struct callchain_cursor *cursor, |
434 | struct callchain_root *dst, struct callchain_root *src) | ||
435 | { | ||
436 | return merge_chain_branch(cursor, &dst->node, &src->node); | ||
437 | } | ||
438 | |||
439 | int callchain_cursor_append(struct callchain_cursor *cursor, | ||
440 | u64 ip, struct map *map, struct symbol *sym) | ||
448 | { | 441 | { |
449 | struct resolved_chain *chain; | 442 | struct callchain_cursor_node *node = *cursor->last; |
450 | int err; | ||
451 | 443 | ||
452 | chain = malloc(sizeof(*chain) + | 444 | if (!node) { |
453 | src->max_depth * sizeof(struct resolved_ip)); | 445 | node = calloc(sizeof(*node), 1); |
454 | if (!chain) | 446 | if (!node) |
455 | return -ENOMEM; | 447 | return -ENOMEM; |
456 | 448 | ||
457 | chain->nr = 0; | 449 | *cursor->last = node; |
450 | } | ||
458 | 451 | ||
459 | err = merge_chain_branch(&dst->node, &src->node, chain); | 452 | node->ip = ip; |
453 | node->map = map; | ||
454 | node->sym = sym; | ||
460 | 455 | ||
461 | free(chain); | 456 | cursor->nr++; |
462 | 457 | ||
463 | return err; | 458 | cursor->last = &node->next; |
459 | |||
460 | return 0; | ||
464 | } | 461 | } |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index c15fb8c24ad2..1a79df9f739f 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); | ||
97 | |||
98 | bool ip_callchain__valid(struct ip_callchain *chain, | ||
99 | const union perf_event *event); | ||
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); | ||
73 | 112 | ||
74 | bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); | 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/cgroup.c b/tools/perf/util/cgroup.c new file mode 100644 index 000000000000..96bee5c46008 --- /dev/null +++ b/tools/perf/util/cgroup.c | |||
@@ -0,0 +1,178 @@ | |||
1 | #include "util.h" | ||
2 | #include "../perf.h" | ||
3 | #include "parse-options.h" | ||
4 | #include "evsel.h" | ||
5 | #include "cgroup.h" | ||
6 | #include "debugfs.h" /* MAX_PATH, STR() */ | ||
7 | #include "evlist.h" | ||
8 | |||
9 | int nr_cgroups; | ||
10 | |||
11 | static int | ||
12 | cgroupfs_find_mountpoint(char *buf, size_t maxlen) | ||
13 | { | ||
14 | FILE *fp; | ||
15 | char mountpoint[MAX_PATH+1], tokens[MAX_PATH+1], type[MAX_PATH+1]; | ||
16 | char *token, *saved_ptr = NULL; | ||
17 | int found = 0; | ||
18 | |||
19 | fp = fopen("/proc/mounts", "r"); | ||
20 | if (!fp) | ||
21 | return -1; | ||
22 | |||
23 | /* | ||
24 | * in order to handle split hierarchy, we need to scan /proc/mounts | ||
25 | * and inspect every cgroupfs mount point to find one that has | ||
26 | * perf_event subsystem | ||
27 | */ | ||
28 | while (fscanf(fp, "%*s %"STR(MAX_PATH)"s %"STR(MAX_PATH)"s %" | ||
29 | STR(MAX_PATH)"s %*d %*d\n", | ||
30 | mountpoint, type, tokens) == 3) { | ||
31 | |||
32 | if (!strcmp(type, "cgroup")) { | ||
33 | |||
34 | token = strtok_r(tokens, ",", &saved_ptr); | ||
35 | |||
36 | while (token != NULL) { | ||
37 | if (!strcmp(token, "perf_event")) { | ||
38 | found = 1; | ||
39 | break; | ||
40 | } | ||
41 | token = strtok_r(NULL, ",", &saved_ptr); | ||
42 | } | ||
43 | } | ||
44 | if (found) | ||
45 | break; | ||
46 | } | ||
47 | fclose(fp); | ||
48 | if (!found) | ||
49 | return -1; | ||
50 | |||
51 | if (strlen(mountpoint) < maxlen) { | ||
52 | strcpy(buf, mountpoint); | ||
53 | return 0; | ||
54 | } | ||
55 | return -1; | ||
56 | } | ||
57 | |||
58 | static int open_cgroup(char *name) | ||
59 | { | ||
60 | char path[MAX_PATH+1]; | ||
61 | char mnt[MAX_PATH+1]; | ||
62 | int fd; | ||
63 | |||
64 | |||
65 | if (cgroupfs_find_mountpoint(mnt, MAX_PATH+1)) | ||
66 | return -1; | ||
67 | |||
68 | snprintf(path, MAX_PATH, "%s/%s", mnt, name); | ||
69 | |||
70 | fd = open(path, O_RDONLY); | ||
71 | if (fd == -1) | ||
72 | fprintf(stderr, "no access to cgroup %s\n", path); | ||
73 | |||
74 | return fd; | ||
75 | } | ||
76 | |||
77 | static int add_cgroup(struct perf_evlist *evlist, char *str) | ||
78 | { | ||
79 | struct perf_evsel *counter; | ||
80 | struct cgroup_sel *cgrp = NULL; | ||
81 | int n; | ||
82 | /* | ||
83 | * check if cgrp is already defined, if so we reuse it | ||
84 | */ | ||
85 | list_for_each_entry(counter, &evlist->entries, node) { | ||
86 | cgrp = counter->cgrp; | ||
87 | if (!cgrp) | ||
88 | continue; | ||
89 | if (!strcmp(cgrp->name, str)) | ||
90 | break; | ||
91 | |||
92 | cgrp = NULL; | ||
93 | } | ||
94 | |||
95 | if (!cgrp) { | ||
96 | cgrp = zalloc(sizeof(*cgrp)); | ||
97 | if (!cgrp) | ||
98 | return -1; | ||
99 | |||
100 | cgrp->name = str; | ||
101 | |||
102 | cgrp->fd = open_cgroup(str); | ||
103 | if (cgrp->fd == -1) { | ||
104 | free(cgrp); | ||
105 | return -1; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * find corresponding event | ||
111 | * if add cgroup N, then need to find event N | ||
112 | */ | ||
113 | n = 0; | ||
114 | list_for_each_entry(counter, &evlist->entries, node) { | ||
115 | if (n == nr_cgroups) | ||
116 | goto found; | ||
117 | n++; | ||
118 | } | ||
119 | if (cgrp->refcnt == 0) | ||
120 | free(cgrp); | ||
121 | |||
122 | return -1; | ||
123 | found: | ||
124 | cgrp->refcnt++; | ||
125 | counter->cgrp = cgrp; | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | void close_cgroup(struct cgroup_sel *cgrp) | ||
130 | { | ||
131 | if (!cgrp) | ||
132 | return; | ||
133 | |||
134 | /* XXX: not reentrant */ | ||
135 | if (--cgrp->refcnt == 0) { | ||
136 | close(cgrp->fd); | ||
137 | free(cgrp->name); | ||
138 | free(cgrp); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | int parse_cgroups(const struct option *opt __used, const char *str, | ||
143 | int unset __used) | ||
144 | { | ||
145 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
146 | const char *p, *e, *eos = str + strlen(str); | ||
147 | char *s; | ||
148 | int ret; | ||
149 | |||
150 | if (list_empty(&evlist->entries)) { | ||
151 | fprintf(stderr, "must define events before cgroups\n"); | ||
152 | return -1; | ||
153 | } | ||
154 | |||
155 | for (;;) { | ||
156 | p = strchr(str, ','); | ||
157 | e = p ? p : eos; | ||
158 | |||
159 | /* allow empty cgroups, i.e., skip */ | ||
160 | if (e - str) { | ||
161 | /* termination added */ | ||
162 | s = strndup(str, e - str); | ||
163 | if (!s) | ||
164 | return -1; | ||
165 | ret = add_cgroup(evlist, s); | ||
166 | if (ret) { | ||
167 | free(s); | ||
168 | return -1; | ||
169 | } | ||
170 | } | ||
171 | /* nr_cgroups is increased een for empty cgroups */ | ||
172 | nr_cgroups++; | ||
173 | if (!p) | ||
174 | break; | ||
175 | str = p+1; | ||
176 | } | ||
177 | return 0; | ||
178 | } | ||
diff --git a/tools/perf/util/cgroup.h b/tools/perf/util/cgroup.h new file mode 100644 index 000000000000..89acd6debdc5 --- /dev/null +++ b/tools/perf/util/cgroup.h | |||
@@ -0,0 +1,17 @@ | |||
1 | #ifndef __CGROUP_H__ | ||
2 | #define __CGROUP_H__ | ||
3 | |||
4 | struct option; | ||
5 | |||
6 | struct cgroup_sel { | ||
7 | char *name; | ||
8 | int fd; | ||
9 | int refcnt; | ||
10 | }; | ||
11 | |||
12 | |||
13 | extern int nr_cgroups; /* number of explicit cgroups defined */ | ||
14 | extern void close_cgroup(struct cgroup_sel *cgrp); | ||
15 | extern int parse_cgroups(const struct option *opt, const char *str, int unset); | ||
16 | |||
17 | #endif /* __CGROUP_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/debug.c b/tools/perf/util/debug.c index 01bbe8ecec3f..155749d74350 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -57,7 +57,17 @@ void ui__warning(const char *format, ...) | |||
57 | } | 57 | } |
58 | #endif | 58 | #endif |
59 | 59 | ||
60 | void trace_event(event_t *event) | 60 | void ui__warning_paranoid(void) |
61 | { | ||
62 | ui__warning("Permission error - are you root?\n" | ||
63 | "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n" | ||
64 | " -1 - Not paranoid at all\n" | ||
65 | " 0 - Disallow raw tracepoint access for unpriv\n" | ||
66 | " 1 - Disallow cpu events for unpriv\n" | ||
67 | " 2 - Disallow kernel profiling for unpriv\n"); | ||
68 | } | ||
69 | |||
70 | void trace_event(union perf_event *event) | ||
61 | { | 71 | { |
62 | unsigned char *raw_event = (void *)event; | 72 | unsigned char *raw_event = (void *)event; |
63 | const char *color = PERF_COLOR_BLUE; | 73 | const char *color = PERF_COLOR_BLUE; |
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index ca35fd66b5df..fd53db47e3de 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
@@ -9,7 +9,7 @@ extern int verbose; | |||
9 | extern bool quiet, dump_trace; | 9 | extern bool quiet, dump_trace; |
10 | 10 | ||
11 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | 11 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); |
12 | void trace_event(event_t *event); | 12 | void trace_event(union perf_event *event); |
13 | 13 | ||
14 | struct ui_progress; | 14 | struct ui_progress; |
15 | 15 | ||
@@ -36,5 +36,6 @@ int ui_helpline__show_help(const char *format, va_list ap); | |||
36 | #endif | 36 | #endif |
37 | 37 | ||
38 | void ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); | 38 | void ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); |
39 | void ui__warning_paranoid(void); | ||
39 | 40 | ||
40 | #endif /* __PERF_DEBUG_H */ | 41 | #endif /* __PERF_DEBUG_H */ |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 2302ec051bb4..1023f67633a4 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -6,8 +6,9 @@ | |||
6 | #include "string.h" | 6 | #include "string.h" |
7 | #include "strlist.h" | 7 | #include "strlist.h" |
8 | #include "thread.h" | 8 | #include "thread.h" |
9 | #include "thread_map.h" | ||
9 | 10 | ||
10 | static const char *event__name[] = { | 11 | static const char *perf_event__names[] = { |
11 | [0] = "TOTAL", | 12 | [0] = "TOTAL", |
12 | [PERF_RECORD_MMAP] = "MMAP", | 13 | [PERF_RECORD_MMAP] = "MMAP", |
13 | [PERF_RECORD_LOST] = "LOST", | 14 | [PERF_RECORD_LOST] = "LOST", |
@@ -25,16 +26,16 @@ static const char *event__name[] = { | |||
25 | [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", | 26 | [PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND", |
26 | }; | 27 | }; |
27 | 28 | ||
28 | const char *event__get_event_name(unsigned int id) | 29 | const char *perf_event__name(unsigned int id) |
29 | { | 30 | { |
30 | if (id >= ARRAY_SIZE(event__name)) | 31 | if (id >= ARRAY_SIZE(perf_event__names)) |
31 | return "INVALID"; | 32 | return "INVALID"; |
32 | if (!event__name[id]) | 33 | if (!perf_event__names[id]) |
33 | return "UNKNOWN"; | 34 | return "UNKNOWN"; |
34 | return event__name[id]; | 35 | return perf_event__names[id]; |
35 | } | 36 | } |
36 | 37 | ||
37 | static struct sample_data synth_sample = { | 38 | static struct perf_sample synth_sample = { |
38 | .pid = -1, | 39 | .pid = -1, |
39 | .tid = -1, | 40 | .tid = -1, |
40 | .time = -1, | 41 | .time = -1, |
@@ -43,9 +44,9 @@ static struct sample_data synth_sample = { | |||
43 | .period = 1, | 44 | .period = 1, |
44 | }; | 45 | }; |
45 | 46 | ||
46 | static pid_t event__synthesize_comm(event_t *event, pid_t pid, int full, | 47 | static pid_t perf_event__synthesize_comm(union perf_event *event, pid_t pid, |
47 | event__handler_t process, | 48 | int full, perf_event__handler_t process, |
48 | struct perf_session *session) | 49 | struct perf_session *session) |
49 | { | 50 | { |
50 | char filename[PATH_MAX]; | 51 | char filename[PATH_MAX]; |
51 | char bf[BUFSIZ]; | 52 | char bf[BUFSIZ]; |
@@ -126,9 +127,10 @@ out: | |||
126 | return tgid; | 127 | return tgid; |
127 | } | 128 | } |
128 | 129 | ||
129 | static int event__synthesize_mmap_events(event_t *event, pid_t pid, pid_t tgid, | 130 | static int perf_event__synthesize_mmap_events(union perf_event *event, |
130 | event__handler_t process, | 131 | pid_t pid, pid_t tgid, |
131 | struct perf_session *session) | 132 | perf_event__handler_t process, |
133 | struct perf_session *session) | ||
132 | { | 134 | { |
133 | char filename[PATH_MAX]; | 135 | char filename[PATH_MAX]; |
134 | FILE *fp; | 136 | FILE *fp; |
@@ -199,14 +201,14 @@ static int event__synthesize_mmap_events(event_t *event, pid_t pid, pid_t tgid, | |||
199 | return 0; | 201 | return 0; |
200 | } | 202 | } |
201 | 203 | ||
202 | int event__synthesize_modules(event__handler_t process, | 204 | int perf_event__synthesize_modules(perf_event__handler_t process, |
203 | struct perf_session *session, | 205 | struct perf_session *session, |
204 | struct machine *machine) | 206 | struct machine *machine) |
205 | { | 207 | { |
206 | struct rb_node *nd; | 208 | struct rb_node *nd; |
207 | struct map_groups *kmaps = &machine->kmaps; | 209 | struct map_groups *kmaps = &machine->kmaps; |
208 | event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size); | 210 | union perf_event *event = zalloc((sizeof(event->mmap) + |
209 | 211 | session->id_hdr_size)); | |
210 | if (event == NULL) { | 212 | if (event == NULL) { |
211 | pr_debug("Not enough memory synthesizing mmap event " | 213 | pr_debug("Not enough memory synthesizing mmap event " |
212 | "for kernel modules\n"); | 214 | "for kernel modules\n"); |
@@ -251,23 +253,25 @@ int event__synthesize_modules(event__handler_t process, | |||
251 | return 0; | 253 | return 0; |
252 | } | 254 | } |
253 | 255 | ||
254 | static int __event__synthesize_thread(event_t *comm_event, event_t *mmap_event, | 256 | static int __event__synthesize_thread(union perf_event *comm_event, |
255 | pid_t pid, event__handler_t process, | 257 | union perf_event *mmap_event, |
258 | pid_t pid, perf_event__handler_t process, | ||
256 | struct perf_session *session) | 259 | struct perf_session *session) |
257 | { | 260 | { |
258 | pid_t tgid = event__synthesize_comm(comm_event, pid, 1, process, | 261 | pid_t tgid = perf_event__synthesize_comm(comm_event, pid, 1, process, |
259 | session); | 262 | session); |
260 | if (tgid == -1) | 263 | if (tgid == -1) |
261 | return -1; | 264 | return -1; |
262 | return event__synthesize_mmap_events(mmap_event, pid, tgid, | 265 | return perf_event__synthesize_mmap_events(mmap_event, pid, tgid, |
263 | process, session); | 266 | process, session); |
264 | } | 267 | } |
265 | 268 | ||
266 | int event__synthesize_thread(pid_t pid, event__handler_t process, | 269 | int perf_event__synthesize_thread_map(struct thread_map *threads, |
267 | struct perf_session *session) | 270 | perf_event__handler_t process, |
271 | struct perf_session *session) | ||
268 | { | 272 | { |
269 | event_t *comm_event, *mmap_event; | 273 | union perf_event *comm_event, *mmap_event; |
270 | int err = -1; | 274 | int err = -1, thread; |
271 | 275 | ||
272 | comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); | 276 | comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); |
273 | if (comm_event == NULL) | 277 | if (comm_event == NULL) |
@@ -277,8 +281,15 @@ int event__synthesize_thread(pid_t pid, event__handler_t process, | |||
277 | if (mmap_event == NULL) | 281 | if (mmap_event == NULL) |
278 | goto out_free_comm; | 282 | goto out_free_comm; |
279 | 283 | ||
280 | err = __event__synthesize_thread(comm_event, mmap_event, pid, | 284 | err = 0; |
281 | process, session); | 285 | for (thread = 0; thread < threads->nr; ++thread) { |
286 | if (__event__synthesize_thread(comm_event, mmap_event, | ||
287 | threads->map[thread], | ||
288 | process, session)) { | ||
289 | err = -1; | ||
290 | break; | ||
291 | } | ||
292 | } | ||
282 | free(mmap_event); | 293 | free(mmap_event); |
283 | out_free_comm: | 294 | out_free_comm: |
284 | free(comm_event); | 295 | free(comm_event); |
@@ -286,12 +297,12 @@ out: | |||
286 | return err; | 297 | return err; |
287 | } | 298 | } |
288 | 299 | ||
289 | int event__synthesize_threads(event__handler_t process, | 300 | int perf_event__synthesize_threads(perf_event__handler_t process, |
290 | struct perf_session *session) | 301 | struct perf_session *session) |
291 | { | 302 | { |
292 | DIR *proc; | 303 | DIR *proc; |
293 | struct dirent dirent, *next; | 304 | struct dirent dirent, *next; |
294 | event_t *comm_event, *mmap_event; | 305 | union perf_event *comm_event, *mmap_event; |
295 | int err = -1; | 306 | int err = -1; |
296 | 307 | ||
297 | comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); | 308 | comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size); |
@@ -349,10 +360,10 @@ static int find_symbol_cb(void *arg, const char *name, char type, | |||
349 | return 1; | 360 | return 1; |
350 | } | 361 | } |
351 | 362 | ||
352 | int event__synthesize_kernel_mmap(event__handler_t process, | 363 | int perf_event__synthesize_kernel_mmap(perf_event__handler_t process, |
353 | struct perf_session *session, | 364 | struct perf_session *session, |
354 | struct machine *machine, | 365 | struct machine *machine, |
355 | const char *symbol_name) | 366 | const char *symbol_name) |
356 | { | 367 | { |
357 | size_t size; | 368 | size_t size; |
358 | const char *filename, *mmap_name; | 369 | const char *filename, *mmap_name; |
@@ -366,8 +377,8 @@ int event__synthesize_kernel_mmap(event__handler_t process, | |||
366 | * kernels. | 377 | * kernels. |
367 | */ | 378 | */ |
368 | struct process_symbol_args args = { .name = symbol_name, }; | 379 | struct process_symbol_args args = { .name = symbol_name, }; |
369 | event_t *event = zalloc(sizeof(event->mmap) + session->id_hdr_size); | 380 | union perf_event *event = zalloc((sizeof(event->mmap) + |
370 | 381 | session->id_hdr_size)); | |
371 | if (event == NULL) { | 382 | if (event == NULL) { |
372 | pr_debug("Not enough memory synthesizing mmap event " | 383 | pr_debug("Not enough memory synthesizing mmap event " |
373 | "for kernel modules\n"); | 384 | "for kernel modules\n"); |
@@ -413,42 +424,15 @@ int event__synthesize_kernel_mmap(event__handler_t process, | |||
413 | return err; | 424 | return err; |
414 | } | 425 | } |
415 | 426 | ||
416 | static void thread__comm_adjust(struct thread *self, struct hists *hists) | 427 | int perf_event__process_comm(union perf_event *event, |
417 | { | 428 | struct perf_sample *sample __used, |
418 | char *comm = self->comm; | 429 | struct perf_session *session) |
419 | |||
420 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
421 | (!symbol_conf.comm_list || | ||
422 | strlist__has_entry(symbol_conf.comm_list, comm))) { | ||
423 | u16 slen = strlen(comm); | ||
424 | |||
425 | if (hists__new_col_len(hists, HISTC_COMM, slen)) | ||
426 | hists__set_col_len(hists, HISTC_THREAD, slen + 6); | ||
427 | } | ||
428 | } | ||
429 | |||
430 | static int thread__set_comm_adjust(struct thread *self, const char *comm, | ||
431 | struct hists *hists) | ||
432 | { | ||
433 | int ret = thread__set_comm(self, comm); | ||
434 | |||
435 | if (ret) | ||
436 | return ret; | ||
437 | |||
438 | thread__comm_adjust(self, hists); | ||
439 | |||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | int event__process_comm(event_t *self, struct sample_data *sample __used, | ||
444 | struct perf_session *session) | ||
445 | { | 430 | { |
446 | struct thread *thread = perf_session__findnew(session, self->comm.tid); | 431 | struct thread *thread = perf_session__findnew(session, event->comm.tid); |
447 | 432 | ||
448 | dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid); | 433 | dump_printf(": %s:%d\n", event->comm.comm, event->comm.tid); |
449 | 434 | ||
450 | if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm, | 435 | if (thread == NULL || thread__set_comm(thread, event->comm.comm)) { |
451 | &session->hists)) { | ||
452 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | 436 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); |
453 | return -1; | 437 | return -1; |
454 | } | 438 | } |
@@ -456,18 +440,21 @@ int event__process_comm(event_t *self, struct sample_data *sample __used, | |||
456 | return 0; | 440 | return 0; |
457 | } | 441 | } |
458 | 442 | ||
459 | int event__process_lost(event_t *self, struct sample_data *sample __used, | 443 | int perf_event__process_lost(union perf_event *event, |
460 | struct perf_session *session) | 444 | struct perf_sample *sample __used, |
445 | struct perf_session *session) | ||
461 | { | 446 | { |
462 | dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost); | 447 | dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", |
463 | session->hists.stats.total_lost += self->lost.lost; | 448 | event->lost.id, event->lost.lost); |
449 | session->hists.stats.total_lost += event->lost.lost; | ||
464 | return 0; | 450 | return 0; |
465 | } | 451 | } |
466 | 452 | ||
467 | static void event_set_kernel_mmap_len(struct map **maps, event_t *self) | 453 | static void perf_event__set_kernel_mmap_len(union perf_event *event, |
454 | struct map **maps) | ||
468 | { | 455 | { |
469 | maps[MAP__FUNCTION]->start = self->mmap.start; | 456 | maps[MAP__FUNCTION]->start = event->mmap.start; |
470 | maps[MAP__FUNCTION]->end = self->mmap.start + self->mmap.len; | 457 | maps[MAP__FUNCTION]->end = event->mmap.start + event->mmap.len; |
471 | /* | 458 | /* |
472 | * Be a bit paranoid here, some perf.data file came with | 459 | * Be a bit paranoid here, some perf.data file came with |
473 | * a zero sized synthesized MMAP event for the kernel. | 460 | * a zero sized synthesized MMAP event for the kernel. |
@@ -476,8 +463,8 @@ static void event_set_kernel_mmap_len(struct map **maps, event_t *self) | |||
476 | maps[MAP__FUNCTION]->end = ~0ULL; | 463 | maps[MAP__FUNCTION]->end = ~0ULL; |
477 | } | 464 | } |
478 | 465 | ||
479 | static int event__process_kernel_mmap(event_t *self, | 466 | static int perf_event__process_kernel_mmap(union perf_event *event, |
480 | struct perf_session *session) | 467 | struct perf_session *session) |
481 | { | 468 | { |
482 | struct map *map; | 469 | struct map *map; |
483 | char kmmap_prefix[PATH_MAX]; | 470 | char kmmap_prefix[PATH_MAX]; |
@@ -485,9 +472,9 @@ static int event__process_kernel_mmap(event_t *self, | |||
485 | enum dso_kernel_type kernel_type; | 472 | enum dso_kernel_type kernel_type; |
486 | bool is_kernel_mmap; | 473 | bool is_kernel_mmap; |
487 | 474 | ||
488 | machine = perf_session__findnew_machine(session, self->mmap.pid); | 475 | machine = perf_session__findnew_machine(session, event->mmap.pid); |
489 | if (!machine) { | 476 | if (!machine) { |
490 | pr_err("Can't find id %d's machine\n", self->mmap.pid); | 477 | pr_err("Can't find id %d's machine\n", event->mmap.pid); |
491 | goto out_problem; | 478 | goto out_problem; |
492 | } | 479 | } |
493 | 480 | ||
@@ -497,17 +484,17 @@ static int event__process_kernel_mmap(event_t *self, | |||
497 | else | 484 | else |
498 | kernel_type = DSO_TYPE_GUEST_KERNEL; | 485 | kernel_type = DSO_TYPE_GUEST_KERNEL; |
499 | 486 | ||
500 | is_kernel_mmap = memcmp(self->mmap.filename, | 487 | is_kernel_mmap = memcmp(event->mmap.filename, |
501 | kmmap_prefix, | 488 | kmmap_prefix, |
502 | strlen(kmmap_prefix)) == 0; | 489 | strlen(kmmap_prefix)) == 0; |
503 | if (self->mmap.filename[0] == '/' || | 490 | if (event->mmap.filename[0] == '/' || |
504 | (!is_kernel_mmap && self->mmap.filename[0] == '[')) { | 491 | (!is_kernel_mmap && event->mmap.filename[0] == '[')) { |
505 | 492 | ||
506 | char short_module_name[1024]; | 493 | char short_module_name[1024]; |
507 | char *name, *dot; | 494 | char *name, *dot; |
508 | 495 | ||
509 | if (self->mmap.filename[0] == '/') { | 496 | if (event->mmap.filename[0] == '/') { |
510 | name = strrchr(self->mmap.filename, '/'); | 497 | name = strrchr(event->mmap.filename, '/'); |
511 | if (name == NULL) | 498 | if (name == NULL) |
512 | goto out_problem; | 499 | goto out_problem; |
513 | 500 | ||
@@ -519,10 +506,10 @@ static int event__process_kernel_mmap(event_t *self, | |||
519 | "[%.*s]", (int)(dot - name), name); | 506 | "[%.*s]", (int)(dot - name), name); |
520 | strxfrchar(short_module_name, '-', '_'); | 507 | strxfrchar(short_module_name, '-', '_'); |
521 | } else | 508 | } else |
522 | strcpy(short_module_name, self->mmap.filename); | 509 | strcpy(short_module_name, event->mmap.filename); |
523 | 510 | ||
524 | map = machine__new_module(machine, self->mmap.start, | 511 | map = machine__new_module(machine, event->mmap.start, |
525 | self->mmap.filename); | 512 | event->mmap.filename); |
526 | if (map == NULL) | 513 | if (map == NULL) |
527 | goto out_problem; | 514 | goto out_problem; |
528 | 515 | ||
@@ -532,9 +519,9 @@ static int event__process_kernel_mmap(event_t *self, | |||
532 | 519 | ||
533 | map->dso->short_name = name; | 520 | map->dso->short_name = name; |
534 | map->dso->sname_alloc = 1; | 521 | map->dso->sname_alloc = 1; |
535 | map->end = map->start + self->mmap.len; | 522 | map->end = map->start + event->mmap.len; |
536 | } else if (is_kernel_mmap) { | 523 | } else if (is_kernel_mmap) { |
537 | const char *symbol_name = (self->mmap.filename + | 524 | const char *symbol_name = (event->mmap.filename + |
538 | strlen(kmmap_prefix)); | 525 | strlen(kmmap_prefix)); |
539 | /* | 526 | /* |
540 | * Should be there already, from the build-id table in | 527 | * Should be there already, from the build-id table in |
@@ -549,10 +536,10 @@ static int event__process_kernel_mmap(event_t *self, | |||
549 | if (__machine__create_kernel_maps(machine, kernel) < 0) | 536 | if (__machine__create_kernel_maps(machine, kernel) < 0) |
550 | goto out_problem; | 537 | goto out_problem; |
551 | 538 | ||
552 | event_set_kernel_mmap_len(machine->vmlinux_maps, self); | 539 | perf_event__set_kernel_mmap_len(event, machine->vmlinux_maps); |
553 | perf_session__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, | 540 | perf_session__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, |
554 | symbol_name, | 541 | symbol_name, |
555 | self->mmap.pgoff); | 542 | event->mmap.pgoff); |
556 | if (machine__is_default_guest(machine)) { | 543 | if (machine__is_default_guest(machine)) { |
557 | /* | 544 | /* |
558 | * preload dso of guest kernel and modules | 545 | * preload dso of guest kernel and modules |
@@ -566,22 +553,23 @@ out_problem: | |||
566 | return -1; | 553 | return -1; |
567 | } | 554 | } |
568 | 555 | ||
569 | int event__process_mmap(event_t *self, struct sample_data *sample __used, | 556 | int perf_event__process_mmap(union perf_event *event, |
570 | struct perf_session *session) | 557 | struct perf_sample *sample __used, |
558 | struct perf_session *session) | ||
571 | { | 559 | { |
572 | struct machine *machine; | 560 | struct machine *machine; |
573 | struct thread *thread; | 561 | struct thread *thread; |
574 | struct map *map; | 562 | struct map *map; |
575 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 563 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
576 | int ret = 0; | 564 | int ret = 0; |
577 | 565 | ||
578 | dump_printf(" %d/%d: [%#Lx(%#Lx) @ %#Lx]: %s\n", | 566 | dump_printf(" %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %s\n", |
579 | self->mmap.pid, self->mmap.tid, self->mmap.start, | 567 | event->mmap.pid, event->mmap.tid, event->mmap.start, |
580 | self->mmap.len, self->mmap.pgoff, self->mmap.filename); | 568 | event->mmap.len, event->mmap.pgoff, event->mmap.filename); |
581 | 569 | ||
582 | if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || | 570 | if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || |
583 | cpumode == PERF_RECORD_MISC_KERNEL) { | 571 | cpumode == PERF_RECORD_MISC_KERNEL) { |
584 | ret = event__process_kernel_mmap(self, session); | 572 | ret = perf_event__process_kernel_mmap(event, session); |
585 | if (ret < 0) | 573 | if (ret < 0) |
586 | goto out_problem; | 574 | goto out_problem; |
587 | return 0; | 575 | return 0; |
@@ -590,12 +578,12 @@ int event__process_mmap(event_t *self, struct sample_data *sample __used, | |||
590 | machine = perf_session__find_host_machine(session); | 578 | machine = perf_session__find_host_machine(session); |
591 | if (machine == NULL) | 579 | if (machine == NULL) |
592 | goto out_problem; | 580 | goto out_problem; |
593 | thread = perf_session__findnew(session, self->mmap.pid); | 581 | thread = perf_session__findnew(session, event->mmap.pid); |
594 | if (thread == NULL) | 582 | if (thread == NULL) |
595 | goto out_problem; | 583 | goto out_problem; |
596 | map = map__new(&machine->user_dsos, self->mmap.start, | 584 | map = map__new(&machine->user_dsos, event->mmap.start, |
597 | self->mmap.len, self->mmap.pgoff, | 585 | event->mmap.len, event->mmap.pgoff, |
598 | self->mmap.pid, self->mmap.filename, | 586 | event->mmap.pid, event->mmap.filename, |
599 | MAP__FUNCTION); | 587 | MAP__FUNCTION); |
600 | if (map == NULL) | 588 | if (map == NULL) |
601 | goto out_problem; | 589 | goto out_problem; |
@@ -608,16 +596,17 @@ out_problem: | |||
608 | return 0; | 596 | return 0; |
609 | } | 597 | } |
610 | 598 | ||
611 | int event__process_task(event_t *self, struct sample_data *sample __used, | 599 | int perf_event__process_task(union perf_event *event, |
612 | struct perf_session *session) | 600 | struct perf_sample *sample __used, |
601 | struct perf_session *session) | ||
613 | { | 602 | { |
614 | struct thread *thread = perf_session__findnew(session, self->fork.tid); | 603 | struct thread *thread = perf_session__findnew(session, event->fork.tid); |
615 | struct thread *parent = perf_session__findnew(session, self->fork.ptid); | 604 | struct thread *parent = perf_session__findnew(session, event->fork.ptid); |
616 | 605 | ||
617 | dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, | 606 | dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, |
618 | self->fork.ppid, self->fork.ptid); | 607 | event->fork.ppid, event->fork.ptid); |
619 | 608 | ||
620 | if (self->header.type == PERF_RECORD_EXIT) { | 609 | if (event->header.type == PERF_RECORD_EXIT) { |
621 | perf_session__remove_thread(session, thread); | 610 | perf_session__remove_thread(session, thread); |
622 | return 0; | 611 | return 0; |
623 | } | 612 | } |
@@ -631,20 +620,22 @@ int event__process_task(event_t *self, struct sample_data *sample __used, | |||
631 | return 0; | 620 | return 0; |
632 | } | 621 | } |
633 | 622 | ||
634 | int event__process(event_t *event, struct sample_data *sample, | 623 | int perf_event__process(union perf_event *event, struct perf_sample *sample, |
635 | struct perf_session *session) | 624 | struct perf_session *session) |
636 | { | 625 | { |
637 | switch (event->header.type) { | 626 | switch (event->header.type) { |
638 | case PERF_RECORD_COMM: | 627 | case PERF_RECORD_COMM: |
639 | event__process_comm(event, sample, session); | 628 | perf_event__process_comm(event, sample, session); |
640 | break; | 629 | break; |
641 | case PERF_RECORD_MMAP: | 630 | case PERF_RECORD_MMAP: |
642 | event__process_mmap(event, sample, session); | 631 | perf_event__process_mmap(event, sample, session); |
643 | break; | 632 | break; |
644 | case PERF_RECORD_FORK: | 633 | case PERF_RECORD_FORK: |
645 | case PERF_RECORD_EXIT: | 634 | case PERF_RECORD_EXIT: |
646 | event__process_task(event, sample, session); | 635 | perf_event__process_task(event, sample, session); |
647 | break; | 636 | break; |
637 | case PERF_RECORD_LOST: | ||
638 | perf_event__process_lost(event, sample, session); | ||
648 | default: | 639 | default: |
649 | break; | 640 | break; |
650 | } | 641 | } |
@@ -719,7 +710,7 @@ try_again: | |||
719 | * in the whole kernel symbol list. | 710 | * in the whole kernel symbol list. |
720 | */ | 711 | */ |
721 | if ((long long)al->addr < 0 && | 712 | if ((long long)al->addr < 0 && |
722 | cpumode == PERF_RECORD_MISC_KERNEL && | 713 | cpumode == PERF_RECORD_MISC_USER && |
723 | machine && mg != &machine->kmaps) { | 714 | machine && mg != &machine->kmaps) { |
724 | mg = &machine->kmaps; | 715 | mg = &machine->kmaps; |
725 | goto try_again; | 716 | goto try_again; |
@@ -741,24 +732,14 @@ void thread__find_addr_location(struct thread *self, | |||
741 | al->sym = NULL; | 732 | al->sym = NULL; |
742 | } | 733 | } |
743 | 734 | ||
744 | static void dso__calc_col_width(struct dso *self, struct hists *hists) | 735 | int perf_event__preprocess_sample(const union perf_event *event, |
745 | { | 736 | struct perf_session *session, |
746 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | 737 | struct addr_location *al, |
747 | (!symbol_conf.dso_list || | 738 | struct perf_sample *sample, |
748 | strlist__has_entry(symbol_conf.dso_list, self->name))) { | 739 | symbol_filter_t filter) |
749 | u16 slen = dso__name_len(self); | ||
750 | hists__new_col_len(hists, HISTC_DSO, slen); | ||
751 | } | ||
752 | |||
753 | self->slen_calculated = 1; | ||
754 | } | ||
755 | |||
756 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | ||
757 | struct addr_location *al, struct sample_data *data, | ||
758 | symbol_filter_t filter) | ||
759 | { | 740 | { |
760 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 741 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
761 | struct thread *thread = perf_session__findnew(session, self->ip.pid); | 742 | struct thread *thread = perf_session__findnew(session, event->ip.pid); |
762 | 743 | ||
763 | if (thread == NULL) | 744 | if (thread == NULL) |
764 | return -1; | 745 | return -1; |
@@ -780,12 +761,12 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, | |||
780 | machine__create_kernel_maps(&session->host_machine); | 761 | machine__create_kernel_maps(&session->host_machine); |
781 | 762 | ||
782 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, | 763 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, |
783 | self->ip.pid, self->ip.ip, al); | 764 | event->ip.pid, event->ip.ip, al); |
784 | dump_printf(" ...... dso: %s\n", | 765 | dump_printf(" ...... dso: %s\n", |
785 | al->map ? al->map->dso->long_name : | 766 | al->map ? al->map->dso->long_name : |
786 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | 767 | al->level == 'H' ? "[hypervisor]" : "<not found>"); |
787 | al->sym = NULL; | 768 | al->sym = NULL; |
788 | al->cpu = data->cpu; | 769 | al->cpu = sample->cpu; |
789 | 770 | ||
790 | if (al->map) { | 771 | if (al->map) { |
791 | if (symbol_conf.dso_list && | 772 | if (symbol_conf.dso_list && |
@@ -796,23 +777,8 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, | |||
796 | strlist__has_entry(symbol_conf.dso_list, | 777 | strlist__has_entry(symbol_conf.dso_list, |
797 | al->map->dso->long_name))))) | 778 | al->map->dso->long_name))))) |
798 | goto out_filtered; | 779 | goto out_filtered; |
799 | /* | ||
800 | * We have to do this here as we may have a dso with no symbol | ||
801 | * hit that has a name longer than the ones with symbols | ||
802 | * sampled. | ||
803 | */ | ||
804 | if (!sort_dso.elide && !al->map->dso->slen_calculated) | ||
805 | dso__calc_col_width(al->map->dso, &session->hists); | ||
806 | 780 | ||
807 | al->sym = map__find_symbol(al->map, al->addr, filter); | 781 | al->sym = map__find_symbol(al->map, al->addr, filter); |
808 | } else { | ||
809 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | ||
810 | |||
811 | if (hists__col_len(&session->hists, HISTC_DSO) < unresolved_col_width && | ||
812 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
813 | !symbol_conf.dso_list) | ||
814 | hists__set_col_len(&session->hists, HISTC_DSO, | ||
815 | unresolved_col_width); | ||
816 | } | 782 | } |
817 | 783 | ||
818 | if (symbol_conf.sym_list && al->sym && | 784 | if (symbol_conf.sym_list && al->sym && |
@@ -825,128 +791,3 @@ out_filtered: | |||
825 | al->filtered = true; | 791 | al->filtered = true; |
826 | return 0; | 792 | return 0; |
827 | } | 793 | } |
828 | |||
829 | static int event__parse_id_sample(const event_t *event, | ||
830 | struct perf_session *session, | ||
831 | struct sample_data *sample) | ||
832 | { | ||
833 | const u64 *array; | ||
834 | u64 type; | ||
835 | |||
836 | sample->cpu = sample->pid = sample->tid = -1; | ||
837 | sample->stream_id = sample->id = sample->time = -1ULL; | ||
838 | |||
839 | if (!session->sample_id_all) | ||
840 | return 0; | ||
841 | |||
842 | array = event->sample.array; | ||
843 | array += ((event->header.size - | ||
844 | sizeof(event->header)) / sizeof(u64)) - 1; | ||
845 | type = session->sample_type; | ||
846 | |||
847 | if (type & PERF_SAMPLE_CPU) { | ||
848 | u32 *p = (u32 *)array; | ||
849 | sample->cpu = *p; | ||
850 | array--; | ||
851 | } | ||
852 | |||
853 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
854 | sample->stream_id = *array; | ||
855 | array--; | ||
856 | } | ||
857 | |||
858 | if (type & PERF_SAMPLE_ID) { | ||
859 | sample->id = *array; | ||
860 | array--; | ||
861 | } | ||
862 | |||
863 | if (type & PERF_SAMPLE_TIME) { | ||
864 | sample->time = *array; | ||
865 | array--; | ||
866 | } | ||
867 | |||
868 | if (type & PERF_SAMPLE_TID) { | ||
869 | u32 *p = (u32 *)array; | ||
870 | sample->pid = p[0]; | ||
871 | sample->tid = p[1]; | ||
872 | } | ||
873 | |||
874 | return 0; | ||
875 | } | ||
876 | |||
877 | int event__parse_sample(const event_t *event, struct perf_session *session, | ||
878 | struct sample_data *data) | ||
879 | { | ||
880 | const u64 *array; | ||
881 | u64 type; | ||
882 | |||
883 | if (event->header.type != PERF_RECORD_SAMPLE) | ||
884 | return event__parse_id_sample(event, session, data); | ||
885 | |||
886 | array = event->sample.array; | ||
887 | type = session->sample_type; | ||
888 | |||
889 | if (type & PERF_SAMPLE_IP) { | ||
890 | data->ip = event->ip.ip; | ||
891 | array++; | ||
892 | } | ||
893 | |||
894 | if (type & PERF_SAMPLE_TID) { | ||
895 | u32 *p = (u32 *)array; | ||
896 | data->pid = p[0]; | ||
897 | data->tid = p[1]; | ||
898 | array++; | ||
899 | } | ||
900 | |||
901 | if (type & PERF_SAMPLE_TIME) { | ||
902 | data->time = *array; | ||
903 | array++; | ||
904 | } | ||
905 | |||
906 | if (type & PERF_SAMPLE_ADDR) { | ||
907 | data->addr = *array; | ||
908 | array++; | ||
909 | } | ||
910 | |||
911 | data->id = -1ULL; | ||
912 | if (type & PERF_SAMPLE_ID) { | ||
913 | data->id = *array; | ||
914 | array++; | ||
915 | } | ||
916 | |||
917 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
918 | data->stream_id = *array; | ||
919 | array++; | ||
920 | } | ||
921 | |||
922 | if (type & PERF_SAMPLE_CPU) { | ||
923 | u32 *p = (u32 *)array; | ||
924 | data->cpu = *p; | ||
925 | array++; | ||
926 | } else | ||
927 | data->cpu = -1; | ||
928 | |||
929 | if (type & PERF_SAMPLE_PERIOD) { | ||
930 | data->period = *array; | ||
931 | array++; | ||
932 | } | ||
933 | |||
934 | if (type & PERF_SAMPLE_READ) { | ||
935 | pr_debug("PERF_SAMPLE_READ is unsuported for now\n"); | ||
936 | return -1; | ||
937 | } | ||
938 | |||
939 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
940 | data->callchain = (struct ip_callchain *)array; | ||
941 | array += 1 + data->callchain->nr; | ||
942 | } | ||
943 | |||
944 | if (type & PERF_SAMPLE_RAW) { | ||
945 | u32 *p = (u32 *)array; | ||
946 | data->raw_size = *p; | ||
947 | p++; | ||
948 | data->raw_data = p; | ||
949 | } | ||
950 | |||
951 | return 0; | ||
952 | } | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 2b7e91902f10..9c35170fb379 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -61,7 +61,7 @@ struct sample_event { | |||
61 | u64 array[]; | 61 | u64 array[]; |
62 | }; | 62 | }; |
63 | 63 | ||
64 | struct sample_data { | 64 | struct perf_sample { |
65 | u64 ip; | 65 | u64 ip; |
66 | u32 pid, tid; | 66 | u32 pid, tid; |
67 | u64 time; | 67 | u64 time; |
@@ -117,7 +117,7 @@ struct tracing_data_event { | |||
117 | u32 size; | 117 | u32 size; |
118 | }; | 118 | }; |
119 | 119 | ||
120 | typedef union event_union { | 120 | union perf_event { |
121 | struct perf_event_header header; | 121 | struct perf_event_header header; |
122 | struct ip_event ip; | 122 | struct ip_event ip; |
123 | struct mmap_event mmap; | 123 | struct mmap_event mmap; |
@@ -130,48 +130,54 @@ typedef union event_union { | |||
130 | struct event_type_event event_type; | 130 | struct event_type_event event_type; |
131 | struct tracing_data_event tracing_data; | 131 | struct tracing_data_event tracing_data; |
132 | struct build_id_event build_id; | 132 | struct build_id_event build_id; |
133 | } event_t; | 133 | }; |
134 | 134 | ||
135 | void event__print_totals(void); | 135 | void perf_event__print_totals(void); |
136 | 136 | ||
137 | struct perf_session; | 137 | struct perf_session; |
138 | struct thread_map; | ||
138 | 139 | ||
139 | typedef int (*event__handler_synth_t)(event_t *event, | 140 | typedef int (*perf_event__handler_synth_t)(union perf_event *event, |
141 | struct perf_session *session); | ||
142 | typedef int (*perf_event__handler_t)(union perf_event *event, | ||
143 | struct perf_sample *sample, | ||
140 | struct perf_session *session); | 144 | struct perf_session *session); |
141 | typedef int (*event__handler_t)(event_t *event, struct sample_data *sample, | ||
142 | struct perf_session *session); | ||
143 | 145 | ||
144 | int event__synthesize_thread(pid_t pid, event__handler_t process, | 146 | int perf_event__synthesize_thread_map(struct thread_map *threads, |
147 | perf_event__handler_t process, | ||
148 | struct perf_session *session); | ||
149 | int perf_event__synthesize_threads(perf_event__handler_t process, | ||
150 | struct perf_session *session); | ||
151 | int perf_event__synthesize_kernel_mmap(perf_event__handler_t process, | ||
152 | struct perf_session *session, | ||
153 | struct machine *machine, | ||
154 | const char *symbol_name); | ||
155 | |||
156 | int perf_event__synthesize_modules(perf_event__handler_t process, | ||
157 | struct perf_session *session, | ||
158 | struct machine *machine); | ||
159 | |||
160 | int perf_event__process_comm(union perf_event *event, struct perf_sample *sample, | ||
145 | struct perf_session *session); | 161 | struct perf_session *session); |
146 | int event__synthesize_threads(event__handler_t process, | 162 | int perf_event__process_lost(union perf_event *event, struct perf_sample *sample, |
147 | struct perf_session *session); | 163 | struct perf_session *session); |
148 | int event__synthesize_kernel_mmap(event__handler_t process, | 164 | int perf_event__process_mmap(union perf_event *event, struct perf_sample *sample, |
149 | struct perf_session *session, | 165 | struct perf_session *session); |
150 | struct machine *machine, | 166 | int perf_event__process_task(union perf_event *event, struct perf_sample *sample, |
151 | const char *symbol_name); | 167 | struct perf_session *session); |
152 | 168 | int perf_event__process(union perf_event *event, struct perf_sample *sample, | |
153 | int event__synthesize_modules(event__handler_t process, | ||
154 | struct perf_session *session, | ||
155 | struct machine *machine); | ||
156 | |||
157 | int event__process_comm(event_t *self, struct sample_data *sample, | ||
158 | struct perf_session *session); | ||
159 | int event__process_lost(event_t *self, struct sample_data *sample, | ||
160 | struct perf_session *session); | ||
161 | int event__process_mmap(event_t *self, struct sample_data *sample, | ||
162 | struct perf_session *session); | ||
163 | int event__process_task(event_t *self, struct sample_data *sample, | ||
164 | struct perf_session *session); | 169 | struct perf_session *session); |
165 | int event__process(event_t *event, struct sample_data *sample, | ||
166 | struct perf_session *session); | ||
167 | 170 | ||
168 | struct addr_location; | 171 | struct addr_location; |
169 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | 172 | int perf_event__preprocess_sample(const union perf_event *self, |
170 | struct addr_location *al, struct sample_data *data, | 173 | struct perf_session *session, |
171 | symbol_filter_t filter); | 174 | struct addr_location *al, |
172 | int event__parse_sample(const event_t *event, struct perf_session *session, | 175 | struct perf_sample *sample, |
173 | struct sample_data *sample); | 176 | symbol_filter_t filter); |
177 | |||
178 | const char *perf_event__name(unsigned int id); | ||
174 | 179 | ||
175 | const char *event__get_event_name(unsigned int id); | 180 | int perf_event__parse_sample(const union perf_event *event, u64 type, |
181 | bool sample_id_all, struct perf_sample *sample); | ||
176 | 182 | ||
177 | #endif /* __PERF_RECORD_H */ | 183 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c new file mode 100644 index 000000000000..45da8d186b49 --- /dev/null +++ b/tools/perf/util/evlist.c | |||
@@ -0,0 +1,400 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Parts came from builtin-{top,stat,record}.c, see those files for further | ||
5 | * copyright notes. | ||
6 | * | ||
7 | * Released under the GPL v2. (and only v2, not any later version) | ||
8 | */ | ||
9 | #include <poll.h> | ||
10 | #include "cpumap.h" | ||
11 | #include "thread_map.h" | ||
12 | #include "evlist.h" | ||
13 | #include "evsel.h" | ||
14 | #include "util.h" | ||
15 | #include "debug.h" | ||
16 | |||
17 | #include <sys/mman.h> | ||
18 | |||
19 | #include <linux/bitops.h> | ||
20 | #include <linux/hash.h> | ||
21 | |||
22 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | ||
23 | #define SID(e, x, y) xyarray__entry(e->sample_id, x, y) | ||
24 | |||
25 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | ||
26 | struct thread_map *threads) | ||
27 | { | ||
28 | int i; | ||
29 | |||
30 | for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i) | ||
31 | INIT_HLIST_HEAD(&evlist->heads[i]); | ||
32 | INIT_LIST_HEAD(&evlist->entries); | ||
33 | perf_evlist__set_maps(evlist, cpus, threads); | ||
34 | } | ||
35 | |||
36 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | ||
37 | struct thread_map *threads) | ||
38 | { | ||
39 | struct perf_evlist *evlist = zalloc(sizeof(*evlist)); | ||
40 | |||
41 | if (evlist != NULL) | ||
42 | perf_evlist__init(evlist, cpus, threads); | ||
43 | |||
44 | return evlist; | ||
45 | } | ||
46 | |||
47 | static void perf_evlist__purge(struct perf_evlist *evlist) | ||
48 | { | ||
49 | struct perf_evsel *pos, *n; | ||
50 | |||
51 | list_for_each_entry_safe(pos, n, &evlist->entries, node) { | ||
52 | list_del_init(&pos->node); | ||
53 | perf_evsel__delete(pos); | ||
54 | } | ||
55 | |||
56 | evlist->nr_entries = 0; | ||
57 | } | ||
58 | |||
59 | void perf_evlist__exit(struct perf_evlist *evlist) | ||
60 | { | ||
61 | free(evlist->mmap); | ||
62 | free(evlist->pollfd); | ||
63 | evlist->mmap = NULL; | ||
64 | evlist->pollfd = NULL; | ||
65 | } | ||
66 | |||
67 | void perf_evlist__delete(struct perf_evlist *evlist) | ||
68 | { | ||
69 | perf_evlist__purge(evlist); | ||
70 | perf_evlist__exit(evlist); | ||
71 | free(evlist); | ||
72 | } | ||
73 | |||
74 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | ||
75 | { | ||
76 | list_add_tail(&entry->node, &evlist->entries); | ||
77 | ++evlist->nr_entries; | ||
78 | } | ||
79 | |||
80 | int perf_evlist__add_default(struct perf_evlist *evlist) | ||
81 | { | ||
82 | struct perf_event_attr attr = { | ||
83 | .type = PERF_TYPE_HARDWARE, | ||
84 | .config = PERF_COUNT_HW_CPU_CYCLES, | ||
85 | }; | ||
86 | struct perf_evsel *evsel = perf_evsel__new(&attr, 0); | ||
87 | |||
88 | if (evsel == NULL) | ||
89 | return -ENOMEM; | ||
90 | |||
91 | perf_evlist__add(evlist, evsel); | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | ||
96 | { | ||
97 | int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries; | ||
98 | evlist->pollfd = malloc(sizeof(struct pollfd) * nfds); | ||
99 | return evlist->pollfd != NULL ? 0 : -ENOMEM; | ||
100 | } | ||
101 | |||
102 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) | ||
103 | { | ||
104 | fcntl(fd, F_SETFL, O_NONBLOCK); | ||
105 | evlist->pollfd[evlist->nr_fds].fd = fd; | ||
106 | evlist->pollfd[evlist->nr_fds].events = POLLIN; | ||
107 | evlist->nr_fds++; | ||
108 | } | ||
109 | |||
110 | static void perf_evlist__id_hash(struct perf_evlist *evlist, | ||
111 | struct perf_evsel *evsel, | ||
112 | int cpu, int thread, u64 id) | ||
113 | { | ||
114 | int hash; | ||
115 | struct perf_sample_id *sid = SID(evsel, cpu, thread); | ||
116 | |||
117 | sid->id = id; | ||
118 | sid->evsel = evsel; | ||
119 | hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS); | ||
120 | hlist_add_head(&sid->node, &evlist->heads[hash]); | ||
121 | } | ||
122 | |||
123 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, | ||
124 | int cpu, int thread, u64 id) | ||
125 | { | ||
126 | perf_evlist__id_hash(evlist, evsel, cpu, thread, id); | ||
127 | evsel->id[evsel->ids++] = id; | ||
128 | } | ||
129 | |||
130 | static int perf_evlist__id_add_fd(struct perf_evlist *evlist, | ||
131 | struct perf_evsel *evsel, | ||
132 | int cpu, int thread, int fd) | ||
133 | { | ||
134 | u64 read_data[4] = { 0, }; | ||
135 | int id_idx = 1; /* The first entry is the counter value */ | ||
136 | |||
137 | if (!(evsel->attr.read_format & PERF_FORMAT_ID) || | ||
138 | read(fd, &read_data, sizeof(read_data)) == -1) | ||
139 | return -1; | ||
140 | |||
141 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | ||
142 | ++id_idx; | ||
143 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | ||
144 | ++id_idx; | ||
145 | |||
146 | perf_evlist__id_add(evlist, evsel, cpu, thread, read_data[id_idx]); | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | ||
151 | { | ||
152 | struct hlist_head *head; | ||
153 | struct hlist_node *pos; | ||
154 | struct perf_sample_id *sid; | ||
155 | int hash; | ||
156 | |||
157 | if (evlist->nr_entries == 1) | ||
158 | return list_entry(evlist->entries.next, struct perf_evsel, node); | ||
159 | |||
160 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); | ||
161 | head = &evlist->heads[hash]; | ||
162 | |||
163 | hlist_for_each_entry(sid, pos, head, node) | ||
164 | if (sid->id == id) | ||
165 | return sid->evsel; | ||
166 | return NULL; | ||
167 | } | ||
168 | |||
169 | union perf_event *perf_evlist__read_on_cpu(struct perf_evlist *evlist, int cpu) | ||
170 | { | ||
171 | /* XXX Move this to perf.c, making it generally available */ | ||
172 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | ||
173 | struct perf_mmap *md = &evlist->mmap[cpu]; | ||
174 | unsigned int head = perf_mmap__read_head(md); | ||
175 | unsigned int old = md->prev; | ||
176 | unsigned char *data = md->base + page_size; | ||
177 | union perf_event *event = NULL; | ||
178 | |||
179 | if (evlist->overwrite) { | ||
180 | /* | ||
181 | * If we're further behind than half the buffer, there's a chance | ||
182 | * the writer will bite our tail and mess up the samples under us. | ||
183 | * | ||
184 | * If we somehow ended up ahead of the head, we got messed up. | ||
185 | * | ||
186 | * In either case, truncate and restart at head. | ||
187 | */ | ||
188 | int diff = head - old; | ||
189 | if (diff > md->mask / 2 || diff < 0) { | ||
190 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); | ||
191 | |||
192 | /* | ||
193 | * head points to a known good entry, start there. | ||
194 | */ | ||
195 | old = head; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | if (old != head) { | ||
200 | size_t size; | ||
201 | |||
202 | event = (union perf_event *)&data[old & md->mask]; | ||
203 | size = event->header.size; | ||
204 | |||
205 | /* | ||
206 | * Event straddles the mmap boundary -- header should always | ||
207 | * be inside due to u64 alignment of output. | ||
208 | */ | ||
209 | if ((old & md->mask) + size != ((old + size) & md->mask)) { | ||
210 | unsigned int offset = old; | ||
211 | unsigned int len = min(sizeof(*event), size), cpy; | ||
212 | void *dst = &evlist->event_copy; | ||
213 | |||
214 | do { | ||
215 | cpy = min(md->mask + 1 - (offset & md->mask), len); | ||
216 | memcpy(dst, &data[offset & md->mask], cpy); | ||
217 | offset += cpy; | ||
218 | dst += cpy; | ||
219 | len -= cpy; | ||
220 | } while (len); | ||
221 | |||
222 | event = &evlist->event_copy; | ||
223 | } | ||
224 | |||
225 | old += size; | ||
226 | } | ||
227 | |||
228 | md->prev = old; | ||
229 | |||
230 | if (!evlist->overwrite) | ||
231 | perf_mmap__write_tail(md, old); | ||
232 | |||
233 | return event; | ||
234 | } | ||
235 | |||
236 | void perf_evlist__munmap(struct perf_evlist *evlist) | ||
237 | { | ||
238 | int cpu; | ||
239 | |||
240 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | ||
241 | if (evlist->mmap[cpu].base != NULL) { | ||
242 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | ||
243 | evlist->mmap[cpu].base = NULL; | ||
244 | } | ||
245 | } | ||
246 | } | ||
247 | |||
248 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist) | ||
249 | { | ||
250 | evlist->mmap = zalloc(evlist->cpus->nr * sizeof(struct perf_mmap)); | ||
251 | return evlist->mmap != NULL ? 0 : -ENOMEM; | ||
252 | } | ||
253 | |||
254 | static int __perf_evlist__mmap(struct perf_evlist *evlist, struct perf_evsel *evsel, | ||
255 | int cpu, int prot, int mask, int fd) | ||
256 | { | ||
257 | evlist->mmap[cpu].prev = 0; | ||
258 | evlist->mmap[cpu].mask = mask; | ||
259 | evlist->mmap[cpu].base = mmap(NULL, evlist->mmap_len, prot, | ||
260 | MAP_SHARED, fd, 0); | ||
261 | if (evlist->mmap[cpu].base == MAP_FAILED) { | ||
262 | if (evlist->cpus->map[cpu] == -1 && evsel->attr.inherit) | ||
263 | ui__warning("Inherit is not allowed on per-task " | ||
264 | "events using mmap.\n"); | ||
265 | return -1; | ||
266 | } | ||
267 | |||
268 | perf_evlist__add_pollfd(evlist, fd); | ||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | /** perf_evlist__mmap - Create per cpu maps to receive events | ||
273 | * | ||
274 | * @evlist - list of events | ||
275 | * @pages - map length in pages | ||
276 | * @overwrite - overwrite older events? | ||
277 | * | ||
278 | * If overwrite is false the user needs to signal event consuption using: | ||
279 | * | ||
280 | * struct perf_mmap *m = &evlist->mmap[cpu]; | ||
281 | * unsigned int head = perf_mmap__read_head(m); | ||
282 | * | ||
283 | * perf_mmap__write_tail(m, head) | ||
284 | * | ||
285 | * Using perf_evlist__read_on_cpu does this automatically. | ||
286 | */ | ||
287 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite) | ||
288 | { | ||
289 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | ||
290 | int mask = pages * page_size - 1, cpu; | ||
291 | struct perf_evsel *first_evsel, *evsel; | ||
292 | const struct cpu_map *cpus = evlist->cpus; | ||
293 | const struct thread_map *threads = evlist->threads; | ||
294 | int thread, prot = PROT_READ | (overwrite ? 0 : PROT_WRITE); | ||
295 | |||
296 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) | ||
297 | return -ENOMEM; | ||
298 | |||
299 | if (evlist->pollfd == NULL && perf_evlist__alloc_pollfd(evlist) < 0) | ||
300 | return -ENOMEM; | ||
301 | |||
302 | evlist->overwrite = overwrite; | ||
303 | evlist->mmap_len = (pages + 1) * page_size; | ||
304 | first_evsel = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
305 | |||
306 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
307 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
308 | evsel->sample_id == NULL && | ||
309 | perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0) | ||
310 | return -ENOMEM; | ||
311 | |||
312 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
313 | for (thread = 0; thread < threads->nr; thread++) { | ||
314 | int fd = FD(evsel, cpu, thread); | ||
315 | |||
316 | if (evsel->idx || thread) { | ||
317 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, | ||
318 | FD(first_evsel, cpu, 0)) != 0) | ||
319 | goto out_unmap; | ||
320 | } else if (__perf_evlist__mmap(evlist, evsel, cpu, | ||
321 | prot, mask, fd) < 0) | ||
322 | goto out_unmap; | ||
323 | |||
324 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
325 | perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) | ||
326 | goto out_unmap; | ||
327 | } | ||
328 | } | ||
329 | } | ||
330 | |||
331 | return 0; | ||
332 | |||
333 | out_unmap: | ||
334 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
335 | if (evlist->mmap[cpu].base != NULL) { | ||
336 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | ||
337 | evlist->mmap[cpu].base = NULL; | ||
338 | } | ||
339 | } | ||
340 | return -1; | ||
341 | } | ||
342 | |||
343 | int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, | ||
344 | pid_t target_tid, const char *cpu_list) | ||
345 | { | ||
346 | evlist->threads = thread_map__new(target_pid, target_tid); | ||
347 | |||
348 | if (evlist->threads == NULL) | ||
349 | return -1; | ||
350 | |||
351 | if (target_tid != -1) | ||
352 | evlist->cpus = cpu_map__dummy_new(); | ||
353 | else | ||
354 | evlist->cpus = cpu_map__new(cpu_list); | ||
355 | |||
356 | if (evlist->cpus == NULL) | ||
357 | goto out_delete_threads; | ||
358 | |||
359 | return 0; | ||
360 | |||
361 | out_delete_threads: | ||
362 | thread_map__delete(evlist->threads); | ||
363 | return -1; | ||
364 | } | ||
365 | |||
366 | void perf_evlist__delete_maps(struct perf_evlist *evlist) | ||
367 | { | ||
368 | cpu_map__delete(evlist->cpus); | ||
369 | thread_map__delete(evlist->threads); | ||
370 | evlist->cpus = NULL; | ||
371 | evlist->threads = NULL; | ||
372 | } | ||
373 | |||
374 | int perf_evlist__set_filters(struct perf_evlist *evlist) | ||
375 | { | ||
376 | const struct thread_map *threads = evlist->threads; | ||
377 | const struct cpu_map *cpus = evlist->cpus; | ||
378 | struct perf_evsel *evsel; | ||
379 | char *filter; | ||
380 | int thread; | ||
381 | int cpu; | ||
382 | int err; | ||
383 | int fd; | ||
384 | |||
385 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
386 | filter = evsel->filter; | ||
387 | if (!filter) | ||
388 | continue; | ||
389 | for (cpu = 0; cpu < cpus->nr; cpu++) { | ||
390 | for (thread = 0; thread < threads->nr; thread++) { | ||
391 | fd = FD(evsel, cpu, thread); | ||
392 | err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter); | ||
393 | if (err) | ||
394 | return err; | ||
395 | } | ||
396 | } | ||
397 | } | ||
398 | |||
399 | return 0; | ||
400 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h new file mode 100644 index 000000000000..8b1cb7a4c5f1 --- /dev/null +++ b/tools/perf/util/evlist.h | |||
@@ -0,0 +1,68 @@ | |||
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 | struct thread_map; | ||
10 | struct cpu_map; | ||
11 | |||
12 | #define PERF_EVLIST__HLIST_BITS 8 | ||
13 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) | ||
14 | |||
15 | struct perf_evlist { | ||
16 | struct list_head entries; | ||
17 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; | ||
18 | int nr_entries; | ||
19 | int nr_fds; | ||
20 | int mmap_len; | ||
21 | bool overwrite; | ||
22 | union perf_event event_copy; | ||
23 | struct perf_mmap *mmap; | ||
24 | struct pollfd *pollfd; | ||
25 | struct thread_map *threads; | ||
26 | struct cpu_map *cpus; | ||
27 | }; | ||
28 | |||
29 | struct perf_evsel; | ||
30 | |||
31 | struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | ||
32 | struct thread_map *threads); | ||
33 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | ||
34 | struct thread_map *threads); | ||
35 | void perf_evlist__exit(struct perf_evlist *evlist); | ||
36 | void perf_evlist__delete(struct perf_evlist *evlist); | ||
37 | |||
38 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry); | ||
39 | int perf_evlist__add_default(struct perf_evlist *evlist); | ||
40 | |||
41 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, | ||
42 | int cpu, int thread, u64 id); | ||
43 | |||
44 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist); | ||
45 | void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); | ||
46 | |||
47 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | ||
48 | |||
49 | union perf_event *perf_evlist__read_on_cpu(struct perf_evlist *self, int cpu); | ||
50 | |||
51 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist); | ||
52 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); | ||
53 | void perf_evlist__munmap(struct perf_evlist *evlist); | ||
54 | |||
55 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | ||
56 | struct cpu_map *cpus, | ||
57 | struct thread_map *threads) | ||
58 | { | ||
59 | evlist->cpus = cpus; | ||
60 | evlist->threads = threads; | ||
61 | } | ||
62 | |||
63 | int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, | ||
64 | pid_t target_tid, const char *cpu_list); | ||
65 | void perf_evlist__delete_maps(struct perf_evlist *evlist); | ||
66 | int perf_evlist__set_filters(struct perf_evlist *evlist); | ||
67 | |||
68 | #endif /* __PERF_EVLIST_H */ | ||
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f5cfed60af98..d6fd59beb860 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -1,20 +1,34 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Parts came from builtin-{top,stat,record}.c, see those files for further | ||
5 | * copyright notes. | ||
6 | * | ||
7 | * Released under the GPL v2. (and only v2, not any later version) | ||
8 | */ | ||
9 | |||
1 | #include "evsel.h" | 10 | #include "evsel.h" |
2 | #include "../perf.h" | 11 | #include "evlist.h" |
3 | #include "util.h" | 12 | #include "util.h" |
4 | #include "cpumap.h" | 13 | #include "cpumap.h" |
5 | #include "thread.h" | 14 | #include "thread_map.h" |
6 | 15 | ||
7 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 16 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
8 | 17 | ||
18 | void perf_evsel__init(struct perf_evsel *evsel, | ||
19 | struct perf_event_attr *attr, int idx) | ||
20 | { | ||
21 | evsel->idx = idx; | ||
22 | evsel->attr = *attr; | ||
23 | INIT_LIST_HEAD(&evsel->node); | ||
24 | } | ||
25 | |||
9 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | 26 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) |
10 | { | 27 | { |
11 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); | 28 | struct perf_evsel *evsel = zalloc(sizeof(*evsel)); |
12 | 29 | ||
13 | if (evsel != NULL) { | 30 | if (evsel != NULL) |
14 | evsel->idx = idx; | 31 | perf_evsel__init(evsel, attr, idx); |
15 | evsel->attr = *attr; | ||
16 | INIT_LIST_HEAD(&evsel->node); | ||
17 | } | ||
18 | 32 | ||
19 | return evsel; | 33 | return evsel; |
20 | } | 34 | } |
@@ -25,6 +39,22 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
25 | return evsel->fd != NULL ? 0 : -ENOMEM; | 39 | return evsel->fd != NULL ? 0 : -ENOMEM; |
26 | } | 40 | } |
27 | 41 | ||
42 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | ||
43 | { | ||
44 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); | ||
45 | if (evsel->sample_id == NULL) | ||
46 | return -ENOMEM; | ||
47 | |||
48 | evsel->id = zalloc(ncpus * nthreads * sizeof(u64)); | ||
49 | if (evsel->id == NULL) { | ||
50 | xyarray__delete(evsel->sample_id); | ||
51 | evsel->sample_id = NULL; | ||
52 | return -ENOMEM; | ||
53 | } | ||
54 | |||
55 | return 0; | ||
56 | } | ||
57 | |||
28 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) | 58 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus) |
29 | { | 59 | { |
30 | evsel->counts = zalloc((sizeof(*evsel->counts) + | 60 | evsel->counts = zalloc((sizeof(*evsel->counts) + |
@@ -38,6 +68,14 @@ void perf_evsel__free_fd(struct perf_evsel *evsel) | |||
38 | evsel->fd = NULL; | 68 | evsel->fd = NULL; |
39 | } | 69 | } |
40 | 70 | ||
71 | void perf_evsel__free_id(struct perf_evsel *evsel) | ||
72 | { | ||
73 | xyarray__delete(evsel->sample_id); | ||
74 | evsel->sample_id = NULL; | ||
75 | free(evsel->id); | ||
76 | evsel->id = NULL; | ||
77 | } | ||
78 | |||
41 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 79 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
42 | { | 80 | { |
43 | int cpu, thread; | 81 | int cpu, thread; |
@@ -49,10 +87,19 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
49 | } | 87 | } |
50 | } | 88 | } |
51 | 89 | ||
52 | void perf_evsel__delete(struct perf_evsel *evsel) | 90 | void perf_evsel__exit(struct perf_evsel *evsel) |
53 | { | 91 | { |
54 | assert(list_empty(&evsel->node)); | 92 | assert(list_empty(&evsel->node)); |
55 | xyarray__delete(evsel->fd); | 93 | xyarray__delete(evsel->fd); |
94 | xyarray__delete(evsel->sample_id); | ||
95 | free(evsel->id); | ||
96 | } | ||
97 | |||
98 | void perf_evsel__delete(struct perf_evsel *evsel) | ||
99 | { | ||
100 | perf_evsel__exit(evsel); | ||
101 | close_cgroup(evsel->cgrp); | ||
102 | free(evsel->name); | ||
56 | free(evsel); | 103 | free(evsel); |
57 | } | 104 | } |
58 | 105 | ||
@@ -90,7 +137,7 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
90 | int cpu, thread; | 137 | int cpu, thread; |
91 | struct perf_counts_values *aggr = &evsel->counts->aggr, count; | 138 | struct perf_counts_values *aggr = &evsel->counts->aggr, count; |
92 | 139 | ||
93 | aggr->val = 0; | 140 | aggr->val = aggr->ena = aggr->run = 0; |
94 | 141 | ||
95 | for (cpu = 0; cpu < ncpus; cpu++) { | 142 | for (cpu = 0; cpu < ncpus; cpu++) { |
96 | for (thread = 0; thread < nthreads; thread++) { | 143 | for (thread = 0; thread < nthreads; thread++) { |
@@ -128,21 +175,38 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
128 | } | 175 | } |
129 | 176 | ||
130 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 177 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
131 | struct thread_map *threads) | 178 | struct thread_map *threads, bool group) |
132 | { | 179 | { |
133 | int cpu, thread; | 180 | int cpu, thread; |
181 | unsigned long flags = 0; | ||
182 | int pid = -1; | ||
134 | 183 | ||
135 | if (evsel->fd == NULL && | 184 | if (evsel->fd == NULL && |
136 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) | 185 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) |
137 | return -1; | 186 | return -1; |
138 | 187 | ||
188 | if (evsel->cgrp) { | ||
189 | flags = PERF_FLAG_PID_CGROUP; | ||
190 | pid = evsel->cgrp->fd; | ||
191 | } | ||
192 | |||
139 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 193 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
194 | int group_fd = -1; | ||
195 | |||
140 | for (thread = 0; thread < threads->nr; thread++) { | 196 | for (thread = 0; thread < threads->nr; thread++) { |
197 | |||
198 | if (!evsel->cgrp) | ||
199 | pid = threads->map[thread]; | ||
200 | |||
141 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, | 201 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, |
142 | threads->map[thread], | 202 | pid, |
143 | cpus->map[cpu], -1, 0); | 203 | cpus->map[cpu], |
204 | group_fd, flags); | ||
144 | if (FD(evsel, cpu, thread) < 0) | 205 | if (FD(evsel, cpu, thread) < 0) |
145 | goto out_close; | 206 | goto out_close; |
207 | |||
208 | if (group && group_fd == -1) | ||
209 | group_fd = FD(evsel, cpu, thread); | ||
146 | } | 210 | } |
147 | } | 211 | } |
148 | 212 | ||
@@ -175,10 +239,9 @@ static struct { | |||
175 | .threads = { -1, }, | 239 | .threads = { -1, }, |
176 | }; | 240 | }; |
177 | 241 | ||
178 | int perf_evsel__open(struct perf_evsel *evsel, | 242 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
179 | struct cpu_map *cpus, struct thread_map *threads) | 243 | struct thread_map *threads, bool group) |
180 | { | 244 | { |
181 | |||
182 | if (cpus == NULL) { | 245 | if (cpus == NULL) { |
183 | /* Work around old compiler warnings about strict aliasing */ | 246 | /* Work around old compiler warnings about strict aliasing */ |
184 | cpus = &empty_cpu_map.map; | 247 | cpus = &empty_cpu_map.map; |
@@ -187,15 +250,135 @@ int perf_evsel__open(struct perf_evsel *evsel, | |||
187 | if (threads == NULL) | 250 | if (threads == NULL) |
188 | threads = &empty_thread_map.map; | 251 | threads = &empty_thread_map.map; |
189 | 252 | ||
190 | return __perf_evsel__open(evsel, cpus, threads); | 253 | return __perf_evsel__open(evsel, cpus, threads, group); |
191 | } | 254 | } |
192 | 255 | ||
193 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus) | 256 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, |
257 | struct cpu_map *cpus, bool group) | ||
194 | { | 258 | { |
195 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map); | 259 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group); |
260 | } | ||
261 | |||
262 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, | ||
263 | struct thread_map *threads, bool group) | ||
264 | { | ||
265 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group); | ||
266 | } | ||
267 | |||
268 | static int perf_event__parse_id_sample(const union perf_event *event, u64 type, | ||
269 | struct perf_sample *sample) | ||
270 | { | ||
271 | const u64 *array = event->sample.array; | ||
272 | |||
273 | array += ((event->header.size - | ||
274 | sizeof(event->header)) / sizeof(u64)) - 1; | ||
275 | |||
276 | if (type & PERF_SAMPLE_CPU) { | ||
277 | u32 *p = (u32 *)array; | ||
278 | sample->cpu = *p; | ||
279 | array--; | ||
280 | } | ||
281 | |||
282 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
283 | sample->stream_id = *array; | ||
284 | array--; | ||
285 | } | ||
286 | |||
287 | if (type & PERF_SAMPLE_ID) { | ||
288 | sample->id = *array; | ||
289 | array--; | ||
290 | } | ||
291 | |||
292 | if (type & PERF_SAMPLE_TIME) { | ||
293 | sample->time = *array; | ||
294 | array--; | ||
295 | } | ||
296 | |||
297 | if (type & PERF_SAMPLE_TID) { | ||
298 | u32 *p = (u32 *)array; | ||
299 | sample->pid = p[0]; | ||
300 | sample->tid = p[1]; | ||
301 | } | ||
302 | |||
303 | return 0; | ||
196 | } | 304 | } |
197 | 305 | ||
198 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, struct thread_map *threads) | 306 | int perf_event__parse_sample(const union perf_event *event, u64 type, |
307 | bool sample_id_all, struct perf_sample *data) | ||
199 | { | 308 | { |
200 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads); | 309 | const u64 *array; |
310 | |||
311 | data->cpu = data->pid = data->tid = -1; | ||
312 | data->stream_id = data->id = data->time = -1ULL; | ||
313 | |||
314 | if (event->header.type != PERF_RECORD_SAMPLE) { | ||
315 | if (!sample_id_all) | ||
316 | return 0; | ||
317 | return perf_event__parse_id_sample(event, type, data); | ||
318 | } | ||
319 | |||
320 | array = event->sample.array; | ||
321 | |||
322 | if (type & PERF_SAMPLE_IP) { | ||
323 | data->ip = event->ip.ip; | ||
324 | array++; | ||
325 | } | ||
326 | |||
327 | if (type & PERF_SAMPLE_TID) { | ||
328 | u32 *p = (u32 *)array; | ||
329 | data->pid = p[0]; | ||
330 | data->tid = p[1]; | ||
331 | array++; | ||
332 | } | ||
333 | |||
334 | if (type & PERF_SAMPLE_TIME) { | ||
335 | data->time = *array; | ||
336 | array++; | ||
337 | } | ||
338 | |||
339 | if (type & PERF_SAMPLE_ADDR) { | ||
340 | data->addr = *array; | ||
341 | array++; | ||
342 | } | ||
343 | |||
344 | data->id = -1ULL; | ||
345 | if (type & PERF_SAMPLE_ID) { | ||
346 | data->id = *array; | ||
347 | array++; | ||
348 | } | ||
349 | |||
350 | if (type & PERF_SAMPLE_STREAM_ID) { | ||
351 | data->stream_id = *array; | ||
352 | array++; | ||
353 | } | ||
354 | |||
355 | if (type & PERF_SAMPLE_CPU) { | ||
356 | u32 *p = (u32 *)array; | ||
357 | data->cpu = *p; | ||
358 | array++; | ||
359 | } | ||
360 | |||
361 | if (type & PERF_SAMPLE_PERIOD) { | ||
362 | data->period = *array; | ||
363 | array++; | ||
364 | } | ||
365 | |||
366 | if (type & PERF_SAMPLE_READ) { | ||
367 | fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n"); | ||
368 | return -1; | ||
369 | } | ||
370 | |||
371 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
372 | data->callchain = (struct ip_callchain *)array; | ||
373 | array += 1 + data->callchain->nr; | ||
374 | } | ||
375 | |||
376 | if (type & PERF_SAMPLE_RAW) { | ||
377 | u32 *p = (u32 *)array; | ||
378 | data->raw_size = *p; | ||
379 | p++; | ||
380 | data->raw_data = p; | ||
381 | } | ||
382 | |||
383 | return 0; | ||
201 | } | 384 | } |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index b2d755fe88a5..f79bb2c09a6c 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -6,6 +6,8 @@ | |||
6 | #include "../../../include/linux/perf_event.h" | 6 | #include "../../../include/linux/perf_event.h" |
7 | #include "types.h" | 7 | #include "types.h" |
8 | #include "xyarray.h" | 8 | #include "xyarray.h" |
9 | #include "cgroup.h" | ||
10 | #include "hist.h" | ||
9 | 11 | ||
10 | struct perf_counts_values { | 12 | struct perf_counts_values { |
11 | union { | 13 | union { |
@@ -24,31 +26,66 @@ struct perf_counts { | |||
24 | struct perf_counts_values cpu[]; | 26 | struct perf_counts_values cpu[]; |
25 | }; | 27 | }; |
26 | 28 | ||
29 | struct perf_evsel; | ||
30 | |||
31 | /* | ||
32 | * Per fd, to map back from PERF_SAMPLE_ID to evsel, only used when there are | ||
33 | * more than one entry in the evlist. | ||
34 | */ | ||
35 | struct perf_sample_id { | ||
36 | struct hlist_node node; | ||
37 | u64 id; | ||
38 | struct perf_evsel *evsel; | ||
39 | }; | ||
40 | |||
41 | /** struct perf_evsel - event selector | ||
42 | * | ||
43 | * @name - Can be set to retain the original event name passed by the user, | ||
44 | * so that when showing results in tools such as 'perf stat', we | ||
45 | * show the name used, not some alias. | ||
46 | */ | ||
27 | struct perf_evsel { | 47 | struct perf_evsel { |
28 | struct list_head node; | 48 | struct list_head node; |
29 | struct perf_event_attr attr; | 49 | struct perf_event_attr attr; |
30 | char *filter; | 50 | char *filter; |
31 | struct xyarray *fd; | 51 | struct xyarray *fd; |
52 | struct xyarray *sample_id; | ||
53 | u64 *id; | ||
32 | struct perf_counts *counts; | 54 | struct perf_counts *counts; |
33 | int idx; | 55 | int idx; |
34 | void *priv; | 56 | int ids; |
57 | struct hists hists; | ||
58 | char *name; | ||
59 | union { | ||
60 | void *priv; | ||
61 | off_t id_offset; | ||
62 | }; | ||
63 | struct cgroup_sel *cgrp; | ||
35 | }; | 64 | }; |
36 | 65 | ||
37 | struct cpu_map; | 66 | struct cpu_map; |
38 | struct thread_map; | 67 | struct thread_map; |
68 | struct perf_evlist; | ||
39 | 69 | ||
40 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); | 70 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx); |
71 | void perf_evsel__init(struct perf_evsel *evsel, | ||
72 | struct perf_event_attr *attr, int idx); | ||
73 | void perf_evsel__exit(struct perf_evsel *evsel); | ||
41 | void perf_evsel__delete(struct perf_evsel *evsel); | 74 | void perf_evsel__delete(struct perf_evsel *evsel); |
42 | 75 | ||
43 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 76 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
77 | 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); | 78 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); |
45 | void perf_evsel__free_fd(struct perf_evsel *evsel); | 79 | void perf_evsel__free_fd(struct perf_evsel *evsel); |
80 | void perf_evsel__free_id(struct perf_evsel *evsel); | ||
46 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 81 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
47 | 82 | ||
48 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus); | 83 | 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); | 84 | struct cpu_map *cpus, bool group); |
50 | int perf_evsel__open(struct perf_evsel *evsel, | 85 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, |
51 | struct cpu_map *cpus, struct thread_map *threads); | 86 | struct thread_map *threads, bool group); |
87 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | ||
88 | struct thread_map *threads, bool group); | ||
52 | 89 | ||
53 | #define perf_evsel__match(evsel, t, c) \ | 90 | #define perf_evsel__match(evsel, t, c) \ |
54 | (evsel->attr.type == PERF_TYPE_##t && \ | 91 | (evsel->attr.type == PERF_TYPE_##t && \ |
diff --git a/tools/perf/util/exec_cmd.c b/tools/perf/util/exec_cmd.c index 67eeff571568..7adf4ad15d8f 100644 --- a/tools/perf/util/exec_cmd.c +++ b/tools/perf/util/exec_cmd.c | |||
@@ -11,31 +11,12 @@ static const char *argv0_path; | |||
11 | 11 | ||
12 | const char *system_path(const char *path) | 12 | const char *system_path(const char *path) |
13 | { | 13 | { |
14 | #ifdef RUNTIME_PREFIX | ||
15 | static const char *prefix; | ||
16 | #else | ||
17 | static const char *prefix = PREFIX; | 14 | static const char *prefix = PREFIX; |
18 | #endif | ||
19 | struct strbuf d = STRBUF_INIT; | 15 | struct strbuf d = STRBUF_INIT; |
20 | 16 | ||
21 | if (is_absolute_path(path)) | 17 | if (is_absolute_path(path)) |
22 | return path; | 18 | return path; |
23 | 19 | ||
24 | #ifdef RUNTIME_PREFIX | ||
25 | assert(argv0_path); | ||
26 | assert(is_absolute_path(argv0_path)); | ||
27 | |||
28 | if (!prefix && | ||
29 | !(prefix = strip_path_suffix(argv0_path, PERF_EXEC_PATH)) && | ||
30 | !(prefix = strip_path_suffix(argv0_path, BINDIR)) && | ||
31 | !(prefix = strip_path_suffix(argv0_path, "perf"))) { | ||
32 | prefix = PREFIX; | ||
33 | fprintf(stderr, "RUNTIME_PREFIX requested, " | ||
34 | "but prefix computation failed. " | ||
35 | "Using static fallback '%s'.\n", prefix); | ||
36 | } | ||
37 | #endif | ||
38 | |||
39 | strbuf_addf(&d, "%s/%s", prefix, path); | 20 | strbuf_addf(&d, "%s/%s", prefix, path); |
40 | path = strbuf_detach(&d, NULL); | 21 | path = strbuf_detach(&d, NULL); |
41 | return path; | 22 | return path; |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 989fa2dee2fd..93862a8027ea 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -8,6 +8,8 @@ | |||
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" | ||
12 | #include "evsel.h" | ||
11 | #include "util.h" | 13 | #include "util.h" |
12 | #include "header.h" | 14 | #include "header.h" |
13 | #include "../perf.h" | 15 | #include "../perf.h" |
@@ -18,89 +20,6 @@ | |||
18 | 20 | ||
19 | static bool no_buildid_cache = false; | 21 | static bool no_buildid_cache = false; |
20 | 22 | ||
21 | /* | ||
22 | * Create new perf.data header attribute: | ||
23 | */ | ||
24 | struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr) | ||
25 | { | ||
26 | struct perf_header_attr *self = malloc(sizeof(*self)); | ||
27 | |||
28 | if (self != NULL) { | ||
29 | self->attr = *attr; | ||
30 | self->ids = 0; | ||
31 | self->size = 1; | ||
32 | self->id = malloc(sizeof(u64)); | ||
33 | if (self->id == NULL) { | ||
34 | free(self); | ||
35 | self = NULL; | ||
36 | } | ||
37 | } | ||
38 | |||
39 | return self; | ||
40 | } | ||
41 | |||
42 | void perf_header_attr__delete(struct perf_header_attr *self) | ||
43 | { | ||
44 | free(self->id); | ||
45 | free(self); | ||
46 | } | ||
47 | |||
48 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id) | ||
49 | { | ||
50 | int pos = self->ids; | ||
51 | |||
52 | self->ids++; | ||
53 | if (self->ids > self->size) { | ||
54 | int nsize = self->size * 2; | ||
55 | u64 *nid = realloc(self->id, nsize * sizeof(u64)); | ||
56 | |||
57 | if (nid == NULL) | ||
58 | return -1; | ||
59 | |||
60 | self->size = nsize; | ||
61 | self->id = nid; | ||
62 | } | ||
63 | self->id[pos] = id; | ||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | int perf_header__init(struct perf_header *self) | ||
68 | { | ||
69 | self->size = 1; | ||
70 | self->attr = malloc(sizeof(void *)); | ||
71 | return self->attr == NULL ? -ENOMEM : 0; | ||
72 | } | ||
73 | |||
74 | void perf_header__exit(struct perf_header *self) | ||
75 | { | ||
76 | int i; | ||
77 | for (i = 0; i < self->attrs; ++i) | ||
78 | perf_header_attr__delete(self->attr[i]); | ||
79 | free(self->attr); | ||
80 | } | ||
81 | |||
82 | int perf_header__add_attr(struct perf_header *self, | ||
83 | struct perf_header_attr *attr) | ||
84 | { | ||
85 | if (self->frozen) | ||
86 | return -1; | ||
87 | |||
88 | if (self->attrs == self->size) { | ||
89 | int nsize = self->size * 2; | ||
90 | struct perf_header_attr **nattr; | ||
91 | |||
92 | nattr = realloc(self->attr, nsize * sizeof(void *)); | ||
93 | if (nattr == NULL) | ||
94 | return -1; | ||
95 | |||
96 | self->size = nsize; | ||
97 | self->attr = nattr; | ||
98 | } | ||
99 | |||
100 | self->attr[self->attrs++] = attr; | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int event_count; | 23 | static int event_count; |
105 | static struct perf_trace_event_type *events; | 24 | static struct perf_trace_event_type *events; |
106 | 25 | ||
@@ -147,19 +66,19 @@ struct perf_file_attr { | |||
147 | struct perf_file_section ids; | 66 | struct perf_file_section ids; |
148 | }; | 67 | }; |
149 | 68 | ||
150 | void perf_header__set_feat(struct perf_header *self, int feat) | 69 | void perf_header__set_feat(struct perf_header *header, int feat) |
151 | { | 70 | { |
152 | set_bit(feat, self->adds_features); | 71 | set_bit(feat, header->adds_features); |
153 | } | 72 | } |
154 | 73 | ||
155 | void perf_header__clear_feat(struct perf_header *self, int feat) | 74 | void perf_header__clear_feat(struct perf_header *header, int feat) |
156 | { | 75 | { |
157 | clear_bit(feat, self->adds_features); | 76 | clear_bit(feat, header->adds_features); |
158 | } | 77 | } |
159 | 78 | ||
160 | bool perf_header__has_feat(const struct perf_header *self, int feat) | 79 | bool perf_header__has_feat(const struct perf_header *header, int feat) |
161 | { | 80 | { |
162 | return test_bit(feat, self->adds_features); | 81 | return test_bit(feat, header->adds_features); |
163 | } | 82 | } |
164 | 83 | ||
165 | static int do_write(int fd, const void *buf, size_t size) | 84 | static int do_write(int fd, const void *buf, size_t size) |
@@ -228,22 +147,22 @@ static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, | |||
228 | return 0; | 147 | return 0; |
229 | } | 148 | } |
230 | 149 | ||
231 | static int machine__write_buildid_table(struct machine *self, int fd) | 150 | static int machine__write_buildid_table(struct machine *machine, int fd) |
232 | { | 151 | { |
233 | int err; | 152 | int err; |
234 | u16 kmisc = PERF_RECORD_MISC_KERNEL, | 153 | u16 kmisc = PERF_RECORD_MISC_KERNEL, |
235 | umisc = PERF_RECORD_MISC_USER; | 154 | umisc = PERF_RECORD_MISC_USER; |
236 | 155 | ||
237 | if (!machine__is_host(self)) { | 156 | if (!machine__is_host(machine)) { |
238 | kmisc = PERF_RECORD_MISC_GUEST_KERNEL; | 157 | kmisc = PERF_RECORD_MISC_GUEST_KERNEL; |
239 | umisc = PERF_RECORD_MISC_GUEST_USER; | 158 | umisc = PERF_RECORD_MISC_GUEST_USER; |
240 | } | 159 | } |
241 | 160 | ||
242 | err = __dsos__write_buildid_table(&self->kernel_dsos, self->pid, | 161 | err = __dsos__write_buildid_table(&machine->kernel_dsos, machine->pid, |
243 | kmisc, fd); | 162 | kmisc, fd); |
244 | if (err == 0) | 163 | if (err == 0) |
245 | err = __dsos__write_buildid_table(&self->user_dsos, | 164 | err = __dsos__write_buildid_table(&machine->user_dsos, |
246 | self->pid, umisc, fd); | 165 | machine->pid, umisc, fd); |
247 | return err; | 166 | return err; |
248 | } | 167 | } |
249 | 168 | ||
@@ -270,11 +189,15 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | |||
270 | const char *name, bool is_kallsyms) | 189 | const char *name, bool is_kallsyms) |
271 | { | 190 | { |
272 | const size_t size = PATH_MAX; | 191 | const size_t size = PATH_MAX; |
273 | char *realname = realpath(name, NULL), | 192 | char *realname, *filename = malloc(size), |
274 | *filename = malloc(size), | ||
275 | *linkname = malloc(size), *targetname; | 193 | *linkname = malloc(size), *targetname; |
276 | int len, err = -1; | 194 | int len, err = -1; |
277 | 195 | ||
196 | if (is_kallsyms) | ||
197 | realname = (char *)name; | ||
198 | else | ||
199 | realname = realpath(name, NULL); | ||
200 | |||
278 | if (realname == NULL || filename == NULL || linkname == NULL) | 201 | if (realname == NULL || filename == NULL || linkname == NULL) |
279 | goto out_free; | 202 | goto out_free; |
280 | 203 | ||
@@ -306,7 +229,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | |||
306 | if (symlink(targetname, linkname) == 0) | 229 | if (symlink(targetname, linkname) == 0) |
307 | err = 0; | 230 | err = 0; |
308 | out_free: | 231 | out_free: |
309 | free(realname); | 232 | if (!is_kallsyms) |
233 | free(realname); | ||
310 | free(filename); | 234 | free(filename); |
311 | free(linkname); | 235 | free(linkname); |
312 | return err; | 236 | return err; |
@@ -361,12 +285,12 @@ out_free: | |||
361 | return err; | 285 | return err; |
362 | } | 286 | } |
363 | 287 | ||
364 | static int dso__cache_build_id(struct dso *self, const char *debugdir) | 288 | static int dso__cache_build_id(struct dso *dso, const char *debugdir) |
365 | { | 289 | { |
366 | bool is_kallsyms = self->kernel && self->long_name[0] != '/'; | 290 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; |
367 | 291 | ||
368 | return build_id_cache__add_b(self->build_id, sizeof(self->build_id), | 292 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), |
369 | self->long_name, debugdir, is_kallsyms); | 293 | dso->long_name, debugdir, is_kallsyms); |
370 | } | 294 | } |
371 | 295 | ||
372 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) | 296 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) |
@@ -381,14 +305,14 @@ static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) | |||
381 | return err; | 305 | return err; |
382 | } | 306 | } |
383 | 307 | ||
384 | static int machine__cache_build_ids(struct machine *self, const char *debugdir) | 308 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) |
385 | { | 309 | { |
386 | int ret = __dsos__cache_build_ids(&self->kernel_dsos, debugdir); | 310 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos, debugdir); |
387 | ret |= __dsos__cache_build_ids(&self->user_dsos, debugdir); | 311 | ret |= __dsos__cache_build_ids(&machine->user_dsos, debugdir); |
388 | return ret; | 312 | return ret; |
389 | } | 313 | } |
390 | 314 | ||
391 | static int perf_session__cache_build_ids(struct perf_session *self) | 315 | static int perf_session__cache_build_ids(struct perf_session *session) |
392 | { | 316 | { |
393 | struct rb_node *nd; | 317 | struct rb_node *nd; |
394 | int ret; | 318 | int ret; |
@@ -399,28 +323,28 @@ static int perf_session__cache_build_ids(struct perf_session *self) | |||
399 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | 323 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) |
400 | return -1; | 324 | return -1; |
401 | 325 | ||
402 | ret = machine__cache_build_ids(&self->host_machine, debugdir); | 326 | ret = machine__cache_build_ids(&session->host_machine, debugdir); |
403 | 327 | ||
404 | for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) { | 328 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { |
405 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 329 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
406 | ret |= machine__cache_build_ids(pos, debugdir); | 330 | ret |= machine__cache_build_ids(pos, debugdir); |
407 | } | 331 | } |
408 | return ret ? -1 : 0; | 332 | return ret ? -1 : 0; |
409 | } | 333 | } |
410 | 334 | ||
411 | static bool machine__read_build_ids(struct machine *self, bool with_hits) | 335 | static bool machine__read_build_ids(struct machine *machine, bool with_hits) |
412 | { | 336 | { |
413 | bool ret = __dsos__read_build_ids(&self->kernel_dsos, with_hits); | 337 | bool ret = __dsos__read_build_ids(&machine->kernel_dsos, with_hits); |
414 | ret |= __dsos__read_build_ids(&self->user_dsos, with_hits); | 338 | ret |= __dsos__read_build_ids(&machine->user_dsos, with_hits); |
415 | return ret; | 339 | return ret; |
416 | } | 340 | } |
417 | 341 | ||
418 | static bool perf_session__read_build_ids(struct perf_session *self, bool with_hits) | 342 | static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) |
419 | { | 343 | { |
420 | struct rb_node *nd; | 344 | struct rb_node *nd; |
421 | bool ret = machine__read_build_ids(&self->host_machine, with_hits); | 345 | bool ret = machine__read_build_ids(&session->host_machine, with_hits); |
422 | 346 | ||
423 | for (nd = rb_first(&self->machines); nd; nd = rb_next(nd)) { | 347 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { |
424 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 348 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
425 | ret |= machine__read_build_ids(pos, with_hits); | 349 | ret |= machine__read_build_ids(pos, with_hits); |
426 | } | 350 | } |
@@ -428,7 +352,8 @@ static bool perf_session__read_build_ids(struct perf_session *self, bool with_hi | |||
428 | return ret; | 352 | return ret; |
429 | } | 353 | } |
430 | 354 | ||
431 | static int perf_header__adds_write(struct perf_header *self, int fd) | 355 | static int perf_header__adds_write(struct perf_header *header, |
356 | struct perf_evlist *evlist, int fd) | ||
432 | { | 357 | { |
433 | int nr_sections; | 358 | int nr_sections; |
434 | struct perf_session *session; | 359 | struct perf_session *session; |
@@ -437,13 +362,13 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
437 | u64 sec_start; | 362 | u64 sec_start; |
438 | int idx = 0, err; | 363 | int idx = 0, err; |
439 | 364 | ||
440 | session = container_of(self, struct perf_session, header); | 365 | session = container_of(header, struct perf_session, header); |
441 | 366 | ||
442 | if (perf_header__has_feat(self, HEADER_BUILD_ID && | 367 | if (perf_header__has_feat(header, HEADER_BUILD_ID && |
443 | !perf_session__read_build_ids(session, true))) | 368 | !perf_session__read_build_ids(session, true))) |
444 | perf_header__clear_feat(self, HEADER_BUILD_ID); | 369 | perf_header__clear_feat(header, HEADER_BUILD_ID); |
445 | 370 | ||
446 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | 371 | nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS); |
447 | if (!nr_sections) | 372 | if (!nr_sections) |
448 | return 0; | 373 | return 0; |
449 | 374 | ||
@@ -453,28 +378,28 @@ static int perf_header__adds_write(struct perf_header *self, int fd) | |||
453 | 378 | ||
454 | sec_size = sizeof(*feat_sec) * nr_sections; | 379 | sec_size = sizeof(*feat_sec) * nr_sections; |
455 | 380 | ||
456 | sec_start = self->data_offset + self->data_size; | 381 | sec_start = header->data_offset + header->data_size; |
457 | lseek(fd, sec_start + sec_size, SEEK_SET); | 382 | lseek(fd, sec_start + sec_size, SEEK_SET); |
458 | 383 | ||
459 | if (perf_header__has_feat(self, HEADER_TRACE_INFO)) { | 384 | if (perf_header__has_feat(header, HEADER_TRACE_INFO)) { |
460 | struct perf_file_section *trace_sec; | 385 | struct perf_file_section *trace_sec; |
461 | 386 | ||
462 | trace_sec = &feat_sec[idx++]; | 387 | trace_sec = &feat_sec[idx++]; |
463 | 388 | ||
464 | /* Write trace info */ | 389 | /* Write trace info */ |
465 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); | 390 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); |
466 | read_tracing_data(fd, &evsel_list); | 391 | read_tracing_data(fd, &evlist->entries); |
467 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; | 392 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; |
468 | } | 393 | } |
469 | 394 | ||
470 | if (perf_header__has_feat(self, HEADER_BUILD_ID)) { | 395 | if (perf_header__has_feat(header, HEADER_BUILD_ID)) { |
471 | struct perf_file_section *buildid_sec; | 396 | struct perf_file_section *buildid_sec; |
472 | 397 | ||
473 | buildid_sec = &feat_sec[idx++]; | 398 | buildid_sec = &feat_sec[idx++]; |
474 | 399 | ||
475 | /* Write build-ids */ | 400 | /* Write build-ids */ |
476 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); | 401 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); |
477 | err = dsos__write_buildid_table(self, fd); | 402 | err = dsos__write_buildid_table(header, fd); |
478 | if (err < 0) { | 403 | if (err < 0) { |
479 | pr_debug("failed to write buildid table\n"); | 404 | pr_debug("failed to write buildid table\n"); |
480 | goto out_free; | 405 | goto out_free; |
@@ -513,32 +438,41 @@ int perf_header__write_pipe(int fd) | |||
513 | return 0; | 438 | return 0; |
514 | } | 439 | } |
515 | 440 | ||
516 | int perf_header__write(struct perf_header *self, int fd, bool at_exit) | 441 | int perf_session__write_header(struct perf_session *session, |
442 | struct perf_evlist *evlist, | ||
443 | int fd, bool at_exit) | ||
517 | { | 444 | { |
518 | struct perf_file_header f_header; | 445 | struct perf_file_header f_header; |
519 | struct perf_file_attr f_attr; | 446 | struct perf_file_attr f_attr; |
520 | struct perf_header_attr *attr; | 447 | struct perf_header *header = &session->header; |
521 | int i, err; | 448 | struct perf_evsel *attr, *pair = NULL; |
449 | int err; | ||
522 | 450 | ||
523 | lseek(fd, sizeof(f_header), SEEK_SET); | 451 | lseek(fd, sizeof(f_header), SEEK_SET); |
524 | 452 | ||
525 | for (i = 0; i < self->attrs; i++) { | 453 | if (session->evlist != evlist) |
526 | attr = self->attr[i]; | 454 | pair = list_entry(session->evlist->entries.next, struct perf_evsel, node); |
527 | 455 | ||
456 | list_for_each_entry(attr, &evlist->entries, node) { | ||
528 | attr->id_offset = lseek(fd, 0, SEEK_CUR); | 457 | attr->id_offset = lseek(fd, 0, SEEK_CUR); |
529 | err = do_write(fd, attr->id, attr->ids * sizeof(u64)); | 458 | err = do_write(fd, attr->id, attr->ids * sizeof(u64)); |
530 | if (err < 0) { | 459 | if (err < 0) { |
460 | out_err_write: | ||
531 | pr_debug("failed to write perf header\n"); | 461 | pr_debug("failed to write perf header\n"); |
532 | return err; | 462 | return err; |
533 | } | 463 | } |
464 | if (session->evlist != evlist) { | ||
465 | err = do_write(fd, pair->id, pair->ids * sizeof(u64)); | ||
466 | if (err < 0) | ||
467 | goto out_err_write; | ||
468 | attr->ids += pair->ids; | ||
469 | pair = list_entry(pair->node.next, struct perf_evsel, node); | ||
470 | } | ||
534 | } | 471 | } |
535 | 472 | ||
473 | header->attr_offset = lseek(fd, 0, SEEK_CUR); | ||
536 | 474 | ||
537 | self->attr_offset = lseek(fd, 0, SEEK_CUR); | 475 | list_for_each_entry(attr, &evlist->entries, node) { |
538 | |||
539 | for (i = 0; i < self->attrs; i++) { | ||
540 | attr = self->attr[i]; | ||
541 | |||
542 | f_attr = (struct perf_file_attr){ | 476 | f_attr = (struct perf_file_attr){ |
543 | .attr = attr->attr, | 477 | .attr = attr->attr, |
544 | .ids = { | 478 | .ids = { |
@@ -553,20 +487,20 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) | |||
553 | } | 487 | } |
554 | } | 488 | } |
555 | 489 | ||
556 | self->event_offset = lseek(fd, 0, SEEK_CUR); | 490 | header->event_offset = lseek(fd, 0, SEEK_CUR); |
557 | self->event_size = event_count * sizeof(struct perf_trace_event_type); | 491 | header->event_size = event_count * sizeof(struct perf_trace_event_type); |
558 | if (events) { | 492 | if (events) { |
559 | err = do_write(fd, events, self->event_size); | 493 | err = do_write(fd, events, header->event_size); |
560 | if (err < 0) { | 494 | if (err < 0) { |
561 | pr_debug("failed to write perf header events\n"); | 495 | pr_debug("failed to write perf header events\n"); |
562 | return err; | 496 | return err; |
563 | } | 497 | } |
564 | } | 498 | } |
565 | 499 | ||
566 | self->data_offset = lseek(fd, 0, SEEK_CUR); | 500 | header->data_offset = lseek(fd, 0, SEEK_CUR); |
567 | 501 | ||
568 | if (at_exit) { | 502 | if (at_exit) { |
569 | err = perf_header__adds_write(self, fd); | 503 | err = perf_header__adds_write(header, evlist, fd); |
570 | if (err < 0) | 504 | if (err < 0) |
571 | return err; | 505 | return err; |
572 | } | 506 | } |
@@ -576,20 +510,20 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) | |||
576 | .size = sizeof(f_header), | 510 | .size = sizeof(f_header), |
577 | .attr_size = sizeof(f_attr), | 511 | .attr_size = sizeof(f_attr), |
578 | .attrs = { | 512 | .attrs = { |
579 | .offset = self->attr_offset, | 513 | .offset = header->attr_offset, |
580 | .size = self->attrs * sizeof(f_attr), | 514 | .size = evlist->nr_entries * sizeof(f_attr), |
581 | }, | 515 | }, |
582 | .data = { | 516 | .data = { |
583 | .offset = self->data_offset, | 517 | .offset = header->data_offset, |
584 | .size = self->data_size, | 518 | .size = header->data_size, |
585 | }, | 519 | }, |
586 | .event_types = { | 520 | .event_types = { |
587 | .offset = self->event_offset, | 521 | .offset = header->event_offset, |
588 | .size = self->event_size, | 522 | .size = header->event_size, |
589 | }, | 523 | }, |
590 | }; | 524 | }; |
591 | 525 | ||
592 | memcpy(&f_header.adds_features, &self->adds_features, sizeof(self->adds_features)); | 526 | memcpy(&f_header.adds_features, &header->adds_features, sizeof(header->adds_features)); |
593 | 527 | ||
594 | lseek(fd, 0, SEEK_SET); | 528 | lseek(fd, 0, SEEK_SET); |
595 | err = do_write(fd, &f_header, sizeof(f_header)); | 529 | err = do_write(fd, &f_header, sizeof(f_header)); |
@@ -597,26 +531,26 @@ int perf_header__write(struct perf_header *self, int fd, bool at_exit) | |||
597 | pr_debug("failed to write perf header\n"); | 531 | pr_debug("failed to write perf header\n"); |
598 | return err; | 532 | return err; |
599 | } | 533 | } |
600 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | 534 | lseek(fd, header->data_offset + header->data_size, SEEK_SET); |
601 | 535 | ||
602 | self->frozen = 1; | 536 | header->frozen = 1; |
603 | return 0; | 537 | return 0; |
604 | } | 538 | } |
605 | 539 | ||
606 | static int perf_header__getbuffer64(struct perf_header *self, | 540 | static int perf_header__getbuffer64(struct perf_header *header, |
607 | int fd, void *buf, size_t size) | 541 | int fd, void *buf, size_t size) |
608 | { | 542 | { |
609 | if (readn(fd, buf, size) <= 0) | 543 | if (readn(fd, buf, size) <= 0) |
610 | return -1; | 544 | return -1; |
611 | 545 | ||
612 | if (self->needs_swap) | 546 | if (header->needs_swap) |
613 | mem_bswap_64(buf, size); | 547 | mem_bswap_64(buf, size); |
614 | 548 | ||
615 | return 0; | 549 | return 0; |
616 | } | 550 | } |
617 | 551 | ||
618 | int perf_header__process_sections(struct perf_header *self, int fd, | 552 | int perf_header__process_sections(struct perf_header *header, int fd, |
619 | int (*process)(struct perf_file_section *self, | 553 | int (*process)(struct perf_file_section *section, |
620 | struct perf_header *ph, | 554 | struct perf_header *ph, |
621 | int feat, int fd)) | 555 | int feat, int fd)) |
622 | { | 556 | { |
@@ -626,7 +560,7 @@ int perf_header__process_sections(struct perf_header *self, int fd, | |||
626 | int idx = 0; | 560 | int idx = 0; |
627 | int err = -1, feat = 1; | 561 | int err = -1, feat = 1; |
628 | 562 | ||
629 | nr_sections = bitmap_weight(self->adds_features, HEADER_FEAT_BITS); | 563 | nr_sections = bitmap_weight(header->adds_features, HEADER_FEAT_BITS); |
630 | if (!nr_sections) | 564 | if (!nr_sections) |
631 | return 0; | 565 | return 0; |
632 | 566 | ||
@@ -636,17 +570,17 @@ int perf_header__process_sections(struct perf_header *self, int fd, | |||
636 | 570 | ||
637 | sec_size = sizeof(*feat_sec) * nr_sections; | 571 | sec_size = sizeof(*feat_sec) * nr_sections; |
638 | 572 | ||
639 | lseek(fd, self->data_offset + self->data_size, SEEK_SET); | 573 | lseek(fd, header->data_offset + header->data_size, SEEK_SET); |
640 | 574 | ||
641 | if (perf_header__getbuffer64(self, fd, feat_sec, sec_size)) | 575 | if (perf_header__getbuffer64(header, fd, feat_sec, sec_size)) |
642 | goto out_free; | 576 | goto out_free; |
643 | 577 | ||
644 | err = 0; | 578 | err = 0; |
645 | while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { | 579 | while (idx < nr_sections && feat < HEADER_LAST_FEATURE) { |
646 | if (perf_header__has_feat(self, feat)) { | 580 | if (perf_header__has_feat(header, feat)) { |
647 | struct perf_file_section *sec = &feat_sec[idx++]; | 581 | struct perf_file_section *sec = &feat_sec[idx++]; |
648 | 582 | ||
649 | err = process(sec, self, feat, fd); | 583 | err = process(sec, header, feat, fd); |
650 | if (err < 0) | 584 | if (err < 0) |
651 | break; | 585 | break; |
652 | } | 586 | } |
@@ -657,35 +591,35 @@ out_free: | |||
657 | return err; | 591 | return err; |
658 | } | 592 | } |
659 | 593 | ||
660 | int perf_file_header__read(struct perf_file_header *self, | 594 | int perf_file_header__read(struct perf_file_header *header, |
661 | struct perf_header *ph, int fd) | 595 | struct perf_header *ph, int fd) |
662 | { | 596 | { |
663 | lseek(fd, 0, SEEK_SET); | 597 | lseek(fd, 0, SEEK_SET); |
664 | 598 | ||
665 | if (readn(fd, self, sizeof(*self)) <= 0 || | 599 | if (readn(fd, header, sizeof(*header)) <= 0 || |
666 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) | 600 | memcmp(&header->magic, __perf_magic, sizeof(header->magic))) |
667 | return -1; | 601 | return -1; |
668 | 602 | ||
669 | if (self->attr_size != sizeof(struct perf_file_attr)) { | 603 | if (header->attr_size != sizeof(struct perf_file_attr)) { |
670 | u64 attr_size = bswap_64(self->attr_size); | 604 | u64 attr_size = bswap_64(header->attr_size); |
671 | 605 | ||
672 | if (attr_size != sizeof(struct perf_file_attr)) | 606 | if (attr_size != sizeof(struct perf_file_attr)) |
673 | return -1; | 607 | return -1; |
674 | 608 | ||
675 | mem_bswap_64(self, offsetof(struct perf_file_header, | 609 | mem_bswap_64(header, offsetof(struct perf_file_header, |
676 | adds_features)); | 610 | adds_features)); |
677 | ph->needs_swap = true; | 611 | ph->needs_swap = true; |
678 | } | 612 | } |
679 | 613 | ||
680 | if (self->size != sizeof(*self)) { | 614 | if (header->size != sizeof(*header)) { |
681 | /* Support the previous format */ | 615 | /* Support the previous format */ |
682 | if (self->size == offsetof(typeof(*self), adds_features)) | 616 | if (header->size == offsetof(typeof(*header), adds_features)) |
683 | bitmap_zero(self->adds_features, HEADER_FEAT_BITS); | 617 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); |
684 | else | 618 | else |
685 | return -1; | 619 | return -1; |
686 | } | 620 | } |
687 | 621 | ||
688 | memcpy(&ph->adds_features, &self->adds_features, | 622 | memcpy(&ph->adds_features, &header->adds_features, |
689 | sizeof(ph->adds_features)); | 623 | sizeof(ph->adds_features)); |
690 | /* | 624 | /* |
691 | * FIXME: hack that assumes that if we need swap the perf.data file | 625 | * FIXME: hack that assumes that if we need swap the perf.data file |
@@ -699,10 +633,10 @@ int perf_file_header__read(struct perf_file_header *self, | |||
699 | perf_header__set_feat(ph, HEADER_BUILD_ID); | 633 | perf_header__set_feat(ph, HEADER_BUILD_ID); |
700 | } | 634 | } |
701 | 635 | ||
702 | ph->event_offset = self->event_types.offset; | 636 | ph->event_offset = header->event_types.offset; |
703 | ph->event_size = self->event_types.size; | 637 | ph->event_size = header->event_types.size; |
704 | ph->data_offset = self->data.offset; | 638 | ph->data_offset = header->data.offset; |
705 | ph->data_size = self->data.size; | 639 | ph->data_size = header->data.size; |
706 | return 0; | 640 | return 0; |
707 | } | 641 | } |
708 | 642 | ||
@@ -761,14 +695,50 @@ out: | |||
761 | return err; | 695 | return err; |
762 | } | 696 | } |
763 | 697 | ||
764 | static int perf_header__read_build_ids(struct perf_header *self, | 698 | static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, |
765 | int input, u64 offset, u64 size) | 699 | int input, u64 offset, u64 size) |
766 | { | 700 | { |
767 | struct perf_session *session = container_of(self, | 701 | struct perf_session *session = container_of(header, struct perf_session, header); |
768 | struct perf_session, header); | 702 | struct { |
703 | struct perf_event_header header; | ||
704 | u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; | ||
705 | char filename[0]; | ||
706 | } old_bev; | ||
769 | struct build_id_event bev; | 707 | struct build_id_event bev; |
770 | char filename[PATH_MAX]; | 708 | char filename[PATH_MAX]; |
771 | u64 limit = offset + size; | 709 | u64 limit = offset + size; |
710 | |||
711 | while (offset < limit) { | ||
712 | ssize_t len; | ||
713 | |||
714 | if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) | ||
715 | return -1; | ||
716 | |||
717 | if (header->needs_swap) | ||
718 | perf_event_header__bswap(&old_bev.header); | ||
719 | |||
720 | len = old_bev.header.size - sizeof(old_bev); | ||
721 | if (read(input, filename, len) != len) | ||
722 | return -1; | ||
723 | |||
724 | bev.header = old_bev.header; | ||
725 | bev.pid = 0; | ||
726 | memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); | ||
727 | __event_process_build_id(&bev, filename, session); | ||
728 | |||
729 | offset += bev.header.size; | ||
730 | } | ||
731 | |||
732 | return 0; | ||
733 | } | ||
734 | |||
735 | static int perf_header__read_build_ids(struct perf_header *header, | ||
736 | int input, u64 offset, u64 size) | ||
737 | { | ||
738 | struct perf_session *session = container_of(header, struct perf_session, header); | ||
739 | struct build_id_event bev; | ||
740 | char filename[PATH_MAX]; | ||
741 | u64 limit = offset + size, orig_offset = offset; | ||
772 | int err = -1; | 742 | int err = -1; |
773 | 743 | ||
774 | while (offset < limit) { | 744 | while (offset < limit) { |
@@ -777,12 +747,30 @@ static int perf_header__read_build_ids(struct perf_header *self, | |||
777 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | 747 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) |
778 | goto out; | 748 | goto out; |
779 | 749 | ||
780 | if (self->needs_swap) | 750 | if (header->needs_swap) |
781 | perf_event_header__bswap(&bev.header); | 751 | perf_event_header__bswap(&bev.header); |
782 | 752 | ||
783 | len = bev.header.size - sizeof(bev); | 753 | len = bev.header.size - sizeof(bev); |
784 | if (read(input, filename, len) != len) | 754 | if (read(input, filename, len) != len) |
785 | goto out; | 755 | goto out; |
756 | /* | ||
757 | * The a1645ce1 changeset: | ||
758 | * | ||
759 | * "perf: 'perf kvm' tool for monitoring guest performance from host" | ||
760 | * | ||
761 | * Added a field to struct build_id_event that broke the file | ||
762 | * format. | ||
763 | * | ||
764 | * Since the kernel build-id is the first entry, process the | ||
765 | * table using the old format if the well known | ||
766 | * '[kernel.kallsyms]' string for the kernel build-id has the | ||
767 | * first 4 characters chopped off (where the pid_t sits). | ||
768 | */ | ||
769 | if (memcmp(filename, "nel.kallsyms]", 13) == 0) { | ||
770 | if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1) | ||
771 | return -1; | ||
772 | return perf_header__read_build_ids_abi_quirk(header, input, offset, size); | ||
773 | } | ||
786 | 774 | ||
787 | __event_process_build_id(&bev, filename, session); | 775 | __event_process_build_id(&bev, filename, session); |
788 | 776 | ||
@@ -793,13 +781,13 @@ out: | |||
793 | return err; | 781 | return err; |
794 | } | 782 | } |
795 | 783 | ||
796 | static int perf_file_section__process(struct perf_file_section *self, | 784 | static int perf_file_section__process(struct perf_file_section *section, |
797 | struct perf_header *ph, | 785 | struct perf_header *ph, |
798 | int feat, int fd) | 786 | int feat, int fd) |
799 | { | 787 | { |
800 | if (lseek(fd, self->offset, SEEK_SET) == (off_t)-1) { | 788 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { |
801 | pr_debug("Failed to lseek to %Ld offset for feature %d, " | 789 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " |
802 | "continuing...\n", self->offset, feat); | 790 | "%d, continuing...\n", section->offset, feat); |
803 | return 0; | 791 | return 0; |
804 | } | 792 | } |
805 | 793 | ||
@@ -809,7 +797,7 @@ static int perf_file_section__process(struct perf_file_section *self, | |||
809 | break; | 797 | break; |
810 | 798 | ||
811 | case HEADER_BUILD_ID: | 799 | case HEADER_BUILD_ID: |
812 | if (perf_header__read_build_ids(ph, fd, self->offset, self->size)) | 800 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) |
813 | pr_debug("Failed to read buildids, continuing...\n"); | 801 | pr_debug("Failed to read buildids, continuing...\n"); |
814 | break; | 802 | break; |
815 | default: | 803 | default: |
@@ -819,21 +807,21 @@ static int perf_file_section__process(struct perf_file_section *self, | |||
819 | return 0; | 807 | return 0; |
820 | } | 808 | } |
821 | 809 | ||
822 | static int perf_file_header__read_pipe(struct perf_pipe_file_header *self, | 810 | static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, |
823 | struct perf_header *ph, int fd, | 811 | struct perf_header *ph, int fd, |
824 | bool repipe) | 812 | bool repipe) |
825 | { | 813 | { |
826 | if (readn(fd, self, sizeof(*self)) <= 0 || | 814 | if (readn(fd, header, sizeof(*header)) <= 0 || |
827 | memcmp(&self->magic, __perf_magic, sizeof(self->magic))) | 815 | memcmp(&header->magic, __perf_magic, sizeof(header->magic))) |
828 | return -1; | 816 | return -1; |
829 | 817 | ||
830 | if (repipe && do_write(STDOUT_FILENO, self, sizeof(*self)) < 0) | 818 | if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0) |
831 | return -1; | 819 | return -1; |
832 | 820 | ||
833 | if (self->size != sizeof(*self)) { | 821 | if (header->size != sizeof(*header)) { |
834 | u64 size = bswap_64(self->size); | 822 | u64 size = bswap_64(header->size); |
835 | 823 | ||
836 | if (size != sizeof(*self)) | 824 | if (size != sizeof(*header)) |
837 | return -1; | 825 | return -1; |
838 | 826 | ||
839 | ph->needs_swap = true; | 827 | ph->needs_swap = true; |
@@ -844,10 +832,10 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *self, | |||
844 | 832 | ||
845 | static int perf_header__read_pipe(struct perf_session *session, int fd) | 833 | static int perf_header__read_pipe(struct perf_session *session, int fd) |
846 | { | 834 | { |
847 | struct perf_header *self = &session->header; | 835 | struct perf_header *header = &session->header; |
848 | struct perf_pipe_file_header f_header; | 836 | struct perf_pipe_file_header f_header; |
849 | 837 | ||
850 | if (perf_file_header__read_pipe(&f_header, self, fd, | 838 | if (perf_file_header__read_pipe(&f_header, header, fd, |
851 | session->repipe) < 0) { | 839 | session->repipe) < 0) { |
852 | pr_debug("incompatible file format\n"); | 840 | pr_debug("incompatible file format\n"); |
853 | return -EINVAL; | 841 | return -EINVAL; |
@@ -858,18 +846,22 @@ static int perf_header__read_pipe(struct perf_session *session, int fd) | |||
858 | return 0; | 846 | return 0; |
859 | } | 847 | } |
860 | 848 | ||
861 | int perf_header__read(struct perf_session *session, int fd) | 849 | int perf_session__read_header(struct perf_session *session, int fd) |
862 | { | 850 | { |
863 | struct perf_header *self = &session->header; | 851 | struct perf_header *header = &session->header; |
864 | struct perf_file_header f_header; | 852 | struct perf_file_header f_header; |
865 | struct perf_file_attr f_attr; | 853 | struct perf_file_attr f_attr; |
866 | u64 f_id; | 854 | u64 f_id; |
867 | int nr_attrs, nr_ids, i, j; | 855 | int nr_attrs, nr_ids, i, j; |
868 | 856 | ||
857 | session->evlist = perf_evlist__new(NULL, NULL); | ||
858 | if (session->evlist == NULL) | ||
859 | return -ENOMEM; | ||
860 | |||
869 | if (session->fd_pipe) | 861 | if (session->fd_pipe) |
870 | return perf_header__read_pipe(session, fd); | 862 | return perf_header__read_pipe(session, fd); |
871 | 863 | ||
872 | if (perf_file_header__read(&f_header, self, fd) < 0) { | 864 | if (perf_file_header__read(&f_header, header, fd) < 0) { |
873 | pr_debug("incompatible file format\n"); | 865 | pr_debug("incompatible file format\n"); |
874 | return -EINVAL; | 866 | return -EINVAL; |
875 | } | 867 | } |
@@ -878,33 +870,39 @@ int perf_header__read(struct perf_session *session, int fd) | |||
878 | lseek(fd, f_header.attrs.offset, SEEK_SET); | 870 | lseek(fd, f_header.attrs.offset, SEEK_SET); |
879 | 871 | ||
880 | for (i = 0; i < nr_attrs; i++) { | 872 | for (i = 0; i < nr_attrs; i++) { |
881 | struct perf_header_attr *attr; | 873 | struct perf_evsel *evsel; |
882 | off_t tmp; | 874 | off_t tmp; |
883 | 875 | ||
884 | if (perf_header__getbuffer64(self, fd, &f_attr, sizeof(f_attr))) | 876 | if (perf_header__getbuffer64(header, fd, &f_attr, sizeof(f_attr))) |
885 | goto out_errno; | 877 | goto out_errno; |
886 | 878 | ||
887 | tmp = lseek(fd, 0, SEEK_CUR); | 879 | tmp = lseek(fd, 0, SEEK_CUR); |
880 | evsel = perf_evsel__new(&f_attr.attr, i); | ||
888 | 881 | ||
889 | attr = perf_header_attr__new(&f_attr.attr); | 882 | if (evsel == NULL) |
890 | if (attr == NULL) | 883 | goto out_delete_evlist; |
891 | return -ENOMEM; | 884 | /* |
885 | * Do it before so that if perf_evsel__alloc_id fails, this | ||
886 | * entry gets purged too at perf_evlist__delete(). | ||
887 | */ | ||
888 | perf_evlist__add(session->evlist, evsel); | ||
892 | 889 | ||
893 | nr_ids = f_attr.ids.size / sizeof(u64); | 890 | nr_ids = f_attr.ids.size / sizeof(u64); |
891 | /* | ||
892 | * We don't have the cpu and thread maps on the header, so | ||
893 | * for allocating the perf_sample_id table we fake 1 cpu and | ||
894 | * hattr->ids threads. | ||
895 | */ | ||
896 | if (perf_evsel__alloc_id(evsel, 1, nr_ids)) | ||
897 | goto out_delete_evlist; | ||
898 | |||
894 | lseek(fd, f_attr.ids.offset, SEEK_SET); | 899 | lseek(fd, f_attr.ids.offset, SEEK_SET); |
895 | 900 | ||
896 | for (j = 0; j < nr_ids; j++) { | 901 | for (j = 0; j < nr_ids; j++) { |
897 | if (perf_header__getbuffer64(self, fd, &f_id, sizeof(f_id))) | 902 | if (perf_header__getbuffer64(header, fd, &f_id, sizeof(f_id))) |
898 | goto out_errno; | 903 | goto out_errno; |
899 | 904 | ||
900 | if (perf_header_attr__add_id(attr, f_id) < 0) { | 905 | perf_evlist__id_add(session->evlist, evsel, 0, j, f_id); |
901 | perf_header_attr__delete(attr); | ||
902 | return -ENOMEM; | ||
903 | } | ||
904 | } | ||
905 | if (perf_header__add_attr(self, attr) < 0) { | ||
906 | perf_header_attr__delete(attr); | ||
907 | return -ENOMEM; | ||
908 | } | 906 | } |
909 | 907 | ||
910 | lseek(fd, tmp, SEEK_SET); | 908 | lseek(fd, tmp, SEEK_SET); |
@@ -915,93 +913,63 @@ int perf_header__read(struct perf_session *session, int fd) | |||
915 | events = malloc(f_header.event_types.size); | 913 | events = malloc(f_header.event_types.size); |
916 | if (events == NULL) | 914 | if (events == NULL) |
917 | return -ENOMEM; | 915 | return -ENOMEM; |
918 | if (perf_header__getbuffer64(self, fd, events, | 916 | if (perf_header__getbuffer64(header, fd, events, |
919 | f_header.event_types.size)) | 917 | f_header.event_types.size)) |
920 | goto out_errno; | 918 | goto out_errno; |
921 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | 919 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
922 | } | 920 | } |
923 | 921 | ||
924 | perf_header__process_sections(self, fd, perf_file_section__process); | 922 | perf_header__process_sections(header, fd, perf_file_section__process); |
925 | 923 | ||
926 | lseek(fd, self->data_offset, SEEK_SET); | 924 | lseek(fd, header->data_offset, SEEK_SET); |
927 | 925 | ||
928 | self->frozen = 1; | 926 | header->frozen = 1; |
929 | return 0; | 927 | return 0; |
930 | out_errno: | 928 | out_errno: |
931 | return -errno; | 929 | return -errno; |
930 | |||
931 | out_delete_evlist: | ||
932 | perf_evlist__delete(session->evlist); | ||
933 | session->evlist = NULL; | ||
934 | return -ENOMEM; | ||
932 | } | 935 | } |
933 | 936 | ||
934 | u64 perf_header__sample_type(struct perf_header *header) | 937 | u64 perf_evlist__sample_type(struct perf_evlist *evlist) |
935 | { | 938 | { |
939 | struct perf_evsel *pos; | ||
936 | u64 type = 0; | 940 | u64 type = 0; |
937 | int i; | ||
938 | |||
939 | for (i = 0; i < header->attrs; i++) { | ||
940 | struct perf_header_attr *attr = header->attr[i]; | ||
941 | 941 | ||
942 | list_for_each_entry(pos, &evlist->entries, node) { | ||
942 | if (!type) | 943 | if (!type) |
943 | type = attr->attr.sample_type; | 944 | type = pos->attr.sample_type; |
944 | else if (type != attr->attr.sample_type) | 945 | else if (type != pos->attr.sample_type) |
945 | die("non matching sample_type"); | 946 | die("non matching sample_type"); |
946 | } | 947 | } |
947 | 948 | ||
948 | return type; | 949 | return type; |
949 | } | 950 | } |
950 | 951 | ||
951 | bool perf_header__sample_id_all(const struct perf_header *header) | 952 | bool perf_evlist__sample_id_all(const struct perf_evlist *evlist) |
952 | { | 953 | { |
953 | bool value = false, first = true; | 954 | bool value = false, first = true; |
954 | int i; | 955 | struct perf_evsel *pos; |
955 | |||
956 | for (i = 0; i < header->attrs; i++) { | ||
957 | struct perf_header_attr *attr = header->attr[i]; | ||
958 | 956 | ||
957 | list_for_each_entry(pos, &evlist->entries, node) { | ||
959 | if (first) { | 958 | if (first) { |
960 | value = attr->attr.sample_id_all; | 959 | value = pos->attr.sample_id_all; |
961 | first = false; | 960 | first = false; |
962 | } else if (value != attr->attr.sample_id_all) | 961 | } else if (value != pos->attr.sample_id_all) |
963 | die("non matching sample_id_all"); | 962 | die("non matching sample_id_all"); |
964 | } | 963 | } |
965 | 964 | ||
966 | return value; | 965 | return value; |
967 | } | 966 | } |
968 | 967 | ||
969 | struct perf_event_attr * | 968 | int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, |
970 | perf_header__find_attr(u64 id, struct perf_header *header) | 969 | perf_event__handler_t process, |
971 | { | 970 | struct perf_session *session) |
972 | int i; | ||
973 | |||
974 | /* | ||
975 | * We set id to -1 if the data file doesn't contain sample | ||
976 | * ids. This can happen when the data file contains one type | ||
977 | * of event and in that case, the header can still store the | ||
978 | * event attribute information. Check for this and avoid | ||
979 | * walking through the entire list of ids which may be large. | ||
980 | */ | ||
981 | if (id == -1ULL) { | ||
982 | if (header->attrs > 0) | ||
983 | return &header->attr[0]->attr; | ||
984 | return NULL; | ||
985 | } | ||
986 | |||
987 | for (i = 0; i < header->attrs; i++) { | ||
988 | struct perf_header_attr *attr = header->attr[i]; | ||
989 | int j; | ||
990 | |||
991 | for (j = 0; j < attr->ids; j++) { | ||
992 | if (attr->id[j] == id) | ||
993 | return &attr->attr; | ||
994 | } | ||
995 | } | ||
996 | |||
997 | return NULL; | ||
998 | } | ||
999 | |||
1000 | int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, | ||
1001 | event__handler_t process, | ||
1002 | struct perf_session *session) | ||
1003 | { | 971 | { |
1004 | event_t *ev; | 972 | union perf_event *ev; |
1005 | size_t size; | 973 | size_t size; |
1006 | int err; | 974 | int err; |
1007 | 975 | ||
@@ -1028,17 +996,15 @@ int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, | |||
1028 | return err; | 996 | return err; |
1029 | } | 997 | } |
1030 | 998 | ||
1031 | int event__synthesize_attrs(struct perf_header *self, event__handler_t process, | 999 | int perf_session__synthesize_attrs(struct perf_session *session, |
1032 | struct perf_session *session) | 1000 | perf_event__handler_t process) |
1033 | { | 1001 | { |
1034 | struct perf_header_attr *attr; | 1002 | struct perf_evsel *attr; |
1035 | int i, err = 0; | 1003 | int err = 0; |
1036 | |||
1037 | for (i = 0; i < self->attrs; i++) { | ||
1038 | attr = self->attr[i]; | ||
1039 | 1004 | ||
1040 | err = event__synthesize_attr(&attr->attr, attr->ids, attr->id, | 1005 | list_for_each_entry(attr, &session->evlist->entries, node) { |
1041 | process, session); | 1006 | err = perf_event__synthesize_attr(&attr->attr, attr->ids, |
1007 | attr->id, process, session); | ||
1042 | if (err) { | 1008 | if (err) { |
1043 | pr_debug("failed to create perf header attribute\n"); | 1009 | pr_debug("failed to create perf header attribute\n"); |
1044 | return err; | 1010 | return err; |
@@ -1048,29 +1014,39 @@ int event__synthesize_attrs(struct perf_header *self, event__handler_t process, | |||
1048 | return err; | 1014 | return err; |
1049 | } | 1015 | } |
1050 | 1016 | ||
1051 | int event__process_attr(event_t *self, struct perf_session *session) | 1017 | int perf_event__process_attr(union perf_event *event, |
1018 | struct perf_session *session) | ||
1052 | { | 1019 | { |
1053 | struct perf_header_attr *attr; | ||
1054 | unsigned int i, ids, n_ids; | 1020 | unsigned int i, ids, n_ids; |
1021 | struct perf_evsel *evsel; | ||
1055 | 1022 | ||
1056 | attr = perf_header_attr__new(&self->attr.attr); | 1023 | if (session->evlist == NULL) { |
1057 | if (attr == NULL) | 1024 | session->evlist = perf_evlist__new(NULL, NULL); |
1025 | if (session->evlist == NULL) | ||
1026 | return -ENOMEM; | ||
1027 | } | ||
1028 | |||
1029 | evsel = perf_evsel__new(&event->attr.attr, | ||
1030 | session->evlist->nr_entries); | ||
1031 | if (evsel == NULL) | ||
1058 | return -ENOMEM; | 1032 | return -ENOMEM; |
1059 | 1033 | ||
1060 | ids = self->header.size; | 1034 | perf_evlist__add(session->evlist, evsel); |
1061 | ids -= (void *)&self->attr.id - (void *)self; | 1035 | |
1036 | ids = event->header.size; | ||
1037 | ids -= (void *)&event->attr.id - (void *)event; | ||
1062 | n_ids = ids / sizeof(u64); | 1038 | n_ids = ids / sizeof(u64); |
1039 | /* | ||
1040 | * We don't have the cpu and thread maps on the header, so | ||
1041 | * for allocating the perf_sample_id table we fake 1 cpu and | ||
1042 | * hattr->ids threads. | ||
1043 | */ | ||
1044 | if (perf_evsel__alloc_id(evsel, 1, n_ids)) | ||
1045 | return -ENOMEM; | ||
1063 | 1046 | ||
1064 | for (i = 0; i < n_ids; i++) { | 1047 | for (i = 0; i < n_ids; i++) { |
1065 | if (perf_header_attr__add_id(attr, self->attr.id[i]) < 0) { | 1048 | perf_evlist__id_add(session->evlist, evsel, 0, i, |
1066 | perf_header_attr__delete(attr); | 1049 | event->attr.id[i]); |
1067 | return -ENOMEM; | ||
1068 | } | ||
1069 | } | ||
1070 | |||
1071 | if (perf_header__add_attr(&session->header, attr) < 0) { | ||
1072 | perf_header_attr__delete(attr); | ||
1073 | return -ENOMEM; | ||
1074 | } | 1050 | } |
1075 | 1051 | ||
1076 | perf_session__update_sample_type(session); | 1052 | perf_session__update_sample_type(session); |
@@ -1078,11 +1054,11 @@ int event__process_attr(event_t *self, struct perf_session *session) | |||
1078 | return 0; | 1054 | return 0; |
1079 | } | 1055 | } |
1080 | 1056 | ||
1081 | int event__synthesize_event_type(u64 event_id, char *name, | 1057 | int perf_event__synthesize_event_type(u64 event_id, char *name, |
1082 | event__handler_t process, | 1058 | perf_event__handler_t process, |
1083 | struct perf_session *session) | 1059 | struct perf_session *session) |
1084 | { | 1060 | { |
1085 | event_t ev; | 1061 | union perf_event ev; |
1086 | size_t size = 0; | 1062 | size_t size = 0; |
1087 | int err = 0; | 1063 | int err = 0; |
1088 | 1064 | ||
@@ -1103,8 +1079,8 @@ int event__synthesize_event_type(u64 event_id, char *name, | |||
1103 | return err; | 1079 | return err; |
1104 | } | 1080 | } |
1105 | 1081 | ||
1106 | int event__synthesize_event_types(event__handler_t process, | 1082 | int perf_event__synthesize_event_types(perf_event__handler_t process, |
1107 | struct perf_session *session) | 1083 | struct perf_session *session) |
1108 | { | 1084 | { |
1109 | struct perf_trace_event_type *type; | 1085 | struct perf_trace_event_type *type; |
1110 | int i, err = 0; | 1086 | int i, err = 0; |
@@ -1112,8 +1088,9 @@ int event__synthesize_event_types(event__handler_t process, | |||
1112 | for (i = 0; i < event_count; i++) { | 1088 | for (i = 0; i < event_count; i++) { |
1113 | type = &events[i]; | 1089 | type = &events[i]; |
1114 | 1090 | ||
1115 | err = event__synthesize_event_type(type->event_id, type->name, | 1091 | err = perf_event__synthesize_event_type(type->event_id, |
1116 | process, session); | 1092 | type->name, process, |
1093 | session); | ||
1117 | if (err) { | 1094 | if (err) { |
1118 | pr_debug("failed to create perf header event type\n"); | 1095 | pr_debug("failed to create perf header event type\n"); |
1119 | return err; | 1096 | return err; |
@@ -1123,28 +1100,28 @@ int event__synthesize_event_types(event__handler_t process, | |||
1123 | return err; | 1100 | return err; |
1124 | } | 1101 | } |
1125 | 1102 | ||
1126 | int event__process_event_type(event_t *self, | 1103 | int perf_event__process_event_type(union perf_event *event, |
1127 | struct perf_session *session __unused) | 1104 | struct perf_session *session __unused) |
1128 | { | 1105 | { |
1129 | if (perf_header__push_event(self->event_type.event_type.event_id, | 1106 | if (perf_header__push_event(event->event_type.event_type.event_id, |
1130 | self->event_type.event_type.name) < 0) | 1107 | event->event_type.event_type.name) < 0) |
1131 | return -ENOMEM; | 1108 | return -ENOMEM; |
1132 | 1109 | ||
1133 | return 0; | 1110 | return 0; |
1134 | } | 1111 | } |
1135 | 1112 | ||
1136 | int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | 1113 | int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, |
1137 | event__handler_t process, | 1114 | perf_event__handler_t process, |
1138 | struct perf_session *session __unused) | 1115 | struct perf_session *session __unused) |
1139 | { | 1116 | { |
1140 | event_t ev; | 1117 | union perf_event ev; |
1141 | ssize_t size = 0, aligned_size = 0, padding; | 1118 | ssize_t size = 0, aligned_size = 0, padding; |
1142 | int err = 0; | 1119 | int err __used = 0; |
1143 | 1120 | ||
1144 | memset(&ev, 0, sizeof(ev)); | 1121 | memset(&ev, 0, sizeof(ev)); |
1145 | 1122 | ||
1146 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; | 1123 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; |
1147 | size = read_tracing_data_size(fd, pattrs); | 1124 | size = read_tracing_data_size(fd, &evlist->entries); |
1148 | if (size <= 0) | 1125 | if (size <= 0) |
1149 | return size; | 1126 | return size; |
1150 | aligned_size = ALIGN(size, sizeof(u64)); | 1127 | aligned_size = ALIGN(size, sizeof(u64)); |
@@ -1154,16 +1131,16 @@ int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | |||
1154 | 1131 | ||
1155 | process(&ev, NULL, session); | 1132 | process(&ev, NULL, session); |
1156 | 1133 | ||
1157 | err = read_tracing_data(fd, pattrs); | 1134 | err = read_tracing_data(fd, &evlist->entries); |
1158 | write_padded(fd, NULL, 0, padding); | 1135 | write_padded(fd, NULL, 0, padding); |
1159 | 1136 | ||
1160 | return aligned_size; | 1137 | return aligned_size; |
1161 | } | 1138 | } |
1162 | 1139 | ||
1163 | int event__process_tracing_data(event_t *self, | 1140 | int perf_event__process_tracing_data(union perf_event *event, |
1164 | struct perf_session *session) | 1141 | struct perf_session *session) |
1165 | { | 1142 | { |
1166 | ssize_t size_read, padding, size = self->tracing_data.size; | 1143 | ssize_t size_read, padding, size = event->tracing_data.size; |
1167 | off_t offset = lseek(session->fd, 0, SEEK_CUR); | 1144 | off_t offset = lseek(session->fd, 0, SEEK_CUR); |
1168 | char buf[BUFSIZ]; | 1145 | char buf[BUFSIZ]; |
1169 | 1146 | ||
@@ -1189,12 +1166,12 @@ int event__process_tracing_data(event_t *self, | |||
1189 | return size_read + padding; | 1166 | return size_read + padding; |
1190 | } | 1167 | } |
1191 | 1168 | ||
1192 | int event__synthesize_build_id(struct dso *pos, u16 misc, | 1169 | int perf_event__synthesize_build_id(struct dso *pos, u16 misc, |
1193 | event__handler_t process, | 1170 | perf_event__handler_t process, |
1194 | struct machine *machine, | 1171 | struct machine *machine, |
1195 | struct perf_session *session) | 1172 | struct perf_session *session) |
1196 | { | 1173 | { |
1197 | event_t ev; | 1174 | union perf_event ev; |
1198 | size_t len; | 1175 | size_t len; |
1199 | int err = 0; | 1176 | int err = 0; |
1200 | 1177 | ||
@@ -1217,11 +1194,11 @@ int event__synthesize_build_id(struct dso *pos, u16 misc, | |||
1217 | return err; | 1194 | return err; |
1218 | } | 1195 | } |
1219 | 1196 | ||
1220 | int event__process_build_id(event_t *self, | 1197 | int perf_event__process_build_id(union perf_event *event, |
1221 | struct perf_session *session) | 1198 | struct perf_session *session) |
1222 | { | 1199 | { |
1223 | __event_process_build_id(&self->build_id, | 1200 | __event_process_build_id(&event->build_id, |
1224 | self->build_id.filename, | 1201 | event->build_id.filename, |
1225 | session); | 1202 | session); |
1226 | return 0; | 1203 | return 0; |
1227 | } | 1204 | } |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 33f16be7b72f..456661d7f10e 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -9,13 +9,6 @@ | |||
9 | 9 | ||
10 | #include <linux/bitmap.h> | 10 | #include <linux/bitmap.h> |
11 | 11 | ||
12 | struct perf_header_attr { | ||
13 | struct perf_event_attr attr; | ||
14 | int ids, size; | ||
15 | u64 *id; | ||
16 | off_t id_offset; | ||
17 | }; | ||
18 | |||
19 | enum { | 12 | enum { |
20 | HEADER_TRACE_INFO = 1, | 13 | HEADER_TRACE_INFO = 1, |
21 | HEADER_BUILD_ID, | 14 | HEADER_BUILD_ID, |
@@ -46,14 +39,12 @@ struct perf_pipe_file_header { | |||
46 | 39 | ||
47 | struct perf_header; | 40 | struct perf_header; |
48 | 41 | ||
49 | int perf_file_header__read(struct perf_file_header *self, | 42 | int perf_file_header__read(struct perf_file_header *header, |
50 | struct perf_header *ph, int fd); | 43 | struct perf_header *ph, int fd); |
51 | 44 | ||
52 | struct perf_header { | 45 | struct perf_header { |
53 | int frozen; | 46 | int frozen; |
54 | int attrs, size; | ||
55 | bool needs_swap; | 47 | bool needs_swap; |
56 | struct perf_header_attr **attr; | ||
57 | s64 attr_offset; | 48 | s64 attr_offset; |
58 | u64 data_offset; | 49 | u64 data_offset; |
59 | u64 data_size; | 50 | u64 data_size; |
@@ -62,34 +53,25 @@ struct perf_header { | |||
62 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | 53 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); |
63 | }; | 54 | }; |
64 | 55 | ||
65 | int perf_header__init(struct perf_header *self); | 56 | struct perf_evlist; |
66 | void perf_header__exit(struct perf_header *self); | ||
67 | 57 | ||
68 | int perf_header__read(struct perf_session *session, int fd); | 58 | int perf_session__read_header(struct perf_session *session, int fd); |
69 | int perf_header__write(struct perf_header *self, int fd, bool at_exit); | 59 | int perf_session__write_header(struct perf_session *session, |
60 | struct perf_evlist *evlist, | ||
61 | int fd, bool at_exit); | ||
70 | int perf_header__write_pipe(int fd); | 62 | int perf_header__write_pipe(int fd); |
71 | 63 | ||
72 | int perf_header__add_attr(struct perf_header *self, | ||
73 | struct perf_header_attr *attr); | ||
74 | |||
75 | int perf_header__push_event(u64 id, const char *name); | 64 | int perf_header__push_event(u64 id, const char *name); |
76 | char *perf_header__find_event(u64 id); | 65 | char *perf_header__find_event(u64 id); |
77 | 66 | ||
78 | struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr); | 67 | u64 perf_evlist__sample_type(struct perf_evlist *evlist); |
79 | void perf_header_attr__delete(struct perf_header_attr *self); | 68 | bool perf_evlist__sample_id_all(const struct perf_evlist *evlist); |
69 | void perf_header__set_feat(struct perf_header *header, int feat); | ||
70 | void perf_header__clear_feat(struct perf_header *header, int feat); | ||
71 | bool perf_header__has_feat(const struct perf_header *header, int feat); | ||
80 | 72 | ||
81 | int perf_header_attr__add_id(struct perf_header_attr *self, u64 id); | 73 | int perf_header__process_sections(struct perf_header *header, int fd, |
82 | 74 | int (*process)(struct perf_file_section *section, | |
83 | u64 perf_header__sample_type(struct perf_header *header); | ||
84 | bool perf_header__sample_id_all(const struct perf_header *header); | ||
85 | struct perf_event_attr * | ||
86 | perf_header__find_attr(u64 id, struct perf_header *header); | ||
87 | void perf_header__set_feat(struct perf_header *self, int feat); | ||
88 | void perf_header__clear_feat(struct perf_header *self, int feat); | ||
89 | bool perf_header__has_feat(const struct perf_header *self, int feat); | ||
90 | |||
91 | int perf_header__process_sections(struct perf_header *self, int fd, | ||
92 | int (*process)(struct perf_file_section *self, | ||
93 | struct perf_header *ph, | 75 | struct perf_header *ph, |
94 | int feat, int fd)); | 76 | int feat, int fd)); |
95 | 77 | ||
@@ -97,32 +79,31 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | |||
97 | const char *name, bool is_kallsyms); | 79 | const char *name, bool is_kallsyms); |
98 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); | 80 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir); |
99 | 81 | ||
100 | int event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, | 82 | int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id, |
101 | event__handler_t process, | 83 | perf_event__handler_t process, |
102 | struct perf_session *session); | ||
103 | int event__synthesize_attrs(struct perf_header *self, | ||
104 | event__handler_t process, | ||
105 | struct perf_session *session); | ||
106 | int event__process_attr(event_t *self, struct perf_session *session); | ||
107 | |||
108 | int event__synthesize_event_type(u64 event_id, char *name, | ||
109 | event__handler_t process, | ||
110 | struct perf_session *session); | ||
111 | int event__synthesize_event_types(event__handler_t process, | ||
112 | struct perf_session *session); | ||
113 | int event__process_event_type(event_t *self, | ||
114 | struct perf_session *session); | ||
115 | |||
116 | int event__synthesize_tracing_data(int fd, struct list_head *pattrs, | ||
117 | event__handler_t process, | ||
118 | struct perf_session *session); | ||
119 | int event__process_tracing_data(event_t *self, | ||
120 | struct perf_session *session); | 84 | struct perf_session *session); |
85 | int perf_session__synthesize_attrs(struct perf_session *session, | ||
86 | perf_event__handler_t process); | ||
87 | int perf_event__process_attr(union perf_event *event, struct perf_session *session); | ||
88 | |||
89 | int perf_event__synthesize_event_type(u64 event_id, char *name, | ||
90 | perf_event__handler_t process, | ||
91 | struct perf_session *session); | ||
92 | int perf_event__synthesize_event_types(perf_event__handler_t process, | ||
93 | struct perf_session *session); | ||
94 | int perf_event__process_event_type(union perf_event *event, | ||
95 | struct perf_session *session); | ||
121 | 96 | ||
122 | int event__synthesize_build_id(struct dso *pos, u16 misc, | 97 | int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, |
123 | event__handler_t process, | 98 | perf_event__handler_t process, |
124 | struct machine *machine, | 99 | struct perf_session *session); |
125 | struct perf_session *session); | 100 | int perf_event__process_tracing_data(union perf_event *event, |
126 | int event__process_build_id(event_t *self, struct perf_session *session); | 101 | struct perf_session *session); |
127 | 102 | ||
103 | int perf_event__synthesize_build_id(struct dso *pos, u16 misc, | ||
104 | perf_event__handler_t process, | ||
105 | struct machine *machine, | ||
106 | struct perf_session *session); | ||
107 | int perf_event__process_build_id(union perf_event *event, | ||
108 | struct perf_session *session); | ||
128 | #endif /* __PERF_HEADER_H */ | 109 | #endif /* __PERF_HEADER_H */ |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index c749ba6136a0..627a02e03c57 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -1,3 +1,4 @@ | |||
1 | #include "annotate.h" | ||
1 | #include "util.h" | 2 | #include "util.h" |
2 | #include "build-id.h" | 3 | #include "build-id.h" |
3 | #include "hist.h" | 4 | #include "hist.h" |
@@ -49,6 +50,15 @@ static void hists__calc_col_len(struct hists *self, struct hist_entry *h) | |||
49 | 50 | ||
50 | if (h->ms.sym) | 51 | if (h->ms.sym) |
51 | hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); | 52 | hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); |
53 | else { | ||
54 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | ||
55 | |||
56 | if (hists__col_len(self, HISTC_DSO) < unresolved_col_width && | ||
57 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
58 | !symbol_conf.dso_list) | ||
59 | hists__set_col_len(self, HISTC_DSO, | ||
60 | unresolved_col_width); | ||
61 | } | ||
52 | 62 | ||
53 | len = thread__comm_len(h->thread); | 63 | len = thread__comm_len(h->thread); |
54 | if (hists__new_col_len(self, HISTC_COMM, len)) | 64 | if (hists__new_col_len(self, HISTC_COMM, len)) |
@@ -211,7 +221,9 @@ void hist_entry__free(struct hist_entry *he) | |||
211 | * collapse the histogram | 221 | * collapse the histogram |
212 | */ | 222 | */ |
213 | 223 | ||
214 | static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | 224 | static bool hists__collapse_insert_entry(struct hists *self, |
225 | struct rb_root *root, | ||
226 | struct hist_entry *he) | ||
215 | { | 227 | { |
216 | struct rb_node **p = &root->rb_node; | 228 | struct rb_node **p = &root->rb_node; |
217 | struct rb_node *parent = NULL; | 229 | struct rb_node *parent = NULL; |
@@ -226,8 +238,11 @@ static bool collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | |||
226 | 238 | ||
227 | if (!cmp) { | 239 | if (!cmp) { |
228 | iter->period += he->period; | 240 | iter->period += he->period; |
229 | if (symbol_conf.use_callchain) | 241 | if (symbol_conf.use_callchain) { |
230 | callchain_merge(iter->callchain, he->callchain); | 242 | callchain_cursor_reset(&self->callchain_cursor); |
243 | callchain_merge(&self->callchain_cursor, iter->callchain, | ||
244 | he->callchain); | ||
245 | } | ||
231 | hist_entry__free(he); | 246 | hist_entry__free(he); |
232 | return false; | 247 | return false; |
233 | } | 248 | } |
@@ -262,7 +277,7 @@ void hists__collapse_resort(struct hists *self) | |||
262 | next = rb_next(&n->rb_node); | 277 | next = rb_next(&n->rb_node); |
263 | 278 | ||
264 | rb_erase(&n->rb_node, &self->entries); | 279 | rb_erase(&n->rb_node, &self->entries); |
265 | if (collapse__insert_entry(&tmp, n)) | 280 | if (hists__collapse_insert_entry(self, &tmp, n)) |
266 | hists__inc_nr_entries(self, n); | 281 | hists__inc_nr_entries(self, n); |
267 | } | 282 | } |
268 | 283 | ||
@@ -425,7 +440,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
425 | u64 cumul; | 440 | u64 cumul; |
426 | 441 | ||
427 | child = rb_entry(node, struct callchain_node, rb_node); | 442 | child = rb_entry(node, struct callchain_node, rb_node); |
428 | cumul = cumul_hits(child); | 443 | cumul = callchain_cumul_hits(child); |
429 | remaining -= cumul; | 444 | remaining -= cumul; |
430 | 445 | ||
431 | /* | 446 | /* |
@@ -585,6 +600,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
585 | { | 600 | { |
586 | struct sort_entry *se; | 601 | struct sort_entry *se; |
587 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; | 602 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; |
603 | u64 nr_events; | ||
588 | const char *sep = symbol_conf.field_sep; | 604 | const char *sep = symbol_conf.field_sep; |
589 | int ret; | 605 | int ret; |
590 | 606 | ||
@@ -593,6 +609,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
593 | 609 | ||
594 | if (pair_hists) { | 610 | if (pair_hists) { |
595 | period = self->pair ? self->pair->period : 0; | 611 | period = self->pair ? self->pair->period : 0; |
612 | nr_events = self->pair ? self->pair->nr_events : 0; | ||
596 | total = pair_hists->stats.total_period; | 613 | total = pair_hists->stats.total_period; |
597 | period_sys = self->pair ? self->pair->period_sys : 0; | 614 | period_sys = self->pair ? self->pair->period_sys : 0; |
598 | period_us = self->pair ? self->pair->period_us : 0; | 615 | period_us = self->pair ? self->pair->period_us : 0; |
@@ -600,6 +617,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
600 | period_guest_us = self->pair ? self->pair->period_guest_us : 0; | 617 | period_guest_us = self->pair ? self->pair->period_guest_us : 0; |
601 | } else { | 618 | } else { |
602 | period = self->period; | 619 | period = self->period; |
620 | nr_events = self->nr_events; | ||
603 | total = session_total; | 621 | total = session_total; |
604 | period_sys = self->period_sys; | 622 | period_sys = self->period_sys; |
605 | period_us = self->period_us; | 623 | period_us = self->period_us; |
@@ -636,13 +654,13 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
636 | } | 654 | } |
637 | } | 655 | } |
638 | } else | 656 | } else |
639 | ret = snprintf(s, size, sep ? "%lld" : "%12lld ", period); | 657 | ret = snprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period); |
640 | 658 | ||
641 | if (symbol_conf.show_nr_samples) { | 659 | if (symbol_conf.show_nr_samples) { |
642 | if (sep) | 660 | if (sep) |
643 | ret += snprintf(s + ret, size - ret, "%c%lld", *sep, period); | 661 | ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events); |
644 | else | 662 | else |
645 | ret += snprintf(s + ret, size - ret, "%11lld", period); | 663 | ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); |
646 | } | 664 | } |
647 | 665 | ||
648 | if (pair_hists) { | 666 | if (pair_hists) { |
@@ -944,224 +962,14 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) | |||
944 | } | 962 | } |
945 | } | 963 | } |
946 | 964 | ||
947 | static int symbol__alloc_hist(struct symbol *self) | 965 | int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) |
948 | { | ||
949 | struct sym_priv *priv = symbol__priv(self); | ||
950 | const int size = (sizeof(*priv->hist) + | ||
951 | (self->end - self->start) * sizeof(u64)); | ||
952 | |||
953 | priv->hist = zalloc(size); | ||
954 | return priv->hist == NULL ? -1 : 0; | ||
955 | } | ||
956 | |||
957 | int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip) | ||
958 | { | ||
959 | unsigned int sym_size, offset; | ||
960 | struct symbol *sym = self->ms.sym; | ||
961 | struct sym_priv *priv; | ||
962 | struct sym_hist *h; | ||
963 | |||
964 | if (!sym || !self->ms.map) | ||
965 | return 0; | ||
966 | |||
967 | priv = symbol__priv(sym); | ||
968 | if (priv->hist == NULL && symbol__alloc_hist(sym) < 0) | ||
969 | return -ENOMEM; | ||
970 | |||
971 | sym_size = sym->end - sym->start; | ||
972 | offset = ip - sym->start; | ||
973 | |||
974 | pr_debug3("%s: ip=%#Lx\n", __func__, self->ms.map->unmap_ip(self->ms.map, ip)); | ||
975 | |||
976 | if (offset >= sym_size) | ||
977 | return 0; | ||
978 | |||
979 | h = priv->hist; | ||
980 | h->sum++; | ||
981 | h->ip[offset]++; | ||
982 | |||
983 | pr_debug3("%#Lx %s: period++ [ip: %#Lx, %#Lx] => %Ld\n", self->ms.sym->start, | ||
984 | self->ms.sym->name, ip, ip - self->ms.sym->start, h->ip[offset]); | ||
985 | return 0; | ||
986 | } | ||
987 | |||
988 | static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) | ||
989 | { | ||
990 | struct objdump_line *self = malloc(sizeof(*self) + privsize); | ||
991 | |||
992 | if (self != NULL) { | ||
993 | self->offset = offset; | ||
994 | self->line = line; | ||
995 | } | ||
996 | |||
997 | return self; | ||
998 | } | ||
999 | |||
1000 | void objdump_line__free(struct objdump_line *self) | ||
1001 | { | ||
1002 | free(self->line); | ||
1003 | free(self); | ||
1004 | } | ||
1005 | |||
1006 | static void objdump__add_line(struct list_head *head, struct objdump_line *line) | ||
1007 | { | ||
1008 | list_add_tail(&line->node, head); | ||
1009 | } | ||
1010 | |||
1011 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
1012 | struct objdump_line *pos) | ||
1013 | { | ||
1014 | list_for_each_entry_continue(pos, head, node) | ||
1015 | if (pos->offset >= 0) | ||
1016 | return pos; | ||
1017 | |||
1018 | return NULL; | ||
1019 | } | ||
1020 | |||
1021 | static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, | ||
1022 | struct list_head *head, size_t privsize) | ||
1023 | { | 966 | { |
1024 | struct symbol *sym = self->ms.sym; | 967 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); |
1025 | struct objdump_line *objdump_line; | ||
1026 | char *line = NULL, *tmp, *tmp2, *c; | ||
1027 | size_t line_len; | ||
1028 | s64 line_ip, offset = -1; | ||
1029 | |||
1030 | if (getline(&line, &line_len, file) < 0) | ||
1031 | return -1; | ||
1032 | |||
1033 | if (!line) | ||
1034 | return -1; | ||
1035 | |||
1036 | while (line_len != 0 && isspace(line[line_len - 1])) | ||
1037 | line[--line_len] = '\0'; | ||
1038 | |||
1039 | c = strchr(line, '\n'); | ||
1040 | if (c) | ||
1041 | *c = 0; | ||
1042 | |||
1043 | line_ip = -1; | ||
1044 | |||
1045 | /* | ||
1046 | * Strip leading spaces: | ||
1047 | */ | ||
1048 | tmp = line; | ||
1049 | while (*tmp) { | ||
1050 | if (*tmp != ' ') | ||
1051 | break; | ||
1052 | tmp++; | ||
1053 | } | ||
1054 | |||
1055 | if (*tmp) { | ||
1056 | /* | ||
1057 | * Parse hexa addresses followed by ':' | ||
1058 | */ | ||
1059 | line_ip = strtoull(tmp, &tmp2, 16); | ||
1060 | if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') | ||
1061 | line_ip = -1; | ||
1062 | } | ||
1063 | |||
1064 | if (line_ip != -1) { | ||
1065 | u64 start = map__rip_2objdump(self->ms.map, sym->start), | ||
1066 | end = map__rip_2objdump(self->ms.map, sym->end); | ||
1067 | |||
1068 | offset = line_ip - start; | ||
1069 | if (offset < 0 || (u64)line_ip > end) | ||
1070 | offset = -1; | ||
1071 | } | ||
1072 | |||
1073 | objdump_line = objdump_line__new(offset, line, privsize); | ||
1074 | if (objdump_line == NULL) { | ||
1075 | free(line); | ||
1076 | return -1; | ||
1077 | } | ||
1078 | objdump__add_line(head, objdump_line); | ||
1079 | |||
1080 | return 0; | ||
1081 | } | 968 | } |
1082 | 969 | ||
1083 | int hist_entry__annotate(struct hist_entry *self, struct list_head *head, | 970 | int hist_entry__annotate(struct hist_entry *he, size_t privsize) |
1084 | size_t privsize) | ||
1085 | { | 971 | { |
1086 | struct symbol *sym = self->ms.sym; | 972 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); |
1087 | struct map *map = self->ms.map; | ||
1088 | struct dso *dso = map->dso; | ||
1089 | char *filename = dso__build_id_filename(dso, NULL, 0); | ||
1090 | bool free_filename = true; | ||
1091 | char command[PATH_MAX * 2]; | ||
1092 | FILE *file; | ||
1093 | int err = 0; | ||
1094 | u64 len; | ||
1095 | char symfs_filename[PATH_MAX]; | ||
1096 | |||
1097 | if (filename) { | ||
1098 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||
1099 | symbol_conf.symfs, filename); | ||
1100 | } | ||
1101 | |||
1102 | if (filename == NULL) { | ||
1103 | if (dso->has_build_id) { | ||
1104 | pr_err("Can't annotate %s: not enough memory\n", | ||
1105 | sym->name); | ||
1106 | return -ENOMEM; | ||
1107 | } | ||
1108 | goto fallback; | ||
1109 | } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || | ||
1110 | strstr(command, "[kernel.kallsyms]") || | ||
1111 | access(symfs_filename, R_OK)) { | ||
1112 | free(filename); | ||
1113 | fallback: | ||
1114 | /* | ||
1115 | * If we don't have build-ids or the build-id file isn't in the | ||
1116 | * cache, or is just a kallsyms file, well, lets hope that this | ||
1117 | * DSO is the same as when 'perf record' ran. | ||
1118 | */ | ||
1119 | filename = dso->long_name; | ||
1120 | snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", | ||
1121 | symbol_conf.symfs, filename); | ||
1122 | free_filename = false; | ||
1123 | } | ||
1124 | |||
1125 | if (dso->origin == DSO__ORIG_KERNEL) { | ||
1126 | if (dso->annotate_warned) | ||
1127 | goto out_free_filename; | ||
1128 | err = -ENOENT; | ||
1129 | dso->annotate_warned = 1; | ||
1130 | pr_err("Can't annotate %s: No vmlinux file was found in the " | ||
1131 | "path\n", sym->name); | ||
1132 | goto out_free_filename; | ||
1133 | } | ||
1134 | |||
1135 | pr_debug("%s: filename=%s, sym=%s, start=%#Lx, end=%#Lx\n", __func__, | ||
1136 | filename, sym->name, map->unmap_ip(map, sym->start), | ||
1137 | map->unmap_ip(map, sym->end)); | ||
1138 | |||
1139 | len = sym->end - sym->start; | ||
1140 | |||
1141 | pr_debug("annotating [%p] %30s : [%p] %30s\n", | ||
1142 | dso, dso->long_name, sym, sym->name); | ||
1143 | |||
1144 | snprintf(command, sizeof(command), | ||
1145 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand", | ||
1146 | map__rip_2objdump(map, sym->start), | ||
1147 | map__rip_2objdump(map, sym->end), | ||
1148 | symfs_filename, filename); | ||
1149 | |||
1150 | pr_debug("Executing: %s\n", command); | ||
1151 | |||
1152 | file = popen(command, "r"); | ||
1153 | if (!file) | ||
1154 | goto out_free_filename; | ||
1155 | |||
1156 | while (!feof(file)) | ||
1157 | if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0) | ||
1158 | break; | ||
1159 | |||
1160 | pclose(file); | ||
1161 | out_free_filename: | ||
1162 | if (free_filename) | ||
1163 | free(filename); | ||
1164 | return err; | ||
1165 | } | 973 | } |
1166 | 974 | ||
1167 | void hists__inc_nr_events(struct hists *self, u32 type) | 975 | void hists__inc_nr_events(struct hists *self, u32 type) |
@@ -1176,8 +984,12 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | |||
1176 | size_t ret = 0; | 984 | size_t ret = 0; |
1177 | 985 | ||
1178 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { | 986 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { |
1179 | const char *name = event__get_event_name(i); | 987 | const char *name; |
988 | |||
989 | if (self->stats.nr_events[i] == 0) | ||
990 | continue; | ||
1180 | 991 | ||
992 | name = perf_event__name(i); | ||
1181 | if (!strcmp(name, "UNKNOWN")) | 993 | if (!strcmp(name, "UNKNOWN")) |
1182 | continue; | 994 | continue; |
1183 | 995 | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index ee789856a8c9..3beb97c4d822 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -9,33 +9,6 @@ extern struct callchain_param callchain_param; | |||
9 | struct hist_entry; | 9 | struct hist_entry; |
10 | struct addr_location; | 10 | struct addr_location; |
11 | struct symbol; | 11 | struct symbol; |
12 | struct rb_root; | ||
13 | |||
14 | struct objdump_line { | ||
15 | struct list_head node; | ||
16 | s64 offset; | ||
17 | char *line; | ||
18 | }; | ||
19 | |||
20 | void objdump_line__free(struct objdump_line *self); | ||
21 | struct objdump_line *objdump__get_next_ip_line(struct list_head *head, | ||
22 | struct objdump_line *pos); | ||
23 | |||
24 | struct sym_hist { | ||
25 | u64 sum; | ||
26 | u64 ip[0]; | ||
27 | }; | ||
28 | |||
29 | struct sym_ext { | ||
30 | struct rb_node node; | ||
31 | double percent; | ||
32 | char *path; | ||
33 | }; | ||
34 | |||
35 | struct sym_priv { | ||
36 | struct sym_hist *hist; | ||
37 | struct sym_ext *ext; | ||
38 | }; | ||
39 | 12 | ||
40 | /* | 13 | /* |
41 | * The kernel collects the number of events it couldn't send in a stretch and | 14 | * The kernel collects the number of events it couldn't send in a stretch and |
@@ -56,6 +29,7 @@ struct events_stats { | |||
56 | u32 nr_events[PERF_RECORD_HEADER_MAX]; | 29 | u32 nr_events[PERF_RECORD_HEADER_MAX]; |
57 | u32 nr_unknown_events; | 30 | u32 nr_unknown_events; |
58 | u32 nr_invalid_chains; | 31 | u32 nr_invalid_chains; |
32 | u32 nr_unknown_id; | ||
59 | }; | 33 | }; |
60 | 34 | ||
61 | enum hist_column { | 35 | enum hist_column { |
@@ -69,14 +43,13 @@ enum hist_column { | |||
69 | }; | 43 | }; |
70 | 44 | ||
71 | struct hists { | 45 | struct hists { |
72 | struct rb_node rb_node; | ||
73 | struct rb_root entries; | 46 | struct rb_root entries; |
74 | u64 nr_entries; | 47 | u64 nr_entries; |
75 | struct events_stats stats; | 48 | struct events_stats stats; |
76 | u64 config; | ||
77 | u64 event_stream; | 49 | u64 event_stream; |
78 | u32 type; | ||
79 | u16 col_len[HISTC_NR_COLS]; | 50 | u16 col_len[HISTC_NR_COLS]; |
51 | /* Best would be to reuse the session callchain cursor */ | ||
52 | struct callchain_cursor callchain_cursor; | ||
80 | }; | 53 | }; |
81 | 54 | ||
82 | struct hist_entry *__hists__add_entry(struct hists *self, | 55 | struct hist_entry *__hists__add_entry(struct hists *self, |
@@ -102,9 +75,8 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); | |||
102 | size_t hists__fprintf(struct hists *self, struct hists *pair, | 75 | size_t hists__fprintf(struct hists *self, struct hists *pair, |
103 | bool show_displacement, FILE *fp); | 76 | bool show_displacement, FILE *fp); |
104 | 77 | ||
105 | int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); | 78 | int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); |
106 | int hist_entry__annotate(struct hist_entry *self, struct list_head *head, | 79 | int hist_entry__annotate(struct hist_entry *self, size_t privsize); |
107 | size_t privsize); | ||
108 | 80 | ||
109 | void hists__filter_by_dso(struct hists *self, const struct dso *dso); | 81 | void hists__filter_by_dso(struct hists *self, const struct dso *dso); |
110 | void hists__filter_by_thread(struct hists *self, const struct thread *thread); | 82 | void hists__filter_by_thread(struct hists *self, const struct thread *thread); |
@@ -113,21 +85,18 @@ u16 hists__col_len(struct hists *self, enum hist_column col); | |||
113 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); | 85 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); |
114 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); | 86 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); |
115 | 87 | ||
116 | #ifdef NO_NEWT_SUPPORT | 88 | struct perf_evlist; |
117 | static inline int hists__browse(struct hists *self __used, | ||
118 | const char *helpline __used, | ||
119 | const char *ev_name __used) | ||
120 | { | ||
121 | return 0; | ||
122 | } | ||
123 | 89 | ||
124 | static inline int hists__tui_browse_tree(struct rb_root *self __used, | 90 | #ifdef NO_NEWT_SUPPORT |
125 | const char *help __used) | 91 | static inline |
92 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used, | ||
93 | const char *help __used) | ||
126 | { | 94 | { |
127 | return 0; | 95 | return 0; |
128 | } | 96 | } |
129 | 97 | ||
130 | static inline int hist_entry__tui_annotate(struct hist_entry *self __used) | 98 | static inline int hist_entry__tui_annotate(struct hist_entry *self __used, |
99 | int evidx __used) | ||
131 | { | 100 | { |
132 | return 0; | 101 | return 0; |
133 | } | 102 | } |
@@ -135,14 +104,12 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self __used) | |||
135 | #define KEY_RIGHT -2 | 104 | #define KEY_RIGHT -2 |
136 | #else | 105 | #else |
137 | #include <newt.h> | 106 | #include <newt.h> |
138 | int hists__browse(struct hists *self, const char *helpline, | 107 | int hist_entry__tui_annotate(struct hist_entry *self, int evidx); |
139 | const char *ev_name); | ||
140 | int hist_entry__tui_annotate(struct hist_entry *self); | ||
141 | 108 | ||
142 | #define KEY_LEFT NEWT_KEY_LEFT | 109 | #define KEY_LEFT NEWT_KEY_LEFT |
143 | #define KEY_RIGHT NEWT_KEY_RIGHT | 110 | #define KEY_RIGHT NEWT_KEY_RIGHT |
144 | 111 | ||
145 | int hists__tui_browse_tree(struct rb_root *self, const char *help); | 112 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help); |
146 | #endif | 113 | #endif |
147 | 114 | ||
148 | unsigned int hists__sort_list_width(struct hists *self); | 115 | unsigned int hists__sort_list_width(struct hists *self); |
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h index 8be0b968ca0b..305c8484f200 100644 --- a/tools/perf/util/include/linux/bitops.h +++ b/tools/perf/util/include/linux/bitops.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define _PERF_LINUX_BITOPS_H_ | 2 | #define _PERF_LINUX_BITOPS_H_ |
3 | 3 | ||
4 | #include <linux/kernel.h> | 4 | #include <linux/kernel.h> |
5 | #include <linux/compiler.h> | ||
5 | #include <asm/hweight.h> | 6 | #include <asm/hweight.h> |
6 | 7 | ||
7 | #define BITS_PER_LONG __WORDSIZE | 8 | #define BITS_PER_LONG __WORDSIZE |
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/map.c b/tools/perf/util/map.c index 3a7eb6ec0eec..a16ecab5229d 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #include "symbol.h" | 1 | #include "symbol.h" |
2 | #include <errno.h> | 2 | #include <errno.h> |
3 | #include <inttypes.h> | ||
3 | #include <limits.h> | 4 | #include <limits.h> |
4 | #include <stdlib.h> | 5 | #include <stdlib.h> |
5 | #include <string.h> | 6 | #include <string.h> |
@@ -195,7 +196,7 @@ int map__overlap(struct map *l, struct map *r) | |||
195 | 196 | ||
196 | size_t map__fprintf(struct map *self, FILE *fp) | 197 | size_t map__fprintf(struct map *self, FILE *fp) |
197 | { | 198 | { |
198 | return fprintf(fp, " %Lx-%Lx %Lx %s\n", | 199 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s\n", |
199 | self->start, self->end, self->pgoff, self->dso->name); | 200 | self->start, self->end, self->pgoff, self->dso->name); |
200 | } | 201 | } |
201 | 202 | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 5cb6f4bde905..952b4ae3d954 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; |
@@ -266,11 +263,36 @@ static char *event_cache_name(u8 cache_type, u8 cache_op, u8 cache_result) | |||
266 | return name; | 263 | return name; |
267 | } | 264 | } |
268 | 265 | ||
266 | const char *event_type(int type) | ||
267 | { | ||
268 | switch (type) { | ||
269 | case PERF_TYPE_HARDWARE: | ||
270 | return "hardware"; | ||
271 | |||
272 | case PERF_TYPE_SOFTWARE: | ||
273 | return "software"; | ||
274 | |||
275 | case PERF_TYPE_TRACEPOINT: | ||
276 | return "tracepoint"; | ||
277 | |||
278 | case PERF_TYPE_HW_CACHE: | ||
279 | return "hardware-cache"; | ||
280 | |||
281 | default: | ||
282 | break; | ||
283 | } | ||
284 | |||
285 | return "unknown"; | ||
286 | } | ||
287 | |||
269 | const char *event_name(struct perf_evsel *evsel) | 288 | const char *event_name(struct perf_evsel *evsel) |
270 | { | 289 | { |
271 | u64 config = evsel->attr.config; | 290 | u64 config = evsel->attr.config; |
272 | int type = evsel->attr.type; | 291 | int type = evsel->attr.type; |
273 | 292 | ||
293 | if (evsel->name) | ||
294 | return evsel->name; | ||
295 | |||
274 | return __event_name(type, config); | 296 | return __event_name(type, config); |
275 | } | 297 | } |
276 | 298 | ||
@@ -279,7 +301,7 @@ const char *__event_name(int type, u64 config) | |||
279 | static char buf[32]; | 301 | static char buf[32]; |
280 | 302 | ||
281 | if (type == PERF_TYPE_RAW) { | 303 | if (type == PERF_TYPE_RAW) { |
282 | sprintf(buf, "raw 0x%llx", config); | 304 | sprintf(buf, "raw 0x%" PRIx64, config); |
283 | return buf; | 305 | return buf; |
284 | } | 306 | } |
285 | 307 | ||
@@ -449,8 +471,8 @@ parse_single_tracepoint_event(char *sys_name, | |||
449 | /* sys + ':' + event + ':' + flags*/ | 471 | /* sys + ':' + event + ':' + flags*/ |
450 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 472 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) |
451 | static enum event_result | 473 | static enum event_result |
452 | parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, | 474 | parse_multiple_tracepoint_event(const struct option *opt, char *sys_name, |
453 | char *flags) | 475 | const char *evt_exp, char *flags) |
454 | { | 476 | { |
455 | char evt_path[MAXPATHLEN]; | 477 | char evt_path[MAXPATHLEN]; |
456 | struct dirent *evt_ent; | 478 | struct dirent *evt_ent; |
@@ -483,41 +505,16 @@ parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp, | |||
483 | if (len < 0) | 505 | if (len < 0) |
484 | return EVT_FAILED; | 506 | return EVT_FAILED; |
485 | 507 | ||
486 | if (parse_events(NULL, event_opt, 0)) | 508 | if (parse_events(opt, event_opt, 0)) |
487 | return EVT_FAILED; | 509 | return EVT_FAILED; |
488 | } | 510 | } |
489 | 511 | ||
490 | return EVT_HANDLED_ALL; | 512 | return EVT_HANDLED_ALL; |
491 | } | 513 | } |
492 | 514 | ||
493 | static int store_event_type(const char *orgname) | 515 | static enum event_result |
494 | { | 516 | parse_tracepoint_event(const struct option *opt, const char **strp, |
495 | char filename[PATH_MAX], *c; | 517 | struct perf_event_attr *attr) |
496 | FILE *file; | ||
497 | int id, n; | ||
498 | |||
499 | sprintf(filename, "%s/", debugfs_path); | ||
500 | strncat(filename, orgname, strlen(orgname)); | ||
501 | strcat(filename, "/id"); | ||
502 | |||
503 | c = strchr(filename, ':'); | ||
504 | if (c) | ||
505 | *c = '/'; | ||
506 | |||
507 | file = fopen(filename, "r"); | ||
508 | if (!file) | ||
509 | return 0; | ||
510 | n = fscanf(file, "%i", &id); | ||
511 | fclose(file); | ||
512 | if (n < 1) { | ||
513 | pr_err("cannot store event ID\n"); | ||
514 | return -EINVAL; | ||
515 | } | ||
516 | return perf_header__push_event(id, orgname); | ||
517 | } | ||
518 | |||
519 | static enum event_result parse_tracepoint_event(const char **strp, | ||
520 | struct perf_event_attr *attr) | ||
521 | { | 518 | { |
522 | const char *evt_name; | 519 | const char *evt_name; |
523 | char *flags = NULL, *comma_loc; | 520 | char *flags = NULL, *comma_loc; |
@@ -555,13 +552,10 @@ static enum event_result parse_tracepoint_event(const char **strp, | |||
555 | if (evt_length >= MAX_EVENT_LENGTH) | 552 | if (evt_length >= MAX_EVENT_LENGTH) |
556 | return EVT_FAILED; | 553 | return EVT_FAILED; |
557 | if (strpbrk(evt_name, "*?")) { | 554 | if (strpbrk(evt_name, "*?")) { |
558 | *strp += strlen(sys_name) + evt_length; | 555 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ |
559 | return parse_multiple_tracepoint_event(sys_name, evt_name, | 556 | return parse_multiple_tracepoint_event(opt, sys_name, evt_name, |
560 | flags); | 557 | flags); |
561 | } else { | 558 | } else { |
562 | if (store_event_type(evt_name) < 0) | ||
563 | return EVT_FAILED; | ||
564 | |||
565 | return parse_single_tracepoint_event(sys_name, evt_name, | 559 | return parse_single_tracepoint_event(sys_name, evt_name, |
566 | evt_length, attr, strp); | 560 | evt_length, attr, strp); |
567 | } | 561 | } |
@@ -769,11 +763,12 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) | |||
769 | * Symbolic names are (almost) exactly matched. | 763 | * Symbolic names are (almost) exactly matched. |
770 | */ | 764 | */ |
771 | static enum event_result | 765 | static enum event_result |
772 | parse_event_symbols(const char **str, struct perf_event_attr *attr) | 766 | parse_event_symbols(const struct option *opt, const char **str, |
767 | struct perf_event_attr *attr) | ||
773 | { | 768 | { |
774 | enum event_result ret; | 769 | enum event_result ret; |
775 | 770 | ||
776 | ret = parse_tracepoint_event(str, attr); | 771 | ret = parse_tracepoint_event(opt, str, attr); |
777 | if (ret != EVT_FAILED) | 772 | if (ret != EVT_FAILED) |
778 | goto modifier; | 773 | goto modifier; |
779 | 774 | ||
@@ -807,14 +802,17 @@ modifier: | |||
807 | return ret; | 802 | return ret; |
808 | } | 803 | } |
809 | 804 | ||
810 | int parse_events(const struct option *opt __used, const char *str, int unset __used) | 805 | int parse_events(const struct option *opt, const char *str, int unset __used) |
811 | { | 806 | { |
807 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
812 | struct perf_event_attr attr; | 808 | struct perf_event_attr attr; |
813 | enum event_result ret; | 809 | enum event_result ret; |
810 | const char *ostr; | ||
814 | 811 | ||
815 | for (;;) { | 812 | for (;;) { |
813 | ostr = str; | ||
816 | memset(&attr, 0, sizeof(attr)); | 814 | memset(&attr, 0, sizeof(attr)); |
817 | ret = parse_event_symbols(&str, &attr); | 815 | ret = parse_event_symbols(opt, &str, &attr); |
818 | if (ret == EVT_FAILED) | 816 | if (ret == EVT_FAILED) |
819 | return -1; | 817 | return -1; |
820 | 818 | ||
@@ -823,12 +821,15 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
823 | 821 | ||
824 | if (ret != EVT_HANDLED_ALL) { | 822 | if (ret != EVT_HANDLED_ALL) { |
825 | struct perf_evsel *evsel; | 823 | struct perf_evsel *evsel; |
826 | evsel = perf_evsel__new(&attr, | 824 | evsel = perf_evsel__new(&attr, evlist->nr_entries); |
827 | nr_counters); | ||
828 | if (evsel == NULL) | 825 | if (evsel == NULL) |
829 | return -1; | 826 | return -1; |
830 | list_add_tail(&evsel->node, &evsel_list); | 827 | perf_evlist__add(evlist, evsel); |
831 | ++nr_counters; | 828 | |
829 | evsel->name = calloc(str - ostr + 1, 1); | ||
830 | if (!evsel->name) | ||
831 | return -1; | ||
832 | strncpy(evsel->name, ostr, str - ostr); | ||
832 | } | 833 | } |
833 | 834 | ||
834 | if (*str == 0) | 835 | if (*str == 0) |
@@ -842,13 +843,14 @@ int parse_events(const struct option *opt __used, const char *str, int unset __u | |||
842 | return 0; | 843 | return 0; |
843 | } | 844 | } |
844 | 845 | ||
845 | int parse_filter(const struct option *opt __used, const char *str, | 846 | int parse_filter(const struct option *opt, const char *str, |
846 | int unset __used) | 847 | int unset __used) |
847 | { | 848 | { |
849 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
848 | struct perf_evsel *last = NULL; | 850 | struct perf_evsel *last = NULL; |
849 | 851 | ||
850 | if (!list_empty(&evsel_list)) | 852 | if (evlist->nr_entries > 0) |
851 | last = list_entry(evsel_list.prev, struct perf_evsel, node); | 853 | last = list_entry(evlist->entries.prev, struct perf_evsel, node); |
852 | 854 | ||
853 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { | 855 | if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) { |
854 | fprintf(stderr, | 856 | fprintf(stderr, |
@@ -878,7 +880,7 @@ static const char * const event_type_descriptors[] = { | |||
878 | * Print the events from <debugfs_mount_point>/tracing/events | 880 | * Print the events from <debugfs_mount_point>/tracing/events |
879 | */ | 881 | */ |
880 | 882 | ||
881 | static void print_tracepoint_events(void) | 883 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob) |
882 | { | 884 | { |
883 | DIR *sys_dir, *evt_dir; | 885 | DIR *sys_dir, *evt_dir; |
884 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | 886 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; |
@@ -893,6 +895,9 @@ static void print_tracepoint_events(void) | |||
893 | return; | 895 | return; |
894 | 896 | ||
895 | for_each_subsystem(sys_dir, sys_dirent, sys_next) { | 897 | for_each_subsystem(sys_dir, sys_dirent, sys_next) { |
898 | if (subsys_glob != NULL && | ||
899 | !strglobmatch(sys_dirent.d_name, subsys_glob)) | ||
900 | continue; | ||
896 | 901 | ||
897 | snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path, | 902 | snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path, |
898 | sys_dirent.d_name); | 903 | sys_dirent.d_name); |
@@ -901,6 +906,10 @@ static void print_tracepoint_events(void) | |||
901 | continue; | 906 | continue; |
902 | 907 | ||
903 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { | 908 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { |
909 | if (event_glob != NULL && | ||
910 | !strglobmatch(evt_dirent.d_name, event_glob)) | ||
911 | continue; | ||
912 | |||
904 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | 913 | snprintf(evt_path, MAXPATHLEN, "%s:%s", |
905 | sys_dirent.d_name, evt_dirent.d_name); | 914 | sys_dirent.d_name, evt_dirent.d_name); |
906 | printf(" %-42s [%s]\n", evt_path, | 915 | printf(" %-42s [%s]\n", evt_path, |
@@ -952,13 +961,61 @@ int is_valid_tracepoint(const char *event_string) | |||
952 | return 0; | 961 | return 0; |
953 | } | 962 | } |
954 | 963 | ||
964 | void print_events_type(u8 type) | ||
965 | { | ||
966 | struct event_symbol *syms = event_symbols; | ||
967 | unsigned int i; | ||
968 | char name[64]; | ||
969 | |||
970 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | ||
971 | if (type != syms->type) | ||
972 | continue; | ||
973 | |||
974 | if (strlen(syms->alias)) | ||
975 | snprintf(name, sizeof(name), "%s OR %s", | ||
976 | syms->symbol, syms->alias); | ||
977 | else | ||
978 | snprintf(name, sizeof(name), "%s", syms->symbol); | ||
979 | |||
980 | printf(" %-42s [%s]\n", name, | ||
981 | event_type_descriptors[type]); | ||
982 | } | ||
983 | } | ||
984 | |||
985 | int print_hwcache_events(const char *event_glob) | ||
986 | { | ||
987 | unsigned int type, op, i, printed = 0; | ||
988 | |||
989 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | ||
990 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | ||
991 | /* skip invalid cache type */ | ||
992 | if (!is_cache_op_valid(type, op)) | ||
993 | continue; | ||
994 | |||
995 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | ||
996 | char *name = event_cache_name(type, op, i); | ||
997 | |||
998 | if (event_glob != NULL && | ||
999 | !strglobmatch(name, event_glob)) | ||
1000 | continue; | ||
1001 | |||
1002 | printf(" %-42s [%s]\n", name, | ||
1003 | event_type_descriptors[PERF_TYPE_HW_CACHE]); | ||
1004 | ++printed; | ||
1005 | } | ||
1006 | } | ||
1007 | } | ||
1008 | |||
1009 | return printed; | ||
1010 | } | ||
1011 | |||
955 | /* | 1012 | /* |
956 | * Print the help text for the event symbols: | 1013 | * Print the help text for the event symbols: |
957 | */ | 1014 | */ |
958 | void print_events(void) | 1015 | void print_events(const char *event_glob) |
959 | { | 1016 | { |
960 | struct event_symbol *syms = event_symbols; | 1017 | struct event_symbol *syms = event_symbols; |
961 | unsigned int i, type, op, prev_type = -1; | 1018 | unsigned int i, type, prev_type = -1, printed = 0, ntypes_printed = 0; |
962 | char name[40]; | 1019 | char name[40]; |
963 | 1020 | ||
964 | printf("\n"); | 1021 | printf("\n"); |
@@ -967,8 +1024,16 @@ void print_events(void) | |||
967 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | 1024 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
968 | type = syms->type; | 1025 | type = syms->type; |
969 | 1026 | ||
970 | if (type != prev_type) | 1027 | if (type != prev_type && printed) { |
971 | printf("\n"); | 1028 | printf("\n"); |
1029 | printed = 0; | ||
1030 | ntypes_printed++; | ||
1031 | } | ||
1032 | |||
1033 | if (event_glob != NULL && | ||
1034 | !(strglobmatch(syms->symbol, event_glob) || | ||
1035 | (syms->alias && strglobmatch(syms->alias, event_glob)))) | ||
1036 | continue; | ||
972 | 1037 | ||
973 | if (strlen(syms->alias)) | 1038 | if (strlen(syms->alias)) |
974 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); | 1039 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); |
@@ -978,22 +1043,17 @@ void print_events(void) | |||
978 | event_type_descriptors[type]); | 1043 | event_type_descriptors[type]); |
979 | 1044 | ||
980 | prev_type = type; | 1045 | prev_type = type; |
1046 | ++printed; | ||
981 | } | 1047 | } |
982 | 1048 | ||
983 | printf("\n"); | 1049 | if (ntypes_printed) { |
984 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | 1050 | printed = 0; |
985 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | 1051 | printf("\n"); |
986 | /* skip invalid cache type */ | ||
987 | if (!is_cache_op_valid(type, op)) | ||
988 | continue; | ||
989 | |||
990 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | ||
991 | printf(" %-42s [%s]\n", | ||
992 | event_cache_name(type, op, i), | ||
993 | event_type_descriptors[PERF_TYPE_HW_CACHE]); | ||
994 | } | ||
995 | } | ||
996 | } | 1052 | } |
1053 | print_hwcache_events(event_glob); | ||
1054 | |||
1055 | if (event_glob != NULL) | ||
1056 | return; | ||
997 | 1057 | ||
998 | printf("\n"); | 1058 | printf("\n"); |
999 | printf(" %-42s [%s]\n", | 1059 | printf(" %-42s [%s]\n", |
@@ -1006,37 +1066,7 @@ void print_events(void) | |||
1006 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); | 1066 | event_type_descriptors[PERF_TYPE_BREAKPOINT]); |
1007 | printf("\n"); | 1067 | printf("\n"); |
1008 | 1068 | ||
1009 | print_tracepoint_events(); | 1069 | print_tracepoint_events(NULL, NULL); |
1010 | 1070 | ||
1011 | exit(129); | 1071 | exit(129); |
1012 | } | 1072 | } |
1013 | |||
1014 | int perf_evsel_list__create_default(void) | ||
1015 | { | ||
1016 | struct perf_evsel *evsel; | ||
1017 | struct perf_event_attr attr; | ||
1018 | |||
1019 | memset(&attr, 0, sizeof(attr)); | ||
1020 | attr.type = PERF_TYPE_HARDWARE; | ||
1021 | attr.config = PERF_COUNT_HW_CPU_CYCLES; | ||
1022 | |||
1023 | evsel = perf_evsel__new(&attr, 0); | ||
1024 | |||
1025 | if (evsel == NULL) | ||
1026 | return -ENOMEM; | ||
1027 | |||
1028 | list_add(&evsel->node, &evsel_list); | ||
1029 | ++nr_counters; | ||
1030 | return 0; | ||
1031 | } | ||
1032 | |||
1033 | void perf_evsel_list__delete(void) | ||
1034 | { | ||
1035 | struct perf_evsel *pos, *n; | ||
1036 | |||
1037 | list_for_each_entry_safe(pos, n, &evsel_list, node) { | ||
1038 | list_del_init(&pos->node); | ||
1039 | perf_evsel__delete(pos); | ||
1040 | } | ||
1041 | nr_counters = 0; | ||
1042 | } | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index b82cafb83772..746d3fcbfc2a 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 { |
@@ -23,10 +18,9 @@ struct tracepoint_path { | |||
23 | }; | 18 | }; |
24 | 19 | ||
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 *evsel_list); | 21 | extern bool have_tracepoints(struct list_head *evlist); |
27 | |||
28 | extern int nr_counters; | ||
29 | 22 | ||
23 | const char *event_type(int type); | ||
30 | const char *event_name(struct perf_evsel *event); | 24 | const char *event_name(struct perf_evsel *event); |
31 | extern const char *__event_name(int type, u64 config); | 25 | extern const char *__event_name(int type, u64 config); |
32 | 26 | ||
@@ -35,7 +29,10 @@ extern int parse_filter(const struct option *opt, const char *str, int unset); | |||
35 | 29 | ||
36 | #define EVENTS_HELP_MAX (128*1024) | 30 | #define EVENTS_HELP_MAX (128*1024) |
37 | 31 | ||
38 | extern void print_events(void); | 32 | void print_events(const char *event_glob); |
33 | void print_events_type(u8 type); | ||
34 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob); | ||
35 | int print_hwcache_events(const char *event_glob); | ||
39 | extern int is_valid_tracepoint(const char *event_string); | 36 | extern int is_valid_tracepoint(const char *event_string); |
40 | 37 | ||
41 | extern char debugfs_path[]; | 38 | extern char debugfs_path[]; |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 128aaab0aeda..f0223166e761 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 |
@@ -172,7 +197,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | |||
172 | sym = __find_kernel_function_by_name(tp->symbol, &map); | 197 | sym = __find_kernel_function_by_name(tp->symbol, &map); |
173 | if (sym) { | 198 | if (sym) { |
174 | addr = map->unmap_ip(map, sym->start + tp->offset); | 199 | addr = map->unmap_ip(map, sym->start + tp->offset); |
175 | pr_debug("try to find %s+%ld@%llx\n", tp->symbol, | 200 | pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, |
176 | tp->offset, addr); | 201 | tp->offset, addr); |
177 | ret = find_perf_probe_point((unsigned long)addr, pp); | 202 | ret = find_perf_probe_point((unsigned long)addr, pp); |
178 | } | 203 | } |
@@ -209,7 +234,6 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
209 | 234 | ||
210 | /* Searching trace events corresponding to probe event */ | 235 | /* Searching trace events corresponding to probe event */ |
211 | ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs); | 236 | ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs); |
212 | close(fd); | ||
213 | 237 | ||
214 | if (ntevs > 0) { /* Succeeded to find trace events */ | 238 | if (ntevs > 0) { /* Succeeded to find trace events */ |
215 | pr_debug("find %d probe_trace_events.\n", ntevs); | 239 | pr_debug("find %d probe_trace_events.\n", ntevs); |
@@ -363,7 +387,6 @@ int show_line_range(struct line_range *lr, const char *module) | |||
363 | } | 387 | } |
364 | 388 | ||
365 | ret = find_line_range(fd, lr); | 389 | ret = find_line_range(fd, lr); |
366 | close(fd); | ||
367 | if (ret == 0) { | 390 | if (ret == 0) { |
368 | pr_warning("Specified source line is not found.\n"); | 391 | pr_warning("Specified source line is not found.\n"); |
369 | return -ENOENT; | 392 | return -ENOENT; |
@@ -384,7 +407,7 @@ int show_line_range(struct line_range *lr, const char *module) | |||
384 | setup_pager(); | 407 | setup_pager(); |
385 | 408 | ||
386 | if (lr->function) | 409 | if (lr->function) |
387 | fprintf(stdout, "<%s:%d>\n", lr->function, | 410 | fprintf(stdout, "<%s@%s:%d>\n", lr->function, lr->path, |
388 | lr->start - lr->offset); | 411 | lr->start - lr->offset); |
389 | else | 412 | else |
390 | fprintf(stdout, "<%s:%d>\n", lr->path, lr->start); | 413 | fprintf(stdout, "<%s:%d>\n", lr->path, lr->start); |
@@ -426,12 +449,14 @@ end: | |||
426 | } | 449 | } |
427 | 450 | ||
428 | static int show_available_vars_at(int fd, struct perf_probe_event *pev, | 451 | static int show_available_vars_at(int fd, struct perf_probe_event *pev, |
429 | int max_vls, bool externs) | 452 | int max_vls, struct strfilter *_filter, |
453 | bool externs) | ||
430 | { | 454 | { |
431 | char *buf; | 455 | char *buf; |
432 | int ret, i; | 456 | int ret, i, nvars; |
433 | struct str_node *node; | 457 | struct str_node *node; |
434 | struct variable_list *vls = NULL, *vl; | 458 | struct variable_list *vls = NULL, *vl; |
459 | const char *var; | ||
435 | 460 | ||
436 | buf = synthesize_perf_probe_point(&pev->point); | 461 | buf = synthesize_perf_probe_point(&pev->point); |
437 | if (!buf) | 462 | if (!buf) |
@@ -439,36 +464,45 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev, | |||
439 | pr_debug("Searching variables at %s\n", buf); | 464 | pr_debug("Searching variables at %s\n", buf); |
440 | 465 | ||
441 | ret = find_available_vars_at(fd, pev, &vls, max_vls, externs); | 466 | ret = find_available_vars_at(fd, pev, &vls, max_vls, externs); |
442 | if (ret > 0) { | 467 | if (ret <= 0) { |
443 | /* Some variables were found */ | 468 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); |
444 | fprintf(stdout, "Available variables at %s\n", buf); | 469 | goto end; |
445 | for (i = 0; i < ret; i++) { | 470 | } |
446 | vl = &vls[i]; | 471 | /* Some variables are found */ |
447 | /* | 472 | fprintf(stdout, "Available variables at %s\n", buf); |
448 | * A probe point might be converted to | 473 | for (i = 0; i < ret; i++) { |
449 | * several trace points. | 474 | vl = &vls[i]; |
450 | */ | 475 | /* |
451 | fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, | 476 | * A probe point might be converted to |
452 | vl->point.offset); | 477 | * several trace points. |
453 | free(vl->point.symbol); | 478 | */ |
454 | if (vl->vars) { | 479 | fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, |
455 | strlist__for_each(node, vl->vars) | 480 | vl->point.offset); |
481 | free(vl->point.symbol); | ||
482 | nvars = 0; | ||
483 | if (vl->vars) { | ||
484 | strlist__for_each(node, vl->vars) { | ||
485 | var = strchr(node->s, '\t') + 1; | ||
486 | if (strfilter__compare(_filter, var)) { | ||
456 | fprintf(stdout, "\t\t%s\n", node->s); | 487 | fprintf(stdout, "\t\t%s\n", node->s); |
457 | strlist__delete(vl->vars); | 488 | nvars++; |
458 | } else | 489 | } |
459 | fprintf(stdout, "(No variables)\n"); | 490 | } |
491 | strlist__delete(vl->vars); | ||
460 | } | 492 | } |
461 | free(vls); | 493 | if (nvars == 0) |
462 | } else | 494 | fprintf(stdout, "\t\t(No matched variables)\n"); |
463 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); | 495 | } |
464 | 496 | free(vls); | |
497 | end: | ||
465 | free(buf); | 498 | free(buf); |
466 | return ret; | 499 | return ret; |
467 | } | 500 | } |
468 | 501 | ||
469 | /* Show available variables on given probe point */ | 502 | /* Show available variables on given probe point */ |
470 | int show_available_vars(struct perf_probe_event *pevs, int npevs, | 503 | int show_available_vars(struct perf_probe_event *pevs, int npevs, |
471 | int max_vls, const char *module, bool externs) | 504 | int max_vls, const char *module, |
505 | struct strfilter *_filter, bool externs) | ||
472 | { | 506 | { |
473 | int i, fd, ret = 0; | 507 | int i, fd, ret = 0; |
474 | 508 | ||
@@ -476,18 +510,18 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
476 | if (ret < 0) | 510 | if (ret < 0) |
477 | return ret; | 511 | return ret; |
478 | 512 | ||
479 | fd = open_vmlinux(module); | ||
480 | if (fd < 0) { | ||
481 | pr_warning("Failed to open debug information file.\n"); | ||
482 | return fd; | ||
483 | } | ||
484 | |||
485 | setup_pager(); | 513 | setup_pager(); |
486 | 514 | ||
487 | for (i = 0; i < npevs && ret >= 0; i++) | 515 | for (i = 0; i < npevs && ret >= 0; i++) { |
488 | ret = show_available_vars_at(fd, &pevs[i], max_vls, externs); | 516 | fd = open_vmlinux(module); |
489 | 517 | if (fd < 0) { | |
490 | close(fd); | 518 | pr_warning("Failed to open debug information file.\n"); |
519 | ret = fd; | ||
520 | break; | ||
521 | } | ||
522 | ret = show_available_vars_at(fd, &pevs[i], max_vls, _filter, | ||
523 | externs); | ||
524 | } | ||
491 | return ret; | 525 | return ret; |
492 | } | 526 | } |
493 | 527 | ||
@@ -531,7 +565,9 @@ int show_line_range(struct line_range *lr __unused, const char *module __unused) | |||
531 | 565 | ||
532 | int show_available_vars(struct perf_probe_event *pevs __unused, | 566 | int show_available_vars(struct perf_probe_event *pevs __unused, |
533 | int npevs __unused, int max_vls __unused, | 567 | int npevs __unused, int max_vls __unused, |
534 | const char *module __unused, bool externs __unused) | 568 | const char *module __unused, |
569 | struct strfilter *filter __unused, | ||
570 | bool externs __unused) | ||
535 | { | 571 | { |
536 | pr_warning("Debuginfo-analysis is not supported.\n"); | 572 | pr_warning("Debuginfo-analysis is not supported.\n"); |
537 | return -ENOSYS; | 573 | return -ENOSYS; |
@@ -556,11 +592,11 @@ static int parse_line_num(char **ptr, int *val, const char *what) | |||
556 | * The line range syntax is described by: | 592 | * The line range syntax is described by: |
557 | * | 593 | * |
558 | * SRC[:SLN[+NUM|-ELN]] | 594 | * SRC[:SLN[+NUM|-ELN]] |
559 | * FNC[:SLN[+NUM|-ELN]] | 595 | * FNC[@SRC][:SLN[+NUM|-ELN]] |
560 | */ | 596 | */ |
561 | int parse_line_range_desc(const char *arg, struct line_range *lr) | 597 | int parse_line_range_desc(const char *arg, struct line_range *lr) |
562 | { | 598 | { |
563 | char *range, *name = strdup(arg); | 599 | char *range, *file, *name = strdup(arg); |
564 | int err; | 600 | int err; |
565 | 601 | ||
566 | if (!name) | 602 | if (!name) |
@@ -610,7 +646,16 @@ int parse_line_range_desc(const char *arg, struct line_range *lr) | |||
610 | } | 646 | } |
611 | } | 647 | } |
612 | 648 | ||
613 | if (strchr(name, '.')) | 649 | file = strchr(name, '@'); |
650 | if (file) { | ||
651 | *file = '\0'; | ||
652 | lr->file = strdup(++file); | ||
653 | if (lr->file == NULL) { | ||
654 | err = -ENOMEM; | ||
655 | goto err; | ||
656 | } | ||
657 | lr->function = name; | ||
658 | } else if (strchr(name, '.')) | ||
614 | lr->file = name; | 659 | lr->file = name; |
615 | else | 660 | else |
616 | lr->function = name; | 661 | lr->function = name; |
@@ -1784,9 +1829,12 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
1784 | } | 1829 | } |
1785 | 1830 | ||
1786 | /* Loop 2: add all events */ | 1831 | /* Loop 2: add all events */ |
1787 | for (i = 0; i < npevs && ret >= 0; i++) | 1832 | for (i = 0; i < npevs; i++) { |
1788 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, | 1833 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, |
1789 | pkgs[i].ntevs, force_add); | 1834 | pkgs[i].ntevs, force_add); |
1835 | if (ret < 0) | ||
1836 | break; | ||
1837 | } | ||
1790 | end: | 1838 | end: |
1791 | /* Loop 3: cleanup and free trace events */ | 1839 | /* Loop 3: cleanup and free trace events */ |
1792 | for (i = 0; i < npevs; i++) { | 1840 | for (i = 0; i < npevs; i++) { |
@@ -1912,4 +1960,46 @@ int del_perf_probe_events(struct strlist *dellist) | |||
1912 | 1960 | ||
1913 | return ret; | 1961 | return ret; |
1914 | } | 1962 | } |
1963 | /* TODO: don't use a global variable for filter ... */ | ||
1964 | static struct strfilter *available_func_filter; | ||
1965 | |||
1966 | /* | ||
1967 | * If a symbol corresponds to a function with global binding and | ||
1968 | * matches filter return 0. For all others return 1. | ||
1969 | */ | ||
1970 | static int filter_available_functions(struct map *map __unused, | ||
1971 | struct symbol *sym) | ||
1972 | { | ||
1973 | if (sym->binding == STB_GLOBAL && | ||
1974 | strfilter__compare(available_func_filter, sym->name)) | ||
1975 | return 0; | ||
1976 | return 1; | ||
1977 | } | ||
1915 | 1978 | ||
1979 | int show_available_funcs(const char *module, struct strfilter *_filter) | ||
1980 | { | ||
1981 | struct map *map; | ||
1982 | int ret; | ||
1983 | |||
1984 | setup_pager(); | ||
1985 | |||
1986 | ret = init_vmlinux(); | ||
1987 | if (ret < 0) | ||
1988 | return ret; | ||
1989 | |||
1990 | map = kernel_get_module_map(module); | ||
1991 | if (!map) { | ||
1992 | pr_err("Failed to find %s map.\n", (module) ? : "kernel"); | ||
1993 | return -EINVAL; | ||
1994 | } | ||
1995 | available_func_filter = _filter; | ||
1996 | if (map__load(map, filter_available_functions)) { | ||
1997 | pr_err("Failed to load map.\n"); | ||
1998 | return -EINVAL; | ||
1999 | } | ||
2000 | if (!dso__sorted_by_name(map->dso, map->type)) | ||
2001 | dso__sort_by_name(map->dso, map->type); | ||
2002 | |||
2003 | dso__fprintf_symbols_by_name(map->dso, map->type, stdout); | ||
2004 | return 0; | ||
2005 | } | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 5accbedfea37..3434fc9d79d5 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include "strlist.h" | 5 | #include "strlist.h" |
6 | #include "strfilter.h" | ||
6 | 7 | ||
7 | extern bool probe_event_dry_run; | 8 | extern bool probe_event_dry_run; |
8 | 9 | ||
@@ -126,7 +127,8 @@ extern int show_perf_probe_events(void); | |||
126 | extern int show_line_range(struct line_range *lr, const char *module); | 127 | 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, | 128 | extern int show_available_vars(struct perf_probe_event *pevs, int npevs, |
128 | int max_probe_points, const char *module, | 129 | int max_probe_points, const char *module, |
129 | bool externs); | 130 | struct strfilter *filter, bool externs); |
131 | extern int show_available_funcs(const char *module, struct strfilter *filter); | ||
130 | 132 | ||
131 | 133 | ||
132 | /* Maximum index number of event-name postfix */ | 134 | /* 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..b7c85ce466a1 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <ctype.h> | 33 | #include <ctype.h> |
34 | #include <dwarf-regs.h> | 34 | #include <dwarf-regs.h> |
35 | 35 | ||
36 | #include <linux/bitops.h> | ||
36 | #include "event.h" | 37 | #include "event.h" |
37 | #include "debug.h" | 38 | #include "debug.h" |
38 | #include "util.h" | 39 | #include "util.h" |
@@ -272,6 +273,25 @@ static const char *cu_get_comp_dir(Dwarf_Die *cu_die) | |||
272 | return dwarf_formstring(&attr); | 273 | return dwarf_formstring(&attr); |
273 | } | 274 | } |
274 | 275 | ||
276 | /* Get a line number and file name for given address */ | ||
277 | static int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, | ||
278 | const char **fname, int *lineno) | ||
279 | { | ||
280 | Dwarf_Line *line; | ||
281 | Dwarf_Addr laddr; | ||
282 | |||
283 | line = dwarf_getsrc_die(cudie, (Dwarf_Addr)addr); | ||
284 | if (line && dwarf_lineaddr(line, &laddr) == 0 && | ||
285 | addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) { | ||
286 | *fname = dwarf_linesrc(line, NULL, NULL); | ||
287 | if (!*fname) | ||
288 | /* line number is useless without filename */ | ||
289 | *lineno = 0; | ||
290 | } | ||
291 | |||
292 | return *lineno ?: -ENOENT; | ||
293 | } | ||
294 | |||
275 | /* Compare diename and tname */ | 295 | /* Compare diename and tname */ |
276 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | 296 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) |
277 | { | 297 | { |
@@ -280,6 +300,19 @@ static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | |||
280 | return name ? (strcmp(tname, name) == 0) : false; | 300 | return name ? (strcmp(tname, name) == 0) : false; |
281 | } | 301 | } |
282 | 302 | ||
303 | /* Get callsite line number of inline-function instance */ | ||
304 | static int die_get_call_lineno(Dwarf_Die *in_die) | ||
305 | { | ||
306 | Dwarf_Attribute attr; | ||
307 | Dwarf_Word ret; | ||
308 | |||
309 | if (!dwarf_attr(in_die, DW_AT_call_line, &attr)) | ||
310 | return -ENOENT; | ||
311 | |||
312 | dwarf_formudata(&attr, &ret); | ||
313 | return (int)ret; | ||
314 | } | ||
315 | |||
283 | /* Get type die */ | 316 | /* Get type die */ |
284 | static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | 317 | static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) |
285 | { | 318 | { |
@@ -320,13 +353,23 @@ static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | |||
320 | return vr_die; | 353 | return vr_die; |
321 | } | 354 | } |
322 | 355 | ||
323 | static bool die_is_signed_type(Dwarf_Die *tp_die) | 356 | static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name, |
357 | Dwarf_Word *result) | ||
324 | { | 358 | { |
325 | Dwarf_Attribute attr; | 359 | Dwarf_Attribute attr; |
360 | |||
361 | if (dwarf_attr(tp_die, attr_name, &attr) == NULL || | ||
362 | dwarf_formudata(&attr, result) != 0) | ||
363 | return -ENOENT; | ||
364 | |||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | static bool die_is_signed_type(Dwarf_Die *tp_die) | ||
369 | { | ||
326 | Dwarf_Word ret; | 370 | Dwarf_Word ret; |
327 | 371 | ||
328 | if (dwarf_attr(tp_die, DW_AT_encoding, &attr) == NULL || | 372 | if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret)) |
329 | dwarf_formudata(&attr, &ret) != 0) | ||
330 | return false; | 373 | return false; |
331 | 374 | ||
332 | return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || | 375 | return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || |
@@ -335,11 +378,29 @@ static bool die_is_signed_type(Dwarf_Die *tp_die) | |||
335 | 378 | ||
336 | static int die_get_byte_size(Dwarf_Die *tp_die) | 379 | static int die_get_byte_size(Dwarf_Die *tp_die) |
337 | { | 380 | { |
338 | Dwarf_Attribute attr; | ||
339 | Dwarf_Word ret; | 381 | Dwarf_Word ret; |
340 | 382 | ||
341 | if (dwarf_attr(tp_die, DW_AT_byte_size, &attr) == NULL || | 383 | if (die_get_attr_udata(tp_die, DW_AT_byte_size, &ret)) |
342 | dwarf_formudata(&attr, &ret) != 0) | 384 | return 0; |
385 | |||
386 | return (int)ret; | ||
387 | } | ||
388 | |||
389 | static int die_get_bit_size(Dwarf_Die *tp_die) | ||
390 | { | ||
391 | Dwarf_Word ret; | ||
392 | |||
393 | if (die_get_attr_udata(tp_die, DW_AT_bit_size, &ret)) | ||
394 | return 0; | ||
395 | |||
396 | return (int)ret; | ||
397 | } | ||
398 | |||
399 | static int die_get_bit_offset(Dwarf_Die *tp_die) | ||
400 | { | ||
401 | Dwarf_Word ret; | ||
402 | |||
403 | if (die_get_attr_udata(tp_die, DW_AT_bit_offset, &ret)) | ||
343 | return 0; | 404 | return 0; |
344 | 405 | ||
345 | return (int)ret; | 406 | return (int)ret; |
@@ -455,7 +516,165 @@ static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) | |||
455 | static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | 516 | static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, |
456 | Dwarf_Die *die_mem) | 517 | Dwarf_Die *die_mem) |
457 | { | 518 | { |
458 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); | 519 | Dwarf_Die tmp_die; |
520 | |||
521 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, &tmp_die); | ||
522 | if (!sp_die) | ||
523 | return NULL; | ||
524 | |||
525 | /* Inlined function could be recursive. Trace it until fail */ | ||
526 | while (sp_die) { | ||
527 | memcpy(die_mem, sp_die, sizeof(Dwarf_Die)); | ||
528 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, | ||
529 | &tmp_die); | ||
530 | } | ||
531 | |||
532 | return die_mem; | ||
533 | } | ||
534 | |||
535 | /* Walker on lines (Note: line number will not be sorted) */ | ||
536 | typedef int (* line_walk_handler_t) (const char *fname, int lineno, | ||
537 | Dwarf_Addr addr, void *data); | ||
538 | |||
539 | struct __line_walk_param { | ||
540 | const char *fname; | ||
541 | line_walk_handler_t handler; | ||
542 | void *data; | ||
543 | int retval; | ||
544 | }; | ||
545 | |||
546 | static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) | ||
547 | { | ||
548 | struct __line_walk_param *lw = data; | ||
549 | Dwarf_Addr addr; | ||
550 | int lineno; | ||
551 | |||
552 | if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { | ||
553 | lineno = die_get_call_lineno(in_die); | ||
554 | if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { | ||
555 | lw->retval = lw->handler(lw->fname, lineno, addr, | ||
556 | lw->data); | ||
557 | if (lw->retval != 0) | ||
558 | return DIE_FIND_CB_FOUND; | ||
559 | } | ||
560 | } | ||
561 | return DIE_FIND_CB_SIBLING; | ||
562 | } | ||
563 | |||
564 | /* Walk on lines of blocks included in given DIE */ | ||
565 | static int __die_walk_funclines(Dwarf_Die *sp_die, | ||
566 | line_walk_handler_t handler, void *data) | ||
567 | { | ||
568 | struct __line_walk_param lw = { | ||
569 | .handler = handler, | ||
570 | .data = data, | ||
571 | .retval = 0, | ||
572 | }; | ||
573 | Dwarf_Die die_mem; | ||
574 | Dwarf_Addr addr; | ||
575 | int lineno; | ||
576 | |||
577 | /* Handle function declaration line */ | ||
578 | lw.fname = dwarf_decl_file(sp_die); | ||
579 | if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
580 | dwarf_entrypc(sp_die, &addr) == 0) { | ||
581 | lw.retval = handler(lw.fname, lineno, addr, data); | ||
582 | if (lw.retval != 0) | ||
583 | goto done; | ||
584 | } | ||
585 | die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem); | ||
586 | done: | ||
587 | return lw.retval; | ||
588 | } | ||
589 | |||
590 | static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) | ||
591 | { | ||
592 | struct __line_walk_param *lw = data; | ||
593 | |||
594 | lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data); | ||
595 | if (lw->retval != 0) | ||
596 | return DWARF_CB_ABORT; | ||
597 | |||
598 | return DWARF_CB_OK; | ||
599 | } | ||
600 | |||
601 | /* | ||
602 | * Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on | ||
603 | * the lines inside the subprogram, otherwise PDIE must be a CU DIE. | ||
604 | */ | ||
605 | static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler, | ||
606 | void *data) | ||
607 | { | ||
608 | Dwarf_Lines *lines; | ||
609 | Dwarf_Line *line; | ||
610 | Dwarf_Addr addr; | ||
611 | const char *fname; | ||
612 | int lineno, ret = 0; | ||
613 | Dwarf_Die die_mem, *cu_die; | ||
614 | size_t nlines, i; | ||
615 | |||
616 | /* Get the CU die */ | ||
617 | if (dwarf_tag(pdie) == DW_TAG_subprogram) | ||
618 | cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL); | ||
619 | else | ||
620 | cu_die = pdie; | ||
621 | if (!cu_die) { | ||
622 | pr_debug2("Failed to get CU from subprogram\n"); | ||
623 | return -EINVAL; | ||
624 | } | ||
625 | |||
626 | /* Get lines list in the CU */ | ||
627 | if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { | ||
628 | pr_debug2("Failed to get source lines on this CU.\n"); | ||
629 | return -ENOENT; | ||
630 | } | ||
631 | pr_debug2("Get %zd lines from this CU\n", nlines); | ||
632 | |||
633 | /* Walk on the lines on lines list */ | ||
634 | for (i = 0; i < nlines; i++) { | ||
635 | line = dwarf_onesrcline(lines, i); | ||
636 | if (line == NULL || | ||
637 | dwarf_lineno(line, &lineno) != 0 || | ||
638 | dwarf_lineaddr(line, &addr) != 0) { | ||
639 | pr_debug2("Failed to get line info. " | ||
640 | "Possible error in debuginfo.\n"); | ||
641 | continue; | ||
642 | } | ||
643 | /* Filter lines based on address */ | ||
644 | if (pdie != cu_die) | ||
645 | /* | ||
646 | * Address filtering | ||
647 | * The line is included in given function, and | ||
648 | * no inline block includes it. | ||
649 | */ | ||
650 | if (!dwarf_haspc(pdie, addr) || | ||
651 | die_find_inlinefunc(pdie, addr, &die_mem)) | ||
652 | continue; | ||
653 | /* Get source line */ | ||
654 | fname = dwarf_linesrc(line, NULL, NULL); | ||
655 | |||
656 | ret = handler(fname, lineno, addr, data); | ||
657 | if (ret != 0) | ||
658 | return ret; | ||
659 | } | ||
660 | |||
661 | /* | ||
662 | * Dwarf lines doesn't include function declarations and inlined | ||
663 | * subroutines. We have to check functions list or given function. | ||
664 | */ | ||
665 | if (pdie != cu_die) | ||
666 | ret = __die_walk_funclines(pdie, handler, data); | ||
667 | else { | ||
668 | struct __line_walk_param param = { | ||
669 | .handler = handler, | ||
670 | .data = data, | ||
671 | .retval = 0, | ||
672 | }; | ||
673 | dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); | ||
674 | ret = param.retval; | ||
675 | } | ||
676 | |||
677 | return ret; | ||
459 | } | 678 | } |
460 | 679 | ||
461 | struct __find_variable_param { | 680 | struct __find_variable_param { |
@@ -669,6 +888,8 @@ static_var: | |||
669 | return 0; | 888 | return 0; |
670 | } | 889 | } |
671 | 890 | ||
891 | #define BYTES_TO_BITS(nb) ((nb) * BITS_PER_LONG / sizeof(long)) | ||
892 | |||
672 | static int convert_variable_type(Dwarf_Die *vr_die, | 893 | static int convert_variable_type(Dwarf_Die *vr_die, |
673 | struct probe_trace_arg *tvar, | 894 | struct probe_trace_arg *tvar, |
674 | const char *cast) | 895 | const char *cast) |
@@ -685,6 +906,14 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
685 | return (tvar->type == NULL) ? -ENOMEM : 0; | 906 | return (tvar->type == NULL) ? -ENOMEM : 0; |
686 | } | 907 | } |
687 | 908 | ||
909 | if (die_get_bit_size(vr_die) != 0) { | ||
910 | /* This is a bitfield */ | ||
911 | ret = snprintf(buf, 16, "b%d@%d/%zd", die_get_bit_size(vr_die), | ||
912 | die_get_bit_offset(vr_die), | ||
913 | BYTES_TO_BITS(die_get_byte_size(vr_die))); | ||
914 | goto formatted; | ||
915 | } | ||
916 | |||
688 | if (die_get_real_type(vr_die, &type) == NULL) { | 917 | if (die_get_real_type(vr_die, &type) == NULL) { |
689 | pr_warning("Failed to get a type information of %s.\n", | 918 | pr_warning("Failed to get a type information of %s.\n", |
690 | dwarf_diename(vr_die)); | 919 | dwarf_diename(vr_die)); |
@@ -729,29 +958,31 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
729 | return (tvar->type == NULL) ? -ENOMEM : 0; | 958 | return (tvar->type == NULL) ? -ENOMEM : 0; |
730 | } | 959 | } |
731 | 960 | ||
732 | ret = die_get_byte_size(&type) * 8; | 961 | ret = BYTES_TO_BITS(die_get_byte_size(&type)); |
733 | if (ret) { | 962 | if (!ret) |
734 | /* Check the bitwidth */ | 963 | /* No size ... try to use default type */ |
735 | if (ret > MAX_BASIC_TYPE_BITS) { | 964 | return 0; |
736 | pr_info("%s exceeds max-bitwidth." | ||
737 | " Cut down to %d bits.\n", | ||
738 | dwarf_diename(&type), MAX_BASIC_TYPE_BITS); | ||
739 | ret = MAX_BASIC_TYPE_BITS; | ||
740 | } | ||
741 | 965 | ||
742 | ret = snprintf(buf, 16, "%c%d", | 966 | /* Check the bitwidth */ |
743 | die_is_signed_type(&type) ? 's' : 'u', ret); | 967 | if (ret > MAX_BASIC_TYPE_BITS) { |
744 | if (ret < 0 || ret >= 16) { | 968 | pr_info("%s exceeds max-bitwidth. Cut down to %d bits.\n", |
745 | if (ret >= 16) | 969 | dwarf_diename(&type), MAX_BASIC_TYPE_BITS); |
746 | ret = -E2BIG; | 970 | ret = MAX_BASIC_TYPE_BITS; |
747 | pr_warning("Failed to convert variable type: %s\n", | 971 | } |
748 | strerror(-ret)); | 972 | ret = snprintf(buf, 16, "%c%d", |
749 | return ret; | 973 | die_is_signed_type(&type) ? 's' : 'u', ret); |
750 | } | 974 | |
751 | tvar->type = strdup(buf); | 975 | formatted: |
752 | if (tvar->type == NULL) | 976 | if (ret < 0 || ret >= 16) { |
753 | return -ENOMEM; | 977 | if (ret >= 16) |
978 | ret = -E2BIG; | ||
979 | pr_warning("Failed to convert variable type: %s\n", | ||
980 | strerror(-ret)); | ||
981 | return ret; | ||
754 | } | 982 | } |
983 | tvar->type = strdup(buf); | ||
984 | if (tvar->type == NULL) | ||
985 | return -ENOMEM; | ||
755 | return 0; | 986 | return 0; |
756 | } | 987 | } |
757 | 988 | ||
@@ -1050,157 +1281,102 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1050 | return ret; | 1281 | return ret; |
1051 | } | 1282 | } |
1052 | 1283 | ||
1053 | /* Find probe point from its line number */ | 1284 | static int probe_point_line_walker(const char *fname, int lineno, |
1054 | static int find_probe_point_by_line(struct probe_finder *pf) | 1285 | Dwarf_Addr addr, void *data) |
1055 | { | 1286 | { |
1056 | Dwarf_Lines *lines; | 1287 | struct probe_finder *pf = data; |
1057 | Dwarf_Line *line; | 1288 | 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 | 1289 | ||
1068 | for (i = 0; i < nlines && ret == 0; i++) { | 1290 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) |
1069 | line = dwarf_onesrcline(lines, i); | 1291 | return 0; |
1070 | if (dwarf_lineno(line, &lineno) != 0 || | ||
1071 | lineno != pf->lno) | ||
1072 | continue; | ||
1073 | 1292 | ||
1074 | /* TODO: Get fileno from line, but how? */ | 1293 | pf->addr = addr; |
1075 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | 1294 | ret = call_probe_finder(NULL, pf); |
1076 | continue; | ||
1077 | 1295 | ||
1078 | if (dwarf_lineaddr(line, &addr) != 0) { | 1296 | /* Continue if no error, because the line will be in inline function */ |
1079 | pr_warning("Failed to get the address of the line.\n"); | 1297 | return ret < 0 ? ret : 0; |
1080 | return -ENOENT; | 1298 | } |
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 | 1299 | ||
1086 | ret = call_probe_finder(NULL, pf); | 1300 | /* Find probe point from its line number */ |
1087 | /* Continuing, because target line might be inlined. */ | 1301 | static int find_probe_point_by_line(struct probe_finder *pf) |
1088 | } | 1302 | { |
1089 | return ret; | 1303 | return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf); |
1090 | } | 1304 | } |
1091 | 1305 | ||
1092 | /* Find lines which match lazy pattern */ | 1306 | /* Find lines which match lazy pattern */ |
1093 | static int find_lazy_match_lines(struct list_head *head, | 1307 | static int find_lazy_match_lines(struct list_head *head, |
1094 | const char *fname, const char *pat) | 1308 | const char *fname, const char *pat) |
1095 | { | 1309 | { |
1096 | char *fbuf, *p1, *p2; | 1310 | FILE *fp; |
1097 | int fd, line, nlines = -1; | 1311 | char *line = NULL; |
1098 | struct stat st; | 1312 | size_t line_len; |
1099 | 1313 | ssize_t len; | |
1100 | fd = open(fname, O_RDONLY); | 1314 | int count = 0, linenum = 1; |
1101 | if (fd < 0) { | 1315 | |
1102 | pr_warning("Failed to open %s: %s\n", fname, strerror(-fd)); | 1316 | fp = fopen(fname, "r"); |
1317 | if (!fp) { | ||
1318 | pr_warning("Failed to open %s: %s\n", fname, strerror(errno)); | ||
1103 | return -errno; | 1319 | return -errno; |
1104 | } | 1320 | } |
1105 | 1321 | ||
1106 | if (fstat(fd, &st) < 0) { | 1322 | while ((len = getline(&line, &line_len, fp)) > 0) { |
1107 | pr_warning("Failed to get the size of %s: %s\n", | 1323 | |
1108 | fname, strerror(errno)); | 1324 | if (line[len - 1] == '\n') |
1109 | nlines = -errno; | 1325 | line[len - 1] = '\0'; |
1110 | goto out_close; | 1326 | |
1111 | } | 1327 | if (strlazymatch(line, pat)) { |
1112 | 1328 | line_list__add_line(head, linenum); | |
1113 | nlines = -ENOMEM; | 1329 | count++; |
1114 | fbuf = malloc(st.st_size + 2); | ||
1115 | if (fbuf == NULL) | ||
1116 | goto out_close; | ||
1117 | if (read(fd, fbuf, st.st_size) < 0) { | ||
1118 | pr_warning("Failed to read %s: %s\n", fname, strerror(errno)); | ||
1119 | nlines = -errno; | ||
1120 | goto out_free_fbuf; | ||
1121 | } | ||
1122 | fbuf[st.st_size] = '\n'; /* Dummy line */ | ||
1123 | fbuf[st.st_size + 1] = '\0'; | ||
1124 | p1 = fbuf; | ||
1125 | line = 1; | ||
1126 | nlines = 0; | ||
1127 | while ((p2 = strchr(p1, '\n')) != NULL) { | ||
1128 | *p2 = '\0'; | ||
1129 | if (strlazymatch(p1, pat)) { | ||
1130 | line_list__add_line(head, line); | ||
1131 | nlines++; | ||
1132 | } | 1330 | } |
1133 | line++; | 1331 | linenum++; |
1134 | p1 = p2 + 1; | ||
1135 | } | 1332 | } |
1136 | out_free_fbuf: | 1333 | |
1137 | free(fbuf); | 1334 | if (ferror(fp)) |
1138 | out_close: | 1335 | count = -errno; |
1139 | close(fd); | 1336 | free(line); |
1140 | return nlines; | 1337 | fclose(fp); |
1338 | |||
1339 | if (count == 0) | ||
1340 | pr_debug("No matched lines found in %s.\n", fname); | ||
1341 | return count; | ||
1342 | } | ||
1343 | |||
1344 | static int probe_point_lazy_walker(const char *fname, int lineno, | ||
1345 | Dwarf_Addr addr, void *data) | ||
1346 | { | ||
1347 | struct probe_finder *pf = data; | ||
1348 | int ret; | ||
1349 | |||
1350 | if (!line_list__has_line(&pf->lcache, lineno) || | ||
1351 | strtailcmp(fname, pf->fname) != 0) | ||
1352 | return 0; | ||
1353 | |||
1354 | pr_debug("Probe line found: line:%d addr:0x%llx\n", | ||
1355 | lineno, (unsigned long long)addr); | ||
1356 | pf->addr = addr; | ||
1357 | ret = call_probe_finder(NULL, pf); | ||
1358 | |||
1359 | /* | ||
1360 | * Continue if no error, because the lazy pattern will match | ||
1361 | * to other lines | ||
1362 | */ | ||
1363 | return ret < 0 ? ret : 0; | ||
1141 | } | 1364 | } |
1142 | 1365 | ||
1143 | /* Find probe points from lazy pattern */ | 1366 | /* Find probe points from lazy pattern */ |
1144 | static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | 1367 | static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) |
1145 | { | 1368 | { |
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; | 1369 | int ret = 0; |
1153 | 1370 | ||
1154 | if (list_empty(&pf->lcache)) { | 1371 | if (list_empty(&pf->lcache)) { |
1155 | /* Matching lazy line pattern */ | 1372 | /* Matching lazy line pattern */ |
1156 | ret = find_lazy_match_lines(&pf->lcache, pf->fname, | 1373 | ret = find_lazy_match_lines(&pf->lcache, pf->fname, |
1157 | pf->pev->point.lazy_line); | 1374 | pf->pev->point.lazy_line); |
1158 | if (ret == 0) { | 1375 | if (ret <= 0) |
1159 | pr_debug("No matched lines found in %s.\n", pf->fname); | ||
1160 | return 0; | ||
1161 | } else if (ret < 0) | ||
1162 | return ret; | 1376 | return ret; |
1163 | } | 1377 | } |
1164 | 1378 | ||
1165 | if (dwarf_getsrclines(&pf->cu_die, &lines, &nlines) != 0) { | 1379 | 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 | } | 1380 | } |
1205 | 1381 | ||
1206 | /* Callback parameter with return value */ | 1382 | /* Callback parameter with return value */ |
@@ -1251,6 +1427,10 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | |||
1251 | !die_compare_name(sp_die, pp->function)) | 1427 | !die_compare_name(sp_die, pp->function)) |
1252 | return DWARF_CB_OK; | 1428 | return DWARF_CB_OK; |
1253 | 1429 | ||
1430 | /* Check declared file */ | ||
1431 | if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die))) | ||
1432 | return DWARF_CB_OK; | ||
1433 | |||
1254 | pf->fname = dwarf_decl_file(sp_die); | 1434 | pf->fname = dwarf_decl_file(sp_die); |
1255 | if (pp->line) { /* Function relative line */ | 1435 | if (pp->line) { /* Function relative line */ |
1256 | dwarf_decl_line(sp_die, &pf->lno); | 1436 | dwarf_decl_line(sp_die, &pf->lno); |
@@ -1307,6 +1487,7 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
1307 | if (!dbg) { | 1487 | if (!dbg) { |
1308 | pr_warning("No debug information found in the vmlinux - " | 1488 | pr_warning("No debug information found in the vmlinux - " |
1309 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | 1489 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); |
1490 | close(fd); /* Without dwfl_end(), fd isn't closed. */ | ||
1310 | return -EBADF; | 1491 | return -EBADF; |
1311 | } | 1492 | } |
1312 | 1493 | ||
@@ -1318,8 +1499,7 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
1318 | off = 0; | 1499 | off = 0; |
1319 | line_list__init(&pf->lcache); | 1500 | line_list__init(&pf->lcache); |
1320 | /* Loop on CUs (Compilation Unit) */ | 1501 | /* Loop on CUs (Compilation Unit) */ |
1321 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) && | 1502 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { |
1322 | ret >= 0) { | ||
1323 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1503 | /* Get the DIE(Debugging Information Entry) of this CU */ |
1324 | diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die); | 1504 | diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die); |
1325 | if (!diep) | 1505 | if (!diep) |
@@ -1340,6 +1520,8 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
1340 | pf->lno = pp->line; | 1520 | pf->lno = pp->line; |
1341 | ret = find_probe_point_by_line(pf); | 1521 | ret = find_probe_point_by_line(pf); |
1342 | } | 1522 | } |
1523 | if (ret < 0) | ||
1524 | break; | ||
1343 | } | 1525 | } |
1344 | off = noff; | 1526 | off = noff; |
1345 | } | 1527 | } |
@@ -1541,11 +1723,9 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) | |||
1541 | Dwarf_Die cudie, spdie, indie; | 1723 | Dwarf_Die cudie, spdie, indie; |
1542 | Dwarf *dbg = NULL; | 1724 | Dwarf *dbg = NULL; |
1543 | Dwfl *dwfl = NULL; | 1725 | Dwfl *dwfl = NULL; |
1544 | Dwarf_Line *line; | 1726 | Dwarf_Addr _addr, baseaddr, bias = 0; |
1545 | Dwarf_Addr laddr, eaddr, bias = 0; | 1727 | const char *fname = NULL, *func = NULL, *tmp; |
1546 | const char *tmp; | 1728 | int baseline = 0, lineno = 0, ret = 0; |
1547 | int lineno, ret = 0; | ||
1548 | bool found = false; | ||
1549 | 1729 | ||
1550 | /* Open the live linux kernel */ | 1730 | /* Open the live linux kernel */ |
1551 | dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias); | 1731 | dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias); |
@@ -1566,68 +1746,79 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) | |||
1566 | goto end; | 1746 | goto end; |
1567 | } | 1747 | } |
1568 | 1748 | ||
1569 | /* Find a corresponding line */ | 1749 | /* Find a corresponding line (filename and lineno) */ |
1570 | line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr); | 1750 | cu_find_lineinfo(&cudie, addr, &fname, &lineno); |
1571 | if (line) { | 1751 | /* Don't care whether it failed or not */ |
1572 | if (dwarf_lineaddr(line, &laddr) == 0 && | ||
1573 | (Dwarf_Addr)addr == laddr && | ||
1574 | dwarf_lineno(line, &lineno) == 0) { | ||
1575 | tmp = dwarf_linesrc(line, NULL, NULL); | ||
1576 | if (tmp) { | ||
1577 | ppt->line = lineno; | ||
1578 | ppt->file = strdup(tmp); | ||
1579 | if (ppt->file == NULL) { | ||
1580 | ret = -ENOMEM; | ||
1581 | goto end; | ||
1582 | } | ||
1583 | found = true; | ||
1584 | } | ||
1585 | } | ||
1586 | } | ||
1587 | 1752 | ||
1588 | /* Find a corresponding function */ | 1753 | /* Find a corresponding function (name, baseline and baseaddr) */ |
1589 | if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { | 1754 | if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { |
1755 | /* Get function entry information */ | ||
1590 | tmp = dwarf_diename(&spdie); | 1756 | tmp = dwarf_diename(&spdie); |
1591 | if (!tmp || dwarf_entrypc(&spdie, &eaddr) != 0) | 1757 | if (!tmp || |
1592 | goto end; | 1758 | dwarf_entrypc(&spdie, &baseaddr) != 0 || |
1593 | 1759 | dwarf_decl_line(&spdie, &baseline) != 0) | |
1594 | if (ppt->line) { | 1760 | goto post; |
1595 | if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, | 1761 | func = tmp; |
1596 | &indie)) { | 1762 | |
1597 | /* addr in an inline function */ | 1763 | if (addr == (unsigned long)baseaddr) |
1764 | /* Function entry - Relative line number is 0 */ | ||
1765 | lineno = baseline; | ||
1766 | else if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, | ||
1767 | &indie)) { | ||
1768 | if (dwarf_entrypc(&indie, &_addr) == 0 && | ||
1769 | _addr == addr) | ||
1770 | /* | ||
1771 | * addr is at an inline function entry. | ||
1772 | * In this case, lineno should be the call-site | ||
1773 | * line number. | ||
1774 | */ | ||
1775 | lineno = die_get_call_lineno(&indie); | ||
1776 | else { | ||
1777 | /* | ||
1778 | * addr is in an inline function body. | ||
1779 | * Since lineno points one of the lines | ||
1780 | * of the inline function, baseline should | ||
1781 | * be the entry line of the inline function. | ||
1782 | */ | ||
1598 | tmp = dwarf_diename(&indie); | 1783 | tmp = dwarf_diename(&indie); |
1599 | if (!tmp) | 1784 | if (tmp && |
1600 | goto end; | 1785 | dwarf_decl_line(&spdie, &baseline) == 0) |
1601 | ret = dwarf_decl_line(&indie, &lineno); | 1786 | func = tmp; |
1602 | } else { | ||
1603 | if (eaddr == addr) { /* Function entry */ | ||
1604 | lineno = ppt->line; | ||
1605 | ret = 0; | ||
1606 | } else | ||
1607 | ret = dwarf_decl_line(&spdie, &lineno); | ||
1608 | } | ||
1609 | if (ret == 0) { | ||
1610 | /* Make a relative line number */ | ||
1611 | ppt->line -= lineno; | ||
1612 | goto found; | ||
1613 | } | 1787 | } |
1614 | } | 1788 | } |
1615 | /* We don't have a line number, let's use offset */ | 1789 | } |
1616 | ppt->offset = addr - (unsigned long)eaddr; | 1790 | |
1617 | found: | 1791 | post: |
1618 | ppt->function = strdup(tmp); | 1792 | /* Make a relative line number or an offset */ |
1793 | if (lineno) | ||
1794 | ppt->line = lineno - baseline; | ||
1795 | else if (func) | ||
1796 | ppt->offset = addr - (unsigned long)baseaddr; | ||
1797 | |||
1798 | /* Duplicate strings */ | ||
1799 | if (func) { | ||
1800 | ppt->function = strdup(func); | ||
1619 | if (ppt->function == NULL) { | 1801 | if (ppt->function == NULL) { |
1620 | ret = -ENOMEM; | 1802 | ret = -ENOMEM; |
1621 | goto end; | 1803 | goto end; |
1622 | } | 1804 | } |
1623 | found = true; | ||
1624 | } | 1805 | } |
1625 | 1806 | if (fname) { | |
1807 | ppt->file = strdup(fname); | ||
1808 | if (ppt->file == NULL) { | ||
1809 | if (ppt->function) { | ||
1810 | free(ppt->function); | ||
1811 | ppt->function = NULL; | ||
1812 | } | ||
1813 | ret = -ENOMEM; | ||
1814 | goto end; | ||
1815 | } | ||
1816 | } | ||
1626 | end: | 1817 | end: |
1627 | if (dwfl) | 1818 | if (dwfl) |
1628 | dwfl_end(dwfl); | 1819 | dwfl_end(dwfl); |
1629 | if (ret >= 0) | 1820 | if (ret == 0 && (fname || func)) |
1630 | ret = found ? 1 : 0; | 1821 | ret = 1; /* Found a point */ |
1631 | return ret; | 1822 | return ret; |
1632 | } | 1823 | } |
1633 | 1824 | ||
@@ -1644,91 +1835,28 @@ static int line_range_add_line(const char *src, unsigned int lineno, | |||
1644 | return line_list__add_line(&lr->line_list, lineno); | 1835 | return line_list__add_line(&lr->line_list, lineno); |
1645 | } | 1836 | } |
1646 | 1837 | ||
1647 | /* Search function declaration lines */ | 1838 | static int line_range_walk_cb(const char *fname, int lineno, |
1648 | static int line_range_funcdecl_cb(Dwarf_Die *sp_die, void *data) | 1839 | Dwarf_Addr addr __used, |
1840 | void *data) | ||
1649 | { | 1841 | { |
1650 | struct dwarf_callback_param *param = data; | 1842 | struct line_finder *lf = data; |
1651 | struct line_finder *lf = param->data; | ||
1652 | const char *src; | ||
1653 | int lineno; | ||
1654 | |||
1655 | src = dwarf_decl_file(sp_die); | ||
1656 | if (src && strtailcmp(src, lf->fname) != 0) | ||
1657 | return DWARF_CB_OK; | ||
1658 | 1843 | ||
1659 | if (dwarf_decl_line(sp_die, &lineno) != 0 || | 1844 | if ((strtailcmp(fname, lf->fname) != 0) || |
1660 | (lf->lno_s > lineno || lf->lno_e < lineno)) | 1845 | (lf->lno_s > lineno || lf->lno_e < lineno)) |
1661 | return DWARF_CB_OK; | 1846 | return 0; |
1662 | 1847 | ||
1663 | param->retval = line_range_add_line(src, lineno, lf->lr); | 1848 | if (line_range_add_line(fname, lineno, lf->lr) < 0) |
1664 | if (param->retval < 0) | 1849 | return -EINVAL; |
1665 | return DWARF_CB_ABORT; | ||
1666 | return DWARF_CB_OK; | ||
1667 | } | ||
1668 | 1850 | ||
1669 | static int find_line_range_func_decl_lines(struct line_finder *lf) | 1851 | 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 | } | 1852 | } |
1675 | 1853 | ||
1676 | /* Find line range from its line number */ | 1854 | /* Find line range from its line number */ |
1677 | static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | 1855 | static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) |
1678 | { | 1856 | { |
1679 | Dwarf_Lines *lines; | 1857 | 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 | 1858 | ||
1721 | /* | 1859 | 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 | 1860 | ||
1733 | /* Update status */ | 1861 | /* Update status */ |
1734 | if (ret >= 0) | 1862 | if (ret >= 0) |
@@ -1758,9 +1886,10 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | |||
1758 | struct line_finder *lf = param->data; | 1886 | struct line_finder *lf = param->data; |
1759 | struct line_range *lr = lf->lr; | 1887 | struct line_range *lr = lf->lr; |
1760 | 1888 | ||
1761 | pr_debug("find (%llx) %s\n", | 1889 | /* Check declared file */ |
1762 | (unsigned long long)dwarf_dieoffset(sp_die), | 1890 | if (lr->file && strtailcmp(lr->file, dwarf_decl_file(sp_die))) |
1763 | dwarf_diename(sp_die)); | 1891 | return DWARF_CB_OK; |
1892 | |||
1764 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | 1893 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && |
1765 | die_compare_name(sp_die, lr->function)) { | 1894 | die_compare_name(sp_die, lr->function)) { |
1766 | lf->fname = dwarf_decl_file(sp_die); | 1895 | lf->fname = dwarf_decl_file(sp_die); |
@@ -1813,6 +1942,7 @@ int find_line_range(int fd, struct line_range *lr) | |||
1813 | if (!dbg) { | 1942 | if (!dbg) { |
1814 | pr_warning("No debug information found in the vmlinux - " | 1943 | pr_warning("No debug information found in the vmlinux - " |
1815 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | 1944 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); |
1945 | close(fd); /* Without dwfl_end(), fd isn't closed. */ | ||
1816 | return -EBADF; | 1946 | return -EBADF; |
1817 | } | 1947 | } |
1818 | 1948 | ||
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c new file mode 100644 index 000000000000..f5e38451fdc5 --- /dev/null +++ b/tools/perf/util/python.c | |||
@@ -0,0 +1,897 @@ | |||
1 | #include <Python.h> | ||
2 | #include <structmember.h> | ||
3 | #include <inttypes.h> | ||
4 | #include <poll.h> | ||
5 | #include "evlist.h" | ||
6 | #include "evsel.h" | ||
7 | #include "event.h" | ||
8 | #include "cpumap.h" | ||
9 | #include "thread_map.h" | ||
10 | |||
11 | /* Define PyVarObject_HEAD_INIT for python 2.5 */ | ||
12 | #ifndef PyVarObject_HEAD_INIT | ||
13 | # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, | ||
14 | #endif | ||
15 | |||
16 | struct throttle_event { | ||
17 | struct perf_event_header header; | ||
18 | u64 time; | ||
19 | u64 id; | ||
20 | u64 stream_id; | ||
21 | }; | ||
22 | |||
23 | PyMODINIT_FUNC initperf(void); | ||
24 | |||
25 | #define member_def(type, member, ptype, help) \ | ||
26 | { #member, ptype, \ | ||
27 | offsetof(struct pyrf_event, event) + offsetof(struct type, member), \ | ||
28 | 0, help } | ||
29 | |||
30 | #define sample_member_def(name, member, ptype, help) \ | ||
31 | { #name, ptype, \ | ||
32 | offsetof(struct pyrf_event, sample) + offsetof(struct perf_sample, member), \ | ||
33 | 0, help } | ||
34 | |||
35 | struct pyrf_event { | ||
36 | PyObject_HEAD | ||
37 | struct perf_sample sample; | ||
38 | union perf_event event; | ||
39 | }; | ||
40 | |||
41 | #define sample_members \ | ||
42 | sample_member_def(sample_ip, ip, T_ULONGLONG, "event type"), \ | ||
43 | sample_member_def(sample_pid, pid, T_INT, "event pid"), \ | ||
44 | sample_member_def(sample_tid, tid, T_INT, "event tid"), \ | ||
45 | sample_member_def(sample_time, time, T_ULONGLONG, "event timestamp"), \ | ||
46 | sample_member_def(sample_addr, addr, T_ULONGLONG, "event addr"), \ | ||
47 | sample_member_def(sample_id, id, T_ULONGLONG, "event id"), \ | ||
48 | sample_member_def(sample_stream_id, stream_id, T_ULONGLONG, "event stream id"), \ | ||
49 | sample_member_def(sample_period, period, T_ULONGLONG, "event period"), \ | ||
50 | sample_member_def(sample_cpu, cpu, T_UINT, "event cpu"), | ||
51 | |||
52 | static char pyrf_mmap_event__doc[] = PyDoc_STR("perf mmap event object."); | ||
53 | |||
54 | static PyMemberDef pyrf_mmap_event__members[] = { | ||
55 | sample_members | ||
56 | member_def(perf_event_header, type, T_UINT, "event type"), | ||
57 | member_def(mmap_event, pid, T_UINT, "event pid"), | ||
58 | member_def(mmap_event, tid, T_UINT, "event tid"), | ||
59 | member_def(mmap_event, start, T_ULONGLONG, "start of the map"), | ||
60 | member_def(mmap_event, len, T_ULONGLONG, "map length"), | ||
61 | member_def(mmap_event, pgoff, T_ULONGLONG, "page offset"), | ||
62 | member_def(mmap_event, filename, T_STRING_INPLACE, "backing store"), | ||
63 | { .name = NULL, }, | ||
64 | }; | ||
65 | |||
66 | static PyObject *pyrf_mmap_event__repr(struct pyrf_event *pevent) | ||
67 | { | ||
68 | PyObject *ret; | ||
69 | char *s; | ||
70 | |||
71 | if (asprintf(&s, "{ type: mmap, pid: %u, tid: %u, start: %#" PRIx64 ", " | ||
72 | "length: %#" PRIx64 ", offset: %#" PRIx64 ", " | ||
73 | "filename: %s }", | ||
74 | pevent->event.mmap.pid, pevent->event.mmap.tid, | ||
75 | pevent->event.mmap.start, pevent->event.mmap.len, | ||
76 | pevent->event.mmap.pgoff, pevent->event.mmap.filename) < 0) { | ||
77 | ret = PyErr_NoMemory(); | ||
78 | } else { | ||
79 | ret = PyString_FromString(s); | ||
80 | free(s); | ||
81 | } | ||
82 | return ret; | ||
83 | } | ||
84 | |||
85 | static PyTypeObject pyrf_mmap_event__type = { | ||
86 | PyVarObject_HEAD_INIT(NULL, 0) | ||
87 | .tp_name = "perf.mmap_event", | ||
88 | .tp_basicsize = sizeof(struct pyrf_event), | ||
89 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
90 | .tp_doc = pyrf_mmap_event__doc, | ||
91 | .tp_members = pyrf_mmap_event__members, | ||
92 | .tp_repr = (reprfunc)pyrf_mmap_event__repr, | ||
93 | }; | ||
94 | |||
95 | static char pyrf_task_event__doc[] = PyDoc_STR("perf task (fork/exit) event object."); | ||
96 | |||
97 | static PyMemberDef pyrf_task_event__members[] = { | ||
98 | sample_members | ||
99 | member_def(perf_event_header, type, T_UINT, "event type"), | ||
100 | member_def(fork_event, pid, T_UINT, "event pid"), | ||
101 | member_def(fork_event, ppid, T_UINT, "event ppid"), | ||
102 | member_def(fork_event, tid, T_UINT, "event tid"), | ||
103 | member_def(fork_event, ptid, T_UINT, "event ptid"), | ||
104 | member_def(fork_event, time, T_ULONGLONG, "timestamp"), | ||
105 | { .name = NULL, }, | ||
106 | }; | ||
107 | |||
108 | static PyObject *pyrf_task_event__repr(struct pyrf_event *pevent) | ||
109 | { | ||
110 | return PyString_FromFormat("{ type: %s, pid: %u, ppid: %u, tid: %u, " | ||
111 | "ptid: %u, time: %" PRIu64 "}", | ||
112 | pevent->event.header.type == PERF_RECORD_FORK ? "fork" : "exit", | ||
113 | pevent->event.fork.pid, | ||
114 | pevent->event.fork.ppid, | ||
115 | pevent->event.fork.tid, | ||
116 | pevent->event.fork.ptid, | ||
117 | pevent->event.fork.time); | ||
118 | } | ||
119 | |||
120 | static PyTypeObject pyrf_task_event__type = { | ||
121 | PyVarObject_HEAD_INIT(NULL, 0) | ||
122 | .tp_name = "perf.task_event", | ||
123 | .tp_basicsize = sizeof(struct pyrf_event), | ||
124 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
125 | .tp_doc = pyrf_task_event__doc, | ||
126 | .tp_members = pyrf_task_event__members, | ||
127 | .tp_repr = (reprfunc)pyrf_task_event__repr, | ||
128 | }; | ||
129 | |||
130 | static char pyrf_comm_event__doc[] = PyDoc_STR("perf comm event object."); | ||
131 | |||
132 | static PyMemberDef pyrf_comm_event__members[] = { | ||
133 | sample_members | ||
134 | member_def(perf_event_header, type, T_UINT, "event type"), | ||
135 | member_def(comm_event, pid, T_UINT, "event pid"), | ||
136 | member_def(comm_event, tid, T_UINT, "event tid"), | ||
137 | member_def(comm_event, comm, T_STRING_INPLACE, "process name"), | ||
138 | { .name = NULL, }, | ||
139 | }; | ||
140 | |||
141 | static PyObject *pyrf_comm_event__repr(struct pyrf_event *pevent) | ||
142 | { | ||
143 | return PyString_FromFormat("{ type: comm, pid: %u, tid: %u, comm: %s }", | ||
144 | pevent->event.comm.pid, | ||
145 | pevent->event.comm.tid, | ||
146 | pevent->event.comm.comm); | ||
147 | } | ||
148 | |||
149 | static PyTypeObject pyrf_comm_event__type = { | ||
150 | PyVarObject_HEAD_INIT(NULL, 0) | ||
151 | .tp_name = "perf.comm_event", | ||
152 | .tp_basicsize = sizeof(struct pyrf_event), | ||
153 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
154 | .tp_doc = pyrf_comm_event__doc, | ||
155 | .tp_members = pyrf_comm_event__members, | ||
156 | .tp_repr = (reprfunc)pyrf_comm_event__repr, | ||
157 | }; | ||
158 | |||
159 | static char pyrf_throttle_event__doc[] = PyDoc_STR("perf throttle event object."); | ||
160 | |||
161 | static PyMemberDef pyrf_throttle_event__members[] = { | ||
162 | sample_members | ||
163 | member_def(perf_event_header, type, T_UINT, "event type"), | ||
164 | member_def(throttle_event, time, T_ULONGLONG, "timestamp"), | ||
165 | member_def(throttle_event, id, T_ULONGLONG, "event id"), | ||
166 | member_def(throttle_event, stream_id, T_ULONGLONG, "event stream id"), | ||
167 | { .name = NULL, }, | ||
168 | }; | ||
169 | |||
170 | static PyObject *pyrf_throttle_event__repr(struct pyrf_event *pevent) | ||
171 | { | ||
172 | struct throttle_event *te = (struct throttle_event *)(&pevent->event.header + 1); | ||
173 | |||
174 | return PyString_FromFormat("{ type: %sthrottle, time: %" PRIu64 ", id: %" PRIu64 | ||
175 | ", stream_id: %" PRIu64 " }", | ||
176 | pevent->event.header.type == PERF_RECORD_THROTTLE ? "" : "un", | ||
177 | te->time, te->id, te->stream_id); | ||
178 | } | ||
179 | |||
180 | static PyTypeObject pyrf_throttle_event__type = { | ||
181 | PyVarObject_HEAD_INIT(NULL, 0) | ||
182 | .tp_name = "perf.throttle_event", | ||
183 | .tp_basicsize = sizeof(struct pyrf_event), | ||
184 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
185 | .tp_doc = pyrf_throttle_event__doc, | ||
186 | .tp_members = pyrf_throttle_event__members, | ||
187 | .tp_repr = (reprfunc)pyrf_throttle_event__repr, | ||
188 | }; | ||
189 | |||
190 | static int pyrf_event__setup_types(void) | ||
191 | { | ||
192 | int err; | ||
193 | pyrf_mmap_event__type.tp_new = | ||
194 | pyrf_task_event__type.tp_new = | ||
195 | pyrf_comm_event__type.tp_new = | ||
196 | pyrf_throttle_event__type.tp_new = PyType_GenericNew; | ||
197 | err = PyType_Ready(&pyrf_mmap_event__type); | ||
198 | if (err < 0) | ||
199 | goto out; | ||
200 | err = PyType_Ready(&pyrf_task_event__type); | ||
201 | if (err < 0) | ||
202 | goto out; | ||
203 | err = PyType_Ready(&pyrf_comm_event__type); | ||
204 | if (err < 0) | ||
205 | goto out; | ||
206 | err = PyType_Ready(&pyrf_throttle_event__type); | ||
207 | if (err < 0) | ||
208 | goto out; | ||
209 | out: | ||
210 | return err; | ||
211 | } | ||
212 | |||
213 | static PyTypeObject *pyrf_event__type[] = { | ||
214 | [PERF_RECORD_MMAP] = &pyrf_mmap_event__type, | ||
215 | [PERF_RECORD_LOST] = &pyrf_mmap_event__type, | ||
216 | [PERF_RECORD_COMM] = &pyrf_comm_event__type, | ||
217 | [PERF_RECORD_EXIT] = &pyrf_task_event__type, | ||
218 | [PERF_RECORD_THROTTLE] = &pyrf_throttle_event__type, | ||
219 | [PERF_RECORD_UNTHROTTLE] = &pyrf_throttle_event__type, | ||
220 | [PERF_RECORD_FORK] = &pyrf_task_event__type, | ||
221 | [PERF_RECORD_READ] = &pyrf_mmap_event__type, | ||
222 | [PERF_RECORD_SAMPLE] = &pyrf_mmap_event__type, | ||
223 | }; | ||
224 | |||
225 | static PyObject *pyrf_event__new(union perf_event *event) | ||
226 | { | ||
227 | struct pyrf_event *pevent; | ||
228 | PyTypeObject *ptype; | ||
229 | |||
230 | if (event->header.type < PERF_RECORD_MMAP || | ||
231 | event->header.type > PERF_RECORD_SAMPLE) | ||
232 | return NULL; | ||
233 | |||
234 | ptype = pyrf_event__type[event->header.type]; | ||
235 | pevent = PyObject_New(struct pyrf_event, ptype); | ||
236 | if (pevent != NULL) | ||
237 | memcpy(&pevent->event, event, event->header.size); | ||
238 | return (PyObject *)pevent; | ||
239 | } | ||
240 | |||
241 | struct pyrf_cpu_map { | ||
242 | PyObject_HEAD | ||
243 | |||
244 | struct cpu_map *cpus; | ||
245 | }; | ||
246 | |||
247 | static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus, | ||
248 | PyObject *args, PyObject *kwargs) | ||
249 | { | ||
250 | static char *kwlist[] = { "cpustr", NULL, NULL, }; | ||
251 | char *cpustr = NULL; | ||
252 | |||
253 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", | ||
254 | kwlist, &cpustr)) | ||
255 | return -1; | ||
256 | |||
257 | pcpus->cpus = cpu_map__new(cpustr); | ||
258 | if (pcpus->cpus == NULL) | ||
259 | return -1; | ||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | static void pyrf_cpu_map__delete(struct pyrf_cpu_map *pcpus) | ||
264 | { | ||
265 | cpu_map__delete(pcpus->cpus); | ||
266 | pcpus->ob_type->tp_free((PyObject*)pcpus); | ||
267 | } | ||
268 | |||
269 | static Py_ssize_t pyrf_cpu_map__length(PyObject *obj) | ||
270 | { | ||
271 | struct pyrf_cpu_map *pcpus = (void *)obj; | ||
272 | |||
273 | return pcpus->cpus->nr; | ||
274 | } | ||
275 | |||
276 | static PyObject *pyrf_cpu_map__item(PyObject *obj, Py_ssize_t i) | ||
277 | { | ||
278 | struct pyrf_cpu_map *pcpus = (void *)obj; | ||
279 | |||
280 | if (i >= pcpus->cpus->nr) | ||
281 | return NULL; | ||
282 | |||
283 | return Py_BuildValue("i", pcpus->cpus->map[i]); | ||
284 | } | ||
285 | |||
286 | static PySequenceMethods pyrf_cpu_map__sequence_methods = { | ||
287 | .sq_length = pyrf_cpu_map__length, | ||
288 | .sq_item = pyrf_cpu_map__item, | ||
289 | }; | ||
290 | |||
291 | static char pyrf_cpu_map__doc[] = PyDoc_STR("cpu map object."); | ||
292 | |||
293 | static PyTypeObject pyrf_cpu_map__type = { | ||
294 | PyVarObject_HEAD_INIT(NULL, 0) | ||
295 | .tp_name = "perf.cpu_map", | ||
296 | .tp_basicsize = sizeof(struct pyrf_cpu_map), | ||
297 | .tp_dealloc = (destructor)pyrf_cpu_map__delete, | ||
298 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
299 | .tp_doc = pyrf_cpu_map__doc, | ||
300 | .tp_as_sequence = &pyrf_cpu_map__sequence_methods, | ||
301 | .tp_init = (initproc)pyrf_cpu_map__init, | ||
302 | }; | ||
303 | |||
304 | static int pyrf_cpu_map__setup_types(void) | ||
305 | { | ||
306 | pyrf_cpu_map__type.tp_new = PyType_GenericNew; | ||
307 | return PyType_Ready(&pyrf_cpu_map__type); | ||
308 | } | ||
309 | |||
310 | struct pyrf_thread_map { | ||
311 | PyObject_HEAD | ||
312 | |||
313 | struct thread_map *threads; | ||
314 | }; | ||
315 | |||
316 | static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, | ||
317 | PyObject *args, PyObject *kwargs) | ||
318 | { | ||
319 | static char *kwlist[] = { "pid", "tid", NULL, NULL, }; | ||
320 | int pid = -1, tid = -1; | ||
321 | |||
322 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", | ||
323 | kwlist, &pid, &tid)) | ||
324 | return -1; | ||
325 | |||
326 | pthreads->threads = thread_map__new(pid, tid); | ||
327 | if (pthreads->threads == NULL) | ||
328 | return -1; | ||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static void pyrf_thread_map__delete(struct pyrf_thread_map *pthreads) | ||
333 | { | ||
334 | thread_map__delete(pthreads->threads); | ||
335 | pthreads->ob_type->tp_free((PyObject*)pthreads); | ||
336 | } | ||
337 | |||
338 | static Py_ssize_t pyrf_thread_map__length(PyObject *obj) | ||
339 | { | ||
340 | struct pyrf_thread_map *pthreads = (void *)obj; | ||
341 | |||
342 | return pthreads->threads->nr; | ||
343 | } | ||
344 | |||
345 | static PyObject *pyrf_thread_map__item(PyObject *obj, Py_ssize_t i) | ||
346 | { | ||
347 | struct pyrf_thread_map *pthreads = (void *)obj; | ||
348 | |||
349 | if (i >= pthreads->threads->nr) | ||
350 | return NULL; | ||
351 | |||
352 | return Py_BuildValue("i", pthreads->threads->map[i]); | ||
353 | } | ||
354 | |||
355 | static PySequenceMethods pyrf_thread_map__sequence_methods = { | ||
356 | .sq_length = pyrf_thread_map__length, | ||
357 | .sq_item = pyrf_thread_map__item, | ||
358 | }; | ||
359 | |||
360 | static char pyrf_thread_map__doc[] = PyDoc_STR("thread map object."); | ||
361 | |||
362 | static PyTypeObject pyrf_thread_map__type = { | ||
363 | PyVarObject_HEAD_INIT(NULL, 0) | ||
364 | .tp_name = "perf.thread_map", | ||
365 | .tp_basicsize = sizeof(struct pyrf_thread_map), | ||
366 | .tp_dealloc = (destructor)pyrf_thread_map__delete, | ||
367 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
368 | .tp_doc = pyrf_thread_map__doc, | ||
369 | .tp_as_sequence = &pyrf_thread_map__sequence_methods, | ||
370 | .tp_init = (initproc)pyrf_thread_map__init, | ||
371 | }; | ||
372 | |||
373 | static int pyrf_thread_map__setup_types(void) | ||
374 | { | ||
375 | pyrf_thread_map__type.tp_new = PyType_GenericNew; | ||
376 | return PyType_Ready(&pyrf_thread_map__type); | ||
377 | } | ||
378 | |||
379 | struct pyrf_evsel { | ||
380 | PyObject_HEAD | ||
381 | |||
382 | struct perf_evsel evsel; | ||
383 | }; | ||
384 | |||
385 | static int pyrf_evsel__init(struct pyrf_evsel *pevsel, | ||
386 | PyObject *args, PyObject *kwargs) | ||
387 | { | ||
388 | struct perf_event_attr attr = { | ||
389 | .type = PERF_TYPE_HARDWARE, | ||
390 | .config = PERF_COUNT_HW_CPU_CYCLES, | ||
391 | .sample_type = PERF_SAMPLE_PERIOD | PERF_SAMPLE_TID, | ||
392 | }; | ||
393 | static char *kwlist[] = { | ||
394 | "type", | ||
395 | "config", | ||
396 | "sample_freq", | ||
397 | "sample_period", | ||
398 | "sample_type", | ||
399 | "read_format", | ||
400 | "disabled", | ||
401 | "inherit", | ||
402 | "pinned", | ||
403 | "exclusive", | ||
404 | "exclude_user", | ||
405 | "exclude_kernel", | ||
406 | "exclude_hv", | ||
407 | "exclude_idle", | ||
408 | "mmap", | ||
409 | "comm", | ||
410 | "freq", | ||
411 | "inherit_stat", | ||
412 | "enable_on_exec", | ||
413 | "task", | ||
414 | "watermark", | ||
415 | "precise_ip", | ||
416 | "mmap_data", | ||
417 | "sample_id_all", | ||
418 | "wakeup_events", | ||
419 | "bp_type", | ||
420 | "bp_addr", | ||
421 | "bp_len", NULL, NULL, }; | ||
422 | u64 sample_period = 0; | ||
423 | u32 disabled = 0, | ||
424 | inherit = 0, | ||
425 | pinned = 0, | ||
426 | exclusive = 0, | ||
427 | exclude_user = 0, | ||
428 | exclude_kernel = 0, | ||
429 | exclude_hv = 0, | ||
430 | exclude_idle = 0, | ||
431 | mmap = 0, | ||
432 | comm = 0, | ||
433 | freq = 1, | ||
434 | inherit_stat = 0, | ||
435 | enable_on_exec = 0, | ||
436 | task = 0, | ||
437 | watermark = 0, | ||
438 | precise_ip = 0, | ||
439 | mmap_data = 0, | ||
440 | sample_id_all = 1; | ||
441 | int idx = 0; | ||
442 | |||
443 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, | ||
444 | "|iKiKKiiiiiiiiiiiiiiiiiiiiiKK", kwlist, | ||
445 | &attr.type, &attr.config, &attr.sample_freq, | ||
446 | &sample_period, &attr.sample_type, | ||
447 | &attr.read_format, &disabled, &inherit, | ||
448 | &pinned, &exclusive, &exclude_user, | ||
449 | &exclude_kernel, &exclude_hv, &exclude_idle, | ||
450 | &mmap, &comm, &freq, &inherit_stat, | ||
451 | &enable_on_exec, &task, &watermark, | ||
452 | &precise_ip, &mmap_data, &sample_id_all, | ||
453 | &attr.wakeup_events, &attr.bp_type, | ||
454 | &attr.bp_addr, &attr.bp_len, &idx)) | ||
455 | return -1; | ||
456 | |||
457 | /* union... */ | ||
458 | if (sample_period != 0) { | ||
459 | if (attr.sample_freq != 0) | ||
460 | return -1; /* FIXME: throw right exception */ | ||
461 | attr.sample_period = sample_period; | ||
462 | } | ||
463 | |||
464 | /* Bitfields */ | ||
465 | attr.disabled = disabled; | ||
466 | attr.inherit = inherit; | ||
467 | attr.pinned = pinned; | ||
468 | attr.exclusive = exclusive; | ||
469 | attr.exclude_user = exclude_user; | ||
470 | attr.exclude_kernel = exclude_kernel; | ||
471 | attr.exclude_hv = exclude_hv; | ||
472 | attr.exclude_idle = exclude_idle; | ||
473 | attr.mmap = mmap; | ||
474 | attr.comm = comm; | ||
475 | attr.freq = freq; | ||
476 | attr.inherit_stat = inherit_stat; | ||
477 | attr.enable_on_exec = enable_on_exec; | ||
478 | attr.task = task; | ||
479 | attr.watermark = watermark; | ||
480 | attr.precise_ip = precise_ip; | ||
481 | attr.mmap_data = mmap_data; | ||
482 | attr.sample_id_all = sample_id_all; | ||
483 | |||
484 | perf_evsel__init(&pevsel->evsel, &attr, idx); | ||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | static void pyrf_evsel__delete(struct pyrf_evsel *pevsel) | ||
489 | { | ||
490 | perf_evsel__exit(&pevsel->evsel); | ||
491 | pevsel->ob_type->tp_free((PyObject*)pevsel); | ||
492 | } | ||
493 | |||
494 | static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, | ||
495 | PyObject *args, PyObject *kwargs) | ||
496 | { | ||
497 | struct perf_evsel *evsel = &pevsel->evsel; | ||
498 | struct cpu_map *cpus = NULL; | ||
499 | struct thread_map *threads = NULL; | ||
500 | PyObject *pcpus = NULL, *pthreads = NULL; | ||
501 | int group = 0, inherit = 0; | ||
502 | static char *kwlist[] = {"cpus", "threads", "group", "inherit", NULL, NULL}; | ||
503 | |||
504 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, | ||
505 | &pcpus, &pthreads, &group, &inherit)) | ||
506 | return NULL; | ||
507 | |||
508 | if (pthreads != NULL) | ||
509 | threads = ((struct pyrf_thread_map *)pthreads)->threads; | ||
510 | |||
511 | if (pcpus != NULL) | ||
512 | cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; | ||
513 | |||
514 | evsel->attr.inherit = inherit; | ||
515 | if (perf_evsel__open(evsel, cpus, threads, group) < 0) { | ||
516 | PyErr_SetFromErrno(PyExc_OSError); | ||
517 | return NULL; | ||
518 | } | ||
519 | |||
520 | Py_INCREF(Py_None); | ||
521 | return Py_None; | ||
522 | } | ||
523 | |||
524 | static PyMethodDef pyrf_evsel__methods[] = { | ||
525 | { | ||
526 | .ml_name = "open", | ||
527 | .ml_meth = (PyCFunction)pyrf_evsel__open, | ||
528 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | ||
529 | .ml_doc = PyDoc_STR("open the event selector file descriptor table.") | ||
530 | }, | ||
531 | { .ml_name = NULL, } | ||
532 | }; | ||
533 | |||
534 | static char pyrf_evsel__doc[] = PyDoc_STR("perf event selector list object."); | ||
535 | |||
536 | static PyTypeObject pyrf_evsel__type = { | ||
537 | PyVarObject_HEAD_INIT(NULL, 0) | ||
538 | .tp_name = "perf.evsel", | ||
539 | .tp_basicsize = sizeof(struct pyrf_evsel), | ||
540 | .tp_dealloc = (destructor)pyrf_evsel__delete, | ||
541 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
542 | .tp_doc = pyrf_evsel__doc, | ||
543 | .tp_methods = pyrf_evsel__methods, | ||
544 | .tp_init = (initproc)pyrf_evsel__init, | ||
545 | }; | ||
546 | |||
547 | static int pyrf_evsel__setup_types(void) | ||
548 | { | ||
549 | pyrf_evsel__type.tp_new = PyType_GenericNew; | ||
550 | return PyType_Ready(&pyrf_evsel__type); | ||
551 | } | ||
552 | |||
553 | struct pyrf_evlist { | ||
554 | PyObject_HEAD | ||
555 | |||
556 | struct perf_evlist evlist; | ||
557 | }; | ||
558 | |||
559 | static int pyrf_evlist__init(struct pyrf_evlist *pevlist, | ||
560 | PyObject *args, PyObject *kwargs __used) | ||
561 | { | ||
562 | PyObject *pcpus = NULL, *pthreads = NULL; | ||
563 | struct cpu_map *cpus; | ||
564 | struct thread_map *threads; | ||
565 | |||
566 | if (!PyArg_ParseTuple(args, "OO", &pcpus, &pthreads)) | ||
567 | return -1; | ||
568 | |||
569 | threads = ((struct pyrf_thread_map *)pthreads)->threads; | ||
570 | cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; | ||
571 | perf_evlist__init(&pevlist->evlist, cpus, threads); | ||
572 | return 0; | ||
573 | } | ||
574 | |||
575 | static void pyrf_evlist__delete(struct pyrf_evlist *pevlist) | ||
576 | { | ||
577 | perf_evlist__exit(&pevlist->evlist); | ||
578 | pevlist->ob_type->tp_free((PyObject*)pevlist); | ||
579 | } | ||
580 | |||
581 | static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist, | ||
582 | PyObject *args, PyObject *kwargs) | ||
583 | { | ||
584 | struct perf_evlist *evlist = &pevlist->evlist; | ||
585 | static char *kwlist[] = {"pages", "overwrite", | ||
586 | NULL, NULL}; | ||
587 | int pages = 128, overwrite = false; | ||
588 | |||
589 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", kwlist, | ||
590 | &pages, &overwrite)) | ||
591 | return NULL; | ||
592 | |||
593 | if (perf_evlist__mmap(evlist, pages, overwrite) < 0) { | ||
594 | PyErr_SetFromErrno(PyExc_OSError); | ||
595 | return NULL; | ||
596 | } | ||
597 | |||
598 | Py_INCREF(Py_None); | ||
599 | return Py_None; | ||
600 | } | ||
601 | |||
602 | static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist, | ||
603 | PyObject *args, PyObject *kwargs) | ||
604 | { | ||
605 | struct perf_evlist *evlist = &pevlist->evlist; | ||
606 | static char *kwlist[] = {"timeout", NULL, NULL}; | ||
607 | int timeout = -1, n; | ||
608 | |||
609 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) | ||
610 | return NULL; | ||
611 | |||
612 | n = poll(evlist->pollfd, evlist->nr_fds, timeout); | ||
613 | if (n < 0) { | ||
614 | PyErr_SetFromErrno(PyExc_OSError); | ||
615 | return NULL; | ||
616 | } | ||
617 | |||
618 | return Py_BuildValue("i", n); | ||
619 | } | ||
620 | |||
621 | static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist, | ||
622 | PyObject *args __used, PyObject *kwargs __used) | ||
623 | { | ||
624 | struct perf_evlist *evlist = &pevlist->evlist; | ||
625 | PyObject *list = PyList_New(0); | ||
626 | int i; | ||
627 | |||
628 | for (i = 0; i < evlist->nr_fds; ++i) { | ||
629 | PyObject *file; | ||
630 | FILE *fp = fdopen(evlist->pollfd[i].fd, "r"); | ||
631 | |||
632 | if (fp == NULL) | ||
633 | goto free_list; | ||
634 | |||
635 | file = PyFile_FromFile(fp, "perf", "r", NULL); | ||
636 | if (file == NULL) | ||
637 | goto free_list; | ||
638 | |||
639 | if (PyList_Append(list, file) != 0) { | ||
640 | Py_DECREF(file); | ||
641 | goto free_list; | ||
642 | } | ||
643 | |||
644 | Py_DECREF(file); | ||
645 | } | ||
646 | |||
647 | return list; | ||
648 | free_list: | ||
649 | return PyErr_NoMemory(); | ||
650 | } | ||
651 | |||
652 | |||
653 | static PyObject *pyrf_evlist__add(struct pyrf_evlist *pevlist, | ||
654 | PyObject *args, PyObject *kwargs __used) | ||
655 | { | ||
656 | struct perf_evlist *evlist = &pevlist->evlist; | ||
657 | PyObject *pevsel; | ||
658 | struct perf_evsel *evsel; | ||
659 | |||
660 | if (!PyArg_ParseTuple(args, "O", &pevsel)) | ||
661 | return NULL; | ||
662 | |||
663 | Py_INCREF(pevsel); | ||
664 | evsel = &((struct pyrf_evsel *)pevsel)->evsel; | ||
665 | evsel->idx = evlist->nr_entries; | ||
666 | perf_evlist__add(evlist, evsel); | ||
667 | |||
668 | return Py_BuildValue("i", evlist->nr_entries); | ||
669 | } | ||
670 | |||
671 | static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, | ||
672 | PyObject *args, PyObject *kwargs) | ||
673 | { | ||
674 | struct perf_evlist *evlist = &pevlist->evlist; | ||
675 | union perf_event *event; | ||
676 | int sample_id_all = 1, cpu; | ||
677 | static char *kwlist[] = {"sample_id_all", NULL, NULL}; | ||
678 | |||
679 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, | ||
680 | &cpu, &sample_id_all)) | ||
681 | return NULL; | ||
682 | |||
683 | event = perf_evlist__read_on_cpu(evlist, cpu); | ||
684 | if (event != NULL) { | ||
685 | struct perf_evsel *first; | ||
686 | PyObject *pyevent = pyrf_event__new(event); | ||
687 | struct pyrf_event *pevent = (struct pyrf_event *)pyevent; | ||
688 | |||
689 | if (pyevent == NULL) | ||
690 | return PyErr_NoMemory(); | ||
691 | |||
692 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
693 | perf_event__parse_sample(event, first->attr.sample_type, sample_id_all, | ||
694 | &pevent->sample); | ||
695 | return pyevent; | ||
696 | } | ||
697 | |||
698 | Py_INCREF(Py_None); | ||
699 | return Py_None; | ||
700 | } | ||
701 | |||
702 | static PyMethodDef pyrf_evlist__methods[] = { | ||
703 | { | ||
704 | .ml_name = "mmap", | ||
705 | .ml_meth = (PyCFunction)pyrf_evlist__mmap, | ||
706 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | ||
707 | .ml_doc = PyDoc_STR("mmap the file descriptor table.") | ||
708 | }, | ||
709 | { | ||
710 | .ml_name = "poll", | ||
711 | .ml_meth = (PyCFunction)pyrf_evlist__poll, | ||
712 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | ||
713 | .ml_doc = PyDoc_STR("poll the file descriptor table.") | ||
714 | }, | ||
715 | { | ||
716 | .ml_name = "get_pollfd", | ||
717 | .ml_meth = (PyCFunction)pyrf_evlist__get_pollfd, | ||
718 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | ||
719 | .ml_doc = PyDoc_STR("get the poll file descriptor table.") | ||
720 | }, | ||
721 | { | ||
722 | .ml_name = "add", | ||
723 | .ml_meth = (PyCFunction)pyrf_evlist__add, | ||
724 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | ||
725 | .ml_doc = PyDoc_STR("adds an event selector to the list.") | ||
726 | }, | ||
727 | { | ||
728 | .ml_name = "read_on_cpu", | ||
729 | .ml_meth = (PyCFunction)pyrf_evlist__read_on_cpu, | ||
730 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | ||
731 | .ml_doc = PyDoc_STR("reads an event.") | ||
732 | }, | ||
733 | { .ml_name = NULL, } | ||
734 | }; | ||
735 | |||
736 | static Py_ssize_t pyrf_evlist__length(PyObject *obj) | ||
737 | { | ||
738 | struct pyrf_evlist *pevlist = (void *)obj; | ||
739 | |||
740 | return pevlist->evlist.nr_entries; | ||
741 | } | ||
742 | |||
743 | static PyObject *pyrf_evlist__item(PyObject *obj, Py_ssize_t i) | ||
744 | { | ||
745 | struct pyrf_evlist *pevlist = (void *)obj; | ||
746 | struct perf_evsel *pos; | ||
747 | |||
748 | if (i >= pevlist->evlist.nr_entries) | ||
749 | return NULL; | ||
750 | |||
751 | list_for_each_entry(pos, &pevlist->evlist.entries, node) | ||
752 | if (i-- == 0) | ||
753 | break; | ||
754 | |||
755 | return Py_BuildValue("O", container_of(pos, struct pyrf_evsel, evsel)); | ||
756 | } | ||
757 | |||
758 | static PySequenceMethods pyrf_evlist__sequence_methods = { | ||
759 | .sq_length = pyrf_evlist__length, | ||
760 | .sq_item = pyrf_evlist__item, | ||
761 | }; | ||
762 | |||
763 | static char pyrf_evlist__doc[] = PyDoc_STR("perf event selector list object."); | ||
764 | |||
765 | static PyTypeObject pyrf_evlist__type = { | ||
766 | PyVarObject_HEAD_INIT(NULL, 0) | ||
767 | .tp_name = "perf.evlist", | ||
768 | .tp_basicsize = sizeof(struct pyrf_evlist), | ||
769 | .tp_dealloc = (destructor)pyrf_evlist__delete, | ||
770 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
771 | .tp_as_sequence = &pyrf_evlist__sequence_methods, | ||
772 | .tp_doc = pyrf_evlist__doc, | ||
773 | .tp_methods = pyrf_evlist__methods, | ||
774 | .tp_init = (initproc)pyrf_evlist__init, | ||
775 | }; | ||
776 | |||
777 | static int pyrf_evlist__setup_types(void) | ||
778 | { | ||
779 | pyrf_evlist__type.tp_new = PyType_GenericNew; | ||
780 | return PyType_Ready(&pyrf_evlist__type); | ||
781 | } | ||
782 | |||
783 | static struct { | ||
784 | const char *name; | ||
785 | int value; | ||
786 | } perf__constants[] = { | ||
787 | { "TYPE_HARDWARE", PERF_TYPE_HARDWARE }, | ||
788 | { "TYPE_SOFTWARE", PERF_TYPE_SOFTWARE }, | ||
789 | { "TYPE_TRACEPOINT", PERF_TYPE_TRACEPOINT }, | ||
790 | { "TYPE_HW_CACHE", PERF_TYPE_HW_CACHE }, | ||
791 | { "TYPE_RAW", PERF_TYPE_RAW }, | ||
792 | { "TYPE_BREAKPOINT", PERF_TYPE_BREAKPOINT }, | ||
793 | |||
794 | { "COUNT_HW_CPU_CYCLES", PERF_COUNT_HW_CPU_CYCLES }, | ||
795 | { "COUNT_HW_INSTRUCTIONS", PERF_COUNT_HW_INSTRUCTIONS }, | ||
796 | { "COUNT_HW_CACHE_REFERENCES", PERF_COUNT_HW_CACHE_REFERENCES }, | ||
797 | { "COUNT_HW_CACHE_MISSES", PERF_COUNT_HW_CACHE_MISSES }, | ||
798 | { "COUNT_HW_BRANCH_INSTRUCTIONS", PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, | ||
799 | { "COUNT_HW_BRANCH_MISSES", PERF_COUNT_HW_BRANCH_MISSES }, | ||
800 | { "COUNT_HW_BUS_CYCLES", PERF_COUNT_HW_BUS_CYCLES }, | ||
801 | { "COUNT_HW_CACHE_L1D", PERF_COUNT_HW_CACHE_L1D }, | ||
802 | { "COUNT_HW_CACHE_L1I", PERF_COUNT_HW_CACHE_L1I }, | ||
803 | { "COUNT_HW_CACHE_LL", PERF_COUNT_HW_CACHE_LL }, | ||
804 | { "COUNT_HW_CACHE_DTLB", PERF_COUNT_HW_CACHE_DTLB }, | ||
805 | { "COUNT_HW_CACHE_ITLB", PERF_COUNT_HW_CACHE_ITLB }, | ||
806 | { "COUNT_HW_CACHE_BPU", PERF_COUNT_HW_CACHE_BPU }, | ||
807 | { "COUNT_HW_CACHE_OP_READ", PERF_COUNT_HW_CACHE_OP_READ }, | ||
808 | { "COUNT_HW_CACHE_OP_WRITE", PERF_COUNT_HW_CACHE_OP_WRITE }, | ||
809 | { "COUNT_HW_CACHE_OP_PREFETCH", PERF_COUNT_HW_CACHE_OP_PREFETCH }, | ||
810 | { "COUNT_HW_CACHE_RESULT_ACCESS", PERF_COUNT_HW_CACHE_RESULT_ACCESS }, | ||
811 | { "COUNT_HW_CACHE_RESULT_MISS", PERF_COUNT_HW_CACHE_RESULT_MISS }, | ||
812 | |||
813 | { "COUNT_SW_CPU_CLOCK", PERF_COUNT_SW_CPU_CLOCK }, | ||
814 | { "COUNT_SW_TASK_CLOCK", PERF_COUNT_SW_TASK_CLOCK }, | ||
815 | { "COUNT_SW_PAGE_FAULTS", PERF_COUNT_SW_PAGE_FAULTS }, | ||
816 | { "COUNT_SW_CONTEXT_SWITCHES", PERF_COUNT_SW_CONTEXT_SWITCHES }, | ||
817 | { "COUNT_SW_CPU_MIGRATIONS", PERF_COUNT_SW_CPU_MIGRATIONS }, | ||
818 | { "COUNT_SW_PAGE_FAULTS_MIN", PERF_COUNT_SW_PAGE_FAULTS_MIN }, | ||
819 | { "COUNT_SW_PAGE_FAULTS_MAJ", PERF_COUNT_SW_PAGE_FAULTS_MAJ }, | ||
820 | { "COUNT_SW_ALIGNMENT_FAULTS", PERF_COUNT_SW_ALIGNMENT_FAULTS }, | ||
821 | { "COUNT_SW_EMULATION_FAULTS", PERF_COUNT_SW_EMULATION_FAULTS }, | ||
822 | |||
823 | { "SAMPLE_IP", PERF_SAMPLE_IP }, | ||
824 | { "SAMPLE_TID", PERF_SAMPLE_TID }, | ||
825 | { "SAMPLE_TIME", PERF_SAMPLE_TIME }, | ||
826 | { "SAMPLE_ADDR", PERF_SAMPLE_ADDR }, | ||
827 | { "SAMPLE_READ", PERF_SAMPLE_READ }, | ||
828 | { "SAMPLE_CALLCHAIN", PERF_SAMPLE_CALLCHAIN }, | ||
829 | { "SAMPLE_ID", PERF_SAMPLE_ID }, | ||
830 | { "SAMPLE_CPU", PERF_SAMPLE_CPU }, | ||
831 | { "SAMPLE_PERIOD", PERF_SAMPLE_PERIOD }, | ||
832 | { "SAMPLE_STREAM_ID", PERF_SAMPLE_STREAM_ID }, | ||
833 | { "SAMPLE_RAW", PERF_SAMPLE_RAW }, | ||
834 | |||
835 | { "FORMAT_TOTAL_TIME_ENABLED", PERF_FORMAT_TOTAL_TIME_ENABLED }, | ||
836 | { "FORMAT_TOTAL_TIME_RUNNING", PERF_FORMAT_TOTAL_TIME_RUNNING }, | ||
837 | { "FORMAT_ID", PERF_FORMAT_ID }, | ||
838 | { "FORMAT_GROUP", PERF_FORMAT_GROUP }, | ||
839 | |||
840 | { "RECORD_MMAP", PERF_RECORD_MMAP }, | ||
841 | { "RECORD_LOST", PERF_RECORD_LOST }, | ||
842 | { "RECORD_COMM", PERF_RECORD_COMM }, | ||
843 | { "RECORD_EXIT", PERF_RECORD_EXIT }, | ||
844 | { "RECORD_THROTTLE", PERF_RECORD_THROTTLE }, | ||
845 | { "RECORD_UNTHROTTLE", PERF_RECORD_UNTHROTTLE }, | ||
846 | { "RECORD_FORK", PERF_RECORD_FORK }, | ||
847 | { "RECORD_READ", PERF_RECORD_READ }, | ||
848 | { "RECORD_SAMPLE", PERF_RECORD_SAMPLE }, | ||
849 | { .name = NULL, }, | ||
850 | }; | ||
851 | |||
852 | static PyMethodDef perf__methods[] = { | ||
853 | { .ml_name = NULL, } | ||
854 | }; | ||
855 | |||
856 | PyMODINIT_FUNC initperf(void) | ||
857 | { | ||
858 | PyObject *obj; | ||
859 | int i; | ||
860 | PyObject *dict, *module = Py_InitModule("perf", perf__methods); | ||
861 | |||
862 | if (module == NULL || | ||
863 | pyrf_event__setup_types() < 0 || | ||
864 | pyrf_evlist__setup_types() < 0 || | ||
865 | pyrf_evsel__setup_types() < 0 || | ||
866 | pyrf_thread_map__setup_types() < 0 || | ||
867 | pyrf_cpu_map__setup_types() < 0) | ||
868 | return; | ||
869 | |||
870 | Py_INCREF(&pyrf_evlist__type); | ||
871 | PyModule_AddObject(module, "evlist", (PyObject*)&pyrf_evlist__type); | ||
872 | |||
873 | Py_INCREF(&pyrf_evsel__type); | ||
874 | PyModule_AddObject(module, "evsel", (PyObject*)&pyrf_evsel__type); | ||
875 | |||
876 | Py_INCREF(&pyrf_thread_map__type); | ||
877 | PyModule_AddObject(module, "thread_map", (PyObject*)&pyrf_thread_map__type); | ||
878 | |||
879 | Py_INCREF(&pyrf_cpu_map__type); | ||
880 | PyModule_AddObject(module, "cpu_map", (PyObject*)&pyrf_cpu_map__type); | ||
881 | |||
882 | dict = PyModule_GetDict(module); | ||
883 | if (dict == NULL) | ||
884 | goto error; | ||
885 | |||
886 | for (i = 0; perf__constants[i].name != NULL; i++) { | ||
887 | obj = PyInt_FromLong(perf__constants[i].value); | ||
888 | if (obj == NULL) | ||
889 | goto error; | ||
890 | PyDict_SetItemString(dict, perf__constants[i].name, obj); | ||
891 | Py_DECREF(obj); | ||
892 | } | ||
893 | |||
894 | error: | ||
895 | if (PyErr_Occurred()) | ||
896 | PyErr_SetString(PyExc_ImportError, "perf: Init failed!"); | ||
897 | } | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index 93680818e244..74350ffb57fe 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -245,9 +245,11 @@ static inline struct event *find_cache_event(int type) | |||
245 | return event; | 245 | return event; |
246 | } | 246 | } |
247 | 247 | ||
248 | static void perl_process_event(int cpu, void *data, | 248 | static void perl_process_event(union perf_event *pevent __unused, |
249 | int size __unused, | 249 | struct perf_sample *sample, |
250 | unsigned long long nsecs, char *comm) | 250 | struct perf_evsel *evsel, |
251 | struct perf_session *session __unused, | ||
252 | struct thread *thread) | ||
251 | { | 253 | { |
252 | struct format_field *field; | 254 | struct format_field *field; |
253 | static char handler[256]; | 255 | static char handler[256]; |
@@ -256,6 +258,10 @@ static void perl_process_event(int cpu, void *data, | |||
256 | struct event *event; | 258 | struct event *event; |
257 | int type; | 259 | int type; |
258 | int pid; | 260 | int pid; |
261 | int cpu = sample->cpu; | ||
262 | void *data = sample->raw_data; | ||
263 | unsigned long long nsecs = sample->time; | ||
264 | char *comm = thread->comm; | ||
259 | 265 | ||
260 | dSP; | 266 | dSP; |
261 | 267 | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index c6d99334bdfa..6ccf70e8d8f2 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -204,9 +204,11 @@ static inline struct event *find_cache_event(int type) | |||
204 | return event; | 204 | return event; |
205 | } | 205 | } |
206 | 206 | ||
207 | static void python_process_event(int cpu, void *data, | 207 | static void python_process_event(union perf_event *pevent __unused, |
208 | int size __unused, | 208 | struct perf_sample *sample, |
209 | unsigned long long nsecs, char *comm) | 209 | struct perf_evsel *evsel __unused, |
210 | struct perf_session *session __unused, | ||
211 | struct thread *thread) | ||
210 | { | 212 | { |
211 | PyObject *handler, *retval, *context, *t, *obj, *dict = NULL; | 213 | PyObject *handler, *retval, *context, *t, *obj, *dict = NULL; |
212 | static char handler_name[256]; | 214 | static char handler_name[256]; |
@@ -217,6 +219,10 @@ static void python_process_event(int cpu, void *data, | |||
217 | unsigned n = 0; | 219 | unsigned n = 0; |
218 | int type; | 220 | int type; |
219 | int pid; | 221 | int pid; |
222 | int cpu = sample->cpu; | ||
223 | void *data = sample->raw_data; | ||
224 | unsigned long long nsecs = sample->time; | ||
225 | char *comm = thread->comm; | ||
220 | 226 | ||
221 | t = PyTuple_New(MAX_FIELDS); | 227 | t = PyTuple_New(MAX_FIELDS); |
222 | if (!t) | 228 | if (!t) |
@@ -248,8 +254,7 @@ static void python_process_event(int cpu, void *data, | |||
248 | context = PyCObject_FromVoidPtr(scripting_context, NULL); | 254 | context = PyCObject_FromVoidPtr(scripting_context, NULL); |
249 | 255 | ||
250 | PyTuple_SetItem(t, n++, PyString_FromString(handler_name)); | 256 | PyTuple_SetItem(t, n++, PyString_FromString(handler_name)); |
251 | PyTuple_SetItem(t, n++, | 257 | PyTuple_SetItem(t, n++, context); |
252 | PyCObject_FromVoidPtr(scripting_context, NULL)); | ||
253 | 258 | ||
254 | if (handler) { | 259 | if (handler) { |
255 | PyTuple_SetItem(t, n++, PyInt_FromLong(cpu)); | 260 | PyTuple_SetItem(t, n++, PyInt_FromLong(cpu)); |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 313dac2d94ce..caa224522fea 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -7,6 +7,8 @@ | |||
7 | #include <sys/types.h> | 7 | #include <sys/types.h> |
8 | #include <sys/mman.h> | 8 | #include <sys/mman.h> |
9 | 9 | ||
10 | #include "evlist.h" | ||
11 | #include "evsel.h" | ||
10 | #include "session.h" | 12 | #include "session.h" |
11 | #include "sort.h" | 13 | #include "sort.h" |
12 | #include "util.h" | 14 | #include "util.h" |
@@ -19,7 +21,7 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
19 | self->fd_pipe = true; | 21 | self->fd_pipe = true; |
20 | self->fd = STDIN_FILENO; | 22 | self->fd = STDIN_FILENO; |
21 | 23 | ||
22 | if (perf_header__read(self, self->fd) < 0) | 24 | if (perf_session__read_header(self, self->fd) < 0) |
23 | pr_err("incompatible file format"); | 25 | pr_err("incompatible file format"); |
24 | 26 | ||
25 | return 0; | 27 | return 0; |
@@ -51,7 +53,7 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
51 | goto out_close; | 53 | goto out_close; |
52 | } | 54 | } |
53 | 55 | ||
54 | if (perf_header__read(self, self->fd) < 0) { | 56 | if (perf_session__read_header(self, self->fd) < 0) { |
55 | pr_err("incompatible file format"); | 57 | pr_err("incompatible file format"); |
56 | goto out_close; | 58 | goto out_close; |
57 | } | 59 | } |
@@ -67,7 +69,7 @@ out_close: | |||
67 | 69 | ||
68 | static void perf_session__id_header_size(struct perf_session *session) | 70 | static void perf_session__id_header_size(struct perf_session *session) |
69 | { | 71 | { |
70 | struct sample_data *data; | 72 | struct perf_sample *data; |
71 | u64 sample_type = session->sample_type; | 73 | u64 sample_type = session->sample_type; |
72 | u16 size = 0; | 74 | u16 size = 0; |
73 | 75 | ||
@@ -92,21 +94,10 @@ out: | |||
92 | session->id_hdr_size = size; | 94 | session->id_hdr_size = size; |
93 | } | 95 | } |
94 | 96 | ||
95 | void perf_session__set_sample_id_all(struct perf_session *session, bool value) | ||
96 | { | ||
97 | session->sample_id_all = value; | ||
98 | perf_session__id_header_size(session); | ||
99 | } | ||
100 | |||
101 | void perf_session__set_sample_type(struct perf_session *session, u64 type) | ||
102 | { | ||
103 | session->sample_type = type; | ||
104 | } | ||
105 | |||
106 | void perf_session__update_sample_type(struct perf_session *self) | 97 | void perf_session__update_sample_type(struct perf_session *self) |
107 | { | 98 | { |
108 | self->sample_type = perf_header__sample_type(&self->header); | 99 | self->sample_type = perf_evlist__sample_type(self->evlist); |
109 | self->sample_id_all = perf_header__sample_id_all(&self->header); | 100 | self->sample_id_all = perf_evlist__sample_id_all(self->evlist); |
110 | perf_session__id_header_size(self); | 101 | perf_session__id_header_size(self); |
111 | } | 102 | } |
112 | 103 | ||
@@ -135,13 +126,9 @@ struct perf_session *perf_session__new(const char *filename, int mode, | |||
135 | if (self == NULL) | 126 | if (self == NULL) |
136 | goto out; | 127 | goto out; |
137 | 128 | ||
138 | if (perf_header__init(&self->header) < 0) | ||
139 | goto out_free; | ||
140 | |||
141 | memcpy(self->filename, filename, len); | 129 | memcpy(self->filename, filename, len); |
142 | self->threads = RB_ROOT; | 130 | self->threads = RB_ROOT; |
143 | INIT_LIST_HEAD(&self->dead_threads); | 131 | INIT_LIST_HEAD(&self->dead_threads); |
144 | self->hists_tree = RB_ROOT; | ||
145 | self->last_match = NULL; | 132 | self->last_match = NULL; |
146 | /* | 133 | /* |
147 | * On 64bit we can mmap the data file in one go. No need for tiny mmap | 134 | * On 64bit we can mmap the data file in one go. No need for tiny mmap |
@@ -162,17 +149,16 @@ struct perf_session *perf_session__new(const char *filename, int mode, | |||
162 | if (mode == O_RDONLY) { | 149 | if (mode == O_RDONLY) { |
163 | if (perf_session__open(self, force) < 0) | 150 | if (perf_session__open(self, force) < 0) |
164 | goto out_delete; | 151 | goto out_delete; |
152 | perf_session__update_sample_type(self); | ||
165 | } else if (mode == O_WRONLY) { | 153 | } else if (mode == O_WRONLY) { |
166 | /* | 154 | /* |
167 | * In O_RDONLY mode this will be performed when reading the | 155 | * In O_RDONLY mode this will be performed when reading the |
168 | * kernel MMAP event, in event__process_mmap(). | 156 | * kernel MMAP event, in perf_event__process_mmap(). |
169 | */ | 157 | */ |
170 | if (perf_session__create_kernel_maps(self) < 0) | 158 | if (perf_session__create_kernel_maps(self) < 0) |
171 | goto out_delete; | 159 | goto out_delete; |
172 | } | 160 | } |
173 | 161 | ||
174 | perf_session__update_sample_type(self); | ||
175 | |||
176 | if (ops && ops->ordering_requires_timestamps && | 162 | if (ops && ops->ordering_requires_timestamps && |
177 | ops->ordered_samples && !self->sample_id_all) { | 163 | ops->ordered_samples && !self->sample_id_all) { |
178 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); | 164 | dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n"); |
@@ -181,9 +167,6 @@ struct perf_session *perf_session__new(const char *filename, int mode, | |||
181 | 167 | ||
182 | out: | 168 | out: |
183 | return self; | 169 | return self; |
184 | out_free: | ||
185 | free(self); | ||
186 | return NULL; | ||
187 | out_delete: | 170 | out_delete: |
188 | perf_session__delete(self); | 171 | perf_session__delete(self); |
189 | return NULL; | 172 | return NULL; |
@@ -214,7 +197,6 @@ static void perf_session__delete_threads(struct perf_session *self) | |||
214 | 197 | ||
215 | void perf_session__delete(struct perf_session *self) | 198 | void perf_session__delete(struct perf_session *self) |
216 | { | 199 | { |
217 | perf_header__exit(&self->header); | ||
218 | perf_session__destroy_kernel_maps(self); | 200 | perf_session__destroy_kernel_maps(self); |
219 | perf_session__delete_dead_threads(self); | 201 | perf_session__delete_dead_threads(self); |
220 | perf_session__delete_threads(self); | 202 | perf_session__delete_threads(self); |
@@ -242,17 +224,16 @@ static bool symbol__match_parent_regex(struct symbol *sym) | |||
242 | return 0; | 224 | return 0; |
243 | } | 225 | } |
244 | 226 | ||
245 | struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | 227 | int perf_session__resolve_callchain(struct perf_session *self, |
246 | struct thread *thread, | 228 | struct thread *thread, |
247 | struct ip_callchain *chain, | 229 | struct ip_callchain *chain, |
248 | struct symbol **parent) | 230 | struct symbol **parent) |
249 | { | 231 | { |
250 | u8 cpumode = PERF_RECORD_MISC_USER; | 232 | u8 cpumode = PERF_RECORD_MISC_USER; |
251 | unsigned int i; | 233 | unsigned int i; |
252 | struct map_symbol *syms = calloc(chain->nr, sizeof(*syms)); | 234 | int err; |
253 | 235 | ||
254 | if (!syms) | 236 | callchain_cursor_reset(&self->callchain_cursor); |
255 | return NULL; | ||
256 | 237 | ||
257 | for (i = 0; i < chain->nr; i++) { | 238 | for (i = 0; i < chain->nr; i++) { |
258 | u64 ip = chain->ips[i]; | 239 | u64 ip = chain->ips[i]; |
@@ -281,30 +262,42 @@ struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | |||
281 | *parent = al.sym; | 262 | *parent = al.sym; |
282 | if (!symbol_conf.use_callchain) | 263 | if (!symbol_conf.use_callchain) |
283 | break; | 264 | break; |
284 | syms[i].map = al.map; | ||
285 | syms[i].sym = al.sym; | ||
286 | } | 265 | } |
266 | |||
267 | err = callchain_cursor_append(&self->callchain_cursor, | ||
268 | ip, al.map, al.sym); | ||
269 | if (err) | ||
270 | return err; | ||
287 | } | 271 | } |
288 | 272 | ||
289 | return syms; | 273 | return 0; |
290 | } | 274 | } |
291 | 275 | ||
292 | static int process_event_synth_stub(event_t *event __used, | 276 | static int process_event_synth_stub(union perf_event *event __used, |
293 | struct perf_session *session __used) | 277 | struct perf_session *session __used) |
294 | { | 278 | { |
295 | dump_printf(": unhandled!\n"); | 279 | dump_printf(": unhandled!\n"); |
296 | return 0; | 280 | return 0; |
297 | } | 281 | } |
298 | 282 | ||
299 | static int process_event_stub(event_t *event __used, | 283 | static int process_event_sample_stub(union perf_event *event __used, |
300 | struct sample_data *sample __used, | 284 | struct perf_sample *sample __used, |
285 | struct perf_evsel *evsel __used, | ||
286 | struct perf_session *session __used) | ||
287 | { | ||
288 | dump_printf(": unhandled!\n"); | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static int process_event_stub(union perf_event *event __used, | ||
293 | struct perf_sample *sample __used, | ||
301 | struct perf_session *session __used) | 294 | struct perf_session *session __used) |
302 | { | 295 | { |
303 | dump_printf(": unhandled!\n"); | 296 | dump_printf(": unhandled!\n"); |
304 | return 0; | 297 | return 0; |
305 | } | 298 | } |
306 | 299 | ||
307 | static int process_finished_round_stub(event_t *event __used, | 300 | static int process_finished_round_stub(union perf_event *event __used, |
308 | struct perf_session *session __used, | 301 | struct perf_session *session __used, |
309 | struct perf_event_ops *ops __used) | 302 | struct perf_event_ops *ops __used) |
310 | { | 303 | { |
@@ -312,14 +305,14 @@ static int process_finished_round_stub(event_t *event __used, | |||
312 | return 0; | 305 | return 0; |
313 | } | 306 | } |
314 | 307 | ||
315 | static int process_finished_round(event_t *event, | 308 | static int process_finished_round(union perf_event *event, |
316 | struct perf_session *session, | 309 | struct perf_session *session, |
317 | struct perf_event_ops *ops); | 310 | struct perf_event_ops *ops); |
318 | 311 | ||
319 | static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | 312 | static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) |
320 | { | 313 | { |
321 | if (handler->sample == NULL) | 314 | if (handler->sample == NULL) |
322 | handler->sample = process_event_stub; | 315 | handler->sample = process_event_sample_stub; |
323 | if (handler->mmap == NULL) | 316 | if (handler->mmap == NULL) |
324 | handler->mmap = process_event_stub; | 317 | handler->mmap = process_event_stub; |
325 | if (handler->comm == NULL) | 318 | if (handler->comm == NULL) |
@@ -329,7 +322,7 @@ static void perf_event_ops__fill_defaults(struct perf_event_ops *handler) | |||
329 | if (handler->exit == NULL) | 322 | if (handler->exit == NULL) |
330 | handler->exit = process_event_stub; | 323 | handler->exit = process_event_stub; |
331 | if (handler->lost == NULL) | 324 | if (handler->lost == NULL) |
332 | handler->lost = event__process_lost; | 325 | handler->lost = perf_event__process_lost; |
333 | if (handler->read == NULL) | 326 | if (handler->read == NULL) |
334 | handler->read = process_event_stub; | 327 | handler->read = process_event_stub; |
335 | if (handler->throttle == NULL) | 328 | if (handler->throttle == NULL) |
@@ -363,98 +356,98 @@ void mem_bswap_64(void *src, int byte_size) | |||
363 | } | 356 | } |
364 | } | 357 | } |
365 | 358 | ||
366 | static void event__all64_swap(event_t *self) | 359 | static void perf_event__all64_swap(union perf_event *event) |
367 | { | 360 | { |
368 | struct perf_event_header *hdr = &self->header; | 361 | struct perf_event_header *hdr = &event->header; |
369 | mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr)); | 362 | mem_bswap_64(hdr + 1, event->header.size - sizeof(*hdr)); |
370 | } | 363 | } |
371 | 364 | ||
372 | static void event__comm_swap(event_t *self) | 365 | static void perf_event__comm_swap(union perf_event *event) |
373 | { | 366 | { |
374 | self->comm.pid = bswap_32(self->comm.pid); | 367 | event->comm.pid = bswap_32(event->comm.pid); |
375 | self->comm.tid = bswap_32(self->comm.tid); | 368 | event->comm.tid = bswap_32(event->comm.tid); |
376 | } | 369 | } |
377 | 370 | ||
378 | static void event__mmap_swap(event_t *self) | 371 | static void perf_event__mmap_swap(union perf_event *event) |
379 | { | 372 | { |
380 | self->mmap.pid = bswap_32(self->mmap.pid); | 373 | event->mmap.pid = bswap_32(event->mmap.pid); |
381 | self->mmap.tid = bswap_32(self->mmap.tid); | 374 | event->mmap.tid = bswap_32(event->mmap.tid); |
382 | self->mmap.start = bswap_64(self->mmap.start); | 375 | event->mmap.start = bswap_64(event->mmap.start); |
383 | self->mmap.len = bswap_64(self->mmap.len); | 376 | event->mmap.len = bswap_64(event->mmap.len); |
384 | self->mmap.pgoff = bswap_64(self->mmap.pgoff); | 377 | event->mmap.pgoff = bswap_64(event->mmap.pgoff); |
385 | } | 378 | } |
386 | 379 | ||
387 | static void event__task_swap(event_t *self) | 380 | static void perf_event__task_swap(union perf_event *event) |
388 | { | 381 | { |
389 | self->fork.pid = bswap_32(self->fork.pid); | 382 | event->fork.pid = bswap_32(event->fork.pid); |
390 | self->fork.tid = bswap_32(self->fork.tid); | 383 | event->fork.tid = bswap_32(event->fork.tid); |
391 | self->fork.ppid = bswap_32(self->fork.ppid); | 384 | event->fork.ppid = bswap_32(event->fork.ppid); |
392 | self->fork.ptid = bswap_32(self->fork.ptid); | 385 | event->fork.ptid = bswap_32(event->fork.ptid); |
393 | self->fork.time = bswap_64(self->fork.time); | 386 | event->fork.time = bswap_64(event->fork.time); |
394 | } | 387 | } |
395 | 388 | ||
396 | static void event__read_swap(event_t *self) | 389 | static void perf_event__read_swap(union perf_event *event) |
397 | { | 390 | { |
398 | self->read.pid = bswap_32(self->read.pid); | 391 | event->read.pid = bswap_32(event->read.pid); |
399 | self->read.tid = bswap_32(self->read.tid); | 392 | event->read.tid = bswap_32(event->read.tid); |
400 | self->read.value = bswap_64(self->read.value); | 393 | event->read.value = bswap_64(event->read.value); |
401 | self->read.time_enabled = bswap_64(self->read.time_enabled); | 394 | event->read.time_enabled = bswap_64(event->read.time_enabled); |
402 | self->read.time_running = bswap_64(self->read.time_running); | 395 | event->read.time_running = bswap_64(event->read.time_running); |
403 | self->read.id = bswap_64(self->read.id); | 396 | event->read.id = bswap_64(event->read.id); |
404 | } | 397 | } |
405 | 398 | ||
406 | static void event__attr_swap(event_t *self) | 399 | static void perf_event__attr_swap(union perf_event *event) |
407 | { | 400 | { |
408 | size_t size; | 401 | size_t size; |
409 | 402 | ||
410 | self->attr.attr.type = bswap_32(self->attr.attr.type); | 403 | event->attr.attr.type = bswap_32(event->attr.attr.type); |
411 | self->attr.attr.size = bswap_32(self->attr.attr.size); | 404 | event->attr.attr.size = bswap_32(event->attr.attr.size); |
412 | self->attr.attr.config = bswap_64(self->attr.attr.config); | 405 | event->attr.attr.config = bswap_64(event->attr.attr.config); |
413 | self->attr.attr.sample_period = bswap_64(self->attr.attr.sample_period); | 406 | event->attr.attr.sample_period = bswap_64(event->attr.attr.sample_period); |
414 | self->attr.attr.sample_type = bswap_64(self->attr.attr.sample_type); | 407 | event->attr.attr.sample_type = bswap_64(event->attr.attr.sample_type); |
415 | self->attr.attr.read_format = bswap_64(self->attr.attr.read_format); | 408 | event->attr.attr.read_format = bswap_64(event->attr.attr.read_format); |
416 | self->attr.attr.wakeup_events = bswap_32(self->attr.attr.wakeup_events); | 409 | event->attr.attr.wakeup_events = bswap_32(event->attr.attr.wakeup_events); |
417 | self->attr.attr.bp_type = bswap_32(self->attr.attr.bp_type); | 410 | event->attr.attr.bp_type = bswap_32(event->attr.attr.bp_type); |
418 | self->attr.attr.bp_addr = bswap_64(self->attr.attr.bp_addr); | 411 | event->attr.attr.bp_addr = bswap_64(event->attr.attr.bp_addr); |
419 | self->attr.attr.bp_len = bswap_64(self->attr.attr.bp_len); | 412 | event->attr.attr.bp_len = bswap_64(event->attr.attr.bp_len); |
420 | 413 | ||
421 | size = self->header.size; | 414 | size = event->header.size; |
422 | size -= (void *)&self->attr.id - (void *)self; | 415 | size -= (void *)&event->attr.id - (void *)event; |
423 | mem_bswap_64(self->attr.id, size); | 416 | mem_bswap_64(event->attr.id, size); |
424 | } | 417 | } |
425 | 418 | ||
426 | static void event__event_type_swap(event_t *self) | 419 | static void perf_event__event_type_swap(union perf_event *event) |
427 | { | 420 | { |
428 | self->event_type.event_type.event_id = | 421 | event->event_type.event_type.event_id = |
429 | bswap_64(self->event_type.event_type.event_id); | 422 | bswap_64(event->event_type.event_type.event_id); |
430 | } | 423 | } |
431 | 424 | ||
432 | static void event__tracing_data_swap(event_t *self) | 425 | static void perf_event__tracing_data_swap(union perf_event *event) |
433 | { | 426 | { |
434 | self->tracing_data.size = bswap_32(self->tracing_data.size); | 427 | event->tracing_data.size = bswap_32(event->tracing_data.size); |
435 | } | 428 | } |
436 | 429 | ||
437 | typedef void (*event__swap_op)(event_t *self); | 430 | typedef void (*perf_event__swap_op)(union perf_event *event); |
438 | 431 | ||
439 | static event__swap_op event__swap_ops[] = { | 432 | static perf_event__swap_op perf_event__swap_ops[] = { |
440 | [PERF_RECORD_MMAP] = event__mmap_swap, | 433 | [PERF_RECORD_MMAP] = perf_event__mmap_swap, |
441 | [PERF_RECORD_COMM] = event__comm_swap, | 434 | [PERF_RECORD_COMM] = perf_event__comm_swap, |
442 | [PERF_RECORD_FORK] = event__task_swap, | 435 | [PERF_RECORD_FORK] = perf_event__task_swap, |
443 | [PERF_RECORD_EXIT] = event__task_swap, | 436 | [PERF_RECORD_EXIT] = perf_event__task_swap, |
444 | [PERF_RECORD_LOST] = event__all64_swap, | 437 | [PERF_RECORD_LOST] = perf_event__all64_swap, |
445 | [PERF_RECORD_READ] = event__read_swap, | 438 | [PERF_RECORD_READ] = perf_event__read_swap, |
446 | [PERF_RECORD_SAMPLE] = event__all64_swap, | 439 | [PERF_RECORD_SAMPLE] = perf_event__all64_swap, |
447 | [PERF_RECORD_HEADER_ATTR] = event__attr_swap, | 440 | [PERF_RECORD_HEADER_ATTR] = perf_event__attr_swap, |
448 | [PERF_RECORD_HEADER_EVENT_TYPE] = event__event_type_swap, | 441 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, |
449 | [PERF_RECORD_HEADER_TRACING_DATA] = event__tracing_data_swap, | 442 | [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, |
450 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, | 443 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, |
451 | [PERF_RECORD_HEADER_MAX] = NULL, | 444 | [PERF_RECORD_HEADER_MAX] = NULL, |
452 | }; | 445 | }; |
453 | 446 | ||
454 | struct sample_queue { | 447 | struct sample_queue { |
455 | u64 timestamp; | 448 | u64 timestamp; |
456 | u64 file_offset; | 449 | u64 file_offset; |
457 | event_t *event; | 450 | union perf_event *event; |
458 | struct list_head list; | 451 | struct list_head list; |
459 | }; | 452 | }; |
460 | 453 | ||
@@ -472,8 +465,8 @@ static void perf_session_free_sample_buffers(struct perf_session *session) | |||
472 | } | 465 | } |
473 | 466 | ||
474 | static int perf_session_deliver_event(struct perf_session *session, | 467 | static int perf_session_deliver_event(struct perf_session *session, |
475 | event_t *event, | 468 | union perf_event *event, |
476 | struct sample_data *sample, | 469 | struct perf_sample *sample, |
477 | struct perf_event_ops *ops, | 470 | struct perf_event_ops *ops, |
478 | u64 file_offset); | 471 | u64 file_offset); |
479 | 472 | ||
@@ -483,7 +476,7 @@ static void flush_sample_queue(struct perf_session *s, | |||
483 | struct ordered_samples *os = &s->ordered_samples; | 476 | struct ordered_samples *os = &s->ordered_samples; |
484 | struct list_head *head = &os->samples; | 477 | struct list_head *head = &os->samples; |
485 | struct sample_queue *tmp, *iter; | 478 | struct sample_queue *tmp, *iter; |
486 | struct sample_data sample; | 479 | struct perf_sample sample; |
487 | u64 limit = os->next_flush; | 480 | u64 limit = os->next_flush; |
488 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; | 481 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; |
489 | 482 | ||
@@ -494,7 +487,7 @@ static void flush_sample_queue(struct perf_session *s, | |||
494 | if (iter->timestamp > limit) | 487 | if (iter->timestamp > limit) |
495 | break; | 488 | break; |
496 | 489 | ||
497 | event__parse_sample(iter->event, s, &sample); | 490 | perf_session__parse_sample(s, iter->event, &sample); |
498 | perf_session_deliver_event(s, iter->event, &sample, ops, | 491 | perf_session_deliver_event(s, iter->event, &sample, ops, |
499 | iter->file_offset); | 492 | iter->file_offset); |
500 | 493 | ||
@@ -550,7 +543,7 @@ static void flush_sample_queue(struct perf_session *s, | |||
550 | * Flush every events below timestamp 7 | 543 | * Flush every events below timestamp 7 |
551 | * etc... | 544 | * etc... |
552 | */ | 545 | */ |
553 | static int process_finished_round(event_t *event __used, | 546 | static int process_finished_round(union perf_event *event __used, |
554 | struct perf_session *session, | 547 | struct perf_session *session, |
555 | struct perf_event_ops *ops) | 548 | struct perf_event_ops *ops) |
556 | { | 549 | { |
@@ -607,12 +600,12 @@ static void __queue_event(struct sample_queue *new, struct perf_session *s) | |||
607 | 600 | ||
608 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) | 601 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) |
609 | 602 | ||
610 | static int perf_session_queue_event(struct perf_session *s, event_t *event, | 603 | static int perf_session_queue_event(struct perf_session *s, union perf_event *event, |
611 | struct sample_data *data, u64 file_offset) | 604 | struct perf_sample *sample, u64 file_offset) |
612 | { | 605 | { |
613 | struct ordered_samples *os = &s->ordered_samples; | 606 | struct ordered_samples *os = &s->ordered_samples; |
614 | struct list_head *sc = &os->sample_cache; | 607 | struct list_head *sc = &os->sample_cache; |
615 | u64 timestamp = data->time; | 608 | u64 timestamp = sample->time; |
616 | struct sample_queue *new; | 609 | struct sample_queue *new; |
617 | 610 | ||
618 | if (!timestamp || timestamp == ~0ULL) | 611 | if (!timestamp || timestamp == ~0ULL) |
@@ -648,19 +641,20 @@ static int perf_session_queue_event(struct perf_session *s, event_t *event, | |||
648 | return 0; | 641 | return 0; |
649 | } | 642 | } |
650 | 643 | ||
651 | static void callchain__printf(struct sample_data *sample) | 644 | static void callchain__printf(struct perf_sample *sample) |
652 | { | 645 | { |
653 | unsigned int i; | 646 | unsigned int i; |
654 | 647 | ||
655 | printf("... chain: nr:%Lu\n", sample->callchain->nr); | 648 | printf("... chain: nr:%" PRIu64 "\n", sample->callchain->nr); |
656 | 649 | ||
657 | for (i = 0; i < sample->callchain->nr; i++) | 650 | for (i = 0; i < sample->callchain->nr; i++) |
658 | printf("..... %2d: %016Lx\n", i, sample->callchain->ips[i]); | 651 | printf("..... %2d: %016" PRIx64 "\n", |
652 | i, sample->callchain->ips[i]); | ||
659 | } | 653 | } |
660 | 654 | ||
661 | static void perf_session__print_tstamp(struct perf_session *session, | 655 | static void perf_session__print_tstamp(struct perf_session *session, |
662 | event_t *event, | 656 | union perf_event *event, |
663 | struct sample_data *sample) | 657 | struct perf_sample *sample) |
664 | { | 658 | { |
665 | if (event->header.type != PERF_RECORD_SAMPLE && | 659 | if (event->header.type != PERF_RECORD_SAMPLE && |
666 | !session->sample_id_all) { | 660 | !session->sample_id_all) { |
@@ -672,52 +666,60 @@ static void perf_session__print_tstamp(struct perf_session *session, | |||
672 | printf("%u ", sample->cpu); | 666 | printf("%u ", sample->cpu); |
673 | 667 | ||
674 | if (session->sample_type & PERF_SAMPLE_TIME) | 668 | if (session->sample_type & PERF_SAMPLE_TIME) |
675 | printf("%Lu ", sample->time); | 669 | printf("%" PRIu64 " ", sample->time); |
676 | } | 670 | } |
677 | 671 | ||
678 | static void dump_event(struct perf_session *session, event_t *event, | 672 | static void dump_event(struct perf_session *session, union perf_event *event, |
679 | u64 file_offset, struct sample_data *sample) | 673 | u64 file_offset, struct perf_sample *sample) |
680 | { | 674 | { |
681 | if (!dump_trace) | 675 | if (!dump_trace) |
682 | return; | 676 | return; |
683 | 677 | ||
684 | printf("\n%#Lx [%#x]: event: %d\n", file_offset, event->header.size, | 678 | printf("\n%#" PRIx64 " [%#x]: event: %d\n", |
685 | event->header.type); | 679 | file_offset, event->header.size, event->header.type); |
686 | 680 | ||
687 | trace_event(event); | 681 | trace_event(event); |
688 | 682 | ||
689 | if (sample) | 683 | if (sample) |
690 | perf_session__print_tstamp(session, event, sample); | 684 | perf_session__print_tstamp(session, event, sample); |
691 | 685 | ||
692 | printf("%#Lx [%#x]: PERF_RECORD_%s", file_offset, event->header.size, | 686 | printf("%#" PRIx64 " [%#x]: PERF_RECORD_%s", file_offset, |
693 | event__get_event_name(event->header.type)); | 687 | event->header.size, perf_event__name(event->header.type)); |
694 | } | 688 | } |
695 | 689 | ||
696 | static void dump_sample(struct perf_session *session, event_t *event, | 690 | static void dump_sample(struct perf_session *session, union perf_event *event, |
697 | struct sample_data *sample) | 691 | struct perf_sample *sample) |
698 | { | 692 | { |
699 | if (!dump_trace) | 693 | if (!dump_trace) |
700 | return; | 694 | return; |
701 | 695 | ||
702 | printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc, | 696 | printf("(IP, %d): %d/%d: %#" PRIx64 " period: %" PRIu64 "\n", |
703 | sample->pid, sample->tid, sample->ip, sample->period); | 697 | event->header.misc, sample->pid, sample->tid, sample->ip, |
698 | sample->period); | ||
704 | 699 | ||
705 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) | 700 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) |
706 | callchain__printf(sample); | 701 | callchain__printf(sample); |
707 | } | 702 | } |
708 | 703 | ||
709 | static int perf_session_deliver_event(struct perf_session *session, | 704 | static int perf_session_deliver_event(struct perf_session *session, |
710 | event_t *event, | 705 | union perf_event *event, |
711 | struct sample_data *sample, | 706 | struct perf_sample *sample, |
712 | struct perf_event_ops *ops, | 707 | struct perf_event_ops *ops, |
713 | u64 file_offset) | 708 | u64 file_offset) |
714 | { | 709 | { |
710 | struct perf_evsel *evsel; | ||
711 | |||
715 | dump_event(session, event, file_offset, sample); | 712 | dump_event(session, event, file_offset, sample); |
716 | 713 | ||
717 | switch (event->header.type) { | 714 | switch (event->header.type) { |
718 | case PERF_RECORD_SAMPLE: | 715 | case PERF_RECORD_SAMPLE: |
719 | dump_sample(session, event, sample); | 716 | dump_sample(session, event, sample); |
720 | return ops->sample(event, sample, session); | 717 | evsel = perf_evlist__id2evsel(session->evlist, sample->id); |
718 | if (evsel == NULL) { | ||
719 | ++session->hists.stats.nr_unknown_id; | ||
720 | return -1; | ||
721 | } | ||
722 | return ops->sample(event, sample, evsel, session); | ||
721 | case PERF_RECORD_MMAP: | 723 | case PERF_RECORD_MMAP: |
722 | return ops->mmap(event, sample, session); | 724 | return ops->mmap(event, sample, session); |
723 | case PERF_RECORD_COMM: | 725 | case PERF_RECORD_COMM: |
@@ -741,7 +743,7 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
741 | } | 743 | } |
742 | 744 | ||
743 | static int perf_session__preprocess_sample(struct perf_session *session, | 745 | static int perf_session__preprocess_sample(struct perf_session *session, |
744 | event_t *event, struct sample_data *sample) | 746 | union perf_event *event, struct perf_sample *sample) |
745 | { | 747 | { |
746 | if (event->header.type != PERF_RECORD_SAMPLE || | 748 | if (event->header.type != PERF_RECORD_SAMPLE || |
747 | !(session->sample_type & PERF_SAMPLE_CALLCHAIN)) | 749 | !(session->sample_type & PERF_SAMPLE_CALLCHAIN)) |
@@ -756,7 +758,7 @@ static int perf_session__preprocess_sample(struct perf_session *session, | |||
756 | return 0; | 758 | return 0; |
757 | } | 759 | } |
758 | 760 | ||
759 | static int perf_session__process_user_event(struct perf_session *session, event_t *event, | 761 | static int perf_session__process_user_event(struct perf_session *session, union perf_event *event, |
760 | struct perf_event_ops *ops, u64 file_offset) | 762 | struct perf_event_ops *ops, u64 file_offset) |
761 | { | 763 | { |
762 | dump_event(session, event, file_offset, NULL); | 764 | dump_event(session, event, file_offset, NULL); |
@@ -781,15 +783,16 @@ static int perf_session__process_user_event(struct perf_session *session, event_ | |||
781 | } | 783 | } |
782 | 784 | ||
783 | static int perf_session__process_event(struct perf_session *session, | 785 | static int perf_session__process_event(struct perf_session *session, |
784 | event_t *event, | 786 | union perf_event *event, |
785 | struct perf_event_ops *ops, | 787 | struct perf_event_ops *ops, |
786 | u64 file_offset) | 788 | u64 file_offset) |
787 | { | 789 | { |
788 | struct sample_data sample; | 790 | struct perf_sample sample; |
789 | int ret; | 791 | int ret; |
790 | 792 | ||
791 | if (session->header.needs_swap && event__swap_ops[event->header.type]) | 793 | if (session->header.needs_swap && |
792 | event__swap_ops[event->header.type](event); | 794 | perf_event__swap_ops[event->header.type]) |
795 | perf_event__swap_ops[event->header.type](event); | ||
793 | 796 | ||
794 | if (event->header.type >= PERF_RECORD_HEADER_MAX) | 797 | if (event->header.type >= PERF_RECORD_HEADER_MAX) |
795 | return -EINVAL; | 798 | return -EINVAL; |
@@ -802,7 +805,7 @@ static int perf_session__process_event(struct perf_session *session, | |||
802 | /* | 805 | /* |
803 | * For all kernel events we get the sample data | 806 | * For all kernel events we get the sample data |
804 | */ | 807 | */ |
805 | event__parse_sample(event, session, &sample); | 808 | perf_session__parse_sample(session, event, &sample); |
806 | 809 | ||
807 | /* Preprocess sample records - precheck callchains */ | 810 | /* Preprocess sample records - precheck callchains */ |
808 | if (perf_session__preprocess_sample(session, event, &sample)) | 811 | if (perf_session__preprocess_sample(session, event, &sample)) |
@@ -841,10 +844,10 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se | |||
841 | static void perf_session__warn_about_errors(const struct perf_session *session, | 844 | static void perf_session__warn_about_errors(const struct perf_session *session, |
842 | const struct perf_event_ops *ops) | 845 | const struct perf_event_ops *ops) |
843 | { | 846 | { |
844 | if (ops->lost == event__process_lost && | 847 | if (ops->lost == perf_event__process_lost && |
845 | session->hists.stats.total_lost != 0) { | 848 | session->hists.stats.total_lost != 0) { |
846 | ui__warning("Processed %Lu events and LOST %Lu!\n\n" | 849 | ui__warning("Processed %" PRIu64 " events and LOST %" PRIu64 |
847 | "Check IO/CPU overload!\n\n", | 850 | "!\n\nCheck IO/CPU overload!\n\n", |
848 | session->hists.stats.total_period, | 851 | session->hists.stats.total_period, |
849 | session->hists.stats.total_lost); | 852 | session->hists.stats.total_lost); |
850 | } | 853 | } |
@@ -858,6 +861,11 @@ static void perf_session__warn_about_errors(const struct perf_session *session, | |||
858 | session->hists.stats.nr_unknown_events); | 861 | session->hists.stats.nr_unknown_events); |
859 | } | 862 | } |
860 | 863 | ||
864 | if (session->hists.stats.nr_unknown_id != 0) { | ||
865 | ui__warning("%u samples with id not present in the header\n", | ||
866 | session->hists.stats.nr_unknown_id); | ||
867 | } | ||
868 | |||
861 | if (session->hists.stats.nr_invalid_chains != 0) { | 869 | if (session->hists.stats.nr_invalid_chains != 0) { |
862 | ui__warning("Found invalid callchains!\n\n" | 870 | ui__warning("Found invalid callchains!\n\n" |
863 | "%u out of %u events were discarded for this reason.\n\n" | 871 | "%u out of %u events were discarded for this reason.\n\n" |
@@ -873,7 +881,7 @@ volatile int session_done; | |||
873 | static int __perf_session__process_pipe_events(struct perf_session *self, | 881 | static int __perf_session__process_pipe_events(struct perf_session *self, |
874 | struct perf_event_ops *ops) | 882 | struct perf_event_ops *ops) |
875 | { | 883 | { |
876 | event_t event; | 884 | union perf_event event; |
877 | uint32_t size; | 885 | uint32_t size; |
878 | int skip = 0; | 886 | int skip = 0; |
879 | u64 head; | 887 | u64 head; |
@@ -918,7 +926,7 @@ more: | |||
918 | 926 | ||
919 | if (size == 0 || | 927 | if (size == 0 || |
920 | (skip = perf_session__process_event(self, &event, ops, head)) < 0) { | 928 | (skip = perf_session__process_event(self, &event, ops, head)) < 0) { |
921 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", | 929 | dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n", |
922 | head, event.header.size, event.header.type); | 930 | head, event.header.size, event.header.type); |
923 | /* | 931 | /* |
924 | * assume we lost track of the stream, check alignment, and | 932 | * assume we lost track of the stream, check alignment, and |
@@ -954,7 +962,7 @@ int __perf_session__process_events(struct perf_session *session, | |||
954 | struct ui_progress *progress; | 962 | struct ui_progress *progress; |
955 | size_t page_size, mmap_size; | 963 | size_t page_size, mmap_size; |
956 | char *buf, *mmaps[8]; | 964 | char *buf, *mmaps[8]; |
957 | event_t *event; | 965 | union perf_event *event; |
958 | uint32_t size; | 966 | uint32_t size; |
959 | 967 | ||
960 | perf_event_ops__fill_defaults(ops); | 968 | perf_event_ops__fill_defaults(ops); |
@@ -999,7 +1007,7 @@ remap: | |||
999 | file_pos = file_offset + head; | 1007 | file_pos = file_offset + head; |
1000 | 1008 | ||
1001 | more: | 1009 | more: |
1002 | event = (event_t *)(buf + head); | 1010 | event = (union perf_event *)(buf + head); |
1003 | 1011 | ||
1004 | if (session->header.needs_swap) | 1012 | if (session->header.needs_swap) |
1005 | perf_event_header__bswap(&event->header); | 1013 | perf_event_header__bswap(&event->header); |
@@ -1023,7 +1031,7 @@ more: | |||
1023 | 1031 | ||
1024 | if (size == 0 || | 1032 | if (size == 0 || |
1025 | perf_session__process_event(session, event, ops, file_pos) < 0) { | 1033 | perf_session__process_event(session, event, ops, file_pos) < 0) { |
1026 | dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n", | 1034 | dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n", |
1027 | file_offset + head, event->header.size, | 1035 | file_offset + head, event->header.size, |
1028 | event->header.type); | 1036 | event->header.type); |
1029 | /* | 1037 | /* |
@@ -1132,3 +1140,79 @@ size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, | |||
1132 | size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits); | 1140 | size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits); |
1133 | return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); | 1141 | return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); |
1134 | } | 1142 | } |
1143 | |||
1144 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) | ||
1145 | { | ||
1146 | struct perf_evsel *pos; | ||
1147 | size_t ret = fprintf(fp, "Aggregated stats:\n"); | ||
1148 | |||
1149 | ret += hists__fprintf_nr_events(&session->hists, fp); | ||
1150 | |||
1151 | list_for_each_entry(pos, &session->evlist->entries, node) { | ||
1152 | ret += fprintf(fp, "%s stats:\n", event_name(pos)); | ||
1153 | ret += hists__fprintf_nr_events(&pos->hists, fp); | ||
1154 | } | ||
1155 | |||
1156 | return ret; | ||
1157 | } | ||
1158 | |||
1159 | void perf_session__print_symbols(union perf_event *event, | ||
1160 | struct perf_sample *sample, | ||
1161 | struct perf_session *session) | ||
1162 | { | ||
1163 | struct addr_location al; | ||
1164 | const char *symname, *dsoname; | ||
1165 | struct callchain_cursor *cursor = &session->callchain_cursor; | ||
1166 | struct callchain_cursor_node *node; | ||
1167 | |||
1168 | if (perf_event__preprocess_sample(event, session, &al, sample, | ||
1169 | NULL) < 0) { | ||
1170 | error("problem processing %d event, skipping it.\n", | ||
1171 | event->header.type); | ||
1172 | return; | ||
1173 | } | ||
1174 | |||
1175 | if (symbol_conf.use_callchain && sample->callchain) { | ||
1176 | |||
1177 | if (perf_session__resolve_callchain(session, al.thread, | ||
1178 | sample->callchain, NULL) != 0) { | ||
1179 | if (verbose) | ||
1180 | error("Failed to resolve callchain. Skipping\n"); | ||
1181 | return; | ||
1182 | } | ||
1183 | callchain_cursor_commit(cursor); | ||
1184 | |||
1185 | while (1) { | ||
1186 | node = callchain_cursor_current(cursor); | ||
1187 | if (!node) | ||
1188 | break; | ||
1189 | |||
1190 | if (node->sym && node->sym->name) | ||
1191 | symname = node->sym->name; | ||
1192 | else | ||
1193 | symname = ""; | ||
1194 | |||
1195 | if (node->map && node->map->dso && node->map->dso->name) | ||
1196 | dsoname = node->map->dso->name; | ||
1197 | else | ||
1198 | dsoname = ""; | ||
1199 | |||
1200 | printf("\t%16" PRIx64 " %s (%s)\n", node->ip, symname, dsoname); | ||
1201 | |||
1202 | callchain_cursor_advance(cursor); | ||
1203 | } | ||
1204 | |||
1205 | } else { | ||
1206 | if (al.sym && al.sym->name) | ||
1207 | symname = al.sym->name; | ||
1208 | else | ||
1209 | symname = ""; | ||
1210 | |||
1211 | if (al.map && al.map->dso && al.map->dso->name) | ||
1212 | dsoname = al.map->dso->name; | ||
1213 | else | ||
1214 | dsoname = ""; | ||
1215 | |||
1216 | printf("%16" PRIx64 " %s (%s)", al.addr, symname, dsoname); | ||
1217 | } | ||
1218 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index decd83f274fd..1ac481fc1100 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -34,12 +34,12 @@ struct perf_session { | |||
34 | struct thread *last_match; | 34 | struct thread *last_match; |
35 | struct machine host_machine; | 35 | struct machine host_machine; |
36 | struct rb_root machines; | 36 | struct rb_root machines; |
37 | struct rb_root hists_tree; | 37 | struct perf_evlist *evlist; |
38 | /* | 38 | /* |
39 | * FIXME: should point to the first entry in hists_tree and | 39 | * FIXME: Need to split this up further, we need global |
40 | * be a hists instance. Right now its only 'report' | 40 | * stats + per event stats. 'perf diff' also needs |
41 | * that is using ->hists_tree while all the rest use | 41 | * to properly support multiple events in a single |
42 | * ->hists. | 42 | * perf.data file. |
43 | */ | 43 | */ |
44 | struct hists hists; | 44 | struct hists hists; |
45 | u64 sample_type; | 45 | u64 sample_type; |
@@ -51,20 +51,25 @@ 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 | ||
58 | struct perf_evsel; | ||
57 | struct perf_event_ops; | 59 | struct perf_event_ops; |
58 | 60 | ||
59 | typedef int (*event_op)(event_t *self, struct sample_data *sample, | 61 | typedef int (*event_sample)(union perf_event *event, struct perf_sample *sample, |
62 | struct perf_evsel *evsel, struct perf_session *session); | ||
63 | typedef int (*event_op)(union perf_event *self, struct perf_sample *sample, | ||
60 | struct perf_session *session); | 64 | struct perf_session *session); |
61 | typedef int (*event_synth_op)(event_t *self, struct perf_session *session); | 65 | typedef int (*event_synth_op)(union perf_event *self, |
62 | typedef int (*event_op2)(event_t *self, struct perf_session *session, | 66 | struct perf_session *session); |
67 | typedef int (*event_op2)(union perf_event *self, struct perf_session *session, | ||
63 | struct perf_event_ops *ops); | 68 | struct perf_event_ops *ops); |
64 | 69 | ||
65 | struct perf_event_ops { | 70 | struct perf_event_ops { |
66 | event_op sample, | 71 | event_sample sample; |
67 | mmap, | 72 | event_op mmap, |
68 | comm, | 73 | comm, |
69 | fork, | 74 | fork, |
70 | exit, | 75 | exit, |
@@ -94,10 +99,10 @@ int __perf_session__process_events(struct perf_session *self, | |||
94 | int perf_session__process_events(struct perf_session *self, | 99 | int perf_session__process_events(struct perf_session *self, |
95 | struct perf_event_ops *event_ops); | 100 | struct perf_event_ops *event_ops); |
96 | 101 | ||
97 | struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, | 102 | int perf_session__resolve_callchain(struct perf_session *self, |
98 | struct thread *thread, | 103 | struct thread *thread, |
99 | struct ip_callchain *chain, | 104 | struct ip_callchain *chain, |
100 | struct symbol **parent); | 105 | struct symbol **parent); |
101 | 106 | ||
102 | bool perf_session__has_traces(struct perf_session *self, const char *msg); | 107 | bool perf_session__has_traces(struct perf_session *self, const char *msg); |
103 | 108 | ||
@@ -110,8 +115,6 @@ void mem_bswap_64(void *src, int byte_size); | |||
110 | int perf_session__create_kernel_maps(struct perf_session *self); | 115 | int perf_session__create_kernel_maps(struct perf_session *self); |
111 | 116 | ||
112 | void perf_session__update_sample_type(struct perf_session *self); | 117 | void perf_session__update_sample_type(struct perf_session *self); |
113 | void perf_session__set_sample_id_all(struct perf_session *session, bool value); | ||
114 | void perf_session__set_sample_type(struct perf_session *session, u64 type); | ||
115 | void perf_session__remove_thread(struct perf_session *self, struct thread *th); | 118 | void perf_session__remove_thread(struct perf_session *self, struct thread *th); |
116 | 119 | ||
117 | static inline | 120 | static inline |
@@ -149,9 +152,18 @@ size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp); | |||
149 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, | 152 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, |
150 | FILE *fp, bool with_hits); | 153 | FILE *fp, bool with_hits); |
151 | 154 | ||
152 | static inline | 155 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp); |
153 | size_t perf_session__fprintf_nr_events(struct perf_session *self, FILE *fp) | 156 | |
157 | static inline int perf_session__parse_sample(struct perf_session *session, | ||
158 | const union perf_event *event, | ||
159 | struct perf_sample *sample) | ||
154 | { | 160 | { |
155 | return hists__fprintf_nr_events(&self->hists, fp); | 161 | return perf_event__parse_sample(event, session->sample_type, |
162 | session->sample_id_all, sample); | ||
156 | } | 163 | } |
164 | |||
165 | void perf_session__print_symbols(union perf_event *event, | ||
166 | struct perf_sample *sample, | ||
167 | struct perf_session *session); | ||
168 | |||
157 | #endif /* __PERF_SESSION_H */ | 169 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py new file mode 100644 index 000000000000..bbc982f5dd8b --- /dev/null +++ b/tools/perf/util/setup.py | |||
@@ -0,0 +1,24 @@ | |||
1 | #!/usr/bin/python2 | ||
2 | |||
3 | from distutils.core import setup, Extension | ||
4 | from os import getenv | ||
5 | |||
6 | cflags = ['-fno-strict-aliasing', '-Wno-write-strings'] | ||
7 | cflags += getenv('CFLAGS', '').split() | ||
8 | |||
9 | perf = Extension('perf', | ||
10 | sources = ['util/python.c', 'util/ctype.c', 'util/evlist.c', | ||
11 | 'util/evsel.c', 'util/cpumap.c', 'util/thread_map.c', | ||
12 | 'util/util.c', 'util/xyarray.c', 'util/cgroup.c'], | ||
13 | include_dirs = ['util/include'], | ||
14 | extra_compile_args = cflags, | ||
15 | ) | ||
16 | |||
17 | setup(name='perf', | ||
18 | version='0.1', | ||
19 | description='Interface with the Linux profiling infrastructure', | ||
20 | author='Arnaldo Carvalho de Melo', | ||
21 | author_email='acme@redhat.com', | ||
22 | license='GPLv2', | ||
23 | url='http://perf.wiki.kernel.org', | ||
24 | ext_modules=[perf]) | ||
diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c new file mode 100644 index 000000000000..834c8ebfe38e --- /dev/null +++ b/tools/perf/util/strfilter.c | |||
@@ -0,0 +1,199 @@ | |||
1 | #include "util.h" | ||
2 | #include "string.h" | ||
3 | #include "strfilter.h" | ||
4 | |||
5 | /* Operators */ | ||
6 | static const char *OP_and = "&"; /* Logical AND */ | ||
7 | static const char *OP_or = "|"; /* Logical OR */ | ||
8 | static const char *OP_not = "!"; /* Logical NOT */ | ||
9 | |||
10 | #define is_operator(c) ((c) == '|' || (c) == '&' || (c) == '!') | ||
11 | #define is_separator(c) (is_operator(c) || (c) == '(' || (c) == ')') | ||
12 | |||
13 | static void strfilter_node__delete(struct strfilter_node *self) | ||
14 | { | ||
15 | if (self) { | ||
16 | if (self->p && !is_operator(*self->p)) | ||
17 | free((char *)self->p); | ||
18 | strfilter_node__delete(self->l); | ||
19 | strfilter_node__delete(self->r); | ||
20 | free(self); | ||
21 | } | ||
22 | } | ||
23 | |||
24 | void strfilter__delete(struct strfilter *self) | ||
25 | { | ||
26 | if (self) { | ||
27 | strfilter_node__delete(self->root); | ||
28 | free(self); | ||
29 | } | ||
30 | } | ||
31 | |||
32 | static const char *get_token(const char *s, const char **e) | ||
33 | { | ||
34 | const char *p; | ||
35 | |||
36 | while (isspace(*s)) /* Skip spaces */ | ||
37 | s++; | ||
38 | |||
39 | if (*s == '\0') { | ||
40 | p = s; | ||
41 | goto end; | ||
42 | } | ||
43 | |||
44 | p = s + 1; | ||
45 | if (!is_separator(*s)) { | ||
46 | /* End search */ | ||
47 | retry: | ||
48 | while (*p && !is_separator(*p) && !isspace(*p)) | ||
49 | p++; | ||
50 | /* Escape and special case: '!' is also used in glob pattern */ | ||
51 | if (*(p - 1) == '\\' || (*p == '!' && *(p - 1) == '[')) { | ||
52 | p++; | ||
53 | goto retry; | ||
54 | } | ||
55 | } | ||
56 | end: | ||
57 | *e = p; | ||
58 | return s; | ||
59 | } | ||
60 | |||
61 | static struct strfilter_node *strfilter_node__alloc(const char *op, | ||
62 | struct strfilter_node *l, | ||
63 | struct strfilter_node *r) | ||
64 | { | ||
65 | struct strfilter_node *ret = zalloc(sizeof(struct strfilter_node)); | ||
66 | |||
67 | if (ret) { | ||
68 | ret->p = op; | ||
69 | ret->l = l; | ||
70 | ret->r = r; | ||
71 | } | ||
72 | |||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static struct strfilter_node *strfilter_node__new(const char *s, | ||
77 | const char **ep) | ||
78 | { | ||
79 | struct strfilter_node root, *cur, *last_op; | ||
80 | const char *e; | ||
81 | |||
82 | if (!s) | ||
83 | return NULL; | ||
84 | |||
85 | memset(&root, 0, sizeof(root)); | ||
86 | last_op = cur = &root; | ||
87 | |||
88 | s = get_token(s, &e); | ||
89 | while (*s != '\0' && *s != ')') { | ||
90 | switch (*s) { | ||
91 | case '&': /* Exchg last OP->r with AND */ | ||
92 | if (!cur->r || !last_op->r) | ||
93 | goto error; | ||
94 | cur = strfilter_node__alloc(OP_and, last_op->r, NULL); | ||
95 | if (!cur) | ||
96 | goto nomem; | ||
97 | last_op->r = cur; | ||
98 | last_op = cur; | ||
99 | break; | ||
100 | case '|': /* Exchg the root with OR */ | ||
101 | if (!cur->r || !root.r) | ||
102 | goto error; | ||
103 | cur = strfilter_node__alloc(OP_or, root.r, NULL); | ||
104 | if (!cur) | ||
105 | goto nomem; | ||
106 | root.r = cur; | ||
107 | last_op = cur; | ||
108 | break; | ||
109 | case '!': /* Add NOT as a leaf node */ | ||
110 | if (cur->r) | ||
111 | goto error; | ||
112 | cur->r = strfilter_node__alloc(OP_not, NULL, NULL); | ||
113 | if (!cur->r) | ||
114 | goto nomem; | ||
115 | cur = cur->r; | ||
116 | break; | ||
117 | case '(': /* Recursively parses inside the parenthesis */ | ||
118 | if (cur->r) | ||
119 | goto error; | ||
120 | cur->r = strfilter_node__new(s + 1, &s); | ||
121 | if (!s) | ||
122 | goto nomem; | ||
123 | if (!cur->r || *s != ')') | ||
124 | goto error; | ||
125 | e = s + 1; | ||
126 | break; | ||
127 | default: | ||
128 | if (cur->r) | ||
129 | goto error; | ||
130 | cur->r = strfilter_node__alloc(NULL, NULL, NULL); | ||
131 | if (!cur->r) | ||
132 | goto nomem; | ||
133 | cur->r->p = strndup(s, e - s); | ||
134 | if (!cur->r->p) | ||
135 | goto nomem; | ||
136 | } | ||
137 | s = get_token(e, &e); | ||
138 | } | ||
139 | if (!cur->r) | ||
140 | goto error; | ||
141 | *ep = s; | ||
142 | return root.r; | ||
143 | nomem: | ||
144 | s = NULL; | ||
145 | error: | ||
146 | *ep = s; | ||
147 | strfilter_node__delete(root.r); | ||
148 | return NULL; | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * Parse filter rule and return new strfilter. | ||
153 | * Return NULL if fail, and *ep == NULL if memory allocation failed. | ||
154 | */ | ||
155 | struct strfilter *strfilter__new(const char *rules, const char **err) | ||
156 | { | ||
157 | struct strfilter *ret = zalloc(sizeof(struct strfilter)); | ||
158 | const char *ep = NULL; | ||
159 | |||
160 | if (ret) | ||
161 | ret->root = strfilter_node__new(rules, &ep); | ||
162 | |||
163 | if (!ret || !ret->root || *ep != '\0') { | ||
164 | if (err) | ||
165 | *err = ep; | ||
166 | strfilter__delete(ret); | ||
167 | ret = NULL; | ||
168 | } | ||
169 | |||
170 | return ret; | ||
171 | } | ||
172 | |||
173 | static bool strfilter_node__compare(struct strfilter_node *self, | ||
174 | const char *str) | ||
175 | { | ||
176 | if (!self || !self->p) | ||
177 | return false; | ||
178 | |||
179 | switch (*self->p) { | ||
180 | case '|': /* OR */ | ||
181 | return strfilter_node__compare(self->l, str) || | ||
182 | strfilter_node__compare(self->r, str); | ||
183 | case '&': /* AND */ | ||
184 | return strfilter_node__compare(self->l, str) && | ||
185 | strfilter_node__compare(self->r, str); | ||
186 | case '!': /* NOT */ | ||
187 | return !strfilter_node__compare(self->r, str); | ||
188 | default: | ||
189 | return strglobmatch(str, self->p); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | /* Return true if STR matches the filter rules */ | ||
194 | bool strfilter__compare(struct strfilter *self, const char *str) | ||
195 | { | ||
196 | if (!self) | ||
197 | return false; | ||
198 | return strfilter_node__compare(self->root, str); | ||
199 | } | ||
diff --git a/tools/perf/util/strfilter.h b/tools/perf/util/strfilter.h new file mode 100644 index 000000000000..00f58a7506de --- /dev/null +++ b/tools/perf/util/strfilter.h | |||
@@ -0,0 +1,48 @@ | |||
1 | #ifndef __PERF_STRFILTER_H | ||
2 | #define __PERF_STRFILTER_H | ||
3 | /* General purpose glob matching filter */ | ||
4 | |||
5 | #include <linux/list.h> | ||
6 | #include <stdbool.h> | ||
7 | |||
8 | /* A node of string filter */ | ||
9 | struct strfilter_node { | ||
10 | struct strfilter_node *l; /* Tree left branche (for &,|) */ | ||
11 | struct strfilter_node *r; /* Tree right branche (for !,&,|) */ | ||
12 | const char *p; /* Operator or rule */ | ||
13 | }; | ||
14 | |||
15 | /* String filter */ | ||
16 | struct strfilter { | ||
17 | struct strfilter_node *root; | ||
18 | }; | ||
19 | |||
20 | /** | ||
21 | * strfilter__new - Create a new string filter | ||
22 | * @rules: Filter rule, which is a combination of glob expressions. | ||
23 | * @err: Pointer which points an error detected on @rules | ||
24 | * | ||
25 | * Parse @rules and return new strfilter. Return NULL if an error detected. | ||
26 | * In that case, *@err will indicate where it is detected, and *@err is NULL | ||
27 | * if a memory allocation is failed. | ||
28 | */ | ||
29 | struct strfilter *strfilter__new(const char *rules, const char **err); | ||
30 | |||
31 | /** | ||
32 | * strfilter__compare - compare given string and a string filter | ||
33 | * @self: String filter | ||
34 | * @str: target string | ||
35 | * | ||
36 | * Compare @str and @self. Return true if the str match the rule | ||
37 | */ | ||
38 | bool strfilter__compare(struct strfilter *self, const char *str); | ||
39 | |||
40 | /** | ||
41 | * strfilter__delete - delete a string filter | ||
42 | * @self: String filter to delete | ||
43 | * | ||
44 | * Delete @self. | ||
45 | */ | ||
46 | void strfilter__delete(struct strfilter *self); | ||
47 | |||
48 | #endif | ||
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 8fc0bd3a3a4a..b9a985dadd08 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -85,7 +85,7 @@ out: | |||
85 | 85 | ||
86 | /* | 86 | /* |
87 | * Helper function for splitting a string into an argv-like array. | 87 | * Helper function for splitting a string into an argv-like array. |
88 | * originaly copied from lib/argv_split.c | 88 | * originally copied from lib/argv_split.c |
89 | */ | 89 | */ |
90 | static const char *skip_sep(const char *cp) | 90 | static const char *skip_sep(const char *cp) |
91 | { | 91 | { |
diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index b3637db025a2..96c866045d60 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c | |||
@@ -12,6 +12,7 @@ | |||
12 | * of the License. | 12 | * of the License. |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <inttypes.h> | ||
15 | #include <stdio.h> | 16 | #include <stdio.h> |
16 | #include <stdlib.h> | 17 | #include <stdlib.h> |
17 | #include <unistd.h> | 18 | #include <unistd.h> |
@@ -43,11 +44,11 @@ static double cpu2y(int cpu) | |||
43 | return cpu2slot(cpu) * SLOT_MULT; | 44 | return cpu2slot(cpu) * SLOT_MULT; |
44 | } | 45 | } |
45 | 46 | ||
46 | static double time2pixels(u64 time) | 47 | static double time2pixels(u64 __time) |
47 | { | 48 | { |
48 | double X; | 49 | double X; |
49 | 50 | ||
50 | X = 1.0 * svg_page_width * (time - first_time) / (last_time - first_time); | 51 | X = 1.0 * svg_page_width * (__time - first_time) / (last_time - first_time); |
51 | return X; | 52 | return X; |
52 | } | 53 | } |
53 | 54 | ||
@@ -94,7 +95,7 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end) | |||
94 | 95 | ||
95 | total_height = (1 + rows + cpu2slot(cpus)) * SLOT_MULT; | 96 | total_height = (1 + rows + cpu2slot(cpus)) * SLOT_MULT; |
96 | fprintf(svgfile, "<?xml version=\"1.0\" standalone=\"no\"?> \n"); | 97 | fprintf(svgfile, "<?xml version=\"1.0\" standalone=\"no\"?> \n"); |
97 | fprintf(svgfile, "<svg width=\"%i\" height=\"%llu\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n", svg_page_width, total_height); | 98 | fprintf(svgfile, "<svg width=\"%i\" height=\"%" PRIu64 "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n", svg_page_width, total_height); |
98 | 99 | ||
99 | fprintf(svgfile, "<defs>\n <style type=\"text/css\">\n <![CDATA[\n"); | 100 | fprintf(svgfile, "<defs>\n <style type=\"text/css\">\n <![CDATA[\n"); |
100 | 101 | ||
@@ -455,9 +456,9 @@ void svg_legenda(void) | |||
455 | return; | 456 | return; |
456 | 457 | ||
457 | svg_legenda_box(0, "Running", "sample"); | 458 | svg_legenda_box(0, "Running", "sample"); |
458 | svg_legenda_box(100, "Idle","rect.c1"); | 459 | svg_legenda_box(100, "Idle","c1"); |
459 | svg_legenda_box(200, "Deeper Idle", "rect.c3"); | 460 | svg_legenda_box(200, "Deeper Idle", "c3"); |
460 | svg_legenda_box(350, "Deepest Idle", "rect.c6"); | 461 | svg_legenda_box(350, "Deepest Idle", "c6"); |
461 | svg_legenda_box(550, "Sleeping", "process2"); | 462 | svg_legenda_box(550, "Sleeping", "process2"); |
462 | svg_legenda_box(650, "Waiting for cpu", "waiting"); | 463 | svg_legenda_box(650, "Waiting for cpu", "waiting"); |
463 | svg_legenda_box(800, "Blocked on IO", "blocked"); | 464 | svg_legenda_box(800, "Blocked on IO", "blocked"); |
@@ -483,7 +484,7 @@ void svg_time_grid(void) | |||
483 | color = 128; | 484 | color = 128; |
484 | } | 485 | } |
485 | 486 | ||
486 | fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%llu\" style=\"stroke:rgb(%i,%i,%i);stroke-width:%1.3f\"/>\n", | 487 | fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%" PRIu64 "\" style=\"stroke:rgb(%i,%i,%i);stroke-width:%1.3f\"/>\n", |
487 | time2pixels(i), SLOT_MULT/2, time2pixels(i), total_height, color, color, color, thickness); | 488 | time2pixels(i), SLOT_MULT/2, time2pixels(i), total_height, color, color, color, thickness); |
488 | 489 | ||
489 | i += 10000000; | 490 | i += 10000000; |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 15ccfba8cdf8..f06c10f092ba 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <sys/param.h> | 11 | #include <sys/param.h> |
12 | #include <fcntl.h> | 12 | #include <fcntl.h> |
13 | #include <unistd.h> | 13 | #include <unistd.h> |
14 | #include <inttypes.h> | ||
14 | #include "build-id.h" | 15 | #include "build-id.h" |
15 | #include "debug.h" | 16 | #include "debug.h" |
16 | #include "symbol.h" | 17 | #include "symbol.h" |
@@ -153,7 +154,7 @@ static struct symbol *symbol__new(u64 start, u64 len, u8 binding, | |||
153 | self->binding = binding; | 154 | self->binding = binding; |
154 | self->namelen = namelen - 1; | 155 | self->namelen = namelen - 1; |
155 | 156 | ||
156 | pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); | 157 | pr_debug4("%s: %s %#" PRIx64 "-%#" PRIx64 "\n", __func__, name, start, self->end); |
157 | 158 | ||
158 | memcpy(self->name, name, namelen); | 159 | memcpy(self->name, name, namelen); |
159 | 160 | ||
@@ -167,7 +168,7 @@ void symbol__delete(struct symbol *self) | |||
167 | 168 | ||
168 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) | 169 | static size_t symbol__fprintf(struct symbol *self, FILE *fp) |
169 | { | 170 | { |
170 | return fprintf(fp, " %llx-%llx %c %s\n", | 171 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n", |
171 | self->start, self->end, | 172 | self->start, self->end, |
172 | self->binding == STB_GLOBAL ? 'g' : | 173 | self->binding == STB_GLOBAL ? 'g' : |
173 | self->binding == STB_LOCAL ? 'l' : 'w', | 174 | self->binding == STB_LOCAL ? 'l' : 'w', |
@@ -206,8 +207,7 @@ struct dso *dso__new(const char *name) | |||
206 | dso__set_short_name(self, self->name); | 207 | dso__set_short_name(self, self->name); |
207 | for (i = 0; i < MAP__NR_TYPES; ++i) | 208 | for (i = 0; i < MAP__NR_TYPES; ++i) |
208 | self->symbols[i] = self->symbol_names[i] = RB_ROOT; | 209 | self->symbols[i] = self->symbol_names[i] = RB_ROOT; |
209 | self->slen_calculated = 0; | 210 | self->symtab_type = SYMTAB__NOT_FOUND; |
210 | self->origin = DSO__ORIG_NOT_FOUND; | ||
211 | self->loaded = 0; | 211 | self->loaded = 0; |
212 | self->sorted_by_name = 0; | 212 | self->sorted_by_name = 0; |
213 | self->has_build_id = 0; | 213 | self->has_build_id = 0; |
@@ -680,9 +680,9 @@ int dso__load_kallsyms(struct dso *self, const char *filename, | |||
680 | return -1; | 680 | return -1; |
681 | 681 | ||
682 | if (self->kernel == DSO_TYPE_GUEST_KERNEL) | 682 | if (self->kernel == DSO_TYPE_GUEST_KERNEL) |
683 | self->origin = DSO__ORIG_GUEST_KERNEL; | 683 | self->symtab_type = SYMTAB__GUEST_KALLSYMS; |
684 | else | 684 | else |
685 | self->origin = DSO__ORIG_KERNEL; | 685 | self->symtab_type = SYMTAB__KALLSYMS; |
686 | 686 | ||
687 | return dso__split_kallsyms(self, map, filter); | 687 | return dso__split_kallsyms(self, map, filter); |
688 | } | 688 | } |
@@ -1161,6 +1161,13 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
1161 | 1161 | ||
1162 | section_name = elf_sec__name(&shdr, secstrs); | 1162 | section_name = elf_sec__name(&shdr, secstrs); |
1163 | 1163 | ||
1164 | /* On ARM, symbols for thumb functions have 1 added to | ||
1165 | * the symbol address as a flag - remove it */ | ||
1166 | if ((ehdr.e_machine == EM_ARM) && | ||
1167 | (map->type == MAP__FUNCTION) && | ||
1168 | (sym.st_value & 1)) | ||
1169 | --sym.st_value; | ||
1170 | |||
1164 | if (self->kernel != DSO_TYPE_USER || kmodule) { | 1171 | if (self->kernel != DSO_TYPE_USER || kmodule) { |
1165 | char dso_name[PATH_MAX]; | 1172 | char dso_name[PATH_MAX]; |
1166 | 1173 | ||
@@ -1189,6 +1196,8 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
1189 | if (curr_dso == NULL) | 1196 | if (curr_dso == NULL) |
1190 | goto out_elf_end; | 1197 | goto out_elf_end; |
1191 | curr_dso->kernel = self->kernel; | 1198 | curr_dso->kernel = self->kernel; |
1199 | curr_dso->long_name = self->long_name; | ||
1200 | curr_dso->long_name_len = self->long_name_len; | ||
1192 | curr_map = map__new2(start, curr_dso, | 1201 | curr_map = map__new2(start, curr_dso, |
1193 | map->type); | 1202 | map->type); |
1194 | if (curr_map == NULL) { | 1203 | if (curr_map == NULL) { |
@@ -1197,7 +1206,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
1197 | } | 1206 | } |
1198 | curr_map->map_ip = identity__map_ip; | 1207 | curr_map->map_ip = identity__map_ip; |
1199 | curr_map->unmap_ip = identity__map_ip; | 1208 | curr_map->unmap_ip = identity__map_ip; |
1200 | curr_dso->origin = self->origin; | 1209 | curr_dso->symtab_type = self->symtab_type; |
1201 | map_groups__insert(kmap->kmaps, curr_map); | 1210 | map_groups__insert(kmap->kmaps, curr_map); |
1202 | dsos__add(&self->node, curr_dso); | 1211 | dsos__add(&self->node, curr_dso); |
1203 | dso__set_loaded(curr_dso, map->type); | 1212 | dso__set_loaded(curr_dso, map->type); |
@@ -1208,8 +1217,8 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
1208 | } | 1217 | } |
1209 | 1218 | ||
1210 | if (curr_dso->adjust_symbols) { | 1219 | if (curr_dso->adjust_symbols) { |
1211 | pr_debug4("%s: adjusting symbol: st_value: %#Lx " | 1220 | pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " " |
1212 | "sh_addr: %#Lx sh_offset: %#Lx\n", __func__, | 1221 | "sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n", __func__, |
1213 | (u64)sym.st_value, (u64)shdr.sh_addr, | 1222 | (u64)sym.st_value, (u64)shdr.sh_addr, |
1214 | (u64)shdr.sh_offset); | 1223 | (u64)shdr.sh_offset); |
1215 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | 1224 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; |
@@ -1423,21 +1432,21 @@ out: | |||
1423 | char dso__symtab_origin(const struct dso *self) | 1432 | char dso__symtab_origin(const struct dso *self) |
1424 | { | 1433 | { |
1425 | static const char origin[] = { | 1434 | static const char origin[] = { |
1426 | [DSO__ORIG_KERNEL] = 'k', | 1435 | [SYMTAB__KALLSYMS] = 'k', |
1427 | [DSO__ORIG_JAVA_JIT] = 'j', | 1436 | [SYMTAB__JAVA_JIT] = 'j', |
1428 | [DSO__ORIG_BUILD_ID_CACHE] = 'B', | 1437 | [SYMTAB__BUILD_ID_CACHE] = 'B', |
1429 | [DSO__ORIG_FEDORA] = 'f', | 1438 | [SYMTAB__FEDORA_DEBUGINFO] = 'f', |
1430 | [DSO__ORIG_UBUNTU] = 'u', | 1439 | [SYMTAB__UBUNTU_DEBUGINFO] = 'u', |
1431 | [DSO__ORIG_BUILDID] = 'b', | 1440 | [SYMTAB__BUILDID_DEBUGINFO] = 'b', |
1432 | [DSO__ORIG_DSO] = 'd', | 1441 | [SYMTAB__SYSTEM_PATH_DSO] = 'd', |
1433 | [DSO__ORIG_KMODULE] = 'K', | 1442 | [SYMTAB__SYSTEM_PATH_KMODULE] = 'K', |
1434 | [DSO__ORIG_GUEST_KERNEL] = 'g', | 1443 | [SYMTAB__GUEST_KALLSYMS] = 'g', |
1435 | [DSO__ORIG_GUEST_KMODULE] = 'G', | 1444 | [SYMTAB__GUEST_KMODULE] = 'G', |
1436 | }; | 1445 | }; |
1437 | 1446 | ||
1438 | if (self == NULL || self->origin == DSO__ORIG_NOT_FOUND) | 1447 | if (self == NULL || self->symtab_type == SYMTAB__NOT_FOUND) |
1439 | return '!'; | 1448 | return '!'; |
1440 | return origin[self->origin]; | 1449 | return origin[self->symtab_type]; |
1441 | } | 1450 | } |
1442 | 1451 | ||
1443 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | 1452 | int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) |
@@ -1470,8 +1479,8 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1470 | 1479 | ||
1471 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { | 1480 | if (strncmp(self->name, "/tmp/perf-", 10) == 0) { |
1472 | ret = dso__load_perf_map(self, map, filter); | 1481 | ret = dso__load_perf_map(self, map, filter); |
1473 | self->origin = ret > 0 ? DSO__ORIG_JAVA_JIT : | 1482 | self->symtab_type = ret > 0 ? SYMTAB__JAVA_JIT : |
1474 | DSO__ORIG_NOT_FOUND; | 1483 | SYMTAB__NOT_FOUND; |
1475 | return ret; | 1484 | return ret; |
1476 | } | 1485 | } |
1477 | 1486 | ||
@@ -1479,26 +1488,28 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1479 | * On the first pass, only load images if they have a full symtab. | 1488 | * On the first pass, only load images if they have a full symtab. |
1480 | * Failing that, do a second pass where we accept .dynsym also | 1489 | * Failing that, do a second pass where we accept .dynsym also |
1481 | */ | 1490 | */ |
1482 | for (self->origin = DSO__ORIG_BUILD_ID_CACHE, want_symtab = 1; | 1491 | want_symtab = 1; |
1483 | self->origin != DSO__ORIG_NOT_FOUND; | 1492 | restart: |
1484 | self->origin++) { | 1493 | for (self->symtab_type = SYMTAB__BUILD_ID_CACHE; |
1485 | switch (self->origin) { | 1494 | self->symtab_type != SYMTAB__NOT_FOUND; |
1486 | case DSO__ORIG_BUILD_ID_CACHE: | 1495 | self->symtab_type++) { |
1496 | switch (self->symtab_type) { | ||
1497 | case SYMTAB__BUILD_ID_CACHE: | ||
1487 | /* skip the locally configured cache if a symfs is given */ | 1498 | /* skip the locally configured cache if a symfs is given */ |
1488 | if (symbol_conf.symfs[0] || | 1499 | if (symbol_conf.symfs[0] || |
1489 | (dso__build_id_filename(self, name, size) == NULL)) { | 1500 | (dso__build_id_filename(self, name, size) == NULL)) { |
1490 | continue; | 1501 | continue; |
1491 | } | 1502 | } |
1492 | break; | 1503 | break; |
1493 | case DSO__ORIG_FEDORA: | 1504 | case SYMTAB__FEDORA_DEBUGINFO: |
1494 | snprintf(name, size, "%s/usr/lib/debug%s.debug", | 1505 | snprintf(name, size, "%s/usr/lib/debug%s.debug", |
1495 | symbol_conf.symfs, self->long_name); | 1506 | symbol_conf.symfs, self->long_name); |
1496 | break; | 1507 | break; |
1497 | case DSO__ORIG_UBUNTU: | 1508 | case SYMTAB__UBUNTU_DEBUGINFO: |
1498 | snprintf(name, size, "%s/usr/lib/debug%s", | 1509 | snprintf(name, size, "%s/usr/lib/debug%s", |
1499 | symbol_conf.symfs, self->long_name); | 1510 | symbol_conf.symfs, self->long_name); |
1500 | break; | 1511 | break; |
1501 | case DSO__ORIG_BUILDID: { | 1512 | case SYMTAB__BUILDID_DEBUGINFO: { |
1502 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 1513 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
1503 | 1514 | ||
1504 | if (!self->has_build_id) | 1515 | if (!self->has_build_id) |
@@ -1512,34 +1523,24 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1512 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); | 1523 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); |
1513 | } | 1524 | } |
1514 | break; | 1525 | break; |
1515 | case DSO__ORIG_DSO: | 1526 | case SYMTAB__SYSTEM_PATH_DSO: |
1516 | snprintf(name, size, "%s%s", | 1527 | snprintf(name, size, "%s%s", |
1517 | symbol_conf.symfs, self->long_name); | 1528 | symbol_conf.symfs, self->long_name); |
1518 | break; | 1529 | break; |
1519 | case DSO__ORIG_GUEST_KMODULE: | 1530 | case SYMTAB__GUEST_KMODULE: |
1520 | if (map->groups && map->groups->machine) | 1531 | if (map->groups && machine) |
1521 | root_dir = map->groups->machine->root_dir; | 1532 | root_dir = machine->root_dir; |
1522 | else | 1533 | else |
1523 | root_dir = ""; | 1534 | root_dir = ""; |
1524 | snprintf(name, size, "%s%s%s", symbol_conf.symfs, | 1535 | snprintf(name, size, "%s%s%s", symbol_conf.symfs, |
1525 | root_dir, self->long_name); | 1536 | root_dir, self->long_name); |
1526 | break; | 1537 | break; |
1527 | 1538 | ||
1528 | case DSO__ORIG_KMODULE: | 1539 | case SYMTAB__SYSTEM_PATH_KMODULE: |
1529 | snprintf(name, size, "%s%s", symbol_conf.symfs, | 1540 | snprintf(name, size, "%s%s", symbol_conf.symfs, |
1530 | self->long_name); | 1541 | self->long_name); |
1531 | break; | 1542 | break; |
1532 | 1543 | default:; | |
1533 | default: | ||
1534 | /* | ||
1535 | * If we wanted a full symtab but no image had one, | ||
1536 | * relax our requirements and repeat the search. | ||
1537 | */ | ||
1538 | if (want_symtab) { | ||
1539 | want_symtab = 0; | ||
1540 | self->origin = DSO__ORIG_BUILD_ID_CACHE; | ||
1541 | } else | ||
1542 | continue; | ||
1543 | } | 1544 | } |
1544 | 1545 | ||
1545 | /* Name is now the name of the next image to try */ | 1546 | /* Name is now the name of the next image to try */ |
@@ -1566,6 +1567,15 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1566 | } | 1567 | } |
1567 | } | 1568 | } |
1568 | 1569 | ||
1570 | /* | ||
1571 | * If we wanted a full symtab but no image had one, | ||
1572 | * relax our requirements and repeat the search. | ||
1573 | */ | ||
1574 | if (ret <= 0 && want_symtab) { | ||
1575 | want_symtab = 0; | ||
1576 | goto restart; | ||
1577 | } | ||
1578 | |||
1569 | free(name); | 1579 | free(name); |
1570 | if (ret < 0 && strstr(self->name, " (deleted)") != NULL) | 1580 | if (ret < 0 && strstr(self->name, " (deleted)") != NULL) |
1571 | return 0; | 1581 | return 0; |
@@ -1750,9 +1760,9 @@ struct map *machine__new_module(struct machine *self, u64 start, | |||
1750 | return NULL; | 1760 | return NULL; |
1751 | 1761 | ||
1752 | if (machine__is_host(self)) | 1762 | if (machine__is_host(self)) |
1753 | dso->origin = DSO__ORIG_KMODULE; | 1763 | dso->symtab_type = SYMTAB__SYSTEM_PATH_KMODULE; |
1754 | else | 1764 | else |
1755 | dso->origin = DSO__ORIG_GUEST_KMODULE; | 1765 | dso->symtab_type = SYMTAB__GUEST_KMODULE; |
1756 | map_groups__insert(&self->kmaps, map); | 1766 | map_groups__insert(&self->kmaps, map); |
1757 | return map; | 1767 | return map; |
1758 | } | 1768 | } |
@@ -1828,12 +1838,13 @@ int dso__load_vmlinux(struct dso *self, struct map *map, | |||
1828 | int err = -1, fd; | 1838 | int err = -1, fd; |
1829 | char symfs_vmlinux[PATH_MAX]; | 1839 | char symfs_vmlinux[PATH_MAX]; |
1830 | 1840 | ||
1831 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s/%s", | 1841 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s", |
1832 | symbol_conf.symfs, vmlinux); | 1842 | symbol_conf.symfs, vmlinux); |
1833 | fd = open(symfs_vmlinux, O_RDONLY); | 1843 | fd = open(symfs_vmlinux, O_RDONLY); |
1834 | if (fd < 0) | 1844 | if (fd < 0) |
1835 | return -1; | 1845 | return -1; |
1836 | 1846 | ||
1847 | dso__set_long_name(self, (char *)vmlinux); | ||
1837 | dso__set_loaded(self, map->type); | 1848 | dso__set_loaded(self, map->type); |
1838 | err = dso__load_sym(self, map, symfs_vmlinux, fd, filter, 0, 0); | 1849 | err = dso__load_sym(self, map, symfs_vmlinux, fd, filter, 0, 0); |
1839 | close(fd); | 1850 | close(fd); |
@@ -2395,6 +2406,8 @@ int symbol__init(void) | |||
2395 | if (symbol_conf.initialized) | 2406 | if (symbol_conf.initialized) |
2396 | return 0; | 2407 | return 0; |
2397 | 2408 | ||
2409 | symbol_conf.priv_size = ALIGN(symbol_conf.priv_size, sizeof(u64)); | ||
2410 | |||
2398 | elf_version(EV_CURRENT); | 2411 | elf_version(EV_CURRENT); |
2399 | if (symbol_conf.sort_by_name) | 2412 | if (symbol_conf.sort_by_name) |
2400 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - | 2413 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 670cd1c88f54..713b0b40cc4a 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -48,12 +48,17 @@ char *strxfrchar(char *s, char from, char to); | |||
48 | 48 | ||
49 | #define BUILD_ID_SIZE 20 | 49 | #define BUILD_ID_SIZE 20 |
50 | 50 | ||
51 | /** struct symbol - symtab entry | ||
52 | * | ||
53 | * @ignore - resolvable but tools ignore it (e.g. idle routines) | ||
54 | */ | ||
51 | struct symbol { | 55 | struct symbol { |
52 | struct rb_node rb_node; | 56 | struct rb_node rb_node; |
53 | u64 start; | 57 | u64 start; |
54 | u64 end; | 58 | u64 end; |
55 | u16 namelen; | 59 | u16 namelen; |
56 | u8 binding; | 60 | u8 binding; |
61 | bool ignore; | ||
57 | char name[0]; | 62 | char name[0]; |
58 | }; | 63 | }; |
59 | 64 | ||
@@ -132,13 +137,12 @@ struct dso { | |||
132 | struct rb_root symbol_names[MAP__NR_TYPES]; | 137 | struct rb_root symbol_names[MAP__NR_TYPES]; |
133 | enum dso_kernel_type kernel; | 138 | enum dso_kernel_type kernel; |
134 | u8 adjust_symbols:1; | 139 | u8 adjust_symbols:1; |
135 | u8 slen_calculated:1; | ||
136 | u8 has_build_id:1; | 140 | u8 has_build_id:1; |
137 | u8 hit:1; | 141 | u8 hit:1; |
138 | u8 annotate_warned:1; | 142 | u8 annotate_warned:1; |
139 | u8 sname_alloc:1; | 143 | u8 sname_alloc:1; |
140 | u8 lname_alloc:1; | 144 | u8 lname_alloc:1; |
141 | unsigned char origin; | 145 | unsigned char symtab_type; |
142 | u8 sorted_by_name; | 146 | u8 sorted_by_name; |
143 | u8 loaded; | 147 | u8 loaded; |
144 | u8 build_id[BUILD_ID_SIZE]; | 148 | u8 build_id[BUILD_ID_SIZE]; |
@@ -189,18 +193,18 @@ size_t dso__fprintf_buildid(struct dso *self, FILE *fp); | |||
189 | size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp); | 193 | size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp); |
190 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); | 194 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); |
191 | 195 | ||
192 | enum dso_origin { | 196 | enum symtab_type { |
193 | DSO__ORIG_KERNEL = 0, | 197 | SYMTAB__KALLSYMS = 0, |
194 | DSO__ORIG_GUEST_KERNEL, | 198 | SYMTAB__GUEST_KALLSYMS, |
195 | DSO__ORIG_JAVA_JIT, | 199 | SYMTAB__JAVA_JIT, |
196 | DSO__ORIG_BUILD_ID_CACHE, | 200 | SYMTAB__BUILD_ID_CACHE, |
197 | DSO__ORIG_FEDORA, | 201 | SYMTAB__FEDORA_DEBUGINFO, |
198 | DSO__ORIG_UBUNTU, | 202 | SYMTAB__UBUNTU_DEBUGINFO, |
199 | DSO__ORIG_BUILDID, | 203 | SYMTAB__BUILDID_DEBUGINFO, |
200 | DSO__ORIG_DSO, | 204 | SYMTAB__SYSTEM_PATH_DSO, |
201 | DSO__ORIG_GUEST_KMODULE, | 205 | SYMTAB__GUEST_KMODULE, |
202 | DSO__ORIG_KMODULE, | 206 | SYMTAB__SYSTEM_PATH_KMODULE, |
203 | DSO__ORIG_NOT_FOUND, | 207 | SYMTAB__NOT_FOUND, |
204 | }; | 208 | }; |
205 | 209 | ||
206 | char dso__symtab_origin(const struct dso *self); | 210 | char dso__symtab_origin(const struct dso *self); |
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/top.c b/tools/perf/util/top.c new file mode 100644 index 000000000000..a11f60735a18 --- /dev/null +++ b/tools/perf/util/top.c | |||
@@ -0,0 +1,238 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Refactored from builtin-top.c, see that files for further copyright notes. | ||
5 | * | ||
6 | * Released under the GPL v2. (and only v2, not any later version) | ||
7 | */ | ||
8 | |||
9 | #include "cpumap.h" | ||
10 | #include "event.h" | ||
11 | #include "evlist.h" | ||
12 | #include "evsel.h" | ||
13 | #include "parse-events.h" | ||
14 | #include "symbol.h" | ||
15 | #include "top.h" | ||
16 | #include <inttypes.h> | ||
17 | |||
18 | /* | ||
19 | * Ordering weight: count-1 * count-2 * ... / count-n | ||
20 | */ | ||
21 | static double sym_weight(const struct sym_entry *sym, struct perf_top *top) | ||
22 | { | ||
23 | double weight = sym->snap_count; | ||
24 | int counter; | ||
25 | |||
26 | if (!top->display_weighted) | ||
27 | return weight; | ||
28 | |||
29 | for (counter = 1; counter < top->evlist->nr_entries - 1; counter++) | ||
30 | weight *= sym->count[counter]; | ||
31 | |||
32 | weight /= (sym->count[counter] + 1); | ||
33 | |||
34 | return weight; | ||
35 | } | ||
36 | |||
37 | static void perf_top__remove_active_sym(struct perf_top *top, struct sym_entry *syme) | ||
38 | { | ||
39 | pthread_mutex_lock(&top->active_symbols_lock); | ||
40 | list_del_init(&syme->node); | ||
41 | pthread_mutex_unlock(&top->active_symbols_lock); | ||
42 | } | ||
43 | |||
44 | static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) | ||
45 | { | ||
46 | struct rb_node **p = &tree->rb_node; | ||
47 | struct rb_node *parent = NULL; | ||
48 | struct sym_entry *iter; | ||
49 | |||
50 | while (*p != NULL) { | ||
51 | parent = *p; | ||
52 | iter = rb_entry(parent, struct sym_entry, rb_node); | ||
53 | |||
54 | if (se->weight > iter->weight) | ||
55 | p = &(*p)->rb_left; | ||
56 | else | ||
57 | p = &(*p)->rb_right; | ||
58 | } | ||
59 | |||
60 | rb_link_node(&se->rb_node, parent, p); | ||
61 | rb_insert_color(&se->rb_node, tree); | ||
62 | } | ||
63 | |||
64 | #define SNPRINTF(buf, size, fmt, args...) \ | ||
65 | ({ \ | ||
66 | size_t r = snprintf(buf, size, fmt, ## args); \ | ||
67 | r > size ? size : r; \ | ||
68 | }) | ||
69 | |||
70 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | ||
71 | { | ||
72 | struct perf_evsel *counter; | ||
73 | float samples_per_sec = top->samples / top->delay_secs; | ||
74 | float ksamples_per_sec = top->kernel_samples / top->delay_secs; | ||
75 | float esamples_percent = (100.0 * top->exact_samples) / top->samples; | ||
76 | size_t ret = 0; | ||
77 | |||
78 | if (!perf_guest) { | ||
79 | ret = SNPRINTF(bf, size, | ||
80 | " PerfTop:%8.0f irqs/sec kernel:%4.1f%%" | ||
81 | " exact: %4.1f%% [", samples_per_sec, | ||
82 | 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) / | ||
83 | samples_per_sec)), | ||
84 | esamples_percent); | ||
85 | } else { | ||
86 | float us_samples_per_sec = top->us_samples / top->delay_secs; | ||
87 | float guest_kernel_samples_per_sec = top->guest_kernel_samples / top->delay_secs; | ||
88 | float guest_us_samples_per_sec = top->guest_us_samples / top->delay_secs; | ||
89 | |||
90 | ret = SNPRINTF(bf, size, | ||
91 | " PerfTop:%8.0f irqs/sec kernel:%4.1f%% us:%4.1f%%" | ||
92 | " guest kernel:%4.1f%% guest us:%4.1f%%" | ||
93 | " exact: %4.1f%% [", samples_per_sec, | ||
94 | 100.0 - (100.0 * ((samples_per_sec - ksamples_per_sec) / | ||
95 | samples_per_sec)), | ||
96 | 100.0 - (100.0 * ((samples_per_sec - us_samples_per_sec) / | ||
97 | samples_per_sec)), | ||
98 | 100.0 - (100.0 * ((samples_per_sec - | ||
99 | guest_kernel_samples_per_sec) / | ||
100 | samples_per_sec)), | ||
101 | 100.0 - (100.0 * ((samples_per_sec - | ||
102 | guest_us_samples_per_sec) / | ||
103 | samples_per_sec)), | ||
104 | esamples_percent); | ||
105 | } | ||
106 | |||
107 | if (top->evlist->nr_entries == 1 || !top->display_weighted) { | ||
108 | struct perf_evsel *first; | ||
109 | first = list_entry(top->evlist->entries.next, struct perf_evsel, node); | ||
110 | ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", | ||
111 | (uint64_t)first->attr.sample_period, | ||
112 | top->freq ? "Hz" : ""); | ||
113 | } | ||
114 | |||
115 | if (!top->display_weighted) { | ||
116 | ret += SNPRINTF(bf + ret, size - ret, "%s", | ||
117 | event_name(top->sym_evsel)); | ||
118 | } else { | ||
119 | /* | ||
120 | * Don't let events eat all the space. Leaving 30 bytes | ||
121 | * for the rest should be enough. | ||
122 | */ | ||
123 | size_t last_pos = size - 30; | ||
124 | |||
125 | list_for_each_entry(counter, &top->evlist->entries, node) { | ||
126 | ret += SNPRINTF(bf + ret, size - ret, "%s%s", | ||
127 | counter->idx ? "/" : "", | ||
128 | event_name(counter)); | ||
129 | if (ret > last_pos) { | ||
130 | sprintf(bf + last_pos - 3, ".."); | ||
131 | ret = last_pos - 1; | ||
132 | break; | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | |||
137 | ret += SNPRINTF(bf + ret, size - ret, "], "); | ||
138 | |||
139 | if (top->target_pid != -1) | ||
140 | ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %d", | ||
141 | top->target_pid); | ||
142 | else if (top->target_tid != -1) | ||
143 | ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %d", | ||
144 | top->target_tid); | ||
145 | else | ||
146 | ret += SNPRINTF(bf + ret, size - ret, " (all"); | ||
147 | |||
148 | if (top->cpu_list) | ||
149 | ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", | ||
150 | top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list); | ||
151 | else { | ||
152 | if (top->target_tid != -1) | ||
153 | ret += SNPRINTF(bf + ret, size - ret, ")"); | ||
154 | else | ||
155 | ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)", | ||
156 | top->evlist->cpus->nr, | ||
157 | top->evlist->cpus->nr > 1 ? "s" : ""); | ||
158 | } | ||
159 | |||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | void perf_top__reset_sample_counters(struct perf_top *top) | ||
164 | { | ||
165 | top->samples = top->us_samples = top->kernel_samples = | ||
166 | top->exact_samples = top->guest_kernel_samples = | ||
167 | top->guest_us_samples = 0; | ||
168 | } | ||
169 | |||
170 | float perf_top__decay_samples(struct perf_top *top, struct rb_root *root) | ||
171 | { | ||
172 | struct sym_entry *syme, *n; | ||
173 | float sum_ksamples = 0.0; | ||
174 | int snap = !top->display_weighted ? top->sym_evsel->idx : 0, j; | ||
175 | |||
176 | /* Sort the active symbols */ | ||
177 | pthread_mutex_lock(&top->active_symbols_lock); | ||
178 | syme = list_entry(top->active_symbols.next, struct sym_entry, node); | ||
179 | pthread_mutex_unlock(&top->active_symbols_lock); | ||
180 | |||
181 | top->rb_entries = 0; | ||
182 | list_for_each_entry_safe_from(syme, n, &top->active_symbols, node) { | ||
183 | syme->snap_count = syme->count[snap]; | ||
184 | if (syme->snap_count != 0) { | ||
185 | |||
186 | if ((top->hide_user_symbols && | ||
187 | syme->map->dso->kernel == DSO_TYPE_USER) || | ||
188 | (top->hide_kernel_symbols && | ||
189 | syme->map->dso->kernel == DSO_TYPE_KERNEL)) { | ||
190 | perf_top__remove_active_sym(top, syme); | ||
191 | continue; | ||
192 | } | ||
193 | syme->weight = sym_weight(syme, top); | ||
194 | |||
195 | if ((int)syme->snap_count >= top->count_filter) { | ||
196 | rb_insert_active_sym(root, syme); | ||
197 | ++top->rb_entries; | ||
198 | } | ||
199 | sum_ksamples += syme->snap_count; | ||
200 | |||
201 | for (j = 0; j < top->evlist->nr_entries; j++) | ||
202 | syme->count[j] = top->zero ? 0 : syme->count[j] * 7 / 8; | ||
203 | } else | ||
204 | perf_top__remove_active_sym(top, syme); | ||
205 | } | ||
206 | |||
207 | return sum_ksamples; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Find the longest symbol name that will be displayed | ||
212 | */ | ||
213 | void perf_top__find_widths(struct perf_top *top, struct rb_root *root, | ||
214 | int *dso_width, int *dso_short_width, int *sym_width) | ||
215 | { | ||
216 | struct rb_node *nd; | ||
217 | int printed = 0; | ||
218 | |||
219 | *sym_width = *dso_width = *dso_short_width = 0; | ||
220 | |||
221 | for (nd = rb_first(root); nd; nd = rb_next(nd)) { | ||
222 | struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node); | ||
223 | struct symbol *sym = sym_entry__symbol(syme); | ||
224 | |||
225 | if (++printed > top->print_entries || | ||
226 | (int)syme->snap_count < top->count_filter) | ||
227 | continue; | ||
228 | |||
229 | if (syme->map->dso->long_name_len > *dso_width) | ||
230 | *dso_width = syme->map->dso->long_name_len; | ||
231 | |||
232 | if (syme->map->dso->short_name_len > *dso_short_width) | ||
233 | *dso_short_width = syme->map->dso->short_name_len; | ||
234 | |||
235 | if (sym->namelen > *sym_width) | ||
236 | *sym_width = sym->namelen; | ||
237 | } | ||
238 | } | ||
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h new file mode 100644 index 000000000000..bfbf95bcc603 --- /dev/null +++ b/tools/perf/util/top.h | |||
@@ -0,0 +1,64 @@ | |||
1 | #ifndef __PERF_TOP_H | ||
2 | #define __PERF_TOP_H 1 | ||
3 | |||
4 | #include "types.h" | ||
5 | #include "../perf.h" | ||
6 | #include <stddef.h> | ||
7 | #include <pthread.h> | ||
8 | #include <linux/list.h> | ||
9 | #include <linux/rbtree.h> | ||
10 | |||
11 | struct perf_evlist; | ||
12 | struct perf_evsel; | ||
13 | |||
14 | struct sym_entry { | ||
15 | struct rb_node rb_node; | ||
16 | struct list_head node; | ||
17 | unsigned long snap_count; | ||
18 | double weight; | ||
19 | struct map *map; | ||
20 | unsigned long count[0]; | ||
21 | }; | ||
22 | |||
23 | static inline struct symbol *sym_entry__symbol(struct sym_entry *self) | ||
24 | { | ||
25 | return ((void *)self) + symbol_conf.priv_size; | ||
26 | } | ||
27 | |||
28 | struct perf_top { | ||
29 | struct perf_evlist *evlist; | ||
30 | /* | ||
31 | * Symbols will be added here in perf_event__process_sample and will | ||
32 | * get out after decayed. | ||
33 | */ | ||
34 | struct list_head active_symbols; | ||
35 | pthread_mutex_t active_symbols_lock; | ||
36 | pthread_cond_t active_symbols_cond; | ||
37 | u64 samples; | ||
38 | u64 kernel_samples, us_samples; | ||
39 | u64 exact_samples; | ||
40 | u64 guest_us_samples, guest_kernel_samples; | ||
41 | int print_entries, count_filter, delay_secs; | ||
42 | int display_weighted, freq, rb_entries; | ||
43 | pid_t target_pid, target_tid; | ||
44 | bool hide_kernel_symbols, hide_user_symbols, zero; | ||
45 | const char *cpu_list; | ||
46 | struct sym_entry *sym_filter_entry; | ||
47 | struct perf_evsel *sym_evsel; | ||
48 | }; | ||
49 | |||
50 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); | ||
51 | void perf_top__reset_sample_counters(struct perf_top *top); | ||
52 | float perf_top__decay_samples(struct perf_top *top, struct rb_root *root); | ||
53 | void perf_top__find_widths(struct perf_top *top, struct rb_root *root, | ||
54 | int *dso_width, int *dso_short_width, int *sym_width); | ||
55 | |||
56 | #ifdef NO_NEWT_SUPPORT | ||
57 | static inline int perf_top__tui_browser(struct perf_top *top __used) | ||
58 | { | ||
59 | return 0; | ||
60 | } | ||
61 | #else | ||
62 | int perf_top__tui_browser(struct perf_top *top); | ||
63 | #endif | ||
64 | #endif /* __PERF_TOP_H */ | ||
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 73a02223c629..0a7ed5b5e281 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -153,7 +153,7 @@ void parse_proc_kallsyms(char *file, unsigned int size __unused) | |||
153 | char *next = NULL; | 153 | char *next = NULL; |
154 | char *addr_str; | 154 | char *addr_str; |
155 | char ch; | 155 | char ch; |
156 | int ret; | 156 | int ret __used; |
157 | int i; | 157 | int i; |
158 | 158 | ||
159 | line = strtok_r(file, "\n", &next); | 159 | line = strtok_r(file, "\n", &next); |
@@ -2643,68 +2643,13 @@ static void print_lat_fmt(void *data, int size __unused) | |||
2643 | printf("."); | 2643 | printf("."); |
2644 | 2644 | ||
2645 | if (lock_depth < 0) | 2645 | if (lock_depth < 0) |
2646 | printf("."); | 2646 | printf(". "); |
2647 | else | 2647 | else |
2648 | printf("%d", lock_depth); | 2648 | printf("%d ", lock_depth); |
2649 | } | ||
2650 | |||
2651 | /* taken from Linux, written by Frederic Weisbecker */ | ||
2652 | static void print_graph_cpu(int cpu) | ||
2653 | { | ||
2654 | int i; | ||
2655 | int log10_this = log10_cpu(cpu); | ||
2656 | int log10_all = log10_cpu(cpus); | ||
2657 | |||
2658 | |||
2659 | /* | ||
2660 | * Start with a space character - to make it stand out | ||
2661 | * to the right a bit when trace output is pasted into | ||
2662 | * email: | ||
2663 | */ | ||
2664 | printf(" "); | ||
2665 | |||
2666 | /* | ||
2667 | * Tricky - we space the CPU field according to the max | ||
2668 | * number of online CPUs. On a 2-cpu system it would take | ||
2669 | * a maximum of 1 digit - on a 128 cpu system it would | ||
2670 | * take up to 3 digits: | ||
2671 | */ | ||
2672 | for (i = 0; i < log10_all - log10_this; i++) | ||
2673 | printf(" "); | ||
2674 | |||
2675 | printf("%d) ", cpu); | ||
2676 | } | 2649 | } |
2677 | 2650 | ||
2678 | #define TRACE_GRAPH_PROCINFO_LENGTH 14 | ||
2679 | #define TRACE_GRAPH_INDENT 2 | 2651 | #define TRACE_GRAPH_INDENT 2 |
2680 | 2652 | ||
2681 | static void print_graph_proc(int pid, const char *comm) | ||
2682 | { | ||
2683 | /* sign + log10(MAX_INT) + '\0' */ | ||
2684 | char pid_str[11]; | ||
2685 | int spaces = 0; | ||
2686 | int len; | ||
2687 | int i; | ||
2688 | |||
2689 | sprintf(pid_str, "%d", pid); | ||
2690 | |||
2691 | /* 1 stands for the "-" character */ | ||
2692 | len = strlen(comm) + strlen(pid_str) + 1; | ||
2693 | |||
2694 | if (len < TRACE_GRAPH_PROCINFO_LENGTH) | ||
2695 | spaces = TRACE_GRAPH_PROCINFO_LENGTH - len; | ||
2696 | |||
2697 | /* First spaces to align center */ | ||
2698 | for (i = 0; i < spaces / 2; i++) | ||
2699 | printf(" "); | ||
2700 | |||
2701 | printf("%s-%s", comm, pid_str); | ||
2702 | |||
2703 | /* Last spaces to align center */ | ||
2704 | for (i = 0; i < spaces - (spaces / 2); i++) | ||
2705 | printf(" "); | ||
2706 | } | ||
2707 | |||
2708 | static struct record * | 2653 | static struct record * |
2709 | get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func, | 2654 | get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func, |
2710 | struct record *next) | 2655 | struct record *next) |
@@ -2876,21 +2821,13 @@ static void print_graph_nested(struct event *event, void *data) | |||
2876 | 2821 | ||
2877 | static void | 2822 | static void |
2878 | pretty_print_func_ent(void *data, int size, struct event *event, | 2823 | pretty_print_func_ent(void *data, int size, struct event *event, |
2879 | int cpu, int pid, const char *comm, | 2824 | int cpu, int pid) |
2880 | unsigned long secs, unsigned long usecs) | ||
2881 | { | 2825 | { |
2882 | struct format_field *field; | 2826 | struct format_field *field; |
2883 | struct record *rec; | 2827 | struct record *rec; |
2884 | void *copy_data; | 2828 | void *copy_data; |
2885 | unsigned long val; | 2829 | unsigned long val; |
2886 | 2830 | ||
2887 | printf("%5lu.%06lu | ", secs, usecs); | ||
2888 | |||
2889 | print_graph_cpu(cpu); | ||
2890 | print_graph_proc(pid, comm); | ||
2891 | |||
2892 | printf(" | "); | ||
2893 | |||
2894 | if (latency_format) { | 2831 | if (latency_format) { |
2895 | print_lat_fmt(data, size); | 2832 | print_lat_fmt(data, size); |
2896 | printf(" | "); | 2833 | printf(" | "); |
@@ -2923,22 +2860,13 @@ out_free: | |||
2923 | } | 2860 | } |
2924 | 2861 | ||
2925 | static void | 2862 | static void |
2926 | pretty_print_func_ret(void *data, int size __unused, struct event *event, | 2863 | pretty_print_func_ret(void *data, int size __unused, struct event *event) |
2927 | int cpu, int pid, const char *comm, | ||
2928 | unsigned long secs, unsigned long usecs) | ||
2929 | { | 2864 | { |
2930 | unsigned long long rettime, calltime; | 2865 | unsigned long long rettime, calltime; |
2931 | unsigned long long duration, depth; | 2866 | unsigned long long duration, depth; |
2932 | struct format_field *field; | 2867 | struct format_field *field; |
2933 | int i; | 2868 | int i; |
2934 | 2869 | ||
2935 | printf("%5lu.%06lu | ", secs, usecs); | ||
2936 | |||
2937 | print_graph_cpu(cpu); | ||
2938 | print_graph_proc(pid, comm); | ||
2939 | |||
2940 | printf(" | "); | ||
2941 | |||
2942 | if (latency_format) { | 2870 | if (latency_format) { |
2943 | print_lat_fmt(data, size); | 2871 | print_lat_fmt(data, size); |
2944 | printf(" | "); | 2872 | printf(" | "); |
@@ -2976,31 +2904,21 @@ pretty_print_func_ret(void *data, int size __unused, struct event *event, | |||
2976 | 2904 | ||
2977 | static void | 2905 | static void |
2978 | pretty_print_func_graph(void *data, int size, struct event *event, | 2906 | pretty_print_func_graph(void *data, int size, struct event *event, |
2979 | int cpu, int pid, const char *comm, | 2907 | int cpu, int pid) |
2980 | unsigned long secs, unsigned long usecs) | ||
2981 | { | 2908 | { |
2982 | if (event->flags & EVENT_FL_ISFUNCENT) | 2909 | if (event->flags & EVENT_FL_ISFUNCENT) |
2983 | pretty_print_func_ent(data, size, event, | 2910 | pretty_print_func_ent(data, size, event, cpu, pid); |
2984 | cpu, pid, comm, secs, usecs); | ||
2985 | else if (event->flags & EVENT_FL_ISFUNCRET) | 2911 | else if (event->flags & EVENT_FL_ISFUNCRET) |
2986 | pretty_print_func_ret(data, size, event, | 2912 | pretty_print_func_ret(data, size, event); |
2987 | cpu, pid, comm, secs, usecs); | ||
2988 | printf("\n"); | 2913 | printf("\n"); |
2989 | } | 2914 | } |
2990 | 2915 | ||
2991 | void print_event(int cpu, void *data, int size, unsigned long long nsecs, | 2916 | void print_trace_event(int cpu, void *data, int size) |
2992 | char *comm) | ||
2993 | { | 2917 | { |
2994 | struct event *event; | 2918 | struct event *event; |
2995 | unsigned long secs; | ||
2996 | unsigned long usecs; | ||
2997 | int type; | 2919 | int type; |
2998 | int pid; | 2920 | int pid; |
2999 | 2921 | ||
3000 | secs = nsecs / NSECS_PER_SEC; | ||
3001 | nsecs -= secs * NSECS_PER_SEC; | ||
3002 | usecs = nsecs / NSECS_PER_USEC; | ||
3003 | |||
3004 | type = trace_parse_common_type(data); | 2922 | type = trace_parse_common_type(data); |
3005 | 2923 | ||
3006 | event = trace_find_event(type); | 2924 | event = trace_find_event(type); |
@@ -3012,17 +2930,10 @@ void print_event(int cpu, void *data, int size, unsigned long long nsecs, | |||
3012 | pid = trace_parse_common_pid(data); | 2930 | pid = trace_parse_common_pid(data); |
3013 | 2931 | ||
3014 | if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET)) | 2932 | if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET)) |
3015 | return pretty_print_func_graph(data, size, event, cpu, | 2933 | return pretty_print_func_graph(data, size, event, cpu, pid); |
3016 | pid, comm, secs, usecs); | ||
3017 | 2934 | ||
3018 | if (latency_format) { | 2935 | if (latency_format) |
3019 | printf("%8.8s-%-5d %3d", | ||
3020 | comm, pid, cpu); | ||
3021 | print_lat_fmt(data, size); | 2936 | print_lat_fmt(data, size); |
3022 | } else | ||
3023 | printf("%16s-%-5d [%03d]", comm, pid, cpu); | ||
3024 | |||
3025 | printf(" %5lu.%06lu: %s: ", secs, usecs, event->name); | ||
3026 | 2937 | ||
3027 | if (event->flags & EVENT_FL_FAILED) { | 2938 | if (event->flags & EVENT_FL_FAILED) { |
3028 | printf("EVENT '%s' FAILED TO PARSE\n", | 2939 | printf("EVENT '%s' FAILED TO PARSE\n", |
@@ -3031,7 +2942,6 @@ void print_event(int cpu, void *data, int size, unsigned long long nsecs, | |||
3031 | } | 2942 | } |
3032 | 2943 | ||
3033 | pretty_print(data, size, event); | 2944 | pretty_print(data, size, event); |
3034 | printf("\n"); | ||
3035 | } | 2945 | } |
3036 | 2946 | ||
3037 | static void print_fields(struct print_flag_sym *field) | 2947 | static void print_fields(struct print_flag_sym *field) |
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index f7af2fca965d..c9dcbec7d800 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c | |||
@@ -36,11 +36,11 @@ static int stop_script_unsupported(void) | |||
36 | return 0; | 36 | return 0; |
37 | } | 37 | } |
38 | 38 | ||
39 | static void process_event_unsupported(int cpu __unused, | 39 | static void process_event_unsupported(union perf_event *event __unused, |
40 | void *data __unused, | 40 | struct perf_sample *sample __unused, |
41 | int size __unused, | 41 | struct perf_evsel *evsel __unused, |
42 | unsigned long long nsecs __unused, | 42 | struct perf_session *session __unused, |
43 | char *comm __unused) | 43 | struct thread *thread __unused) |
44 | { | 44 | { |
45 | } | 45 | } |
46 | 46 | ||
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index b5f12ca24d99..f674dda3363b 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include "parse-events.h" | 5 | #include "parse-events.h" |
6 | #include "session.h" | ||
6 | 7 | ||
7 | #define __unused __attribute__((unused)) | 8 | #define __unused __attribute__((unused)) |
8 | 9 | ||
@@ -176,8 +177,7 @@ void print_printk(void); | |||
176 | 177 | ||
177 | int parse_ftrace_file(char *buf, unsigned long size); | 178 | int parse_ftrace_file(char *buf, unsigned long size); |
178 | int parse_event_file(char *buf, unsigned long size, char *sys); | 179 | int parse_event_file(char *buf, unsigned long size, char *sys); |
179 | void print_event(int cpu, void *data, int size, unsigned long long nsecs, | 180 | void print_trace_event(int cpu, void *data, int size); |
180 | char *comm); | ||
181 | 181 | ||
182 | extern int file_bigendian; | 182 | extern int file_bigendian; |
183 | extern int host_bigendian; | 183 | extern int host_bigendian; |
@@ -278,8 +278,11 @@ struct scripting_ops { | |||
278 | const char *name; | 278 | const char *name; |
279 | int (*start_script) (const char *script, int argc, const char **argv); | 279 | int (*start_script) (const char *script, int argc, const char **argv); |
280 | int (*stop_script) (void); | 280 | int (*stop_script) (void); |
281 | void (*process_event) (int cpu, void *data, int size, | 281 | void (*process_event) (union perf_event *event, |
282 | unsigned long long nsecs, char *comm); | 282 | struct perf_sample *sample, |
283 | struct perf_evsel *evsel, | ||
284 | struct perf_session *session, | ||
285 | struct thread *thread); | ||
283 | int (*generate_script) (const char *outfile); | 286 | int (*generate_script) (const char *outfile); |
284 | }; | 287 | }; |
285 | 288 | ||
diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h index 7d6b8331f898..5f3689a3d085 100644 --- a/tools/perf/util/types.h +++ b/tools/perf/util/types.h | |||
@@ -1,12 +1,14 @@ | |||
1 | #ifndef __PERF_TYPES_H | 1 | #ifndef __PERF_TYPES_H |
2 | #define __PERF_TYPES_H | 2 | #define __PERF_TYPES_H |
3 | 3 | ||
4 | #include <stdint.h> | ||
5 | |||
4 | /* | 6 | /* |
5 | * We define u64 as unsigned long long for every architecture | 7 | * We define u64 as uint64_t for every architecture |
6 | * so that we can print it with %Lx without getting warnings. | 8 | * so that we can print it with "%"PRIx64 without getting warnings. |
7 | */ | 9 | */ |
8 | typedef unsigned long long u64; | 10 | typedef uint64_t u64; |
9 | typedef signed long long s64; | 11 | typedef int64_t s64; |
10 | typedef unsigned int u32; | 12 | typedef unsigned int u32; |
11 | typedef signed int s32; | 13 | typedef signed int s32; |
12 | typedef unsigned short u16; | 14 | typedef unsigned short u16; |
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 8bc010edca25..611219f80680 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include "libslang.h" | 1 | #include "libslang.h" |
2 | #include "ui.h" | ||
2 | #include <linux/compiler.h> | 3 | #include <linux/compiler.h> |
3 | #include <linux/list.h> | 4 | #include <linux/list.h> |
4 | #include <linux/rbtree.h> | 5 | #include <linux/rbtree.h> |
@@ -156,6 +157,20 @@ void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]) | |||
156 | } | 157 | } |
157 | } | 158 | } |
158 | 159 | ||
160 | void __ui_browser__show_title(struct ui_browser *browser, const char *title) | ||
161 | { | ||
162 | SLsmg_gotorc(0, 0); | ||
163 | ui_browser__set_color(browser, NEWT_COLORSET_ROOT); | ||
164 | slsmg_write_nstring(title, browser->width); | ||
165 | } | ||
166 | |||
167 | void ui_browser__show_title(struct ui_browser *browser, const char *title) | ||
168 | { | ||
169 | pthread_mutex_lock(&ui__lock); | ||
170 | __ui_browser__show_title(browser, title); | ||
171 | pthread_mutex_unlock(&ui__lock); | ||
172 | } | ||
173 | |||
159 | int ui_browser__show(struct ui_browser *self, const char *title, | 174 | int ui_browser__show(struct ui_browser *self, const char *title, |
160 | const char *helpline, ...) | 175 | const char *helpline, ...) |
161 | { | 176 | { |
@@ -178,9 +193,8 @@ int ui_browser__show(struct ui_browser *self, const char *title, | |||
178 | if (self->sb == NULL) | 193 | if (self->sb == NULL) |
179 | return -1; | 194 | return -1; |
180 | 195 | ||
181 | SLsmg_gotorc(0, 0); | 196 | pthread_mutex_lock(&ui__lock); |
182 | ui_browser__set_color(self, NEWT_COLORSET_ROOT); | 197 | __ui_browser__show_title(self, title); |
183 | slsmg_write_nstring(title, self->width); | ||
184 | 198 | ||
185 | ui_browser__add_exit_keys(self, keys); | 199 | ui_browser__add_exit_keys(self, keys); |
186 | newtFormAddComponent(self->form, self->sb); | 200 | newtFormAddComponent(self->form, self->sb); |
@@ -188,25 +202,30 @@ int ui_browser__show(struct ui_browser *self, const char *title, | |||
188 | va_start(ap, helpline); | 202 | va_start(ap, helpline); |
189 | ui_helpline__vpush(helpline, ap); | 203 | ui_helpline__vpush(helpline, ap); |
190 | va_end(ap); | 204 | va_end(ap); |
205 | pthread_mutex_unlock(&ui__lock); | ||
191 | return 0; | 206 | return 0; |
192 | } | 207 | } |
193 | 208 | ||
194 | void ui_browser__hide(struct ui_browser *self) | 209 | void ui_browser__hide(struct ui_browser *self) |
195 | { | 210 | { |
211 | pthread_mutex_lock(&ui__lock); | ||
196 | newtFormDestroy(self->form); | 212 | newtFormDestroy(self->form); |
197 | self->form = NULL; | 213 | self->form = NULL; |
198 | ui_helpline__pop(); | 214 | ui_helpline__pop(); |
215 | pthread_mutex_unlock(&ui__lock); | ||
199 | } | 216 | } |
200 | 217 | ||
201 | int ui_browser__refresh(struct ui_browser *self) | 218 | int ui_browser__refresh(struct ui_browser *self) |
202 | { | 219 | { |
203 | int row; | 220 | int row; |
204 | 221 | ||
222 | pthread_mutex_lock(&ui__lock); | ||
205 | newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); | 223 | newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); |
206 | row = self->refresh(self); | 224 | row = self->refresh(self); |
207 | ui_browser__set_color(self, HE_COLORSET_NORMAL); | 225 | ui_browser__set_color(self, HE_COLORSET_NORMAL); |
208 | SLsmg_fill_region(self->y + row, self->x, | 226 | SLsmg_fill_region(self->y + row, self->x, |
209 | self->height - row, self->width, ' '); | 227 | self->height - row, self->width, ' '); |
228 | pthread_mutex_unlock(&ui__lock); | ||
210 | 229 | ||
211 | return 0; | 230 | return 0; |
212 | } | 231 | } |
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index 0dc7e4da36f5..fc63dda10910 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h | |||
@@ -24,7 +24,6 @@ struct ui_browser { | |||
24 | u32 nr_entries; | 24 | u32 nr_entries; |
25 | }; | 25 | }; |
26 | 26 | ||
27 | |||
28 | void ui_browser__set_color(struct ui_browser *self, int color); | 27 | void ui_browser__set_color(struct ui_browser *self, int color); |
29 | void ui_browser__set_percent_color(struct ui_browser *self, | 28 | void ui_browser__set_percent_color(struct ui_browser *self, |
30 | double percent, bool current); | 29 | double percent, bool current); |
@@ -35,6 +34,8 @@ void ui_browser__reset_index(struct ui_browser *self); | |||
35 | void ui_browser__gotorc(struct ui_browser *self, int y, int x); | 34 | void ui_browser__gotorc(struct ui_browser *self, int y, int x); |
36 | void ui_browser__add_exit_key(struct ui_browser *self, int key); | 35 | void ui_browser__add_exit_key(struct ui_browser *self, int key); |
37 | void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]); | 36 | void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]); |
37 | void __ui_browser__show_title(struct ui_browser *browser, const char *title); | ||
38 | void ui_browser__show_title(struct ui_browser *browser, const char *title); | ||
38 | int ui_browser__show(struct ui_browser *self, const char *title, | 39 | int ui_browser__show(struct ui_browser *self, const char *title, |
39 | const char *helpline, ...); | 40 | const char *helpline, ...); |
40 | void ui_browser__hide(struct ui_browser *self); | 41 | void ui_browser__hide(struct ui_browser *self); |
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 82b78f99251b..15633d608133 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c | |||
@@ -1,9 +1,12 @@ | |||
1 | #include "../browser.h" | 1 | #include "../browser.h" |
2 | #include "../helpline.h" | 2 | #include "../helpline.h" |
3 | #include "../libslang.h" | 3 | #include "../libslang.h" |
4 | #include "../../annotate.h" | ||
4 | #include "../../hist.h" | 5 | #include "../../hist.h" |
5 | #include "../../sort.h" | 6 | #include "../../sort.h" |
6 | #include "../../symbol.h" | 7 | #include "../../symbol.h" |
8 | #include "../../annotate.h" | ||
9 | #include <pthread.h> | ||
7 | 10 | ||
8 | static void ui__error_window(const char *fmt, ...) | 11 | static void ui__error_window(const char *fmt, ...) |
9 | { | 12 | { |
@@ -42,8 +45,6 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
42 | struct objdump_line_rb_node *olrb = objdump_line__rb(ol); | 45 | struct objdump_line_rb_node *olrb = objdump_line__rb(ol); |
43 | ui_browser__set_percent_color(self, olrb->percent, current_entry); | 46 | ui_browser__set_percent_color(self, olrb->percent, current_entry); |
44 | slsmg_printf(" %7.2f ", olrb->percent); | 47 | slsmg_printf(" %7.2f ", olrb->percent); |
45 | if (!current_entry) | ||
46 | ui_browser__set_color(self, HE_COLORSET_CODE); | ||
47 | } else { | 48 | } else { |
48 | ui_browser__set_percent_color(self, 0, current_entry); | 49 | ui_browser__set_percent_color(self, 0, current_entry); |
49 | slsmg_write_nstring(" ", 9); | 50 | slsmg_write_nstring(" ", 9); |
@@ -55,35 +56,40 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
55 | slsmg_write_nstring(" ", width - 18); | 56 | slsmg_write_nstring(" ", width - 18); |
56 | else | 57 | else |
57 | slsmg_write_nstring(ol->line, width - 18); | 58 | slsmg_write_nstring(ol->line, width - 18); |
59 | |||
60 | if (!current_entry) | ||
61 | ui_browser__set_color(self, HE_COLORSET_CODE); | ||
58 | } | 62 | } |
59 | 63 | ||
60 | static double objdump_line__calc_percent(struct objdump_line *self, | 64 | static double objdump_line__calc_percent(struct objdump_line *self, |
61 | struct list_head *head, | 65 | struct symbol *sym, int evidx) |
62 | struct symbol *sym) | ||
63 | { | 66 | { |
64 | double percent = 0.0; | 67 | double percent = 0.0; |
65 | 68 | ||
66 | if (self->offset != -1) { | 69 | if (self->offset != -1) { |
67 | int len = sym->end - sym->start; | 70 | int len = sym->end - sym->start; |
68 | unsigned int hits = 0; | 71 | unsigned int hits = 0; |
69 | struct sym_priv *priv = symbol__priv(sym); | 72 | struct annotation *notes = symbol__annotation(sym); |
70 | struct sym_ext *sym_ext = priv->ext; | 73 | struct source_line *src_line = notes->src->lines; |
71 | struct sym_hist *h = priv->hist; | 74 | struct sym_hist *h = annotation__histogram(notes, evidx); |
72 | s64 offset = self->offset; | 75 | s64 offset = self->offset; |
73 | struct objdump_line *next = objdump__get_next_ip_line(head, self); | 76 | struct objdump_line *next; |
74 | |||
75 | 77 | ||
78 | next = objdump__get_next_ip_line(¬es->src->source, self); | ||
76 | while (offset < (s64)len && | 79 | while (offset < (s64)len && |
77 | (next == NULL || offset < next->offset)) { | 80 | (next == NULL || offset < next->offset)) { |
78 | if (sym_ext) { | 81 | if (src_line) { |
79 | percent += sym_ext[offset].percent; | 82 | percent += src_line[offset].percent; |
80 | } else | 83 | } else |
81 | hits += h->ip[offset]; | 84 | hits += h->addr[offset]; |
82 | 85 | ||
83 | ++offset; | 86 | ++offset; |
84 | } | 87 | } |
85 | 88 | /* | |
86 | if (sym_ext == NULL && h->sum) | 89 | * If the percentage wasn't already calculated in |
90 | * symbol__get_source_line, do it now: | ||
91 | */ | ||
92 | if (src_line == NULL && h->sum) | ||
87 | percent = 100.0 * hits / h->sum; | 93 | percent = 100.0 * hits / h->sum; |
88 | } | 94 | } |
89 | 95 | ||
@@ -133,103 +139,163 @@ static void annotate_browser__set_top(struct annotate_browser *self, | |||
133 | self->curr_hot = nd; | 139 | self->curr_hot = nd; |
134 | } | 140 | } |
135 | 141 | ||
136 | static int annotate_browser__run(struct annotate_browser *self) | 142 | static void annotate_browser__calc_percent(struct annotate_browser *browser, |
143 | int evidx) | ||
137 | { | 144 | { |
138 | struct rb_node *nd; | 145 | struct symbol *sym = browser->b.priv; |
139 | struct hist_entry *he = self->b.priv; | 146 | struct annotation *notes = symbol__annotation(sym); |
140 | int key; | 147 | struct objdump_line *pos; |
141 | 148 | ||
142 | if (ui_browser__show(&self->b, he->ms.sym->name, | 149 | browser->entries = RB_ROOT; |
143 | "<-, -> or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0) | 150 | |
144 | return -1; | 151 | pthread_mutex_lock(¬es->lock); |
152 | |||
153 | list_for_each_entry(pos, ¬es->src->source, node) { | ||
154 | struct objdump_line_rb_node *rbpos = objdump_line__rb(pos); | ||
155 | rbpos->percent = objdump_line__calc_percent(pos, sym, evidx); | ||
156 | if (rbpos->percent < 0.01) { | ||
157 | RB_CLEAR_NODE(&rbpos->rb_node); | ||
158 | continue; | ||
159 | } | ||
160 | objdump__insert_line(&browser->entries, rbpos); | ||
161 | } | ||
162 | pthread_mutex_unlock(¬es->lock); | ||
163 | |||
164 | browser->curr_hot = rb_last(&browser->entries); | ||
165 | } | ||
166 | |||
167 | static int annotate_browser__run(struct annotate_browser *self, int evidx, | ||
168 | int refresh) | ||
169 | { | ||
170 | struct rb_node *nd = NULL; | ||
171 | struct symbol *sym = self->b.priv; | ||
145 | /* | 172 | /* |
146 | * To allow builtin-annotate to cycle thru multiple symbols by | 173 | * RIGHT To allow builtin-annotate to cycle thru multiple symbols by |
147 | * examining the exit key for this function. | 174 | * examining the exit key for this function. |
148 | */ | 175 | */ |
149 | ui_browser__add_exit_key(&self->b, NEWT_KEY_RIGHT); | 176 | int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB, |
177 | NEWT_KEY_RIGHT, 0 }; | ||
178 | int key; | ||
179 | |||
180 | if (ui_browser__show(&self->b, sym->name, | ||
181 | "<-, -> or ESC: exit, TAB/shift+TAB: " | ||
182 | "cycle hottest lines, H: Hottest") < 0) | ||
183 | return -1; | ||
184 | |||
185 | ui_browser__add_exit_keys(&self->b, exit_keys); | ||
186 | annotate_browser__calc_percent(self, evidx); | ||
187 | |||
188 | if (self->curr_hot) | ||
189 | annotate_browser__set_top(self, self->curr_hot); | ||
150 | 190 | ||
151 | nd = self->curr_hot; | 191 | nd = self->curr_hot; |
152 | if (nd) { | 192 | |
153 | int tabs[] = { NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0 }; | 193 | if (refresh != 0) |
154 | ui_browser__add_exit_keys(&self->b, tabs); | 194 | newtFormSetTimer(self->b.form, refresh); |
155 | } | ||
156 | 195 | ||
157 | while (1) { | 196 | while (1) { |
158 | key = ui_browser__run(&self->b); | 197 | key = ui_browser__run(&self->b); |
159 | 198 | ||
199 | if (refresh != 0) { | ||
200 | annotate_browser__calc_percent(self, evidx); | ||
201 | /* | ||
202 | * Current line focus got out of the list of most active | ||
203 | * lines, NULL it so that if TAB|UNTAB is pressed, we | ||
204 | * move to curr_hot (current hottest line). | ||
205 | */ | ||
206 | if (nd != NULL && RB_EMPTY_NODE(nd)) | ||
207 | nd = NULL; | ||
208 | } | ||
209 | |||
160 | switch (key) { | 210 | switch (key) { |
211 | case -1: | ||
212 | /* | ||
213 | * FIXME we need to check if it was | ||
214 | * es.reason == NEWT_EXIT_TIMER | ||
215 | */ | ||
216 | if (refresh != 0) | ||
217 | symbol__annotate_decay_histogram(sym, evidx); | ||
218 | continue; | ||
161 | case NEWT_KEY_TAB: | 219 | case NEWT_KEY_TAB: |
162 | nd = rb_prev(nd); | 220 | if (nd != NULL) { |
163 | if (nd == NULL) | 221 | nd = rb_prev(nd); |
164 | nd = rb_last(&self->entries); | 222 | if (nd == NULL) |
165 | annotate_browser__set_top(self, nd); | 223 | nd = rb_last(&self->entries); |
224 | } else | ||
225 | nd = self->curr_hot; | ||
166 | break; | 226 | break; |
167 | case NEWT_KEY_UNTAB: | 227 | case NEWT_KEY_UNTAB: |
168 | nd = rb_next(nd); | 228 | if (nd != NULL) |
169 | if (nd == NULL) | 229 | nd = rb_next(nd); |
170 | nd = rb_first(&self->entries); | 230 | if (nd == NULL) |
171 | annotate_browser__set_top(self, nd); | 231 | nd = rb_first(&self->entries); |
232 | else | ||
233 | nd = self->curr_hot; | ||
234 | break; | ||
235 | case 'H': | ||
236 | nd = self->curr_hot; | ||
172 | break; | 237 | break; |
173 | default: | 238 | default: |
174 | goto out; | 239 | goto out; |
175 | } | 240 | } |
241 | |||
242 | if (nd != NULL) | ||
243 | annotate_browser__set_top(self, nd); | ||
176 | } | 244 | } |
177 | out: | 245 | out: |
178 | ui_browser__hide(&self->b); | 246 | ui_browser__hide(&self->b); |
179 | return key; | 247 | return key; |
180 | } | 248 | } |
181 | 249 | ||
182 | int hist_entry__tui_annotate(struct hist_entry *self) | 250 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx) |
251 | { | ||
252 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0); | ||
253 | } | ||
254 | |||
255 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | ||
256 | int refresh) | ||
183 | { | 257 | { |
184 | struct objdump_line *pos, *n; | 258 | struct objdump_line *pos, *n; |
185 | struct objdump_line_rb_node *rbpos; | 259 | struct annotation *notes; |
186 | LIST_HEAD(head); | ||
187 | struct annotate_browser browser = { | 260 | struct annotate_browser browser = { |
188 | .b = { | 261 | .b = { |
189 | .entries = &head, | ||
190 | .refresh = ui_browser__list_head_refresh, | 262 | .refresh = ui_browser__list_head_refresh, |
191 | .seek = ui_browser__list_head_seek, | 263 | .seek = ui_browser__list_head_seek, |
192 | .write = annotate_browser__write, | 264 | .write = annotate_browser__write, |
193 | .priv = self, | 265 | .priv = sym, |
194 | }, | 266 | }, |
195 | }; | 267 | }; |
196 | int ret; | 268 | int ret; |
197 | 269 | ||
198 | if (self->ms.sym == NULL) | 270 | if (sym == NULL) |
199 | return -1; | 271 | return -1; |
200 | 272 | ||
201 | if (self->ms.map->dso->annotate_warned) | 273 | if (map->dso->annotate_warned) |
202 | return -1; | 274 | return -1; |
203 | 275 | ||
204 | if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) { | 276 | if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) { |
205 | ui__error_window(ui_helpline__last_msg); | 277 | ui__error_window(ui_helpline__last_msg); |
206 | return -1; | 278 | return -1; |
207 | } | 279 | } |
208 | 280 | ||
209 | ui_helpline__push("Press <- or ESC to exit"); | 281 | ui_helpline__push("Press <- or ESC to exit"); |
210 | 282 | ||
211 | list_for_each_entry(pos, &head, node) { | 283 | notes = symbol__annotation(sym); |
284 | |||
285 | list_for_each_entry(pos, ¬es->src->source, node) { | ||
286 | struct objdump_line_rb_node *rbpos; | ||
212 | size_t line_len = strlen(pos->line); | 287 | size_t line_len = strlen(pos->line); |
288 | |||
213 | if (browser.b.width < line_len) | 289 | if (browser.b.width < line_len) |
214 | browser.b.width = line_len; | 290 | browser.b.width = line_len; |
215 | rbpos = objdump_line__rb(pos); | 291 | rbpos = objdump_line__rb(pos); |
216 | rbpos->idx = browser.b.nr_entries++; | 292 | rbpos->idx = browser.b.nr_entries++; |
217 | rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym); | ||
218 | if (rbpos->percent < 0.01) | ||
219 | continue; | ||
220 | objdump__insert_line(&browser.entries, rbpos); | ||
221 | } | 293 | } |
222 | 294 | ||
223 | /* | 295 | browser.b.entries = ¬es->src->source, |
224 | * Position the browser at the hottest line. | ||
225 | */ | ||
226 | browser.curr_hot = rb_last(&browser.entries); | ||
227 | if (browser.curr_hot) | ||
228 | annotate_browser__set_top(&browser, browser.curr_hot); | ||
229 | |||
230 | browser.b.width += 18; /* Percentage */ | 296 | browser.b.width += 18; /* Percentage */ |
231 | ret = annotate_browser__run(&browser); | 297 | ret = annotate_browser__run(&browser, evidx, refresh); |
232 | list_for_each_entry_safe(pos, n, &head, node) { | 298 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { |
233 | list_del(&pos->node); | 299 | list_del(&pos->node); |
234 | objdump_line__free(pos); | 300 | objdump_line__free(pos); |
235 | } | 301 | } |
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index ebda8c3fde9e..5d767c622dfc 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
@@ -7,6 +7,8 @@ | |||
7 | #include <newt.h> | 7 | #include <newt.h> |
8 | #include <linux/rbtree.h> | 8 | #include <linux/rbtree.h> |
9 | 9 | ||
10 | #include "../../evsel.h" | ||
11 | #include "../../evlist.h" | ||
10 | #include "../../hist.h" | 12 | #include "../../hist.h" |
11 | #include "../../pstack.h" | 13 | #include "../../pstack.h" |
12 | #include "../../sort.h" | 14 | #include "../../sort.h" |
@@ -292,7 +294,8 @@ static int hist_browser__run(struct hist_browser *self, const char *title) | |||
292 | { | 294 | { |
293 | int key; | 295 | int key; |
294 | int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't', | 296 | int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't', |
295 | NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, 0, }; | 297 | NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, |
298 | NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0, }; | ||
296 | 299 | ||
297 | self->b.entries = &self->hists->entries; | 300 | self->b.entries = &self->hists->entries; |
298 | self->b.nr_entries = self->hists->nr_entries; | 301 | self->b.nr_entries = self->hists->nr_entries; |
@@ -350,7 +353,7 @@ static char *callchain_list__sym_name(struct callchain_list *self, | |||
350 | if (self->ms.sym) | 353 | if (self->ms.sym) |
351 | return self->ms.sym->name; | 354 | return self->ms.sym->name; |
352 | 355 | ||
353 | snprintf(bf, bfsize, "%#Lx", self->ip); | 356 | snprintf(bf, bfsize, "%#" PRIx64, self->ip); |
354 | return bf; | 357 | return bf; |
355 | } | 358 | } |
356 | 359 | ||
@@ -377,7 +380,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, | |||
377 | while (node) { | 380 | while (node) { |
378 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | 381 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); |
379 | struct rb_node *next = rb_next(node); | 382 | struct rb_node *next = rb_next(node); |
380 | u64 cumul = cumul_hits(child); | 383 | u64 cumul = callchain_cumul_hits(child); |
381 | struct callchain_list *chain; | 384 | struct callchain_list *chain; |
382 | char folded_sign = ' '; | 385 | char folded_sign = ' '; |
383 | int first = true; | 386 | int first = true; |
@@ -638,6 +641,9 @@ static void ui_browser__hists_seek(struct ui_browser *self, | |||
638 | struct rb_node *nd; | 641 | struct rb_node *nd; |
639 | bool first = true; | 642 | bool first = true; |
640 | 643 | ||
644 | if (self->nr_entries == 0) | ||
645 | return; | ||
646 | |||
641 | switch (whence) { | 647 | switch (whence) { |
642 | case SEEK_SET: | 648 | case SEEK_SET: |
643 | nd = hists__filter_entries(rb_first(self->entries)); | 649 | nd = hists__filter_entries(rb_first(self->entries)); |
@@ -797,8 +803,11 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, | |||
797 | return printed; | 803 | return printed; |
798 | } | 804 | } |
799 | 805 | ||
800 | int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | 806 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, |
807 | const char *helpline, const char *ev_name, | ||
808 | bool left_exits) | ||
801 | { | 809 | { |
810 | struct hists *self = &evsel->hists; | ||
802 | struct hist_browser *browser = hist_browser__new(self); | 811 | struct hist_browser *browser = hist_browser__new(self); |
803 | struct pstack *fstack; | 812 | struct pstack *fstack; |
804 | const struct thread *thread_filter = NULL; | 813 | const struct thread *thread_filter = NULL; |
@@ -818,8 +827,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
818 | hists__browser_title(self, msg, sizeof(msg), ev_name, | 827 | hists__browser_title(self, msg, sizeof(msg), ev_name, |
819 | dso_filter, thread_filter); | 828 | dso_filter, thread_filter); |
820 | while (1) { | 829 | while (1) { |
821 | const struct thread *thread; | 830 | const struct thread *thread = NULL; |
822 | const struct dso *dso; | 831 | const struct dso *dso = NULL; |
823 | char *options[16]; | 832 | char *options[16]; |
824 | int nr_options = 0, choice = 0, i, | 833 | int nr_options = 0, choice = 0, i, |
825 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | 834 | annotate = -2, zoom_dso = -2, zoom_thread = -2, |
@@ -827,8 +836,10 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
827 | 836 | ||
828 | key = hist_browser__run(browser, msg); | 837 | key = hist_browser__run(browser, msg); |
829 | 838 | ||
830 | thread = hist_browser__selected_thread(browser); | 839 | if (browser->he_selection != NULL) { |
831 | dso = browser->selection->map ? browser->selection->map->dso : NULL; | 840 | thread = hist_browser__selected_thread(browser); |
841 | dso = browser->selection->map ? browser->selection->map->dso : NULL; | ||
842 | } | ||
832 | 843 | ||
833 | switch (key) { | 844 | switch (key) { |
834 | case NEWT_KEY_TAB: | 845 | case NEWT_KEY_TAB: |
@@ -839,7 +850,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
839 | */ | 850 | */ |
840 | goto out_free_stack; | 851 | goto out_free_stack; |
841 | case 'a': | 852 | case 'a': |
842 | if (browser->selection->map == NULL && | 853 | if (browser->selection == NULL || |
854 | browser->selection->sym == NULL || | ||
843 | browser->selection->map->dso->annotate_warned) | 855 | browser->selection->map->dso->annotate_warned) |
844 | continue; | 856 | continue; |
845 | goto do_annotate; | 857 | goto do_annotate; |
@@ -858,6 +870,7 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
858 | "E Expand all callchains\n" | 870 | "E Expand all callchains\n" |
859 | "d Zoom into current DSO\n" | 871 | "d Zoom into current DSO\n" |
860 | "t Zoom into current Thread\n" | 872 | "t Zoom into current Thread\n" |
873 | "TAB/UNTAB Switch events\n" | ||
861 | "q/CTRL+C Exit browser"); | 874 | "q/CTRL+C Exit browser"); |
862 | continue; | 875 | continue; |
863 | case NEWT_KEY_ENTER: | 876 | case NEWT_KEY_ENTER: |
@@ -867,8 +880,14 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
867 | case NEWT_KEY_LEFT: { | 880 | case NEWT_KEY_LEFT: { |
868 | const void *top; | 881 | const void *top; |
869 | 882 | ||
870 | if (pstack__empty(fstack)) | 883 | if (pstack__empty(fstack)) { |
884 | /* | ||
885 | * Go back to the perf_evsel_menu__run or other user | ||
886 | */ | ||
887 | if (left_exits) | ||
888 | goto out_free_stack; | ||
871 | continue; | 889 | continue; |
890 | } | ||
872 | top = pstack__pop(fstack); | 891 | top = pstack__pop(fstack); |
873 | if (top == &dso_filter) | 892 | if (top == &dso_filter) |
874 | goto zoom_out_dso; | 893 | goto zoom_out_dso; |
@@ -877,14 +896,16 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
877 | continue; | 896 | continue; |
878 | } | 897 | } |
879 | case NEWT_KEY_ESCAPE: | 898 | case NEWT_KEY_ESCAPE: |
880 | if (!ui__dialog_yesno("Do you really want to exit?")) | 899 | if (!left_exits && |
900 | !ui__dialog_yesno("Do you really want to exit?")) | ||
881 | continue; | 901 | continue; |
882 | /* Fall thru */ | 902 | /* Fall thru */ |
883 | default: | 903 | default: |
884 | goto out_free_stack; | 904 | goto out_free_stack; |
885 | } | 905 | } |
886 | 906 | ||
887 | if (browser->selection->sym != NULL && | 907 | if (browser->selection != NULL && |
908 | browser->selection->sym != NULL && | ||
888 | !browser->selection->map->dso->annotate_warned && | 909 | !browser->selection->map->dso->annotate_warned && |
889 | asprintf(&options[nr_options], "Annotate %s", | 910 | asprintf(&options[nr_options], "Annotate %s", |
890 | browser->selection->sym->name) > 0) | 911 | browser->selection->sym->name) > 0) |
@@ -903,7 +924,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
903 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) | 924 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) |
904 | zoom_dso = nr_options++; | 925 | zoom_dso = nr_options++; |
905 | 926 | ||
906 | if (browser->selection->map != NULL && | 927 | if (browser->selection != NULL && |
928 | browser->selection->map != NULL && | ||
907 | asprintf(&options[nr_options], "Browse map details") > 0) | 929 | asprintf(&options[nr_options], "Browse map details") > 0) |
908 | browse_map = nr_options++; | 930 | browse_map = nr_options++; |
909 | 931 | ||
@@ -923,19 +945,11 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
923 | if (choice == annotate) { | 945 | if (choice == annotate) { |
924 | struct hist_entry *he; | 946 | struct hist_entry *he; |
925 | do_annotate: | 947 | do_annotate: |
926 | if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) { | ||
927 | browser->selection->map->dso->annotate_warned = 1; | ||
928 | ui_helpline__puts("No vmlinux file found, can't " | ||
929 | "annotate with just a " | ||
930 | "kallsyms file"); | ||
931 | continue; | ||
932 | } | ||
933 | |||
934 | he = hist_browser__selected_entry(browser); | 948 | he = hist_browser__selected_entry(browser); |
935 | if (he == NULL) | 949 | if (he == NULL) |
936 | continue; | 950 | continue; |
937 | 951 | ||
938 | hist_entry__tui_annotate(he); | 952 | hist_entry__tui_annotate(he, evsel->idx); |
939 | } else if (choice == browse_map) | 953 | } else if (choice == browse_map) |
940 | map__browse(browser->selection->map); | 954 | map__browse(browser->selection->map); |
941 | else if (choice == zoom_dso) { | 955 | else if (choice == zoom_dso) { |
@@ -984,30 +998,141 @@ out: | |||
984 | return key; | 998 | return key; |
985 | } | 999 | } |
986 | 1000 | ||
987 | int hists__tui_browse_tree(struct rb_root *self, const char *help) | 1001 | struct perf_evsel_menu { |
1002 | struct ui_browser b; | ||
1003 | struct perf_evsel *selection; | ||
1004 | }; | ||
1005 | |||
1006 | static void perf_evsel_menu__write(struct ui_browser *browser, | ||
1007 | void *entry, int row) | ||
1008 | { | ||
1009 | struct perf_evsel_menu *menu = container_of(browser, | ||
1010 | struct perf_evsel_menu, b); | ||
1011 | struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); | ||
1012 | bool current_entry = ui_browser__is_current_entry(browser, row); | ||
1013 | unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | ||
1014 | const char *ev_name = event_name(evsel); | ||
1015 | char bf[256], unit; | ||
1016 | |||
1017 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : | ||
1018 | HE_COLORSET_NORMAL); | ||
1019 | |||
1020 | nr_events = convert_unit(nr_events, &unit); | ||
1021 | snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, | ||
1022 | unit, unit == ' ' ? "" : " ", ev_name); | ||
1023 | slsmg_write_nstring(bf, browser->width); | ||
1024 | |||
1025 | if (current_entry) | ||
1026 | menu->selection = evsel; | ||
1027 | } | ||
1028 | |||
1029 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) | ||
988 | { | 1030 | { |
989 | struct rb_node *first = rb_first(self), *nd = first, *next; | 1031 | int exit_keys[] = { NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; |
990 | int key = 0; | 1032 | struct perf_evlist *evlist = menu->b.priv; |
1033 | struct perf_evsel *pos; | ||
1034 | const char *ev_name, *title = "Available samples"; | ||
1035 | int key; | ||
1036 | |||
1037 | if (ui_browser__show(&menu->b, title, | ||
1038 | "ESC: exit, ENTER|->: Browse histograms") < 0) | ||
1039 | return -1; | ||
1040 | |||
1041 | ui_browser__add_exit_keys(&menu->b, exit_keys); | ||
991 | 1042 | ||
992 | while (nd) { | 1043 | while (1) { |
993 | struct hists *hists = rb_entry(nd, struct hists, rb_node); | 1044 | key = ui_browser__run(&menu->b); |
994 | const char *ev_name = __event_name(hists->type, hists->config); | ||
995 | 1045 | ||
996 | key = hists__browse(hists, help, ev_name); | ||
997 | switch (key) { | 1046 | switch (key) { |
998 | case NEWT_KEY_TAB: | 1047 | case NEWT_KEY_RIGHT: |
999 | next = rb_next(nd); | 1048 | case NEWT_KEY_ENTER: |
1000 | if (next) | 1049 | if (!menu->selection) |
1001 | nd = next; | 1050 | continue; |
1051 | pos = menu->selection; | ||
1052 | browse_hists: | ||
1053 | ev_name = event_name(pos); | ||
1054 | key = perf_evsel__hists_browse(pos, help, ev_name, true); | ||
1055 | ui_browser__show_title(&menu->b, title); | ||
1002 | break; | 1056 | break; |
1003 | case NEWT_KEY_UNTAB: | 1057 | case NEWT_KEY_LEFT: |
1004 | if (nd == first) | 1058 | continue; |
1059 | case NEWT_KEY_ESCAPE: | ||
1060 | if (!ui__dialog_yesno("Do you really want to exit?")) | ||
1005 | continue; | 1061 | continue; |
1006 | nd = rb_prev(nd); | 1062 | /* Fall thru */ |
1063 | default: | ||
1064 | goto out; | ||
1065 | } | ||
1066 | |||
1067 | switch (key) { | ||
1068 | case NEWT_KEY_TAB: | ||
1069 | if (pos->node.next == &evlist->entries) | ||
1070 | pos = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
1071 | else | ||
1072 | pos = list_entry(pos->node.next, struct perf_evsel, node); | ||
1073 | goto browse_hists; | ||
1074 | case NEWT_KEY_UNTAB: | ||
1075 | if (pos->node.prev == &evlist->entries) | ||
1076 | pos = list_entry(evlist->entries.prev, struct perf_evsel, node); | ||
1077 | else | ||
1078 | pos = list_entry(pos->node.prev, struct perf_evsel, node); | ||
1079 | goto browse_hists; | ||
1080 | case 'q': | ||
1081 | case CTRL('c'): | ||
1082 | goto out; | ||
1007 | default: | 1083 | default: |
1008 | return key; | 1084 | break; |
1009 | } | 1085 | } |
1010 | } | 1086 | } |
1011 | 1087 | ||
1088 | out: | ||
1089 | ui_browser__hide(&menu->b); | ||
1012 | return key; | 1090 | return key; |
1013 | } | 1091 | } |
1092 | |||
1093 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | ||
1094 | const char *help) | ||
1095 | { | ||
1096 | struct perf_evsel *pos; | ||
1097 | struct perf_evsel_menu menu = { | ||
1098 | .b = { | ||
1099 | .entries = &evlist->entries, | ||
1100 | .refresh = ui_browser__list_head_refresh, | ||
1101 | .seek = ui_browser__list_head_seek, | ||
1102 | .write = perf_evsel_menu__write, | ||
1103 | .nr_entries = evlist->nr_entries, | ||
1104 | .priv = evlist, | ||
1105 | }, | ||
1106 | }; | ||
1107 | |||
1108 | ui_helpline__push("Press ESC to exit"); | ||
1109 | |||
1110 | list_for_each_entry(pos, &evlist->entries, node) { | ||
1111 | const char *ev_name = event_name(pos); | ||
1112 | size_t line_len = strlen(ev_name) + 7; | ||
1113 | |||
1114 | if (menu.b.width < line_len) | ||
1115 | menu.b.width = line_len; | ||
1116 | /* | ||
1117 | * Cache the evsel name, tracepoints have a _high_ cost per | ||
1118 | * event_name() call. | ||
1119 | */ | ||
1120 | if (pos->name == NULL) | ||
1121 | pos->name = strdup(ev_name); | ||
1122 | } | ||
1123 | |||
1124 | return perf_evsel_menu__run(&menu, help); | ||
1125 | } | ||
1126 | |||
1127 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help) | ||
1128 | { | ||
1129 | |||
1130 | if (evlist->nr_entries == 1) { | ||
1131 | struct perf_evsel *first = list_entry(evlist->entries.next, | ||
1132 | struct perf_evsel, node); | ||
1133 | const char *ev_name = event_name(first); | ||
1134 | return perf_evsel__hists_browse(first, help, ev_name, false); | ||
1135 | } | ||
1136 | |||
1137 | return __perf_evlist__tui_browse_hists(evlist, help); | ||
1138 | } | ||
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c index e35437dfa5b4..8462bffe20bc 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/util/ui/browsers/map.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #include "../libslang.h" | 1 | #include "../libslang.h" |
2 | #include <elf.h> | 2 | #include <elf.h> |
3 | #include <inttypes.h> | ||
3 | #include <sys/ttydefaults.h> | 4 | #include <sys/ttydefaults.h> |
4 | #include <ctype.h> | 5 | #include <ctype.h> |
5 | #include <string.h> | 6 | #include <string.h> |
@@ -40,7 +41,7 @@ static int ui_entry__read(const char *title, char *bf, size_t size, int width) | |||
40 | out_free_form: | 41 | out_free_form: |
41 | newtPopWindow(); | 42 | newtPopWindow(); |
42 | newtFormDestroy(form); | 43 | newtFormDestroy(form); |
43 | return 0; | 44 | return err; |
44 | } | 45 | } |
45 | 46 | ||
46 | struct map_browser { | 47 | struct map_browser { |
@@ -57,7 +58,7 @@ static void map_browser__write(struct ui_browser *self, void *nd, int row) | |||
57 | int width; | 58 | int width; |
58 | 59 | ||
59 | ui_browser__set_percent_color(self, 0, current_entry); | 60 | ui_browser__set_percent_color(self, 0, current_entry); |
60 | slsmg_printf("%*llx %*llx %c ", | 61 | slsmg_printf("%*" PRIx64 " %*" PRIx64 " %c ", |
61 | mb->addrlen, sym->start, mb->addrlen, sym->end, | 62 | mb->addrlen, sym->start, mb->addrlen, sym->end, |
62 | sym->binding == STB_GLOBAL ? 'g' : | 63 | sym->binding == STB_GLOBAL ? 'g' : |
63 | sym->binding == STB_LOCAL ? 'l' : 'w'); | 64 | sym->binding == STB_LOCAL ? 'l' : 'w'); |
@@ -150,6 +151,6 @@ int map__browse(struct map *self) | |||
150 | ++mb.b.nr_entries; | 151 | ++mb.b.nr_entries; |
151 | } | 152 | } |
152 | 153 | ||
153 | mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr); | 154 | mb.addrlen = snprintf(tmp, sizeof(tmp), "%" PRIx64, maxaddr); |
154 | return map_browser__run(&mb); | 155 | return map_browser__run(&mb); |
155 | } | 156 | } |
diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c new file mode 100644 index 000000000000..5a06538532af --- /dev/null +++ b/tools/perf/util/ui/browsers/top.c | |||
@@ -0,0 +1,213 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Parts came from builtin-{top,stat,record}.c, see those files for further | ||
5 | * copyright notes. | ||
6 | * | ||
7 | * Released under the GPL v2. (and only v2, not any later version) | ||
8 | */ | ||
9 | #include "../browser.h" | ||
10 | #include "../../annotate.h" | ||
11 | #include "../helpline.h" | ||
12 | #include "../libslang.h" | ||
13 | #include "../util.h" | ||
14 | #include "../../evlist.h" | ||
15 | #include "../../hist.h" | ||
16 | #include "../../sort.h" | ||
17 | #include "../../symbol.h" | ||
18 | #include "../../top.h" | ||
19 | |||
20 | struct perf_top_browser { | ||
21 | struct ui_browser b; | ||
22 | struct rb_root root; | ||
23 | struct sym_entry *selection; | ||
24 | float sum_ksamples; | ||
25 | int dso_width; | ||
26 | int dso_short_width; | ||
27 | int sym_width; | ||
28 | }; | ||
29 | |||
30 | static void perf_top_browser__write(struct ui_browser *browser, void *entry, int row) | ||
31 | { | ||
32 | struct perf_top_browser *top_browser = container_of(browser, struct perf_top_browser, b); | ||
33 | struct sym_entry *syme = rb_entry(entry, struct sym_entry, rb_node); | ||
34 | bool current_entry = ui_browser__is_current_entry(browser, row); | ||
35 | struct symbol *symbol = sym_entry__symbol(syme); | ||
36 | struct perf_top *top = browser->priv; | ||
37 | int width = browser->width; | ||
38 | double pcnt; | ||
39 | |||
40 | pcnt = 100.0 - (100.0 * ((top_browser->sum_ksamples - syme->snap_count) / | ||
41 | top_browser->sum_ksamples)); | ||
42 | ui_browser__set_percent_color(browser, pcnt, current_entry); | ||
43 | |||
44 | if (top->evlist->nr_entries == 1 || !top->display_weighted) { | ||
45 | slsmg_printf("%20.2f ", syme->weight); | ||
46 | width -= 24; | ||
47 | } else { | ||
48 | slsmg_printf("%9.1f %10ld ", syme->weight, syme->snap_count); | ||
49 | width -= 23; | ||
50 | } | ||
51 | |||
52 | slsmg_printf("%4.1f%%", pcnt); | ||
53 | width -= 7; | ||
54 | |||
55 | if (verbose) { | ||
56 | slsmg_printf(" %016" PRIx64, symbol->start); | ||
57 | width -= 17; | ||
58 | } | ||
59 | |||
60 | slsmg_printf(" %-*.*s ", top_browser->sym_width, top_browser->sym_width, | ||
61 | symbol->name); | ||
62 | width -= top_browser->sym_width; | ||
63 | slsmg_write_nstring(width >= syme->map->dso->long_name_len ? | ||
64 | syme->map->dso->long_name : | ||
65 | syme->map->dso->short_name, width); | ||
66 | |||
67 | if (current_entry) | ||
68 | top_browser->selection = syme; | ||
69 | } | ||
70 | |||
71 | static void perf_top_browser__update_rb_tree(struct perf_top_browser *browser) | ||
72 | { | ||
73 | struct perf_top *top = browser->b.priv; | ||
74 | u64 top_idx = browser->b.top_idx; | ||
75 | |||
76 | browser->root = RB_ROOT; | ||
77 | browser->b.top = NULL; | ||
78 | browser->sum_ksamples = perf_top__decay_samples(top, &browser->root); | ||
79 | /* | ||
80 | * No active symbols | ||
81 | */ | ||
82 | if (top->rb_entries == 0) | ||
83 | return; | ||
84 | |||
85 | perf_top__find_widths(top, &browser->root, &browser->dso_width, | ||
86 | &browser->dso_short_width, | ||
87 | &browser->sym_width); | ||
88 | if (browser->sym_width + browser->dso_width > browser->b.width - 29) { | ||
89 | browser->dso_width = browser->dso_short_width; | ||
90 | if (browser->sym_width + browser->dso_width > browser->b.width - 29) | ||
91 | browser->sym_width = browser->b.width - browser->dso_width - 29; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * Adjust the ui_browser indexes since the entries in the browser->root | ||
96 | * rb_tree may have changed, then seek it from start, so that we get a | ||
97 | * possible new top of the screen. | ||
98 | */ | ||
99 | browser->b.nr_entries = top->rb_entries; | ||
100 | |||
101 | if (top_idx >= browser->b.nr_entries) { | ||
102 | if (browser->b.height >= browser->b.nr_entries) | ||
103 | top_idx = browser->b.nr_entries - browser->b.height; | ||
104 | else | ||
105 | top_idx = 0; | ||
106 | } | ||
107 | |||
108 | if (browser->b.index >= top_idx + browser->b.height) | ||
109 | browser->b.index = top_idx + browser->b.index - browser->b.top_idx; | ||
110 | |||
111 | if (browser->b.index >= browser->b.nr_entries) | ||
112 | browser->b.index = browser->b.nr_entries - 1; | ||
113 | |||
114 | browser->b.top_idx = top_idx; | ||
115 | browser->b.seek(&browser->b, top_idx, SEEK_SET); | ||
116 | } | ||
117 | |||
118 | static void perf_top_browser__annotate(struct perf_top_browser *browser) | ||
119 | { | ||
120 | struct sym_entry *syme = browser->selection; | ||
121 | struct symbol *sym = sym_entry__symbol(syme); | ||
122 | struct annotation *notes = symbol__annotation(sym); | ||
123 | struct perf_top *top = browser->b.priv; | ||
124 | |||
125 | if (notes->src != NULL) | ||
126 | goto do_annotation; | ||
127 | |||
128 | pthread_mutex_lock(¬es->lock); | ||
129 | |||
130 | top->sym_filter_entry = NULL; | ||
131 | |||
132 | if (symbol__alloc_hist(sym, top->evlist->nr_entries) < 0) { | ||
133 | pr_err("Not enough memory for annotating '%s' symbol!\n", | ||
134 | sym->name); | ||
135 | pthread_mutex_unlock(¬es->lock); | ||
136 | return; | ||
137 | } | ||
138 | |||
139 | top->sym_filter_entry = syme; | ||
140 | |||
141 | pthread_mutex_unlock(¬es->lock); | ||
142 | do_annotation: | ||
143 | symbol__tui_annotate(sym, syme->map, 0, top->delay_secs * 1000); | ||
144 | } | ||
145 | |||
146 | static int perf_top_browser__run(struct perf_top_browser *browser) | ||
147 | { | ||
148 | int key; | ||
149 | char title[160]; | ||
150 | struct perf_top *top = browser->b.priv; | ||
151 | int delay_msecs = top->delay_secs * 1000; | ||
152 | int exit_keys[] = { 'a', NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; | ||
153 | |||
154 | perf_top_browser__update_rb_tree(browser); | ||
155 | perf_top__header_snprintf(top, title, sizeof(title)); | ||
156 | perf_top__reset_sample_counters(top); | ||
157 | |||
158 | if (ui_browser__show(&browser->b, title, | ||
159 | "ESC: exit, ENTER|->|a: Live Annotate") < 0) | ||
160 | return -1; | ||
161 | |||
162 | newtFormSetTimer(browser->b.form, delay_msecs); | ||
163 | ui_browser__add_exit_keys(&browser->b, exit_keys); | ||
164 | |||
165 | while (1) { | ||
166 | key = ui_browser__run(&browser->b); | ||
167 | |||
168 | switch (key) { | ||
169 | case -1: | ||
170 | /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */ | ||
171 | perf_top_browser__update_rb_tree(browser); | ||
172 | perf_top__header_snprintf(top, title, sizeof(title)); | ||
173 | perf_top__reset_sample_counters(top); | ||
174 | ui_browser__set_color(&browser->b, NEWT_COLORSET_ROOT); | ||
175 | SLsmg_gotorc(0, 0); | ||
176 | slsmg_write_nstring(title, browser->b.width); | ||
177 | break; | ||
178 | case 'a': | ||
179 | case NEWT_KEY_RIGHT: | ||
180 | case NEWT_KEY_ENTER: | ||
181 | if (browser->selection) | ||
182 | perf_top_browser__annotate(browser); | ||
183 | break; | ||
184 | case NEWT_KEY_LEFT: | ||
185 | continue; | ||
186 | case NEWT_KEY_ESCAPE: | ||
187 | if (!ui__dialog_yesno("Do you really want to exit?")) | ||
188 | continue; | ||
189 | /* Fall thru */ | ||
190 | default: | ||
191 | goto out; | ||
192 | } | ||
193 | } | ||
194 | out: | ||
195 | ui_browser__hide(&browser->b); | ||
196 | return key; | ||
197 | } | ||
198 | |||
199 | int perf_top__tui_browser(struct perf_top *top) | ||
200 | { | ||
201 | struct perf_top_browser browser = { | ||
202 | .b = { | ||
203 | .entries = &browser.root, | ||
204 | .refresh = ui_browser__rb_tree_refresh, | ||
205 | .seek = ui_browser__rb_tree_seek, | ||
206 | .write = perf_top_browser__write, | ||
207 | .priv = top, | ||
208 | }, | ||
209 | }; | ||
210 | |||
211 | ui_helpline__push("Press <- or ESC to exit"); | ||
212 | return perf_top_browser__run(&browser); | ||
213 | } | ||
diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c index 8d79daa4458a..f36d2ff509ed 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/util/ui/helpline.c | |||
@@ -5,6 +5,7 @@ | |||
5 | 5 | ||
6 | #include "../debug.h" | 6 | #include "../debug.h" |
7 | #include "helpline.h" | 7 | #include "helpline.h" |
8 | #include "ui.h" | ||
8 | 9 | ||
9 | void ui_helpline__pop(void) | 10 | void ui_helpline__pop(void) |
10 | { | 11 | { |
@@ -55,7 +56,8 @@ int ui_helpline__show_help(const char *format, va_list ap) | |||
55 | int ret; | 56 | int ret; |
56 | static int backlog; | 57 | static int backlog; |
57 | 58 | ||
58 | ret = vsnprintf(ui_helpline__last_msg + backlog, | 59 | pthread_mutex_lock(&ui__lock); |
60 | ret = vsnprintf(ui_helpline__last_msg + backlog, | ||
59 | sizeof(ui_helpline__last_msg) - backlog, format, ap); | 61 | sizeof(ui_helpline__last_msg) - backlog, format, ap); |
60 | backlog += ret; | 62 | backlog += ret; |
61 | 63 | ||
@@ -64,6 +66,7 @@ int ui_helpline__show_help(const char *format, va_list ap) | |||
64 | newtRefresh(); | 66 | newtRefresh(); |
65 | backlog = 0; | 67 | backlog = 0; |
66 | } | 68 | } |
69 | pthread_mutex_unlock(&ui__lock); | ||
67 | 70 | ||
68 | return ret; | 71 | return ret; |
69 | } | 72 | } |
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h index 5623da8e8080..2b63e1c9b181 100644 --- a/tools/perf/util/ui/libslang.h +++ b/tools/perf/util/ui/libslang.h | |||
@@ -13,11 +13,11 @@ | |||
13 | 13 | ||
14 | #if SLANG_VERSION < 20104 | 14 | #if SLANG_VERSION < 20104 |
15 | #define slsmg_printf(msg, args...) \ | 15 | #define slsmg_printf(msg, args...) \ |
16 | SLsmg_printf((char *)msg, ##args) | 16 | SLsmg_printf((char *)(msg), ##args) |
17 | #define slsmg_write_nstring(msg, len) \ | 17 | #define slsmg_write_nstring(msg, len) \ |
18 | SLsmg_write_nstring((char *)msg, len) | 18 | SLsmg_write_nstring((char *)(msg), len) |
19 | #define sltt_set_color(obj, name, fg, bg) \ | 19 | #define sltt_set_color(obj, name, fg, bg) \ |
20 | SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg) | 20 | SLtt_set_color(obj,(char *)(name), (char *)(fg), (char *)(bg)) |
21 | #else | 21 | #else |
22 | #define slsmg_printf SLsmg_printf | 22 | #define slsmg_printf SLsmg_printf |
23 | #define slsmg_write_nstring SLsmg_write_nstring | 23 | #define slsmg_write_nstring SLsmg_write_nstring |
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c index 662085032eb7..ee46d671db59 100644 --- a/tools/perf/util/ui/setup.c +++ b/tools/perf/util/ui/setup.c | |||
@@ -6,6 +6,9 @@ | |||
6 | #include "../debug.h" | 6 | #include "../debug.h" |
7 | #include "browser.h" | 7 | #include "browser.h" |
8 | #include "helpline.h" | 8 | #include "helpline.h" |
9 | #include "ui.h" | ||
10 | |||
11 | pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; | ||
9 | 12 | ||
10 | static void newt_suspend(void *d __used) | 13 | static void newt_suspend(void *d __used) |
11 | { | 14 | { |
@@ -14,11 +17,12 @@ static void newt_suspend(void *d __used) | |||
14 | newtResume(); | 17 | newtResume(); |
15 | } | 18 | } |
16 | 19 | ||
17 | void setup_browser(void) | 20 | void setup_browser(bool fallback_to_pager) |
18 | { | 21 | { |
19 | if (!isatty(1) || !use_browser || dump_trace) { | 22 | if (!isatty(1) || !use_browser || dump_trace) { |
20 | use_browser = 0; | 23 | use_browser = 0; |
21 | setup_pager(); | 24 | if (fallback_to_pager) |
25 | setup_pager(); | ||
22 | return; | 26 | return; |
23 | } | 27 | } |
24 | 28 | ||
diff --git a/tools/perf/util/ui/ui.h b/tools/perf/util/ui/ui.h new file mode 100644 index 000000000000..d264e059c829 --- /dev/null +++ b/tools/perf/util/ui/ui.h | |||
@@ -0,0 +1,8 @@ | |||
1 | #ifndef _PERF_UI_H_ | ||
2 | #define _PERF_UI_H_ 1 | ||
3 | |||
4 | #include <pthread.h> | ||
5 | |||
6 | extern pthread_mutex_t ui__lock; | ||
7 | |||
8 | #endif /* _PERF_UI_H_ */ | ||
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c index 7b5a8926624e..fdf1fc8f08bc 100644 --- a/tools/perf/util/ui/util.c +++ b/tools/perf/util/ui/util.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include "../debug.h" | 9 | #include "../debug.h" |
10 | #include "browser.h" | 10 | #include "browser.h" |
11 | #include "helpline.h" | 11 | #include "helpline.h" |
12 | #include "ui.h" | ||
12 | #include "util.h" | 13 | #include "util.h" |
13 | 14 | ||
14 | static void newt_form__set_exit_keys(newtComponent self) | 15 | static void newt_form__set_exit_keys(newtComponent self) |
@@ -118,10 +119,12 @@ void ui__warning(const char *format, ...) | |||
118 | va_list args; | 119 | va_list args; |
119 | 120 | ||
120 | va_start(args, format); | 121 | va_start(args, format); |
121 | if (use_browser > 0) | 122 | if (use_browser > 0) { |
123 | pthread_mutex_lock(&ui__lock); | ||
122 | newtWinMessagev((char *)warning_str, (char *)ok, | 124 | newtWinMessagev((char *)warning_str, (char *)ok, |
123 | (char *)format, args); | 125 | (char *)format, args); |
124 | else | 126 | pthread_mutex_unlock(&ui__lock); |
127 | } else | ||
125 | vfprintf(stderr, format, args); | 128 | vfprintf(stderr, format, args); |
126 | va_end(args); | 129 | va_end(args); |
127 | } | 130 | } |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index e833f26f3bfc..fc784284ac8b 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -70,9 +70,7 @@ | |||
70 | #include <sys/poll.h> | 70 | #include <sys/poll.h> |
71 | #include <sys/socket.h> | 71 | #include <sys/socket.h> |
72 | #include <sys/ioctl.h> | 72 | #include <sys/ioctl.h> |
73 | #ifndef NO_SYS_SELECT_H | ||
74 | #include <sys/select.h> | 73 | #include <sys/select.h> |
75 | #endif | ||
76 | #include <netinet/in.h> | 74 | #include <netinet/in.h> |
77 | #include <netinet/tcp.h> | 75 | #include <netinet/tcp.h> |
78 | #include <arpa/inet.h> | 76 | #include <arpa/inet.h> |
@@ -83,10 +81,6 @@ | |||
83 | #include "types.h" | 81 | #include "types.h" |
84 | #include <sys/ttydefaults.h> | 82 | #include <sys/ttydefaults.h> |
85 | 83 | ||
86 | #ifndef NO_ICONV | ||
87 | #include <iconv.h> | ||
88 | #endif | ||
89 | |||
90 | extern const char *graph_line; | 84 | extern const char *graph_line; |
91 | extern const char *graph_dotted_line; | 85 | extern const char *graph_dotted_line; |
92 | extern char buildid_dir[]; | 86 | extern char buildid_dir[]; |
@@ -236,26 +230,6 @@ static inline int sane_case(int x, int high) | |||
236 | return x; | 230 | return x; |
237 | } | 231 | } |
238 | 232 | ||
239 | #ifndef DIR_HAS_BSD_GROUP_SEMANTICS | ||
240 | # define FORCE_DIR_SET_GID S_ISGID | ||
241 | #else | ||
242 | # define FORCE_DIR_SET_GID 0 | ||
243 | #endif | ||
244 | |||
245 | #ifdef NO_NSEC | ||
246 | #undef USE_NSEC | ||
247 | #define ST_CTIME_NSEC(st) 0 | ||
248 | #define ST_MTIME_NSEC(st) 0 | ||
249 | #else | ||
250 | #ifdef USE_ST_TIMESPEC | ||
251 | #define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctimespec.tv_nsec)) | ||
252 | #define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtimespec.tv_nsec)) | ||
253 | #else | ||
254 | #define ST_CTIME_NSEC(st) ((unsigned int)((st).st_ctim.tv_nsec)) | ||
255 | #define ST_MTIME_NSEC(st) ((unsigned int)((st).st_mtim.tv_nsec)) | ||
256 | #endif | ||
257 | #endif | ||
258 | |||
259 | int mkdir_p(char *path, mode_t mode); | 233 | int mkdir_p(char *path, mode_t mode); |
260 | int copyfile(const char *from, const char *to); | 234 | int copyfile(const char *from, const char *to); |
261 | 235 | ||
diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c index cfa55d686e3b..bdd33470b235 100644 --- a/tools/perf/util/values.c +++ b/tools/perf/util/values.c | |||
@@ -150,7 +150,7 @@ static void perf_read_values__display_pretty(FILE *fp, | |||
150 | if (width > tidwidth) | 150 | if (width > tidwidth) |
151 | tidwidth = width; | 151 | tidwidth = width; |
152 | for (j = 0; j < values->counters; j++) { | 152 | for (j = 0; j < values->counters; j++) { |
153 | width = snprintf(NULL, 0, "%Lu", values->value[i][j]); | 153 | width = snprintf(NULL, 0, "%" PRIu64, values->value[i][j]); |
154 | if (width > counterwidth[j]) | 154 | if (width > counterwidth[j]) |
155 | counterwidth[j] = width; | 155 | counterwidth[j] = width; |
156 | } | 156 | } |
@@ -165,7 +165,7 @@ static void perf_read_values__display_pretty(FILE *fp, | |||
165 | fprintf(fp, " %*d %*d", pidwidth, values->pid[i], | 165 | fprintf(fp, " %*d %*d", pidwidth, values->pid[i], |
166 | tidwidth, values->tid[i]); | 166 | tidwidth, values->tid[i]); |
167 | for (j = 0; j < values->counters; j++) | 167 | for (j = 0; j < values->counters; j++) |
168 | fprintf(fp, " %*Lu", | 168 | fprintf(fp, " %*" PRIu64, |
169 | counterwidth[j], values->value[i][j]); | 169 | counterwidth[j], values->value[i][j]); |
170 | fprintf(fp, "\n"); | 170 | fprintf(fp, "\n"); |
171 | } | 171 | } |
@@ -196,13 +196,13 @@ static void perf_read_values__display_raw(FILE *fp, | |||
196 | width = strlen(values->countername[j]); | 196 | width = strlen(values->countername[j]); |
197 | if (width > namewidth) | 197 | if (width > namewidth) |
198 | namewidth = width; | 198 | namewidth = width; |
199 | width = snprintf(NULL, 0, "%llx", values->counterrawid[j]); | 199 | width = snprintf(NULL, 0, "%" PRIx64, values->counterrawid[j]); |
200 | if (width > rawwidth) | 200 | if (width > rawwidth) |
201 | rawwidth = width; | 201 | rawwidth = width; |
202 | } | 202 | } |
203 | for (i = 0; i < values->threads; i++) { | 203 | for (i = 0; i < values->threads; i++) { |
204 | for (j = 0; j < values->counters; j++) { | 204 | for (j = 0; j < values->counters; j++) { |
205 | width = snprintf(NULL, 0, "%Lu", values->value[i][j]); | 205 | width = snprintf(NULL, 0, "%" PRIu64, values->value[i][j]); |
206 | if (width > countwidth) | 206 | if (width > countwidth) |
207 | countwidth = width; | 207 | countwidth = width; |
208 | } | 208 | } |
@@ -214,7 +214,7 @@ static void perf_read_values__display_raw(FILE *fp, | |||
214 | countwidth, "Count"); | 214 | countwidth, "Count"); |
215 | for (i = 0; i < values->threads; i++) | 215 | for (i = 0; i < values->threads; i++) |
216 | for (j = 0; j < values->counters; j++) | 216 | for (j = 0; j < values->counters; j++) |
217 | fprintf(fp, " %*d %*d %*s %*llx %*Lu\n", | 217 | fprintf(fp, " %*d %*d %*s %*" PRIx64 " %*" PRIu64, |
218 | pidwidth, values->pid[i], | 218 | pidwidth, values->pid[i], |
219 | tidwidth, values->tid[i], | 219 | tidwidth, values->tid[i], |
220 | namewidth, values->countername[j], | 220 | namewidth, values->countername[j], |