diff options
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r-- | tools/perf/builtin-report.c | 719 |
1 files changed, 136 insertions, 583 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 8b2ec882e6e0..cdf9a8d27bb9 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -17,19 +17,18 @@ | |||
17 | #include "util/string.h" | 17 | #include "util/string.h" |
18 | #include "util/callchain.h" | 18 | #include "util/callchain.h" |
19 | #include "util/strlist.h" | 19 | #include "util/strlist.h" |
20 | #include "util/values.h" | ||
20 | 21 | ||
21 | #include "perf.h" | 22 | #include "perf.h" |
23 | #include "util/debug.h" | ||
22 | #include "util/header.h" | 24 | #include "util/header.h" |
23 | 25 | ||
24 | #include "util/parse-options.h" | 26 | #include "util/parse-options.h" |
25 | #include "util/parse-events.h" | 27 | #include "util/parse-events.h" |
26 | 28 | ||
27 | #define SHOW_KERNEL 1 | 29 | #include "util/thread.h" |
28 | #define SHOW_USER 2 | ||
29 | #define SHOW_HV 4 | ||
30 | 30 | ||
31 | static char const *input_name = "perf.data"; | 31 | static char const *input_name = "perf.data"; |
32 | static char *vmlinux = NULL; | ||
33 | 32 | ||
34 | static char default_sort_order[] = "comm,dso,symbol"; | 33 | static char default_sort_order[] = "comm,dso,symbol"; |
35 | static char *sort_order = default_sort_order; | 34 | static char *sort_order = default_sort_order; |
@@ -42,18 +41,15 @@ static int force; | |||
42 | static int input; | 41 | static int input; |
43 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; | 42 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; |
44 | 43 | ||
45 | static int dump_trace = 0; | ||
46 | #define dprintf(x...) do { if (dump_trace) printf(x); } while (0) | ||
47 | #define cdprintf(x...) do { if (dump_trace) color_fprintf(stdout, color, x); } while (0) | ||
48 | |||
49 | static int verbose; | ||
50 | #define eprintf(x...) do { if (verbose) fprintf(stderr, x); } while (0) | ||
51 | |||
52 | static int modules; | ||
53 | |||
54 | static int full_paths; | 44 | static int full_paths; |
55 | static int show_nr_samples; | 45 | static int show_nr_samples; |
56 | 46 | ||
47 | static int show_threads; | ||
48 | static struct perf_read_values show_threads_values; | ||
49 | |||
50 | static char default_pretty_printing_style[] = "normal"; | ||
51 | static char *pretty_printing_style = default_pretty_printing_style; | ||
52 | |||
57 | static unsigned long page_size; | 53 | static unsigned long page_size; |
58 | static unsigned long mmap_window = 32; | 54 | static unsigned long mmap_window = 32; |
59 | 55 | ||
@@ -67,6 +63,15 @@ static char callchain_default_opt[] = "fractal,0.5"; | |||
67 | 63 | ||
68 | static int callchain; | 64 | static int callchain; |
69 | 65 | ||
66 | static char __cwd[PATH_MAX]; | ||
67 | static char *cwd = __cwd; | ||
68 | static int cwdlen; | ||
69 | |||
70 | static struct rb_root threads; | ||
71 | static struct thread *last_match; | ||
72 | |||
73 | static struct perf_header *header; | ||
74 | |||
70 | static | 75 | static |
71 | struct callchain_param callchain_param = { | 76 | struct callchain_param callchain_param = { |
72 | .mode = CHAIN_GRAPH_REL, | 77 | .mode = CHAIN_GRAPH_REL, |
@@ -75,59 +80,6 @@ struct callchain_param callchain_param = { | |||
75 | 80 | ||
76 | static u64 sample_type; | 81 | static u64 sample_type; |
77 | 82 | ||
78 | struct ip_event { | ||
79 | struct perf_event_header header; | ||
80 | u64 ip; | ||
81 | u32 pid, tid; | ||
82 | unsigned char __more_data[]; | ||
83 | }; | ||
84 | |||
85 | struct mmap_event { | ||
86 | struct perf_event_header header; | ||
87 | u32 pid, tid; | ||
88 | u64 start; | ||
89 | u64 len; | ||
90 | u64 pgoff; | ||
91 | char filename[PATH_MAX]; | ||
92 | }; | ||
93 | |||
94 | struct comm_event { | ||
95 | struct perf_event_header header; | ||
96 | u32 pid, tid; | ||
97 | char comm[16]; | ||
98 | }; | ||
99 | |||
100 | struct fork_event { | ||
101 | struct perf_event_header header; | ||
102 | u32 pid, ppid; | ||
103 | u32 tid, ptid; | ||
104 | }; | ||
105 | |||
106 | struct lost_event { | ||
107 | struct perf_event_header header; | ||
108 | u64 id; | ||
109 | u64 lost; | ||
110 | }; | ||
111 | |||
112 | struct read_event { | ||
113 | struct perf_event_header header; | ||
114 | u32 pid,tid; | ||
115 | u64 value; | ||
116 | u64 time_enabled; | ||
117 | u64 time_running; | ||
118 | u64 id; | ||
119 | }; | ||
120 | |||
121 | typedef union event_union { | ||
122 | struct perf_event_header header; | ||
123 | struct ip_event ip; | ||
124 | struct mmap_event mmap; | ||
125 | struct comm_event comm; | ||
126 | struct fork_event fork; | ||
127 | struct lost_event lost; | ||
128 | struct read_event read; | ||
129 | } event_t; | ||
130 | |||
131 | static int repsep_fprintf(FILE *fp, const char *fmt, ...) | 83 | static int repsep_fprintf(FILE *fp, const char *fmt, ...) |
132 | { | 84 | { |
133 | int n; | 85 | int n; |
@@ -141,6 +93,7 @@ static int repsep_fprintf(FILE *fp, const char *fmt, ...) | |||
141 | n = vasprintf(&bf, fmt, ap); | 93 | n = vasprintf(&bf, fmt, ap); |
142 | if (n > 0) { | 94 | if (n > 0) { |
143 | char *sep = bf; | 95 | char *sep = bf; |
96 | |||
144 | while (1) { | 97 | while (1) { |
145 | sep = strchr(sep, *field_sep); | 98 | sep = strchr(sep, *field_sep); |
146 | if (sep == NULL) | 99 | if (sep == NULL) |
@@ -155,396 +108,10 @@ static int repsep_fprintf(FILE *fp, const char *fmt, ...) | |||
155 | return n; | 108 | return n; |
156 | } | 109 | } |
157 | 110 | ||
158 | static LIST_HEAD(dsos); | ||
159 | static struct dso *kernel_dso; | ||
160 | static struct dso *vdso; | ||
161 | static struct dso *hypervisor_dso; | ||
162 | |||
163 | static void dsos__add(struct dso *dso) | ||
164 | { | ||
165 | list_add_tail(&dso->node, &dsos); | ||
166 | } | ||
167 | |||
168 | static struct dso *dsos__find(const char *name) | ||
169 | { | ||
170 | struct dso *pos; | ||
171 | |||
172 | list_for_each_entry(pos, &dsos, node) | ||
173 | if (strcmp(pos->name, name) == 0) | ||
174 | return pos; | ||
175 | return NULL; | ||
176 | } | ||
177 | |||
178 | static struct dso *dsos__findnew(const char *name) | ||
179 | { | ||
180 | struct dso *dso = dsos__find(name); | ||
181 | int nr; | ||
182 | |||
183 | if (dso) | ||
184 | return dso; | ||
185 | |||
186 | dso = dso__new(name, 0); | ||
187 | if (!dso) | ||
188 | goto out_delete_dso; | ||
189 | |||
190 | nr = dso__load(dso, NULL, verbose); | ||
191 | if (nr < 0) { | ||
192 | eprintf("Failed to open: %s\n", name); | ||
193 | goto out_delete_dso; | ||
194 | } | ||
195 | if (!nr) | ||
196 | eprintf("No symbols found in: %s, maybe install a debug package?\n", name); | ||
197 | |||
198 | dsos__add(dso); | ||
199 | |||
200 | return dso; | ||
201 | |||
202 | out_delete_dso: | ||
203 | dso__delete(dso); | ||
204 | return NULL; | ||
205 | } | ||
206 | |||
207 | static void dsos__fprintf(FILE *fp) | ||
208 | { | ||
209 | struct dso *pos; | ||
210 | |||
211 | list_for_each_entry(pos, &dsos, node) | ||
212 | dso__fprintf(pos, fp); | ||
213 | } | ||
214 | |||
215 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) | ||
216 | { | ||
217 | return dso__find_symbol(dso, ip); | ||
218 | } | ||
219 | |||
220 | static int load_kernel(void) | ||
221 | { | ||
222 | int err; | ||
223 | |||
224 | kernel_dso = dso__new("[kernel]", 0); | ||
225 | if (!kernel_dso) | ||
226 | return -1; | ||
227 | |||
228 | err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose, modules); | ||
229 | if (err <= 0) { | ||
230 | dso__delete(kernel_dso); | ||
231 | kernel_dso = NULL; | ||
232 | } else | ||
233 | dsos__add(kernel_dso); | ||
234 | |||
235 | vdso = dso__new("[vdso]", 0); | ||
236 | if (!vdso) | ||
237 | return -1; | ||
238 | |||
239 | vdso->find_symbol = vdso__find_symbol; | ||
240 | |||
241 | dsos__add(vdso); | ||
242 | |||
243 | hypervisor_dso = dso__new("[hypervisor]", 0); | ||
244 | if (!hypervisor_dso) | ||
245 | return -1; | ||
246 | dsos__add(hypervisor_dso); | ||
247 | |||
248 | return err; | ||
249 | } | ||
250 | |||
251 | static char __cwd[PATH_MAX]; | ||
252 | static char *cwd = __cwd; | ||
253 | static int cwdlen; | ||
254 | |||
255 | static int strcommon(const char *pathname) | ||
256 | { | ||
257 | int n = 0; | ||
258 | |||
259 | while (n < cwdlen && pathname[n] == cwd[n]) | ||
260 | ++n; | ||
261 | |||
262 | return n; | ||
263 | } | ||
264 | |||
265 | struct map { | ||
266 | struct list_head node; | ||
267 | u64 start; | ||
268 | u64 end; | ||
269 | u64 pgoff; | ||
270 | u64 (*map_ip)(struct map *, u64); | ||
271 | struct dso *dso; | ||
272 | }; | ||
273 | |||
274 | static u64 map__map_ip(struct map *map, u64 ip) | ||
275 | { | ||
276 | return ip - map->start + map->pgoff; | ||
277 | } | ||
278 | |||
279 | static u64 vdso__map_ip(struct map *map __used, u64 ip) | ||
280 | { | ||
281 | return ip; | ||
282 | } | ||
283 | |||
284 | static inline int is_anon_memory(const char *filename) | ||
285 | { | ||
286 | return strcmp(filename, "//anon") == 0; | ||
287 | } | ||
288 | |||
289 | static struct map *map__new(struct mmap_event *event) | ||
290 | { | ||
291 | struct map *self = malloc(sizeof(*self)); | ||
292 | |||
293 | if (self != NULL) { | ||
294 | const char *filename = event->filename; | ||
295 | char newfilename[PATH_MAX]; | ||
296 | int anon; | ||
297 | |||
298 | if (cwd) { | ||
299 | int n = strcommon(filename); | ||
300 | |||
301 | if (n == cwdlen) { | ||
302 | snprintf(newfilename, sizeof(newfilename), | ||
303 | ".%s", filename + n); | ||
304 | filename = newfilename; | ||
305 | } | ||
306 | } | ||
307 | |||
308 | anon = is_anon_memory(filename); | ||
309 | |||
310 | if (anon) { | ||
311 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", event->pid); | ||
312 | filename = newfilename; | ||
313 | } | ||
314 | |||
315 | self->start = event->start; | ||
316 | self->end = event->start + event->len; | ||
317 | self->pgoff = event->pgoff; | ||
318 | |||
319 | self->dso = dsos__findnew(filename); | ||
320 | if (self->dso == NULL) | ||
321 | goto out_delete; | ||
322 | |||
323 | if (self->dso == vdso || anon) | ||
324 | self->map_ip = vdso__map_ip; | ||
325 | else | ||
326 | self->map_ip = map__map_ip; | ||
327 | } | ||
328 | return self; | ||
329 | out_delete: | ||
330 | free(self); | ||
331 | return NULL; | ||
332 | } | ||
333 | |||
334 | static struct map *map__clone(struct map *self) | ||
335 | { | ||
336 | struct map *map = malloc(sizeof(*self)); | ||
337 | |||
338 | if (!map) | ||
339 | return NULL; | ||
340 | |||
341 | memcpy(map, self, sizeof(*self)); | ||
342 | |||
343 | return map; | ||
344 | } | ||
345 | |||
346 | static int map__overlap(struct map *l, struct map *r) | ||
347 | { | ||
348 | if (l->start > r->start) { | ||
349 | struct map *t = l; | ||
350 | l = r; | ||
351 | r = t; | ||
352 | } | ||
353 | |||
354 | if (l->end > r->start) | ||
355 | return 1; | ||
356 | |||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | static size_t map__fprintf(struct map *self, FILE *fp) | ||
361 | { | ||
362 | return fprintf(fp, " %Lx-%Lx %Lx %s\n", | ||
363 | self->start, self->end, self->pgoff, self->dso->name); | ||
364 | } | ||
365 | |||
366 | |||
367 | struct thread { | ||
368 | struct rb_node rb_node; | ||
369 | struct list_head maps; | ||
370 | pid_t pid; | ||
371 | char *comm; | ||
372 | }; | ||
373 | |||
374 | static struct thread *thread__new(pid_t pid) | ||
375 | { | ||
376 | struct thread *self = malloc(sizeof(*self)); | ||
377 | |||
378 | if (self != NULL) { | ||
379 | self->pid = pid; | ||
380 | self->comm = malloc(32); | ||
381 | if (self->comm) | ||
382 | snprintf(self->comm, 32, ":%d", self->pid); | ||
383 | INIT_LIST_HEAD(&self->maps); | ||
384 | } | ||
385 | |||
386 | return self; | ||
387 | } | ||
388 | |||
389 | static unsigned int dsos__col_width, | 111 | static unsigned int dsos__col_width, |
390 | comms__col_width, | 112 | comms__col_width, |
391 | threads__col_width; | 113 | threads__col_width; |
392 | 114 | ||
393 | static int thread__set_comm(struct thread *self, const char *comm) | ||
394 | { | ||
395 | if (self->comm) | ||
396 | free(self->comm); | ||
397 | self->comm = strdup(comm); | ||
398 | if (!self->comm) | ||
399 | return -ENOMEM; | ||
400 | |||
401 | if (!col_width_list_str && !field_sep && | ||
402 | (!comm_list || strlist__has_entry(comm_list, comm))) { | ||
403 | unsigned int slen = strlen(comm); | ||
404 | if (slen > comms__col_width) { | ||
405 | comms__col_width = slen; | ||
406 | threads__col_width = slen + 6; | ||
407 | } | ||
408 | } | ||
409 | |||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | static size_t thread__fprintf(struct thread *self, FILE *fp) | ||
414 | { | ||
415 | struct map *pos; | ||
416 | size_t ret = fprintf(fp, "Thread %d %s\n", self->pid, self->comm); | ||
417 | |||
418 | list_for_each_entry(pos, &self->maps, node) | ||
419 | ret += map__fprintf(pos, fp); | ||
420 | |||
421 | return ret; | ||
422 | } | ||
423 | |||
424 | |||
425 | static struct rb_root threads; | ||
426 | static struct thread *last_match; | ||
427 | |||
428 | static struct thread *threads__findnew(pid_t pid) | ||
429 | { | ||
430 | struct rb_node **p = &threads.rb_node; | ||
431 | struct rb_node *parent = NULL; | ||
432 | struct thread *th; | ||
433 | |||
434 | /* | ||
435 | * Font-end cache - PID lookups come in blocks, | ||
436 | * so most of the time we dont have to look up | ||
437 | * the full rbtree: | ||
438 | */ | ||
439 | if (last_match && last_match->pid == pid) | ||
440 | return last_match; | ||
441 | |||
442 | while (*p != NULL) { | ||
443 | parent = *p; | ||
444 | th = rb_entry(parent, struct thread, rb_node); | ||
445 | |||
446 | if (th->pid == pid) { | ||
447 | last_match = th; | ||
448 | return th; | ||
449 | } | ||
450 | |||
451 | if (pid < th->pid) | ||
452 | p = &(*p)->rb_left; | ||
453 | else | ||
454 | p = &(*p)->rb_right; | ||
455 | } | ||
456 | |||
457 | th = thread__new(pid); | ||
458 | if (th != NULL) { | ||
459 | rb_link_node(&th->rb_node, parent, p); | ||
460 | rb_insert_color(&th->rb_node, &threads); | ||
461 | last_match = th; | ||
462 | } | ||
463 | |||
464 | return th; | ||
465 | } | ||
466 | |||
467 | static void thread__insert_map(struct thread *self, struct map *map) | ||
468 | { | ||
469 | struct map *pos, *tmp; | ||
470 | |||
471 | list_for_each_entry_safe(pos, tmp, &self->maps, node) { | ||
472 | if (map__overlap(pos, map)) { | ||
473 | if (verbose >= 2) { | ||
474 | printf("overlapping maps:\n"); | ||
475 | map__fprintf(map, stdout); | ||
476 | map__fprintf(pos, stdout); | ||
477 | } | ||
478 | |||
479 | if (map->start <= pos->start && map->end > pos->start) | ||
480 | pos->start = map->end; | ||
481 | |||
482 | if (map->end >= pos->end && map->start < pos->end) | ||
483 | pos->end = map->start; | ||
484 | |||
485 | if (verbose >= 2) { | ||
486 | printf("after collision:\n"); | ||
487 | map__fprintf(pos, stdout); | ||
488 | } | ||
489 | |||
490 | if (pos->start >= pos->end) { | ||
491 | list_del_init(&pos->node); | ||
492 | free(pos); | ||
493 | } | ||
494 | } | ||
495 | } | ||
496 | |||
497 | list_add_tail(&map->node, &self->maps); | ||
498 | } | ||
499 | |||
500 | static int thread__fork(struct thread *self, struct thread *parent) | ||
501 | { | ||
502 | struct map *map; | ||
503 | |||
504 | if (self->comm) | ||
505 | free(self->comm); | ||
506 | self->comm = strdup(parent->comm); | ||
507 | if (!self->comm) | ||
508 | return -ENOMEM; | ||
509 | |||
510 | list_for_each_entry(map, &parent->maps, node) { | ||
511 | struct map *new = map__clone(map); | ||
512 | if (!new) | ||
513 | return -ENOMEM; | ||
514 | thread__insert_map(self, new); | ||
515 | } | ||
516 | |||
517 | return 0; | ||
518 | } | ||
519 | |||
520 | static struct map *thread__find_map(struct thread *self, u64 ip) | ||
521 | { | ||
522 | struct map *pos; | ||
523 | |||
524 | if (self == NULL) | ||
525 | return NULL; | ||
526 | |||
527 | list_for_each_entry(pos, &self->maps, node) | ||
528 | if (ip >= pos->start && ip <= pos->end) | ||
529 | return pos; | ||
530 | |||
531 | return NULL; | ||
532 | } | ||
533 | |||
534 | static size_t threads__fprintf(FILE *fp) | ||
535 | { | ||
536 | size_t ret = 0; | ||
537 | struct rb_node *nd; | ||
538 | |||
539 | for (nd = rb_first(&threads); nd; nd = rb_next(nd)) { | ||
540 | struct thread *pos = rb_entry(nd, struct thread, rb_node); | ||
541 | |||
542 | ret += thread__fprintf(pos, fp); | ||
543 | } | ||
544 | |||
545 | return ret; | ||
546 | } | ||
547 | |||
548 | /* | 115 | /* |
549 | * histogram, sorted on item, collects counts | 116 | * histogram, sorted on item, collects counts |
550 | */ | 117 | */ |
@@ -574,7 +141,7 @@ struct hist_entry { | |||
574 | struct sort_entry { | 141 | struct sort_entry { |
575 | struct list_head list; | 142 | struct list_head list; |
576 | 143 | ||
577 | char *header; | 144 | const char *header; |
578 | 145 | ||
579 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); | 146 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); |
580 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); | 147 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); |
@@ -758,7 +325,7 @@ static int sort__need_collapse = 0; | |||
758 | static int sort__has_parent = 0; | 325 | static int sort__has_parent = 0; |
759 | 326 | ||
760 | struct sort_dimension { | 327 | struct sort_dimension { |
761 | char *name; | 328 | const char *name; |
762 | struct sort_entry *entry; | 329 | struct sort_entry *entry; |
763 | int taken; | 330 | int taken; |
764 | }; | 331 | }; |
@@ -773,7 +340,7 @@ static struct sort_dimension sort_dimensions[] = { | |||
773 | 340 | ||
774 | static LIST_HEAD(hist_entry__sort_list); | 341 | static LIST_HEAD(hist_entry__sort_list); |
775 | 342 | ||
776 | static int sort_dimension__add(char *tok) | 343 | static int sort_dimension__add(const char *tok) |
777 | { | 344 | { |
778 | unsigned int i; | 345 | unsigned int i; |
779 | 346 | ||
@@ -1032,6 +599,7 @@ hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |||
1032 | case CHAIN_GRAPH_REL: | 599 | case CHAIN_GRAPH_REL: |
1033 | ret += callchain__fprintf_graph(fp, chain, | 600 | ret += callchain__fprintf_graph(fp, chain, |
1034 | total_samples, 1, 1); | 601 | total_samples, 1, 1); |
602 | case CHAIN_NONE: | ||
1035 | default: | 603 | default: |
1036 | break; | 604 | break; |
1037 | } | 605 | } |
@@ -1098,6 +666,34 @@ static void dso__calc_col_width(struct dso *self) | |||
1098 | self->slen_calculated = 1; | 666 | self->slen_calculated = 1; |
1099 | } | 667 | } |
1100 | 668 | ||
669 | static void thread__comm_adjust(struct thread *self) | ||
670 | { | ||
671 | char *comm = self->comm; | ||
672 | |||
673 | if (!col_width_list_str && !field_sep && | ||
674 | (!comm_list || strlist__has_entry(comm_list, comm))) { | ||
675 | unsigned int slen = strlen(comm); | ||
676 | |||
677 | if (slen > comms__col_width) { | ||
678 | comms__col_width = slen; | ||
679 | threads__col_width = slen + 6; | ||
680 | } | ||
681 | } | ||
682 | } | ||
683 | |||
684 | static int thread__set_comm_adjust(struct thread *self, const char *comm) | ||
685 | { | ||
686 | int ret = thread__set_comm(self, comm); | ||
687 | |||
688 | if (ret) | ||
689 | return ret; | ||
690 | |||
691 | thread__comm_adjust(self); | ||
692 | |||
693 | return 0; | ||
694 | } | ||
695 | |||
696 | |||
1101 | static struct symbol * | 697 | static struct symbol * |
1102 | resolve_symbol(struct thread *thread, struct map **mapp, | 698 | resolve_symbol(struct thread *thread, struct map **mapp, |
1103 | struct dso **dsop, u64 *ipp) | 699 | struct dso **dsop, u64 *ipp) |
@@ -1141,8 +737,8 @@ got_map: | |||
1141 | if ((long long)ip < 0) | 737 | if ((long long)ip < 0) |
1142 | dso = kernel_dso; | 738 | dso = kernel_dso; |
1143 | } | 739 | } |
1144 | dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>"); | 740 | dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>"); |
1145 | dprintf(" ...... map: %Lx -> %Lx\n", *ipp, ip); | 741 | dump_printf(" ...... map: %Lx -> %Lx\n", *ipp, ip); |
1146 | *ipp = ip; | 742 | *ipp = ip; |
1147 | 743 | ||
1148 | if (dsop) | 744 | if (dsop) |
@@ -1398,6 +994,9 @@ static size_t output__fprintf(FILE *fp, u64 total_samples) | |||
1398 | size_t ret = 0; | 994 | size_t ret = 0; |
1399 | unsigned int width; | 995 | unsigned int width; |
1400 | char *col_width = col_width_list_str; | 996 | char *col_width = col_width_list_str; |
997 | int raw_printing_style; | ||
998 | |||
999 | raw_printing_style = !strcmp(pretty_printing_style, "raw"); | ||
1401 | 1000 | ||
1402 | init_rem_hits(); | 1001 | init_rem_hits(); |
1403 | 1002 | ||
@@ -1474,18 +1073,11 @@ print_entries: | |||
1474 | 1073 | ||
1475 | free(rem_sq_bracket); | 1074 | free(rem_sq_bracket); |
1476 | 1075 | ||
1477 | return ret; | 1076 | if (show_threads) |
1478 | } | 1077 | perf_read_values_display(fp, &show_threads_values, |
1078 | raw_printing_style); | ||
1479 | 1079 | ||
1480 | static void register_idle_thread(void) | 1080 | return ret; |
1481 | { | ||
1482 | struct thread *thread = threads__findnew(0); | ||
1483 | |||
1484 | if (thread == NULL || | ||
1485 | thread__set_comm(thread, "[idle]")) { | ||
1486 | fprintf(stderr, "problem inserting idle task.\n"); | ||
1487 | exit(-1); | ||
1488 | } | ||
1489 | } | 1081 | } |
1490 | 1082 | ||
1491 | static unsigned long total = 0, | 1083 | static unsigned long total = 0, |
@@ -1514,7 +1106,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1514 | char level; | 1106 | char level; |
1515 | int show = 0; | 1107 | int show = 0; |
1516 | struct dso *dso = NULL; | 1108 | struct dso *dso = NULL; |
1517 | struct thread *thread = threads__findnew(event->ip.pid); | 1109 | struct thread *thread; |
1518 | u64 ip = event->ip.ip; | 1110 | u64 ip = event->ip.ip; |
1519 | u64 period = 1; | 1111 | u64 period = 1; |
1520 | struct map *map = NULL; | 1112 | struct map *map = NULL; |
@@ -1522,12 +1114,14 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1522 | struct ip_callchain *chain = NULL; | 1114 | struct ip_callchain *chain = NULL; |
1523 | int cpumode; | 1115 | int cpumode; |
1524 | 1116 | ||
1117 | thread = threads__findnew(event->ip.pid, &threads, &last_match); | ||
1118 | |||
1525 | if (sample_type & PERF_SAMPLE_PERIOD) { | 1119 | if (sample_type & PERF_SAMPLE_PERIOD) { |
1526 | period = *(u64 *)more_data; | 1120 | period = *(u64 *)more_data; |
1527 | more_data += sizeof(u64); | 1121 | more_data += sizeof(u64); |
1528 | } | 1122 | } |
1529 | 1123 | ||
1530 | dprintf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", | 1124 | dump_printf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", |
1531 | (void *)(offset + head), | 1125 | (void *)(offset + head), |
1532 | (void *)(long)(event->header.size), | 1126 | (void *)(long)(event->header.size), |
1533 | event->header.misc, | 1127 | event->header.misc, |
@@ -1540,7 +1134,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1540 | 1134 | ||
1541 | chain = (void *)more_data; | 1135 | chain = (void *)more_data; |
1542 | 1136 | ||
1543 | dprintf("... chain: nr:%Lu\n", chain->nr); | 1137 | dump_printf("... chain: nr:%Lu\n", chain->nr); |
1544 | 1138 | ||
1545 | if (validate_chain(chain, event) < 0) { | 1139 | if (validate_chain(chain, event) < 0) { |
1546 | eprintf("call-chain problem with event, skipping it.\n"); | 1140 | eprintf("call-chain problem with event, skipping it.\n"); |
@@ -1549,11 +1143,11 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1549 | 1143 | ||
1550 | if (dump_trace) { | 1144 | if (dump_trace) { |
1551 | for (i = 0; i < chain->nr; i++) | 1145 | for (i = 0; i < chain->nr; i++) |
1552 | dprintf("..... %2d: %016Lx\n", i, chain->ips[i]); | 1146 | dump_printf("..... %2d: %016Lx\n", i, chain->ips[i]); |
1553 | } | 1147 | } |
1554 | } | 1148 | } |
1555 | 1149 | ||
1556 | dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 1150 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); |
1557 | 1151 | ||
1558 | if (thread == NULL) { | 1152 | if (thread == NULL) { |
1559 | eprintf("problem processing %d event, skipping it.\n", | 1153 | eprintf("problem processing %d event, skipping it.\n", |
@@ -1572,7 +1166,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1572 | 1166 | ||
1573 | dso = kernel_dso; | 1167 | dso = kernel_dso; |
1574 | 1168 | ||
1575 | dprintf(" ...... dso: %s\n", dso->name); | 1169 | dump_printf(" ...... dso: %s\n", dso->name); |
1576 | 1170 | ||
1577 | } else if (cpumode == PERF_EVENT_MISC_USER) { | 1171 | } else if (cpumode == PERF_EVENT_MISC_USER) { |
1578 | 1172 | ||
@@ -1585,7 +1179,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1585 | 1179 | ||
1586 | dso = hypervisor_dso; | 1180 | dso = hypervisor_dso; |
1587 | 1181 | ||
1588 | dprintf(" ...... dso: [hypervisor]\n"); | 1182 | dump_printf(" ...... dso: [hypervisor]\n"); |
1589 | } | 1183 | } |
1590 | 1184 | ||
1591 | if (show & show_mask) { | 1185 | if (show & show_mask) { |
@@ -1611,10 +1205,12 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) | |||
1611 | static int | 1205 | static int |
1612 | process_mmap_event(event_t *event, unsigned long offset, unsigned long head) | 1206 | process_mmap_event(event_t *event, unsigned long offset, unsigned long head) |
1613 | { | 1207 | { |
1614 | struct thread *thread = threads__findnew(event->mmap.pid); | 1208 | struct thread *thread; |
1615 | struct map *map = map__new(&event->mmap); | 1209 | struct map *map = map__new(&event->mmap, cwd, cwdlen); |
1616 | 1210 | ||
1617 | dprintf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n", | 1211 | thread = threads__findnew(event->mmap.pid, &threads, &last_match); |
1212 | |||
1213 | dump_printf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n", | ||
1618 | (void *)(offset + head), | 1214 | (void *)(offset + head), |
1619 | (void *)(long)(event->header.size), | 1215 | (void *)(long)(event->header.size), |
1620 | event->mmap.pid, | 1216 | event->mmap.pid, |
@@ -1625,7 +1221,7 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head) | |||
1625 | event->mmap.filename); | 1221 | event->mmap.filename); |
1626 | 1222 | ||
1627 | if (thread == NULL || map == NULL) { | 1223 | if (thread == NULL || map == NULL) { |
1628 | dprintf("problem processing PERF_EVENT_MMAP, skipping event.\n"); | 1224 | dump_printf("problem processing PERF_EVENT_MMAP, skipping event.\n"); |
1629 | return 0; | 1225 | return 0; |
1630 | } | 1226 | } |
1631 | 1227 | ||
@@ -1638,16 +1234,18 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head) | |||
1638 | static int | 1234 | static int |
1639 | process_comm_event(event_t *event, unsigned long offset, unsigned long head) | 1235 | process_comm_event(event_t *event, unsigned long offset, unsigned long head) |
1640 | { | 1236 | { |
1641 | struct thread *thread = threads__findnew(event->comm.pid); | 1237 | struct thread *thread; |
1238 | |||
1239 | thread = threads__findnew(event->comm.pid, &threads, &last_match); | ||
1642 | 1240 | ||
1643 | dprintf("%p [%p]: PERF_EVENT_COMM: %s:%d\n", | 1241 | dump_printf("%p [%p]: PERF_EVENT_COMM: %s:%d\n", |
1644 | (void *)(offset + head), | 1242 | (void *)(offset + head), |
1645 | (void *)(long)(event->header.size), | 1243 | (void *)(long)(event->header.size), |
1646 | event->comm.comm, event->comm.pid); | 1244 | event->comm.comm, event->comm.pid); |
1647 | 1245 | ||
1648 | if (thread == NULL || | 1246 | if (thread == NULL || |
1649 | thread__set_comm(thread, event->comm.comm)) { | 1247 | thread__set_comm_adjust(thread, event->comm.comm)) { |
1650 | dprintf("problem processing PERF_EVENT_COMM, skipping event.\n"); | 1248 | dump_printf("problem processing PERF_EVENT_COMM, skipping event.\n"); |
1651 | return -1; | 1249 | return -1; |
1652 | } | 1250 | } |
1653 | total_comm++; | 1251 | total_comm++; |
@@ -1658,10 +1256,13 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head) | |||
1658 | static int | 1256 | static int |
1659 | process_task_event(event_t *event, unsigned long offset, unsigned long head) | 1257 | process_task_event(event_t *event, unsigned long offset, unsigned long head) |
1660 | { | 1258 | { |
1661 | struct thread *thread = threads__findnew(event->fork.pid); | 1259 | struct thread *thread; |
1662 | struct thread *parent = threads__findnew(event->fork.ppid); | 1260 | struct thread *parent; |
1663 | 1261 | ||
1664 | dprintf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n", | 1262 | thread = threads__findnew(event->fork.pid, &threads, &last_match); |
1263 | parent = threads__findnew(event->fork.ppid, &threads, &last_match); | ||
1264 | |||
1265 | dump_printf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n", | ||
1665 | (void *)(offset + head), | 1266 | (void *)(offset + head), |
1666 | (void *)(long)(event->header.size), | 1267 | (void *)(long)(event->header.size), |
1667 | event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT", | 1268 | event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT", |
@@ -1679,7 +1280,7 @@ process_task_event(event_t *event, unsigned long offset, unsigned long head) | |||
1679 | return 0; | 1280 | return 0; |
1680 | 1281 | ||
1681 | if (!thread || !parent || thread__fork(thread, parent)) { | 1282 | if (!thread || !parent || thread__fork(thread, parent)) { |
1682 | dprintf("problem processing PERF_EVENT_FORK, skipping event.\n"); | 1283 | dump_printf("problem processing PERF_EVENT_FORK, skipping event.\n"); |
1683 | return -1; | 1284 | return -1; |
1684 | } | 1285 | } |
1685 | total_fork++; | 1286 | total_fork++; |
@@ -1690,7 +1291,7 @@ process_task_event(event_t *event, unsigned long offset, unsigned long head) | |||
1690 | static int | 1291 | static int |
1691 | process_lost_event(event_t *event, unsigned long offset, unsigned long head) | 1292 | process_lost_event(event_t *event, unsigned long offset, unsigned long head) |
1692 | { | 1293 | { |
1693 | dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", | 1294 | dump_printf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", |
1694 | (void *)(offset + head), | 1295 | (void *)(offset + head), |
1695 | (void *)(long)(event->header.size), | 1296 | (void *)(long)(event->header.size), |
1696 | event->lost.id, | 1297 | event->lost.id, |
@@ -1701,67 +1302,24 @@ process_lost_event(event_t *event, unsigned long offset, unsigned long head) | |||
1701 | return 0; | 1302 | return 0; |
1702 | } | 1303 | } |
1703 | 1304 | ||
1704 | static void trace_event(event_t *event) | 1305 | static int |
1705 | { | 1306 | process_read_event(event_t *event, unsigned long offset, unsigned long head) |
1706 | unsigned char *raw_event = (void *)event; | ||
1707 | char *color = PERF_COLOR_BLUE; | ||
1708 | int i, j; | ||
1709 | |||
1710 | if (!dump_trace) | ||
1711 | return; | ||
1712 | |||
1713 | dprintf("."); | ||
1714 | cdprintf("\n. ... raw event: size %d bytes\n", event->header.size); | ||
1715 | |||
1716 | for (i = 0; i < event->header.size; i++) { | ||
1717 | if ((i & 15) == 0) { | ||
1718 | dprintf("."); | ||
1719 | cdprintf(" %04x: ", i); | ||
1720 | } | ||
1721 | |||
1722 | cdprintf(" %02x", raw_event[i]); | ||
1723 | |||
1724 | if (((i & 15) == 15) || i == event->header.size-1) { | ||
1725 | cdprintf(" "); | ||
1726 | for (j = 0; j < 15-(i & 15); j++) | ||
1727 | cdprintf(" "); | ||
1728 | for (j = 0; j < (i & 15); j++) { | ||
1729 | if (isprint(raw_event[i-15+j])) | ||
1730 | cdprintf("%c", raw_event[i-15+j]); | ||
1731 | else | ||
1732 | cdprintf("."); | ||
1733 | } | ||
1734 | cdprintf("\n"); | ||
1735 | } | ||
1736 | } | ||
1737 | dprintf(".\n"); | ||
1738 | } | ||
1739 | |||
1740 | static struct perf_header *header; | ||
1741 | |||
1742 | static struct perf_counter_attr *perf_header__find_attr(u64 id) | ||
1743 | { | 1307 | { |
1744 | int i; | 1308 | struct perf_counter_attr *attr; |
1745 | 1309 | ||
1746 | for (i = 0; i < header->attrs; i++) { | 1310 | attr = perf_header__find_attr(event->read.id, header); |
1747 | struct perf_header_attr *attr = header->attr[i]; | ||
1748 | int j; | ||
1749 | 1311 | ||
1750 | for (j = 0; j < attr->ids; j++) { | 1312 | if (show_threads) { |
1751 | if (attr->id[j] == id) | 1313 | const char *name = attr ? __event_name(attr->type, attr->config) |
1752 | return &attr->attr; | 1314 | : "unknown"; |
1753 | } | 1315 | perf_read_values_add_value(&show_threads_values, |
1316 | event->read.pid, event->read.tid, | ||
1317 | event->read.id, | ||
1318 | name, | ||
1319 | event->read.value); | ||
1754 | } | 1320 | } |
1755 | 1321 | ||
1756 | return NULL; | 1322 | dump_printf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n", |
1757 | } | ||
1758 | |||
1759 | static int | ||
1760 | process_read_event(event_t *event, unsigned long offset, unsigned long head) | ||
1761 | { | ||
1762 | struct perf_counter_attr *attr = perf_header__find_attr(event->read.id); | ||
1763 | |||
1764 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n", | ||
1765 | (void *)(offset + head), | 1323 | (void *)(offset + head), |
1766 | (void *)(long)(event->header.size), | 1324 | (void *)(long)(event->header.size), |
1767 | event->read.pid, | 1325 | event->read.pid, |
@@ -1813,34 +1371,22 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
1813 | return 0; | 1371 | return 0; |
1814 | } | 1372 | } |
1815 | 1373 | ||
1816 | static u64 perf_header__sample_type(void) | ||
1817 | { | ||
1818 | u64 sample_type = 0; | ||
1819 | int i; | ||
1820 | |||
1821 | for (i = 0; i < header->attrs; i++) { | ||
1822 | struct perf_header_attr *attr = header->attr[i]; | ||
1823 | |||
1824 | if (!sample_type) | ||
1825 | sample_type = attr->attr.sample_type; | ||
1826 | else if (sample_type != attr->attr.sample_type) | ||
1827 | die("non matching sample_type"); | ||
1828 | } | ||
1829 | |||
1830 | return sample_type; | ||
1831 | } | ||
1832 | |||
1833 | static int __cmd_report(void) | 1374 | static int __cmd_report(void) |
1834 | { | 1375 | { |
1835 | int ret, rc = EXIT_FAILURE; | 1376 | int ret, rc = EXIT_FAILURE; |
1836 | unsigned long offset = 0; | 1377 | unsigned long offset = 0; |
1837 | unsigned long head, shift; | 1378 | unsigned long head, shift; |
1838 | struct stat stat; | 1379 | struct stat input_stat; |
1380 | struct thread *idle; | ||
1839 | event_t *event; | 1381 | event_t *event; |
1840 | uint32_t size; | 1382 | uint32_t size; |
1841 | char *buf; | 1383 | char *buf; |
1842 | 1384 | ||
1843 | register_idle_thread(); | 1385 | idle = register_idle_thread(&threads, &last_match); |
1386 | thread__comm_adjust(idle); | ||
1387 | |||
1388 | if (show_threads) | ||
1389 | perf_read_values_init(&show_threads_values); | ||
1844 | 1390 | ||
1845 | input = open(input_name, O_RDONLY); | 1391 | input = open(input_name, O_RDONLY); |
1846 | if (input < 0) { | 1392 | if (input < 0) { |
@@ -1851,18 +1397,18 @@ static int __cmd_report(void) | |||
1851 | exit(-1); | 1397 | exit(-1); |
1852 | } | 1398 | } |
1853 | 1399 | ||
1854 | ret = fstat(input, &stat); | 1400 | ret = fstat(input, &input_stat); |
1855 | if (ret < 0) { | 1401 | if (ret < 0) { |
1856 | perror("failed to stat file"); | 1402 | perror("failed to stat file"); |
1857 | exit(-1); | 1403 | exit(-1); |
1858 | } | 1404 | } |
1859 | 1405 | ||
1860 | if (!force && (stat.st_uid != geteuid())) { | 1406 | if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { |
1861 | fprintf(stderr, "file: %s not owned by current user\n", input_name); | 1407 | fprintf(stderr, "file: %s not owned by current user or root\n", input_name); |
1862 | exit(-1); | 1408 | exit(-1); |
1863 | } | 1409 | } |
1864 | 1410 | ||
1865 | if (!stat.st_size) { | 1411 | if (!input_stat.st_size) { |
1866 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | 1412 | fprintf(stderr, "zero-sized file, nothing to do!\n"); |
1867 | exit(0); | 1413 | exit(0); |
1868 | } | 1414 | } |
@@ -1870,7 +1416,7 @@ static int __cmd_report(void) | |||
1870 | header = perf_header__read(input); | 1416 | header = perf_header__read(input); |
1871 | head = header->data_offset; | 1417 | head = header->data_offset; |
1872 | 1418 | ||
1873 | sample_type = perf_header__sample_type(); | 1419 | sample_type = perf_header__sample_type(header); |
1874 | 1420 | ||
1875 | if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) { | 1421 | if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) { |
1876 | if (sort__has_parent) { | 1422 | if (sort__has_parent) { |
@@ -1880,7 +1426,7 @@ static int __cmd_report(void) | |||
1880 | exit(-1); | 1426 | exit(-1); |
1881 | } | 1427 | } |
1882 | if (callchain) { | 1428 | if (callchain) { |
1883 | fprintf(stderr, "selected -c but no callchain data." | 1429 | fprintf(stderr, "selected -g but no callchain data." |
1884 | " Did you call perf record without" | 1430 | " Did you call perf record without" |
1885 | " -g?\n"); | 1431 | " -g?\n"); |
1886 | exit(-1); | 1432 | exit(-1); |
@@ -1930,12 +1476,12 @@ more: | |||
1930 | size = 8; | 1476 | size = 8; |
1931 | 1477 | ||
1932 | if (head + event->header.size >= page_size * mmap_window) { | 1478 | if (head + event->header.size >= page_size * mmap_window) { |
1933 | int ret; | 1479 | int munmap_ret; |
1934 | 1480 | ||
1935 | shift = page_size * (head / page_size); | 1481 | shift = page_size * (head / page_size); |
1936 | 1482 | ||
1937 | ret = munmap(buf, page_size * mmap_window); | 1483 | munmap_ret = munmap(buf, page_size * mmap_window); |
1938 | assert(ret == 0); | 1484 | assert(munmap_ret == 0); |
1939 | 1485 | ||
1940 | offset += shift; | 1486 | offset += shift; |
1941 | head -= shift; | 1487 | head -= shift; |
@@ -1944,14 +1490,14 @@ more: | |||
1944 | 1490 | ||
1945 | size = event->header.size; | 1491 | size = event->header.size; |
1946 | 1492 | ||
1947 | dprintf("\n%p [%p]: event: %d\n", | 1493 | dump_printf("\n%p [%p]: event: %d\n", |
1948 | (void *)(offset + head), | 1494 | (void *)(offset + head), |
1949 | (void *)(long)event->header.size, | 1495 | (void *)(long)event->header.size, |
1950 | event->header.type); | 1496 | event->header.type); |
1951 | 1497 | ||
1952 | if (!size || process_event(event, offset, head) < 0) { | 1498 | if (!size || process_event(event, offset, head) < 0) { |
1953 | 1499 | ||
1954 | dprintf("%p [%p]: skipping unknown header type: %d\n", | 1500 | dump_printf("%p [%p]: skipping unknown header type: %d\n", |
1955 | (void *)(offset + head), | 1501 | (void *)(offset + head), |
1956 | (void *)(long)(event->header.size), | 1502 | (void *)(long)(event->header.size), |
1957 | event->header.type); | 1503 | event->header.type); |
@@ -1974,25 +1520,25 @@ more: | |||
1974 | if (offset + head >= header->data_offset + header->data_size) | 1520 | if (offset + head >= header->data_offset + header->data_size) |
1975 | goto done; | 1521 | goto done; |
1976 | 1522 | ||
1977 | if (offset + head < (unsigned long)stat.st_size) | 1523 | if (offset + head < (unsigned long)input_stat.st_size) |
1978 | goto more; | 1524 | goto more; |
1979 | 1525 | ||
1980 | done: | 1526 | done: |
1981 | rc = EXIT_SUCCESS; | 1527 | rc = EXIT_SUCCESS; |
1982 | close(input); | 1528 | close(input); |
1983 | 1529 | ||
1984 | dprintf(" IP events: %10ld\n", total); | 1530 | dump_printf(" IP events: %10ld\n", total); |
1985 | dprintf(" mmap events: %10ld\n", total_mmap); | 1531 | dump_printf(" mmap events: %10ld\n", total_mmap); |
1986 | dprintf(" comm events: %10ld\n", total_comm); | 1532 | dump_printf(" comm events: %10ld\n", total_comm); |
1987 | dprintf(" fork events: %10ld\n", total_fork); | 1533 | dump_printf(" fork events: %10ld\n", total_fork); |
1988 | dprintf(" lost events: %10ld\n", total_lost); | 1534 | dump_printf(" lost events: %10ld\n", total_lost); |
1989 | dprintf(" unknown events: %10ld\n", total_unknown); | 1535 | dump_printf(" unknown events: %10ld\n", total_unknown); |
1990 | 1536 | ||
1991 | if (dump_trace) | 1537 | if (dump_trace) |
1992 | return 0; | 1538 | return 0; |
1993 | 1539 | ||
1994 | if (verbose >= 3) | 1540 | if (verbose >= 3) |
1995 | threads__fprintf(stdout); | 1541 | threads__fprintf(stdout, &threads); |
1996 | 1542 | ||
1997 | if (verbose >= 2) | 1543 | if (verbose >= 2) |
1998 | dsos__fprintf(stdout); | 1544 | dsos__fprintf(stdout); |
@@ -2001,6 +1547,9 @@ done: | |||
2001 | output__resort(total); | 1547 | output__resort(total); |
2002 | output__fprintf(stdout, total); | 1548 | output__fprintf(stdout, total); |
2003 | 1549 | ||
1550 | if (show_threads) | ||
1551 | perf_read_values_destroy(&show_threads_values); | ||
1552 | |||
2004 | return rc; | 1553 | return rc; |
2005 | } | 1554 | } |
2006 | 1555 | ||
@@ -2069,12 +1618,16 @@ static const struct option options[] = { | |||
2069 | "be more verbose (show symbol address, etc)"), | 1618 | "be more verbose (show symbol address, etc)"), |
2070 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 1619 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
2071 | "dump raw trace in ASCII"), | 1620 | "dump raw trace in ASCII"), |
2072 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), | 1621 | OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"), |
2073 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | 1622 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), |
2074 | OPT_BOOLEAN('m', "modules", &modules, | 1623 | OPT_BOOLEAN('m', "modules", &modules, |
2075 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | 1624 | "load module symbols - WARNING: use only with -k and LIVE kernel"), |
2076 | OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, | 1625 | OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, |
2077 | "Show a column with the number of samples"), | 1626 | "Show a column with the number of samples"), |
1627 | OPT_BOOLEAN('T', "threads", &show_threads, | ||
1628 | "Show per-thread event counters"), | ||
1629 | OPT_STRING(0, "pretty", &pretty_printing_style, "key", | ||
1630 | "pretty printing style key: normal raw"), | ||
2078 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 1631 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
2079 | "sort by key(s): pid, comm, dso, symbol, parent"), | 1632 | "sort by key(s): pid, comm, dso, symbol, parent"), |
2080 | OPT_BOOLEAN('P', "full-paths", &full_paths, | 1633 | OPT_BOOLEAN('P', "full-paths", &full_paths, |