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