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