]>
Commit | Line | Data |
---|---|---|
53cb8bc2 | 1 | #include "util/util.h" |
16f762a2 | 2 | #include "builtin.h" |
53cb8bc2 | 3 | |
35a50c8a | 4 | #include "util/list.h" |
a930d2c0 | 5 | #include "util/cache.h" |
35a50c8a | 6 | #include "util/rbtree.h" |
a2928c42 | 7 | #include "util/symbol.h" |
8fa66bdc | 8 | |
53cb8bc2 IM |
9 | #include "perf.h" |
10 | ||
11 | #include "util/parse-options.h" | |
12 | #include "util/parse-events.h" | |
13 | ||
8fa66bdc ACM |
14 | #define SHOW_KERNEL 1 |
15 | #define SHOW_USER 2 | |
16 | #define SHOW_HV 4 | |
17 | ||
23ac9cbe | 18 | static char const *input_name = "perf.data"; |
450aaa2b | 19 | static char *vmlinux = NULL; |
37f440cb | 20 | static char *sort_order = "pid,symbol"; |
8fa66bdc ACM |
21 | static int input; |
22 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; | |
23 | ||
97b07b69 | 24 | static int dump_trace = 0; |
16f762a2 | 25 | static int verbose; |
97b07b69 | 26 | |
8fa66bdc ACM |
27 | static unsigned long page_size; |
28 | static unsigned long mmap_window = 32; | |
29 | ||
53cb8bc2 | 30 | const char *perf_event_names[] = { |
8fa66bdc ACM |
31 | [PERF_EVENT_MMAP] = " PERF_EVENT_MMAP", |
32 | [PERF_EVENT_MUNMAP] = " PERF_EVENT_MUNMAP", | |
33 | [PERF_EVENT_COMM] = " PERF_EVENT_COMM", | |
34 | }; | |
35 | ||
36 | struct ip_event { | |
37 | struct perf_event_header header; | |
38 | __u64 ip; | |
39 | __u32 pid, tid; | |
40 | }; | |
41 | struct mmap_event { | |
42 | struct perf_event_header header; | |
43 | __u32 pid, tid; | |
44 | __u64 start; | |
45 | __u64 len; | |
46 | __u64 pgoff; | |
47 | char filename[PATH_MAX]; | |
48 | }; | |
49 | struct comm_event { | |
50 | struct perf_event_header header; | |
51 | __u32 pid,tid; | |
52 | char comm[16]; | |
53 | }; | |
54 | ||
55 | typedef union event_union { | |
56 | struct perf_event_header header; | |
57 | struct ip_event ip; | |
58 | struct mmap_event mmap; | |
59 | struct comm_event comm; | |
60 | } event_t; | |
61 | ||
8fa66bdc ACM |
62 | static LIST_HEAD(dsos); |
63 | static struct dso *kernel_dso; | |
64 | ||
65 | static void dsos__add(struct dso *dso) | |
66 | { | |
67 | list_add_tail(&dso->node, &dsos); | |
68 | } | |
69 | ||
70 | static struct dso *dsos__find(const char *name) | |
71 | { | |
72 | struct dso *pos; | |
73 | ||
74 | list_for_each_entry(pos, &dsos, node) | |
75 | if (strcmp(pos->name, name) == 0) | |
76 | return pos; | |
77 | return NULL; | |
78 | } | |
79 | ||
80 | static struct dso *dsos__findnew(const char *name) | |
81 | { | |
82 | struct dso *dso = dsos__find(name); | |
b7a16eac | 83 | int nr; |
8fa66bdc ACM |
84 | |
85 | if (dso == NULL) { | |
0085c954 | 86 | dso = dso__new(name, 0); |
b7a16eac PZ |
87 | if (!dso) |
88 | goto out_delete_dso; | |
89 | ||
69ee69f6 | 90 | nr = dso__load(dso, NULL); |
b7a16eac PZ |
91 | if (nr < 0) { |
92 | fprintf(stderr, "Failed to open: %s\n", name); | |
8fa66bdc | 93 | goto out_delete_dso; |
b7a16eac PZ |
94 | } |
95 | if (!nr) { | |
96 | fprintf(stderr, | |
97 | "Failed to find debug symbols for: %s, maybe install a debug package?\n", | |
98 | name); | |
99 | } | |
8fa66bdc ACM |
100 | |
101 | dsos__add(dso); | |
102 | } | |
103 | ||
104 | return dso; | |
105 | ||
106 | out_delete_dso: | |
107 | dso__delete(dso); | |
108 | return NULL; | |
109 | } | |
110 | ||
16f762a2 | 111 | static void dsos__fprintf(FILE *fp) |
8fa66bdc ACM |
112 | { |
113 | struct dso *pos; | |
114 | ||
115 | list_for_each_entry(pos, &dsos, node) | |
116 | dso__fprintf(pos, fp); | |
117 | } | |
118 | ||
450aaa2b PZ |
119 | static int load_kernel(void) |
120 | { | |
a827c875 | 121 | int err; |
450aaa2b | 122 | |
0085c954 | 123 | kernel_dso = dso__new("[kernel]", 0); |
450aaa2b | 124 | if (!kernel_dso) |
a2928c42 | 125 | return -1; |
450aaa2b | 126 | |
69ee69f6 | 127 | err = dso__load_kernel(kernel_dso, vmlinux, NULL); |
a2928c42 ACM |
128 | if (err) { |
129 | dso__delete(kernel_dso); | |
130 | kernel_dso = NULL; | |
131 | } else | |
132 | dsos__add(kernel_dso); | |
450aaa2b | 133 | |
a2928c42 | 134 | return err; |
450aaa2b PZ |
135 | } |
136 | ||
8fa66bdc ACM |
137 | struct map { |
138 | struct list_head node; | |
139 | uint64_t start; | |
140 | uint64_t end; | |
141 | uint64_t pgoff; | |
142 | struct dso *dso; | |
143 | }; | |
144 | ||
145 | static struct map *map__new(struct mmap_event *event) | |
146 | { | |
147 | struct map *self = malloc(sizeof(*self)); | |
148 | ||
149 | if (self != NULL) { | |
150 | self->start = event->start; | |
151 | self->end = event->start + event->len; | |
152 | self->pgoff = event->pgoff; | |
153 | ||
154 | self->dso = dsos__findnew(event->filename); | |
155 | if (self->dso == NULL) | |
156 | goto out_delete; | |
157 | } | |
158 | return self; | |
159 | out_delete: | |
160 | free(self); | |
161 | return NULL; | |
162 | } | |
163 | ||
3a4b8cc7 ACM |
164 | struct thread; |
165 | ||
8fa66bdc | 166 | struct thread { |
ce7e4365 | 167 | struct rb_node rb_node; |
8fa66bdc | 168 | struct list_head maps; |
8fa66bdc ACM |
169 | pid_t pid; |
170 | char *comm; | |
171 | }; | |
172 | ||
173 | static struct thread *thread__new(pid_t pid) | |
174 | { | |
175 | struct thread *self = malloc(sizeof(*self)); | |
176 | ||
177 | if (self != NULL) { | |
178 | self->pid = pid; | |
179 | self->comm = NULL; | |
180 | INIT_LIST_HEAD(&self->maps); | |
8fa66bdc ACM |
181 | } |
182 | ||
183 | return self; | |
184 | } | |
185 | ||
8fa66bdc ACM |
186 | static int thread__set_comm(struct thread *self, const char *comm) |
187 | { | |
188 | self->comm = strdup(comm); | |
189 | return self->comm ? 0 : -ENOMEM; | |
190 | } | |
191 | ||
16f762a2 | 192 | static struct rb_root threads; |
8fa66bdc | 193 | |
ce7e4365 | 194 | static struct thread *threads__findnew(pid_t pid) |
8fa66bdc | 195 | { |
ce7e4365 ACM |
196 | struct rb_node **p = &threads.rb_node; |
197 | struct rb_node *parent = NULL; | |
198 | struct thread *th; | |
8fa66bdc | 199 | |
ce7e4365 ACM |
200 | while (*p != NULL) { |
201 | parent = *p; | |
202 | th = rb_entry(parent, struct thread, rb_node); | |
8fa66bdc | 203 | |
ce7e4365 ACM |
204 | if (th->pid == pid) |
205 | return th; | |
8fa66bdc | 206 | |
ce7e4365 ACM |
207 | if (pid < th->pid) |
208 | p = &(*p)->rb_left; | |
209 | else | |
210 | p = &(*p)->rb_right; | |
8fa66bdc ACM |
211 | } |
212 | ||
ce7e4365 ACM |
213 | th = thread__new(pid); |
214 | if (th != NULL) { | |
215 | rb_link_node(&th->rb_node, parent, p); | |
216 | rb_insert_color(&th->rb_node, &threads); | |
217 | } | |
218 | return th; | |
8fa66bdc ACM |
219 | } |
220 | ||
221 | static void thread__insert_map(struct thread *self, struct map *map) | |
222 | { | |
223 | list_add_tail(&map->node, &self->maps); | |
224 | } | |
225 | ||
226 | static struct map *thread__find_map(struct thread *self, uint64_t ip) | |
227 | { | |
16f762a2 IM |
228 | struct map *pos; |
229 | ||
8fa66bdc ACM |
230 | if (self == NULL) |
231 | return NULL; | |
232 | ||
8fa66bdc ACM |
233 | list_for_each_entry(pos, &self->maps, node) |
234 | if (ip >= pos->start && ip <= pos->end) | |
235 | return pos; | |
236 | ||
237 | return NULL; | |
238 | } | |
239 | ||
e7fb08b1 PZ |
240 | /* |
241 | * histogram, sorted on item, collects counts | |
242 | */ | |
243 | ||
244 | static struct rb_root hist; | |
245 | ||
246 | struct hist_entry { | |
247 | struct rb_node rb_node; | |
248 | ||
249 | struct thread *thread; | |
250 | struct map *map; | |
251 | struct dso *dso; | |
252 | struct symbol *sym; | |
253 | uint64_t ip; | |
254 | char level; | |
255 | ||
256 | uint32_t count; | |
257 | }; | |
258 | ||
1aa16738 PZ |
259 | /* |
260 | * configurable sorting bits | |
261 | */ | |
262 | ||
263 | struct sort_entry { | |
264 | struct list_head list; | |
265 | ||
ca8cdeef PZ |
266 | char *header; |
267 | ||
1aa16738 PZ |
268 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); |
269 | size_t (*print)(FILE *fp, struct hist_entry *); | |
270 | }; | |
271 | ||
e7fb08b1 | 272 | static int64_t |
1aa16738 | 273 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) |
e7fb08b1 | 274 | { |
1aa16738 PZ |
275 | return right->thread->pid - left->thread->pid; |
276 | } | |
277 | ||
278 | static size_t | |
279 | sort__thread_print(FILE *fp, struct hist_entry *self) | |
280 | { | |
ca8cdeef | 281 | return fprintf(fp, " %16s:%5d", self->thread->comm ?: "", self->thread->pid); |
1aa16738 | 282 | } |
e7fb08b1 | 283 | |
1aa16738 | 284 | static struct sort_entry sort_thread = { |
ca8cdeef | 285 | .header = " Command: Pid ", |
1aa16738 PZ |
286 | .cmp = sort__thread_cmp, |
287 | .print = sort__thread_print, | |
288 | }; | |
289 | ||
992444b1 PZ |
290 | static int64_t |
291 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | |
292 | { | |
293 | char *comm_l = left->thread->comm; | |
294 | char *comm_r = right->thread->comm; | |
295 | ||
296 | if (!comm_l || !comm_r) { | |
297 | if (!comm_l && !comm_r) | |
298 | return 0; | |
299 | else if (!comm_l) | |
300 | return -1; | |
301 | else | |
302 | return 1; | |
303 | } | |
304 | ||
305 | return strcmp(comm_l, comm_r); | |
306 | } | |
307 | ||
308 | static size_t | |
309 | sort__comm_print(FILE *fp, struct hist_entry *self) | |
310 | { | |
2d65537e | 311 | return fprintf(fp, " %16s", self->thread->comm ?: "<unknown>"); |
992444b1 PZ |
312 | } |
313 | ||
314 | static struct sort_entry sort_comm = { | |
ca8cdeef | 315 | .header = " Command", |
992444b1 PZ |
316 | .cmp = sort__comm_cmp, |
317 | .print = sort__comm_print, | |
318 | }; | |
319 | ||
55e5ec41 PZ |
320 | static int64_t |
321 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | |
322 | { | |
323 | struct dso *dso_l = left->dso; | |
324 | struct dso *dso_r = right->dso; | |
325 | ||
326 | if (!dso_l || !dso_r) { | |
327 | if (!dso_l && !dso_r) | |
328 | return 0; | |
329 | else if (!dso_l) | |
330 | return -1; | |
331 | else | |
332 | return 1; | |
333 | } | |
334 | ||
335 | return strcmp(dso_l->name, dso_r->name); | |
336 | } | |
337 | ||
338 | static size_t | |
339 | sort__dso_print(FILE *fp, struct hist_entry *self) | |
340 | { | |
2d65537e | 341 | return fprintf(fp, " %64s", self->dso ? self->dso->name : "<unknown>"); |
55e5ec41 PZ |
342 | } |
343 | ||
344 | static struct sort_entry sort_dso = { | |
ca8cdeef | 345 | .header = " Shared Object", |
55e5ec41 PZ |
346 | .cmp = sort__dso_cmp, |
347 | .print = sort__dso_print, | |
348 | }; | |
349 | ||
1aa16738 PZ |
350 | static int64_t |
351 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | |
352 | { | |
353 | uint64_t ip_l, ip_r; | |
e7fb08b1 PZ |
354 | |
355 | if (left->sym == right->sym) | |
356 | return 0; | |
357 | ||
358 | ip_l = left->sym ? left->sym->start : left->ip; | |
359 | ip_r = right->sym ? right->sym->start : right->ip; | |
360 | ||
361 | return (int64_t)(ip_r - ip_l); | |
362 | } | |
363 | ||
1aa16738 PZ |
364 | static size_t |
365 | sort__sym_print(FILE *fp, struct hist_entry *self) | |
366 | { | |
367 | size_t ret = 0; | |
368 | ||
1aa16738 | 369 | if (verbose) |
2d65537e | 370 | ret += fprintf(fp, " %#018llx", (unsigned long long)self->ip); |
1aa16738 | 371 | |
ca8cdeef PZ |
372 | ret += fprintf(fp, " %s: %s", |
373 | self->dso ? self->dso->name : "<unknown>", | |
374 | self->sym ? self->sym->name : "<unknown>"); | |
1aa16738 PZ |
375 | |
376 | return ret; | |
377 | } | |
378 | ||
379 | static struct sort_entry sort_sym = { | |
ca8cdeef PZ |
380 | .header = "Shared Object: Symbol", |
381 | .cmp = sort__sym_cmp, | |
382 | .print = sort__sym_print, | |
1aa16738 PZ |
383 | }; |
384 | ||
37f440cb PZ |
385 | struct sort_dimension { |
386 | char *name; | |
387 | struct sort_entry *entry; | |
388 | int taken; | |
389 | }; | |
390 | ||
391 | static struct sort_dimension sort_dimensions[] = { | |
392 | { .name = "pid", .entry = &sort_thread, }, | |
992444b1 | 393 | { .name = "comm", .entry = &sort_comm, }, |
55e5ec41 | 394 | { .name = "dso", .entry = &sort_dso, }, |
37f440cb PZ |
395 | { .name = "symbol", .entry = &sort_sym, }, |
396 | }; | |
397 | ||
1aa16738 PZ |
398 | static LIST_HEAD(hist_entry__sort_list); |
399 | ||
37f440cb PZ |
400 | static int sort_dimension__add(char *tok) |
401 | { | |
402 | int i; | |
403 | ||
404 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | |
405 | struct sort_dimension *sd = &sort_dimensions[i]; | |
406 | ||
407 | if (sd->taken) | |
408 | continue; | |
409 | ||
410 | if (strcmp(tok, sd->name)) | |
411 | continue; | |
412 | ||
413 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | |
414 | sd->taken = 1; | |
415 | return 0; | |
416 | } | |
417 | ||
418 | return -ESRCH; | |
419 | } | |
420 | ||
1aa16738 PZ |
421 | static void setup_sorting(void) |
422 | { | |
37f440cb PZ |
423 | char *tmp, *tok, *str = strdup(sort_order); |
424 | ||
425 | for (tok = strtok_r(str, ", ", &tmp); | |
426 | tok; tok = strtok_r(NULL, ", ", &tmp)) | |
427 | sort_dimension__add(tok); | |
428 | ||
429 | free(str); | |
1aa16738 PZ |
430 | } |
431 | ||
432 | static int64_t | |
433 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | |
434 | { | |
435 | struct sort_entry *se; | |
436 | int64_t cmp = 0; | |
437 | ||
438 | list_for_each_entry(se, &hist_entry__sort_list, list) { | |
439 | cmp = se->cmp(left, right); | |
440 | if (cmp) | |
441 | break; | |
442 | } | |
443 | ||
444 | return cmp; | |
445 | } | |
446 | ||
447 | static size_t | |
448 | hist_entry__fprintf(FILE *fp, struct hist_entry *self, uint64_t total_samples) | |
449 | { | |
450 | struct sort_entry *se; | |
451 | size_t ret; | |
452 | ||
453 | if (total_samples) { | |
2d65537e | 454 | ret = fprintf(fp, " %5.2f%%", |
1aa16738 PZ |
455 | (self->count * 100.0) / total_samples); |
456 | } else | |
457 | ret = fprintf(fp, "%12d ", self->count); | |
458 | ||
459 | list_for_each_entry(se, &hist_entry__sort_list, list) | |
460 | ret += se->print(fp, self); | |
461 | ||
462 | ret += fprintf(fp, "\n"); | |
463 | ||
464 | return ret; | |
465 | } | |
466 | ||
467 | /* | |
468 | * collect histogram counts | |
469 | */ | |
470 | ||
e7fb08b1 PZ |
471 | static int |
472 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |
473 | struct symbol *sym, uint64_t ip, char level) | |
8fa66bdc | 474 | { |
e7fb08b1 PZ |
475 | struct rb_node **p = &hist.rb_node; |
476 | struct rb_node *parent = NULL; | |
477 | struct hist_entry *he; | |
478 | struct hist_entry entry = { | |
479 | .thread = thread, | |
480 | .map = map, | |
481 | .dso = dso, | |
482 | .sym = sym, | |
483 | .ip = ip, | |
484 | .level = level, | |
485 | .count = 1, | |
486 | }; | |
487 | int cmp; | |
488 | ||
489 | while (*p != NULL) { | |
490 | parent = *p; | |
491 | he = rb_entry(parent, struct hist_entry, rb_node); | |
492 | ||
493 | cmp = hist_entry__cmp(&entry, he); | |
494 | ||
495 | if (!cmp) { | |
496 | he->count++; | |
497 | return 0; | |
498 | } | |
499 | ||
500 | if (cmp < 0) | |
501 | p = &(*p)->rb_left; | |
502 | else | |
503 | p = &(*p)->rb_right; | |
ce7e4365 | 504 | } |
e7fb08b1 PZ |
505 | |
506 | he = malloc(sizeof(*he)); | |
507 | if (!he) | |
508 | return -ENOMEM; | |
509 | *he = entry; | |
510 | rb_link_node(&he->rb_node, parent, p); | |
511 | rb_insert_color(&he->rb_node, &hist); | |
512 | ||
513 | return 0; | |
8fa66bdc ACM |
514 | } |
515 | ||
e7fb08b1 PZ |
516 | /* |
517 | * reverse the map, sort on count. | |
518 | */ | |
519 | ||
520 | static struct rb_root output_hists; | |
521 | ||
522 | static void output__insert_entry(struct hist_entry *he) | |
3a4b8cc7 | 523 | { |
e7fb08b1 | 524 | struct rb_node **p = &output_hists.rb_node; |
3a4b8cc7 | 525 | struct rb_node *parent = NULL; |
e7fb08b1 | 526 | struct hist_entry *iter; |
3a4b8cc7 ACM |
527 | |
528 | while (*p != NULL) { | |
529 | parent = *p; | |
e7fb08b1 | 530 | iter = rb_entry(parent, struct hist_entry, rb_node); |
3a4b8cc7 | 531 | |
e7fb08b1 | 532 | if (he->count > iter->count) |
3a4b8cc7 ACM |
533 | p = &(*p)->rb_left; |
534 | else | |
535 | p = &(*p)->rb_right; | |
536 | } | |
537 | ||
e7fb08b1 PZ |
538 | rb_link_node(&he->rb_node, parent, p); |
539 | rb_insert_color(&he->rb_node, &output_hists); | |
3a4b8cc7 ACM |
540 | } |
541 | ||
e7fb08b1 | 542 | static void output__resort(void) |
3a4b8cc7 | 543 | { |
e7fb08b1 PZ |
544 | struct rb_node *next = rb_first(&hist); |
545 | struct hist_entry *n; | |
3a4b8cc7 | 546 | |
e7fb08b1 PZ |
547 | while (next) { |
548 | n = rb_entry(next, struct hist_entry, rb_node); | |
549 | next = rb_next(&n->rb_node); | |
3a4b8cc7 | 550 | |
e7fb08b1 PZ |
551 | rb_erase(&n->rb_node, &hist); |
552 | output__insert_entry(n); | |
3a4b8cc7 ACM |
553 | } |
554 | } | |
555 | ||
e7fb08b1 | 556 | static size_t output__fprintf(FILE *fp, uint64_t total_samples) |
3a4b8cc7 | 557 | { |
e7fb08b1 | 558 | struct hist_entry *pos; |
2d65537e | 559 | struct sort_entry *se; |
3a4b8cc7 ACM |
560 | struct rb_node *nd; |
561 | size_t ret = 0; | |
562 | ||
ca8cdeef PZ |
563 | fprintf(fp, "#\n"); |
564 | ||
565 | fprintf(fp, "# Overhead"); | |
566 | list_for_each_entry(se, &hist_entry__sort_list, list) | |
567 | fprintf(fp, " %s", se->header); | |
568 | fprintf(fp, "\n"); | |
569 | ||
570 | fprintf(fp, "# ........"); | |
2d65537e | 571 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
ca8cdeef PZ |
572 | int i; |
573 | ||
574 | fprintf(fp, " "); | |
575 | for (i = 0; i < strlen(se->header); i++) | |
576 | fprintf(fp, "."); | |
2d65537e | 577 | } |
ca8cdeef PZ |
578 | fprintf(fp, "\n"); |
579 | ||
580 | fprintf(fp, "#\n"); | |
2d65537e | 581 | |
e7fb08b1 PZ |
582 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { |
583 | pos = rb_entry(nd, struct hist_entry, rb_node); | |
584 | ret += hist_entry__fprintf(fp, pos, total_samples); | |
3a4b8cc7 ACM |
585 | } |
586 | ||
587 | return ret; | |
588 | } | |
589 | ||
e7fb08b1 | 590 | |
53cb8bc2 | 591 | static int __cmd_report(void) |
8fa66bdc ACM |
592 | { |
593 | unsigned long offset = 0; | |
594 | unsigned long head = 0; | |
595 | struct stat stat; | |
596 | char *buf; | |
597 | event_t *event; | |
598 | int ret, rc = EXIT_FAILURE; | |
6142f9ec | 599 | uint32_t size; |
f49515b1 | 600 | unsigned long total = 0, total_mmap = 0, total_comm = 0, total_unknown = 0; |
8fa66bdc | 601 | |
8fa66bdc ACM |
602 | input = open(input_name, O_RDONLY); |
603 | if (input < 0) { | |
604 | perror("failed to open file"); | |
605 | exit(-1); | |
606 | } | |
607 | ||
608 | ret = fstat(input, &stat); | |
609 | if (ret < 0) { | |
610 | perror("failed to stat file"); | |
611 | exit(-1); | |
612 | } | |
613 | ||
614 | if (!stat.st_size) { | |
615 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | |
616 | exit(0); | |
617 | } | |
618 | ||
450aaa2b | 619 | if (load_kernel() < 0) { |
a2928c42 | 620 | perror("failed to load kernel symbols"); |
8fa66bdc ACM |
621 | return EXIT_FAILURE; |
622 | } | |
623 | ||
624 | remap: | |
625 | buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, | |
626 | MAP_SHARED, input, offset); | |
627 | if (buf == MAP_FAILED) { | |
628 | perror("failed to mmap file"); | |
629 | exit(-1); | |
630 | } | |
631 | ||
632 | more: | |
633 | event = (event_t *)(buf + head); | |
634 | ||
6142f9ec PZ |
635 | size = event->header.size; |
636 | if (!size) | |
637 | size = 8; | |
638 | ||
8fa66bdc ACM |
639 | if (head + event->header.size >= page_size * mmap_window) { |
640 | unsigned long shift = page_size * (head / page_size); | |
641 | int ret; | |
642 | ||
643 | ret = munmap(buf, page_size * mmap_window); | |
644 | assert(ret == 0); | |
645 | ||
646 | offset += shift; | |
647 | head -= shift; | |
648 | goto remap; | |
649 | } | |
650 | ||
6142f9ec PZ |
651 | size = event->header.size; |
652 | if (!size) | |
653 | goto broken_event; | |
8fa66bdc | 654 | |
8fa66bdc ACM |
655 | if (event->header.misc & PERF_EVENT_MISC_OVERFLOW) { |
656 | char level; | |
657 | int show = 0; | |
658 | struct dso *dso = NULL; | |
659 | struct thread *thread = threads__findnew(event->ip.pid); | |
f17e04af | 660 | uint64_t ip = event->ip.ip; |
e7fb08b1 | 661 | struct map *map = NULL; |
8fa66bdc | 662 | |
97b07b69 | 663 | if (dump_trace) { |
f49515b1 IM |
664 | fprintf(stderr, "%p [%p]: PERF_EVENT (IP, %d): %d: %p\n", |
665 | (void *)(offset + head), | |
666 | (void *)(long)(event->header.size), | |
97b07b69 IM |
667 | event->header.misc, |
668 | event->ip.pid, | |
16f762a2 | 669 | (void *)(long)ip); |
97b07b69 IM |
670 | } |
671 | ||
ce7e4365 | 672 | if (thread == NULL) { |
55717314 | 673 | fprintf(stderr, "problem processing %d event, skipping it.\n", |
ce7e4365 | 674 | event->header.type); |
55717314 | 675 | goto broken_event; |
ce7e4365 | 676 | } |
8fa66bdc ACM |
677 | |
678 | if (event->header.misc & PERF_EVENT_MISC_KERNEL) { | |
679 | show = SHOW_KERNEL; | |
680 | level = 'k'; | |
e7fb08b1 | 681 | |
8fa66bdc | 682 | dso = kernel_dso; |
e7fb08b1 | 683 | |
8fa66bdc | 684 | } else if (event->header.misc & PERF_EVENT_MISC_USER) { |
16f762a2 | 685 | |
8fa66bdc ACM |
686 | show = SHOW_USER; |
687 | level = '.'; | |
16f762a2 IM |
688 | |
689 | map = thread__find_map(thread, ip); | |
f17e04af | 690 | if (map != NULL) { |
8fa66bdc | 691 | dso = map->dso; |
f17e04af PZ |
692 | ip -= map->start + map->pgoff; |
693 | } | |
e7fb08b1 | 694 | |
8fa66bdc ACM |
695 | } else { |
696 | show = SHOW_HV; | |
697 | level = 'H'; | |
698 | } | |
699 | ||
700 | if (show & show_mask) { | |
f17e04af | 701 | struct symbol *sym = dso__find_symbol(dso, ip); |
8fa66bdc | 702 | |
e7fb08b1 PZ |
703 | if (hist_entry__add(thread, map, dso, sym, ip, level)) { |
704 | fprintf(stderr, | |
55717314 IM |
705 | "problem incrementing symbol count, skipping event\n"); |
706 | goto broken_event; | |
ce7e4365 | 707 | } |
8fa66bdc ACM |
708 | } |
709 | total++; | |
710 | } else switch (event->header.type) { | |
711 | case PERF_EVENT_MMAP: { | |
712 | struct thread *thread = threads__findnew(event->mmap.pid); | |
713 | struct map *map = map__new(&event->mmap); | |
714 | ||
97b07b69 | 715 | if (dump_trace) { |
f49515b1 IM |
716 | fprintf(stderr, "%p [%p]: PERF_EVENT_MMAP: [%p(%p) @ %p]: %s\n", |
717 | (void *)(offset + head), | |
718 | (void *)(long)(event->header.size), | |
16f762a2 IM |
719 | (void *)(long)event->mmap.start, |
720 | (void *)(long)event->mmap.len, | |
721 | (void *)(long)event->mmap.pgoff, | |
97b07b69 IM |
722 | event->mmap.filename); |
723 | } | |
ce7e4365 | 724 | if (thread == NULL || map == NULL) { |
55717314 IM |
725 | fprintf(stderr, "problem processing PERF_EVENT_MMAP, skipping event.\n"); |
726 | goto broken_event; | |
ce7e4365 | 727 | } |
8fa66bdc | 728 | thread__insert_map(thread, map); |
97b07b69 | 729 | total_mmap++; |
8fa66bdc ACM |
730 | break; |
731 | } | |
732 | case PERF_EVENT_COMM: { | |
733 | struct thread *thread = threads__findnew(event->comm.pid); | |
734 | ||
97b07b69 | 735 | if (dump_trace) { |
f49515b1 IM |
736 | fprintf(stderr, "%p [%p]: PERF_EVENT_COMM: %s:%d\n", |
737 | (void *)(offset + head), | |
738 | (void *)(long)(event->header.size), | |
97b07b69 IM |
739 | event->comm.comm, event->comm.pid); |
740 | } | |
8fa66bdc | 741 | if (thread == NULL || |
ce7e4365 | 742 | thread__set_comm(thread, event->comm.comm)) { |
55717314 IM |
743 | fprintf(stderr, "problem processing PERF_EVENT_COMM, skipping event.\n"); |
744 | goto broken_event; | |
ce7e4365 | 745 | } |
97b07b69 | 746 | total_comm++; |
8fa66bdc ACM |
747 | break; |
748 | } | |
97b07b69 | 749 | default: { |
6142f9ec | 750 | broken_event: |
b7a16eac PZ |
751 | if (dump_trace) |
752 | fprintf(stderr, "%p [%p]: skipping unknown header type: %d\n", | |
753 | (void *)(offset + head), | |
754 | (void *)(long)(event->header.size), | |
755 | event->header.type); | |
756 | ||
3e706114 | 757 | total_unknown++; |
6142f9ec PZ |
758 | |
759 | /* | |
760 | * assume we lost track of the stream, check alignment, and | |
761 | * increment a single u64 in the hope to catch on again 'soon'. | |
762 | */ | |
763 | ||
764 | if (unlikely(head & 7)) | |
765 | head &= ~7ULL; | |
766 | ||
767 | size = 8; | |
97b07b69 | 768 | } |
8fa66bdc ACM |
769 | } |
770 | ||
6142f9ec | 771 | head += size; |
f49515b1 | 772 | |
8fa66bdc ACM |
773 | if (offset + head < stat.st_size) |
774 | goto more; | |
775 | ||
776 | rc = EXIT_SUCCESS; | |
8fa66bdc | 777 | close(input); |
97b07b69 IM |
778 | |
779 | if (dump_trace) { | |
3e706114 IM |
780 | fprintf(stderr, " IP events: %10ld\n", total); |
781 | fprintf(stderr, " mmap events: %10ld\n", total_mmap); | |
782 | fprintf(stderr, " comm events: %10ld\n", total_comm); | |
783 | fprintf(stderr, " unknown events: %10ld\n", total_unknown); | |
97b07b69 IM |
784 | |
785 | return 0; | |
786 | } | |
787 | ||
e7fb08b1 | 788 | if (verbose >= 2) |
16f762a2 | 789 | dsos__fprintf(stdout); |
16f762a2 | 790 | |
e7fb08b1 PZ |
791 | output__resort(); |
792 | output__fprintf(stdout, total); | |
8fa66bdc | 793 | |
8fa66bdc ACM |
794 | return rc; |
795 | } | |
796 | ||
53cb8bc2 IM |
797 | static const char * const report_usage[] = { |
798 | "perf report [<options>] <command>", | |
799 | NULL | |
800 | }; | |
801 | ||
802 | static const struct option options[] = { | |
803 | OPT_STRING('i', "input", &input_name, "file", | |
804 | "input file name"), | |
815e777f ACM |
805 | OPT_BOOLEAN('v', "verbose", &verbose, |
806 | "be more verbose (show symbol address, etc)"), | |
97b07b69 IM |
807 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
808 | "dump raw trace in ASCII"), | |
450aaa2b | 809 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), |
63299f05 IM |
810 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
811 | "sort by key(s): pid, comm, dso, symbol. Default: pid,symbol"), | |
53cb8bc2 IM |
812 | OPT_END() |
813 | }; | |
814 | ||
815 | int cmd_report(int argc, const char **argv, const char *prefix) | |
816 | { | |
a2928c42 | 817 | symbol__init(); |
53cb8bc2 IM |
818 | |
819 | page_size = getpagesize(); | |
820 | ||
821 | parse_options(argc, argv, options, report_usage, 0); | |
822 | ||
1aa16738 PZ |
823 | setup_sorting(); |
824 | ||
a930d2c0 IM |
825 | setup_pager(); |
826 | ||
53cb8bc2 IM |
827 | return __cmd_report(); |
828 | } |