]>
Commit | Line | Data |
---|---|---|
bf9e1876 IM |
1 | /* |
2 | * builtin-report.c | |
3 | * | |
4 | * Builtin report command: Analyze the perf.data input file, | |
5 | * look up and read DSOs and symbol information and display | |
6 | * a histogram of results, along various sorting keys. | |
7 | */ | |
16f762a2 | 8 | #include "builtin.h" |
53cb8bc2 | 9 | |
bf9e1876 IM |
10 | #include "util/util.h" |
11 | ||
8fc0321f | 12 | #include "util/color.h" |
5da50258 | 13 | #include <linux/list.h> |
a930d2c0 | 14 | #include "util/cache.h" |
43cbcd8a | 15 | #include <linux/rbtree.h> |
a2928c42 | 16 | #include "util/symbol.h" |
a0055ae2 | 17 | #include "util/string.h" |
f55c5552 | 18 | #include "util/callchain.h" |
25903407 | 19 | #include "util/strlist.h" |
8d513270 | 20 | #include "util/values.h" |
8fa66bdc | 21 | |
53cb8bc2 | 22 | #include "perf.h" |
7c6a1c65 | 23 | #include "util/header.h" |
53cb8bc2 IM |
24 | |
25 | #include "util/parse-options.h" | |
26 | #include "util/parse-events.h" | |
27 | ||
6baa0a5a FW |
28 | #include "util/thread.h" |
29 | ||
23ac9cbe | 30 | static char const *input_name = "perf.data"; |
bd74137e | 31 | |
114cfab2 | 32 | static char default_sort_order[] = "comm,dso,symbol"; |
bd74137e | 33 | static char *sort_order = default_sort_order; |
52d422de ACM |
34 | static char *dso_list_str, *comm_list_str, *sym_list_str, |
35 | *col_width_list_str; | |
7bec7a91 | 36 | static struct strlist *dso_list, *comm_list, *sym_list; |
52d422de | 37 | static char *field_sep; |
bd74137e | 38 | |
8fa66bdc ACM |
39 | static int input; |
40 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; | |
41 | ||
3efa1cc9 | 42 | #define cdprintf(x...) do { if (dump_trace) color_fprintf(stdout, color, x); } while (0) |
3502973d | 43 | |
b78c07d4 | 44 | static int full_paths; |
e3d7e183 | 45 | static int show_nr_samples; |
97b07b69 | 46 | |
8d513270 BG |
47 | static int show_threads; |
48 | static struct perf_read_values show_threads_values; | |
49 | ||
9f866697 BG |
50 | static char default_pretty_printing_style[] = "normal"; |
51 | static char *pretty_printing_style = default_pretty_printing_style; | |
52 | ||
8fa66bdc ACM |
53 | static unsigned long page_size; |
54 | static unsigned long mmap_window = 32; | |
55 | ||
b8e6d829 IM |
56 | static char default_parent_pattern[] = "^sys_|^do_page_fault"; |
57 | static char *parent_pattern = default_parent_pattern; | |
b25bcf2f | 58 | static regex_t parent_regex; |
6e7d6fdc | 59 | |
b8e6d829 | 60 | static int exclude_other = 1; |
be903885 | 61 | |
805d127d FW |
62 | static char callchain_default_opt[] = "fractal,0.5"; |
63 | ||
f55c5552 | 64 | static int callchain; |
805d127d | 65 | |
66e274f3 FW |
66 | static char __cwd[PATH_MAX]; |
67 | static char *cwd = __cwd; | |
68 | static int cwdlen; | |
69 | ||
6baa0a5a FW |
70 | static struct rb_root threads; |
71 | static struct thread *last_match; | |
72 | ||
805d127d FW |
73 | static |
74 | struct callchain_param callchain_param = { | |
b1a88349 | 75 | .mode = CHAIN_GRAPH_REL, |
805d127d FW |
76 | .min_percent = 0.5 |
77 | }; | |
b8e6d829 | 78 | |
e6e18ec7 PZ |
79 | static u64 sample_type; |
80 | ||
52d422de ACM |
81 | static int repsep_fprintf(FILE *fp, const char *fmt, ...) |
82 | { | |
83 | int n; | |
84 | va_list ap; | |
85 | ||
86 | va_start(ap, fmt); | |
87 | if (!field_sep) | |
88 | n = vfprintf(fp, fmt, ap); | |
89 | else { | |
90 | char *bf = NULL; | |
91 | n = vasprintf(&bf, fmt, ap); | |
92 | if (n > 0) { | |
93 | char *sep = bf; | |
83a0944f | 94 | |
52d422de ACM |
95 | while (1) { |
96 | sep = strchr(sep, *field_sep); | |
97 | if (sep == NULL) | |
98 | break; | |
99 | *sep = '.'; | |
100 | } | |
101 | } | |
102 | fputs(bf, fp); | |
103 | free(bf); | |
104 | } | |
105 | va_end(ap); | |
106 | return n; | |
107 | } | |
108 | ||
52d422de ACM |
109 | static unsigned int dsos__col_width, |
110 | comms__col_width, | |
111 | threads__col_width; | |
112 | ||
e7fb08b1 PZ |
113 | /* |
114 | * histogram, sorted on item, collects counts | |
115 | */ | |
116 | ||
117 | static struct rb_root hist; | |
118 | ||
119 | struct hist_entry { | |
f55c5552 FW |
120 | struct rb_node rb_node; |
121 | ||
122 | struct thread *thread; | |
123 | struct map *map; | |
124 | struct dso *dso; | |
125 | struct symbol *sym; | |
126 | struct symbol *parent; | |
127 | u64 ip; | |
128 | char level; | |
129 | struct callchain_node callchain; | |
130 | struct rb_root sorted_chain; | |
131 | ||
132 | u64 count; | |
e7fb08b1 PZ |
133 | }; |
134 | ||
1aa16738 PZ |
135 | /* |
136 | * configurable sorting bits | |
137 | */ | |
138 | ||
139 | struct sort_entry { | |
140 | struct list_head list; | |
141 | ||
83a0944f | 142 | const char *header; |
ca8cdeef | 143 | |
1aa16738 | 144 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); |
8229289b | 145 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); |
52d422de ACM |
146 | size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width); |
147 | unsigned int *width; | |
021191b3 | 148 | bool elide; |
1aa16738 PZ |
149 | }; |
150 | ||
6e7d6fdc PZ |
151 | static int64_t cmp_null(void *l, void *r) |
152 | { | |
153 | if (!l && !r) | |
154 | return 0; | |
155 | else if (!l) | |
156 | return -1; | |
157 | else | |
158 | return 1; | |
159 | } | |
160 | ||
8229289b PZ |
161 | /* --sort pid */ |
162 | ||
e7fb08b1 | 163 | static int64_t |
1aa16738 | 164 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) |
e7fb08b1 | 165 | { |
1aa16738 PZ |
166 | return right->thread->pid - left->thread->pid; |
167 | } | |
168 | ||
169 | static size_t | |
52d422de | 170 | sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) |
1aa16738 | 171 | { |
52d422de ACM |
172 | return repsep_fprintf(fp, "%*s:%5d", width - 6, |
173 | self->thread->comm ?: "", self->thread->pid); | |
1aa16738 | 174 | } |
e7fb08b1 | 175 | |
1aa16738 | 176 | static struct sort_entry sort_thread = { |
52d422de | 177 | .header = "Command: Pid", |
1aa16738 PZ |
178 | .cmp = sort__thread_cmp, |
179 | .print = sort__thread_print, | |
52d422de | 180 | .width = &threads__col_width, |
1aa16738 PZ |
181 | }; |
182 | ||
8229289b PZ |
183 | /* --sort comm */ |
184 | ||
992444b1 PZ |
185 | static int64_t |
186 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | |
8229289b PZ |
187 | { |
188 | return right->thread->pid - left->thread->pid; | |
189 | } | |
190 | ||
191 | static int64_t | |
192 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | |
992444b1 PZ |
193 | { |
194 | char *comm_l = left->thread->comm; | |
195 | char *comm_r = right->thread->comm; | |
196 | ||
6e7d6fdc PZ |
197 | if (!comm_l || !comm_r) |
198 | return cmp_null(comm_l, comm_r); | |
992444b1 PZ |
199 | |
200 | return strcmp(comm_l, comm_r); | |
201 | } | |
202 | ||
203 | static size_t | |
52d422de | 204 | sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) |
992444b1 | 205 | { |
52d422de | 206 | return repsep_fprintf(fp, "%*s", width, self->thread->comm); |
992444b1 PZ |
207 | } |
208 | ||
209 | static struct sort_entry sort_comm = { | |
52d422de | 210 | .header = "Command", |
8229289b PZ |
211 | .cmp = sort__comm_cmp, |
212 | .collapse = sort__comm_collapse, | |
213 | .print = sort__comm_print, | |
52d422de | 214 | .width = &comms__col_width, |
992444b1 PZ |
215 | }; |
216 | ||
8229289b PZ |
217 | /* --sort dso */ |
218 | ||
55e5ec41 PZ |
219 | static int64_t |
220 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | |
221 | { | |
222 | struct dso *dso_l = left->dso; | |
223 | struct dso *dso_r = right->dso; | |
224 | ||
6e7d6fdc PZ |
225 | if (!dso_l || !dso_r) |
226 | return cmp_null(dso_l, dso_r); | |
55e5ec41 PZ |
227 | |
228 | return strcmp(dso_l->name, dso_r->name); | |
229 | } | |
230 | ||
231 | static size_t | |
52d422de | 232 | sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) |
55e5ec41 | 233 | { |
0a520c63 | 234 | if (self->dso) |
52d422de | 235 | return repsep_fprintf(fp, "%-*s", width, self->dso->name); |
0a520c63 | 236 | |
52d422de | 237 | return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); |
55e5ec41 PZ |
238 | } |
239 | ||
240 | static struct sort_entry sort_dso = { | |
52d422de | 241 | .header = "Shared Object", |
55e5ec41 PZ |
242 | .cmp = sort__dso_cmp, |
243 | .print = sort__dso_print, | |
52d422de | 244 | .width = &dsos__col_width, |
55e5ec41 PZ |
245 | }; |
246 | ||
8229289b PZ |
247 | /* --sort symbol */ |
248 | ||
1aa16738 PZ |
249 | static int64_t |
250 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | |
251 | { | |
9cffa8d5 | 252 | u64 ip_l, ip_r; |
e7fb08b1 PZ |
253 | |
254 | if (left->sym == right->sym) | |
255 | return 0; | |
256 | ||
257 | ip_l = left->sym ? left->sym->start : left->ip; | |
258 | ip_r = right->sym ? right->sym->start : right->ip; | |
259 | ||
260 | return (int64_t)(ip_r - ip_l); | |
261 | } | |
262 | ||
1aa16738 | 263 | static size_t |
52d422de | 264 | sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) |
1aa16738 PZ |
265 | { |
266 | size_t ret = 0; | |
267 | ||
1aa16738 | 268 | if (verbose) |
94cb9e38 ACM |
269 | ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, |
270 | dso__symtab_origin(self->dso)); | |
0a520c63 | 271 | |
60c1baf1 | 272 | ret += repsep_fprintf(fp, "[%c] ", self->level); |
8edd4286 | 273 | if (self->sym) { |
60c1baf1 | 274 | ret += repsep_fprintf(fp, "%s", self->sym->name); |
42976487 MG |
275 | |
276 | if (self->sym->module) | |
52d422de ACM |
277 | ret += repsep_fprintf(fp, "\t[%s]", |
278 | self->sym->module->name); | |
8edd4286 | 279 | } else { |
52d422de | 280 | ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); |
8edd4286 | 281 | } |
1aa16738 PZ |
282 | |
283 | return ret; | |
284 | } | |
285 | ||
286 | static struct sort_entry sort_sym = { | |
71dd8945 | 287 | .header = "Symbol", |
ca8cdeef PZ |
288 | .cmp = sort__sym_cmp, |
289 | .print = sort__sym_print, | |
1aa16738 PZ |
290 | }; |
291 | ||
b25bcf2f | 292 | /* --sort parent */ |
6e7d6fdc PZ |
293 | |
294 | static int64_t | |
b25bcf2f | 295 | sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) |
6e7d6fdc | 296 | { |
b25bcf2f IM |
297 | struct symbol *sym_l = left->parent; |
298 | struct symbol *sym_r = right->parent; | |
6e7d6fdc PZ |
299 | |
300 | if (!sym_l || !sym_r) | |
301 | return cmp_null(sym_l, sym_r); | |
302 | ||
303 | return strcmp(sym_l->name, sym_r->name); | |
304 | } | |
305 | ||
306 | static size_t | |
52d422de | 307 | sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) |
6e7d6fdc | 308 | { |
52d422de ACM |
309 | return repsep_fprintf(fp, "%-*s", width, |
310 | self->parent ? self->parent->name : "[other]"); | |
6e7d6fdc PZ |
311 | } |
312 | ||
52d422de ACM |
313 | static unsigned int parent_symbol__col_width; |
314 | ||
b25bcf2f | 315 | static struct sort_entry sort_parent = { |
52d422de | 316 | .header = "Parent symbol", |
b25bcf2f IM |
317 | .cmp = sort__parent_cmp, |
318 | .print = sort__parent_print, | |
52d422de | 319 | .width = &parent_symbol__col_width, |
6e7d6fdc PZ |
320 | }; |
321 | ||
8229289b | 322 | static int sort__need_collapse = 0; |
b25bcf2f | 323 | static int sort__has_parent = 0; |
8229289b | 324 | |
37f440cb | 325 | struct sort_dimension { |
83a0944f | 326 | const char *name; |
8edd4286 IM |
327 | struct sort_entry *entry; |
328 | int taken; | |
37f440cb PZ |
329 | }; |
330 | ||
331 | static struct sort_dimension sort_dimensions[] = { | |
332 | { .name = "pid", .entry = &sort_thread, }, | |
992444b1 | 333 | { .name = "comm", .entry = &sort_comm, }, |
55e5ec41 | 334 | { .name = "dso", .entry = &sort_dso, }, |
37f440cb | 335 | { .name = "symbol", .entry = &sort_sym, }, |
b25bcf2f | 336 | { .name = "parent", .entry = &sort_parent, }, |
37f440cb PZ |
337 | }; |
338 | ||
1aa16738 PZ |
339 | static LIST_HEAD(hist_entry__sort_list); |
340 | ||
83a0944f | 341 | static int sort_dimension__add(const char *tok) |
37f440cb | 342 | { |
f37a291c | 343 | unsigned int i; |
37f440cb PZ |
344 | |
345 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | |
346 | struct sort_dimension *sd = &sort_dimensions[i]; | |
347 | ||
348 | if (sd->taken) | |
349 | continue; | |
350 | ||
5352f35d | 351 | if (strncasecmp(tok, sd->name, strlen(tok))) |
37f440cb PZ |
352 | continue; |
353 | ||
8229289b PZ |
354 | if (sd->entry->collapse) |
355 | sort__need_collapse = 1; | |
356 | ||
b25bcf2f IM |
357 | if (sd->entry == &sort_parent) { |
358 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); | |
6e7d6fdc PZ |
359 | if (ret) { |
360 | char err[BUFSIZ]; | |
361 | ||
b25bcf2f IM |
362 | regerror(ret, &parent_regex, err, sizeof(err)); |
363 | fprintf(stderr, "Invalid regex: %s\n%s", | |
364 | parent_pattern, err); | |
6e7d6fdc PZ |
365 | exit(-1); |
366 | } | |
b25bcf2f | 367 | sort__has_parent = 1; |
6e7d6fdc PZ |
368 | } |
369 | ||
37f440cb PZ |
370 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); |
371 | sd->taken = 1; | |
5352f35d | 372 | |
37f440cb PZ |
373 | return 0; |
374 | } | |
375 | ||
376 | return -ESRCH; | |
377 | } | |
378 | ||
1aa16738 PZ |
379 | static int64_t |
380 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | |
381 | { | |
382 | struct sort_entry *se; | |
383 | int64_t cmp = 0; | |
384 | ||
385 | list_for_each_entry(se, &hist_entry__sort_list, list) { | |
386 | cmp = se->cmp(left, right); | |
387 | if (cmp) | |
388 | break; | |
389 | } | |
390 | ||
391 | return cmp; | |
392 | } | |
393 | ||
8229289b PZ |
394 | static int64_t |
395 | hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |
396 | { | |
397 | struct sort_entry *se; | |
398 | int64_t cmp = 0; | |
399 | ||
400 | list_for_each_entry(se, &hist_entry__sort_list, list) { | |
401 | int64_t (*f)(struct hist_entry *, struct hist_entry *); | |
402 | ||
403 | f = se->collapse ?: se->cmp; | |
404 | ||
405 | cmp = f(left, right); | |
406 | if (cmp) | |
407 | break; | |
408 | } | |
409 | ||
410 | return cmp; | |
411 | } | |
412 | ||
4eb3e478 FW |
413 | static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask) |
414 | { | |
415 | int i; | |
416 | size_t ret = 0; | |
417 | ||
418 | ret += fprintf(fp, "%s", " "); | |
419 | ||
420 | for (i = 0; i < depth; i++) | |
421 | if (depth_mask & (1 << i)) | |
422 | ret += fprintf(fp, "| "); | |
423 | else | |
424 | ret += fprintf(fp, " "); | |
425 | ||
426 | ret += fprintf(fp, "\n"); | |
427 | ||
428 | return ret; | |
429 | } | |
f55c5552 | 430 | static size_t |
4eb3e478 FW |
431 | ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth, |
432 | int depth_mask, int count, u64 total_samples, | |
433 | int hits) | |
434 | { | |
435 | int i; | |
436 | size_t ret = 0; | |
437 | ||
438 | ret += fprintf(fp, "%s", " "); | |
439 | for (i = 0; i < depth; i++) { | |
440 | if (depth_mask & (1 << i)) | |
441 | ret += fprintf(fp, "|"); | |
442 | else | |
443 | ret += fprintf(fp, " "); | |
444 | if (!count && i == depth - 1) { | |
445 | double percent; | |
446 | ||
447 | percent = hits * 100.0 / total_samples; | |
24b57c69 | 448 | ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent); |
4eb3e478 FW |
449 | } else |
450 | ret += fprintf(fp, "%s", " "); | |
451 | } | |
452 | if (chain->sym) | |
453 | ret += fprintf(fp, "%s\n", chain->sym->name); | |
454 | else | |
455 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); | |
456 | ||
457 | return ret; | |
458 | } | |
459 | ||
25446036 FW |
460 | static struct symbol *rem_sq_bracket; |
461 | static struct callchain_list rem_hits; | |
462 | ||
463 | static void init_rem_hits(void) | |
464 | { | |
465 | rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6); | |
466 | if (!rem_sq_bracket) { | |
467 | fprintf(stderr, "Not enough memory to display remaining hits\n"); | |
468 | return; | |
469 | } | |
470 | ||
471 | strcpy(rem_sq_bracket->name, "[...]"); | |
472 | rem_hits.sym = rem_sq_bracket; | |
473 | } | |
474 | ||
4eb3e478 FW |
475 | static size_t |
476 | callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |
477 | u64 total_samples, int depth, int depth_mask) | |
478 | { | |
479 | struct rb_node *node, *next; | |
480 | struct callchain_node *child; | |
481 | struct callchain_list *chain; | |
482 | int new_depth_mask = depth_mask; | |
805d127d | 483 | u64 new_total; |
25446036 | 484 | u64 remaining; |
4eb3e478 FW |
485 | size_t ret = 0; |
486 | int i; | |
487 | ||
805d127d | 488 | if (callchain_param.mode == CHAIN_GRAPH_REL) |
1953287b | 489 | new_total = self->children_hit; |
805d127d FW |
490 | else |
491 | new_total = total_samples; | |
492 | ||
25446036 FW |
493 | remaining = new_total; |
494 | ||
4eb3e478 FW |
495 | node = rb_first(&self->rb_root); |
496 | while (node) { | |
25446036 FW |
497 | u64 cumul; |
498 | ||
4eb3e478 | 499 | child = rb_entry(node, struct callchain_node, rb_node); |
25446036 FW |
500 | cumul = cumul_hits(child); |
501 | remaining -= cumul; | |
4eb3e478 FW |
502 | |
503 | /* | |
504 | * The depth mask manages the output of pipes that show | |
505 | * the depth. We don't want to keep the pipes of the current | |
25446036 FW |
506 | * level for the last child of this depth. |
507 | * Except if we have remaining filtered hits. They will | |
508 | * supersede the last child | |
4eb3e478 FW |
509 | */ |
510 | next = rb_next(node); | |
25446036 | 511 | if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining)) |
4eb3e478 FW |
512 | new_depth_mask &= ~(1 << (depth - 1)); |
513 | ||
514 | /* | |
515 | * But we keep the older depth mask for the line seperator | |
516 | * to keep the level link until we reach the last child | |
517 | */ | |
518 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask); | |
519 | i = 0; | |
520 | list_for_each_entry(chain, &child->val, list) { | |
521 | if (chain->ip >= PERF_CONTEXT_MAX) | |
522 | continue; | |
523 | ret += ipchain__fprintf_graph(fp, chain, depth, | |
524 | new_depth_mask, i++, | |
805d127d | 525 | new_total, |
25446036 | 526 | cumul); |
4eb3e478 | 527 | } |
805d127d | 528 | ret += callchain__fprintf_graph(fp, child, new_total, |
4eb3e478 FW |
529 | depth + 1, |
530 | new_depth_mask | (1 << depth)); | |
531 | node = next; | |
532 | } | |
533 | ||
25446036 FW |
534 | if (callchain_param.mode == CHAIN_GRAPH_REL && |
535 | remaining && remaining != new_total) { | |
536 | ||
537 | if (!rem_sq_bracket) | |
538 | return ret; | |
539 | ||
540 | new_depth_mask &= ~(1 << (depth - 1)); | |
541 | ||
542 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | |
543 | new_depth_mask, 0, new_total, | |
544 | remaining); | |
545 | } | |
546 | ||
4eb3e478 FW |
547 | return ret; |
548 | } | |
549 | ||
550 | static size_t | |
551 | callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | |
552 | u64 total_samples) | |
f55c5552 FW |
553 | { |
554 | struct callchain_list *chain; | |
555 | size_t ret = 0; | |
556 | ||
557 | if (!self) | |
558 | return 0; | |
559 | ||
4eb3e478 | 560 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); |
f55c5552 FW |
561 | |
562 | ||
4424961a FW |
563 | list_for_each_entry(chain, &self->val, list) { |
564 | if (chain->ip >= PERF_CONTEXT_MAX) | |
565 | continue; | |
566 | if (chain->sym) | |
567 | ret += fprintf(fp, " %s\n", chain->sym->name); | |
568 | else | |
569 | ret += fprintf(fp, " %p\n", | |
f37a291c | 570 | (void *)(long)chain->ip); |
4424961a | 571 | } |
f55c5552 FW |
572 | |
573 | return ret; | |
574 | } | |
575 | ||
576 | static size_t | |
577 | hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |
578 | u64 total_samples) | |
579 | { | |
580 | struct rb_node *rb_node; | |
581 | struct callchain_node *chain; | |
582 | size_t ret = 0; | |
583 | ||
584 | rb_node = rb_first(&self->sorted_chain); | |
585 | while (rb_node) { | |
586 | double percent; | |
587 | ||
588 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | |
589 | percent = chain->hit * 100.0 / total_samples; | |
805d127d FW |
590 | switch (callchain_param.mode) { |
591 | case CHAIN_FLAT: | |
24b57c69 FW |
592 | ret += percent_color_fprintf(fp, " %6.2f%%\n", |
593 | percent); | |
4eb3e478 | 594 | ret += callchain__fprintf_flat(fp, chain, total_samples); |
805d127d FW |
595 | break; |
596 | case CHAIN_GRAPH_ABS: /* Falldown */ | |
597 | case CHAIN_GRAPH_REL: | |
4eb3e478 FW |
598 | ret += callchain__fprintf_graph(fp, chain, |
599 | total_samples, 1, 1); | |
83a0944f | 600 | case CHAIN_NONE: |
805d127d FW |
601 | default: |
602 | break; | |
4eb3e478 | 603 | } |
f55c5552 FW |
604 | ret += fprintf(fp, "\n"); |
605 | rb_node = rb_next(rb_node); | |
606 | } | |
607 | ||
608 | return ret; | |
609 | } | |
610 | ||
611 | ||
1aa16738 | 612 | static size_t |
9cffa8d5 | 613 | hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) |
1aa16738 PZ |
614 | { |
615 | struct sort_entry *se; | |
616 | size_t ret; | |
617 | ||
b8e6d829 IM |
618 | if (exclude_other && !self->parent) |
619 | return 0; | |
620 | ||
1e11fd82 | 621 | if (total_samples) |
52d422de ACM |
622 | ret = percent_color_fprintf(fp, |
623 | field_sep ? "%.2f" : " %6.2f%%", | |
624 | (self->count * 100.0) / total_samples); | |
1e11fd82 | 625 | else |
52d422de | 626 | ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count); |
1aa16738 | 627 | |
e3d7e183 ACM |
628 | if (show_nr_samples) { |
629 | if (field_sep) | |
630 | fprintf(fp, "%c%lld", *field_sep, self->count); | |
631 | else | |
632 | fprintf(fp, "%11lld", self->count); | |
633 | } | |
1aa16738 | 634 | |
71dd8945 | 635 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
021191b3 | 636 | if (se->elide) |
b8e6d829 IM |
637 | continue; |
638 | ||
52d422de ACM |
639 | fprintf(fp, "%s", field_sep ?: " "); |
640 | ret += se->print(fp, self, se->width ? *se->width : 0); | |
71dd8945 | 641 | } |
1aa16738 PZ |
642 | |
643 | ret += fprintf(fp, "\n"); | |
644 | ||
f55c5552 FW |
645 | if (callchain) |
646 | hist_entry_callchain__fprintf(fp, self, total_samples); | |
647 | ||
1aa16738 PZ |
648 | return ret; |
649 | } | |
650 | ||
6e7d6fdc PZ |
651 | /* |
652 | * | |
653 | */ | |
654 | ||
52d422de ACM |
655 | static void dso__calc_col_width(struct dso *self) |
656 | { | |
657 | if (!col_width_list_str && !field_sep && | |
658 | (!dso_list || strlist__has_entry(dso_list, self->name))) { | |
659 | unsigned int slen = strlen(self->name); | |
660 | if (slen > dsos__col_width) | |
661 | dsos__col_width = slen; | |
662 | } | |
663 | ||
664 | self->slen_calculated = 1; | |
665 | } | |
666 | ||
6e7d6fdc PZ |
667 | static struct symbol * |
668 | resolve_symbol(struct thread *thread, struct map **mapp, | |
9cffa8d5 | 669 | struct dso **dsop, u64 *ipp) |
6e7d6fdc PZ |
670 | { |
671 | struct dso *dso = dsop ? *dsop : NULL; | |
672 | struct map *map = mapp ? *mapp : NULL; | |
520f2c34 | 673 | u64 ip = *ipp; |
6e7d6fdc PZ |
674 | |
675 | if (!thread) | |
676 | return NULL; | |
677 | ||
678 | if (dso) | |
679 | goto got_dso; | |
680 | ||
681 | if (map) | |
682 | goto got_map; | |
683 | ||
684 | map = thread__find_map(thread, ip); | |
685 | if (map != NULL) { | |
52d422de ACM |
686 | /* |
687 | * We have to do this here as we may have a dso | |
688 | * with no symbol hit that has a name longer than | |
689 | * the ones with symbols sampled. | |
690 | */ | |
021191b3 | 691 | if (!sort_dso.elide && !map->dso->slen_calculated) |
52d422de ACM |
692 | dso__calc_col_width(map->dso); |
693 | ||
6e7d6fdc PZ |
694 | if (mapp) |
695 | *mapp = map; | |
696 | got_map: | |
697 | ip = map->map_ip(map, ip); | |
6e7d6fdc PZ |
698 | |
699 | dso = map->dso; | |
700 | } else { | |
701 | /* | |
702 | * If this is outside of all known maps, | |
703 | * and is a negative address, try to look it | |
704 | * up in the kernel dso, as it might be a | |
705 | * vsyscall (which executes in user-mode): | |
706 | */ | |
707 | if ((long long)ip < 0) | |
708 | dso = kernel_dso; | |
709 | } | |
2cec19d9 FW |
710 | dump_printf(" ...... dso: %s\n", dso ? dso->name : "<not found>"); |
711 | dump_printf(" ...... map: %Lx -> %Lx\n", *ipp, ip); | |
520f2c34 | 712 | *ipp = ip; |
6e7d6fdc PZ |
713 | |
714 | if (dsop) | |
715 | *dsop = dso; | |
716 | ||
717 | if (!dso) | |
718 | return NULL; | |
719 | got_dso: | |
720 | return dso->find_symbol(dso, ip); | |
721 | } | |
722 | ||
2a0a50fe | 723 | static int call__match(struct symbol *sym) |
6e7d6fdc | 724 | { |
b25bcf2f | 725 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) |
2a0a50fe | 726 | return 1; |
6e7d6fdc | 727 | |
2a0a50fe | 728 | return 0; |
6e7d6fdc PZ |
729 | } |
730 | ||
4424961a | 731 | static struct symbol ** |
f37a291c | 732 | resolve_callchain(struct thread *thread, struct map *map __used, |
4424961a FW |
733 | struct ip_callchain *chain, struct hist_entry *entry) |
734 | { | |
4424961a | 735 | u64 context = PERF_CONTEXT_MAX; |
029e5b16 | 736 | struct symbol **syms = NULL; |
f37a291c | 737 | unsigned int i; |
4424961a FW |
738 | |
739 | if (callchain) { | |
740 | syms = calloc(chain->nr, sizeof(*syms)); | |
741 | if (!syms) { | |
742 | fprintf(stderr, "Can't allocate memory for symbols\n"); | |
743 | exit(-1); | |
744 | } | |
745 | } | |
746 | ||
747 | for (i = 0; i < chain->nr; i++) { | |
748 | u64 ip = chain->ips[i]; | |
749 | struct dso *dso = NULL; | |
750 | struct symbol *sym; | |
751 | ||
752 | if (ip >= PERF_CONTEXT_MAX) { | |
753 | context = ip; | |
754 | continue; | |
755 | } | |
756 | ||
757 | switch (context) { | |
88a69dfb IM |
758 | case PERF_CONTEXT_HV: |
759 | dso = hypervisor_dso; | |
760 | break; | |
4424961a FW |
761 | case PERF_CONTEXT_KERNEL: |
762 | dso = kernel_dso; | |
763 | break; | |
764 | default: | |
765 | break; | |
766 | } | |
767 | ||
768 | sym = resolve_symbol(thread, NULL, &dso, &ip); | |
769 | ||
770 | if (sym) { | |
771 | if (sort__has_parent && call__match(sym) && | |
772 | !entry->parent) | |
773 | entry->parent = sym; | |
774 | if (!callchain) | |
775 | break; | |
776 | syms[i] = sym; | |
777 | } | |
778 | } | |
779 | ||
780 | return syms; | |
781 | } | |
782 | ||
1aa16738 PZ |
783 | /* |
784 | * collect histogram counts | |
785 | */ | |
786 | ||
e7fb08b1 PZ |
787 | static int |
788 | hist_entry__add(struct thread *thread, struct map *map, struct dso *dso, | |
9cffa8d5 PM |
789 | struct symbol *sym, u64 ip, struct ip_callchain *chain, |
790 | char level, u64 count) | |
8fa66bdc | 791 | { |
e7fb08b1 PZ |
792 | struct rb_node **p = &hist.rb_node; |
793 | struct rb_node *parent = NULL; | |
794 | struct hist_entry *he; | |
4424961a | 795 | struct symbol **syms = NULL; |
e7fb08b1 PZ |
796 | struct hist_entry entry = { |
797 | .thread = thread, | |
798 | .map = map, | |
799 | .dso = dso, | |
800 | .sym = sym, | |
801 | .ip = ip, | |
802 | .level = level, | |
ea1900e5 | 803 | .count = count, |
b8e6d829 | 804 | .parent = NULL, |
f55c5552 | 805 | .sorted_chain = RB_ROOT |
e7fb08b1 PZ |
806 | }; |
807 | int cmp; | |
808 | ||
4424961a FW |
809 | if ((sort__has_parent || callchain) && chain) |
810 | syms = resolve_callchain(thread, map, chain, &entry); | |
6e7d6fdc | 811 | |
e7fb08b1 PZ |
812 | while (*p != NULL) { |
813 | parent = *p; | |
814 | he = rb_entry(parent, struct hist_entry, rb_node); | |
815 | ||
816 | cmp = hist_entry__cmp(&entry, he); | |
817 | ||
818 | if (!cmp) { | |
ea1900e5 | 819 | he->count += count; |
4424961a FW |
820 | if (callchain) { |
821 | append_chain(&he->callchain, chain, syms); | |
822 | free(syms); | |
823 | } | |
e7fb08b1 PZ |
824 | return 0; |
825 | } | |
826 | ||
827 | if (cmp < 0) | |
828 | p = &(*p)->rb_left; | |
829 | else | |
830 | p = &(*p)->rb_right; | |
ce7e4365 | 831 | } |
e7fb08b1 PZ |
832 | |
833 | he = malloc(sizeof(*he)); | |
834 | if (!he) | |
835 | return -ENOMEM; | |
836 | *he = entry; | |
f55c5552 FW |
837 | if (callchain) { |
838 | callchain_init(&he->callchain); | |
4424961a FW |
839 | append_chain(&he->callchain, chain, syms); |
840 | free(syms); | |
f55c5552 | 841 | } |
e7fb08b1 PZ |
842 | rb_link_node(&he->rb_node, parent, p); |
843 | rb_insert_color(&he->rb_node, &hist); | |
844 | ||
845 | return 0; | |
8fa66bdc ACM |
846 | } |
847 | ||
8229289b PZ |
848 | static void hist_entry__free(struct hist_entry *he) |
849 | { | |
850 | free(he); | |
851 | } | |
852 | ||
853 | /* | |
854 | * collapse the histogram | |
855 | */ | |
856 | ||
857 | static struct rb_root collapse_hists; | |
858 | ||
859 | static void collapse__insert_entry(struct hist_entry *he) | |
860 | { | |
861 | struct rb_node **p = &collapse_hists.rb_node; | |
862 | struct rb_node *parent = NULL; | |
863 | struct hist_entry *iter; | |
864 | int64_t cmp; | |
865 | ||
866 | while (*p != NULL) { | |
867 | parent = *p; | |
868 | iter = rb_entry(parent, struct hist_entry, rb_node); | |
869 | ||
870 | cmp = hist_entry__collapse(iter, he); | |
871 | ||
872 | if (!cmp) { | |
873 | iter->count += he->count; | |
874 | hist_entry__free(he); | |
875 | return; | |
876 | } | |
877 | ||
878 | if (cmp < 0) | |
879 | p = &(*p)->rb_left; | |
880 | else | |
881 | p = &(*p)->rb_right; | |
882 | } | |
883 | ||
884 | rb_link_node(&he->rb_node, parent, p); | |
885 | rb_insert_color(&he->rb_node, &collapse_hists); | |
886 | } | |
887 | ||
888 | static void collapse__resort(void) | |
889 | { | |
890 | struct rb_node *next; | |
891 | struct hist_entry *n; | |
892 | ||
893 | if (!sort__need_collapse) | |
894 | return; | |
895 | ||
896 | next = rb_first(&hist); | |
897 | while (next) { | |
898 | n = rb_entry(next, struct hist_entry, rb_node); | |
899 | next = rb_next(&n->rb_node); | |
900 | ||
901 | rb_erase(&n->rb_node, &hist); | |
902 | collapse__insert_entry(n); | |
903 | } | |
904 | } | |
905 | ||
e7fb08b1 PZ |
906 | /* |
907 | * reverse the map, sort on count. | |
908 | */ | |
909 | ||
910 | static struct rb_root output_hists; | |
911 | ||
c20ab37e | 912 | static void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits) |
3a4b8cc7 | 913 | { |
e7fb08b1 | 914 | struct rb_node **p = &output_hists.rb_node; |
3a4b8cc7 | 915 | struct rb_node *parent = NULL; |
e7fb08b1 | 916 | struct hist_entry *iter; |
3a4b8cc7 | 917 | |
805d127d FW |
918 | if (callchain) |
919 | callchain_param.sort(&he->sorted_chain, &he->callchain, | |
920 | min_callchain_hits, &callchain_param); | |
f55c5552 | 921 | |
3a4b8cc7 ACM |
922 | while (*p != NULL) { |
923 | parent = *p; | |
e7fb08b1 | 924 | iter = rb_entry(parent, struct hist_entry, rb_node); |
3a4b8cc7 | 925 | |
e7fb08b1 | 926 | if (he->count > iter->count) |
3a4b8cc7 ACM |
927 | p = &(*p)->rb_left; |
928 | else | |
929 | p = &(*p)->rb_right; | |
930 | } | |
931 | ||
e7fb08b1 PZ |
932 | rb_link_node(&he->rb_node, parent, p); |
933 | rb_insert_color(&he->rb_node, &output_hists); | |
3a4b8cc7 ACM |
934 | } |
935 | ||
c20ab37e | 936 | static void output__resort(u64 total_samples) |
3a4b8cc7 | 937 | { |
8229289b | 938 | struct rb_node *next; |
e7fb08b1 | 939 | struct hist_entry *n; |
a4c43bea | 940 | struct rb_root *tree = &hist; |
c20ab37e FW |
941 | u64 min_callchain_hits; |
942 | ||
805d127d | 943 | min_callchain_hits = total_samples * (callchain_param.min_percent / 100); |
3a4b8cc7 | 944 | |
8229289b | 945 | if (sort__need_collapse) |
a4c43bea ACM |
946 | tree = &collapse_hists; |
947 | ||
948 | next = rb_first(tree); | |
8229289b | 949 | |
e7fb08b1 PZ |
950 | while (next) { |
951 | n = rb_entry(next, struct hist_entry, rb_node); | |
952 | next = rb_next(&n->rb_node); | |
3a4b8cc7 | 953 | |
a4c43bea | 954 | rb_erase(&n->rb_node, tree); |
c20ab37e | 955 | output__insert_entry(n, min_callchain_hits); |
3a4b8cc7 ACM |
956 | } |
957 | } | |
958 | ||
9cffa8d5 | 959 | static size_t output__fprintf(FILE *fp, u64 total_samples) |
3a4b8cc7 | 960 | { |
e7fb08b1 | 961 | struct hist_entry *pos; |
2d65537e | 962 | struct sort_entry *se; |
3a4b8cc7 ACM |
963 | struct rb_node *nd; |
964 | size_t ret = 0; | |
52d422de ACM |
965 | unsigned int width; |
966 | char *col_width = col_width_list_str; | |
9f866697 BG |
967 | int raw_printing_style; |
968 | ||
969 | raw_printing_style = !strcmp(pretty_printing_style, "raw"); | |
3a4b8cc7 | 970 | |
25446036 FW |
971 | init_rem_hits(); |
972 | ||
021191b3 | 973 | fprintf(fp, "# Samples: %Ld\n", (u64)total_samples); |
ca8cdeef PZ |
974 | fprintf(fp, "#\n"); |
975 | ||
976 | fprintf(fp, "# Overhead"); | |
e3d7e183 ACM |
977 | if (show_nr_samples) { |
978 | if (field_sep) | |
979 | fprintf(fp, "%cSamples", *field_sep); | |
980 | else | |
981 | fputs(" Samples ", fp); | |
982 | } | |
b8e6d829 | 983 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
021191b3 | 984 | if (se->elide) |
b8e6d829 | 985 | continue; |
52d422de ACM |
986 | if (field_sep) { |
987 | fprintf(fp, "%c%s", *field_sep, se->header); | |
b8e6d829 | 988 | continue; |
52d422de ACM |
989 | } |
990 | width = strlen(se->header); | |
991 | if (se->width) { | |
992 | if (col_width_list_str) { | |
993 | if (col_width) { | |
994 | *se->width = atoi(col_width); | |
995 | col_width = strchr(col_width, ','); | |
996 | if (col_width) | |
997 | ++col_width; | |
998 | } | |
999 | } | |
1000 | width = *se->width = max(*se->width, width); | |
1001 | } | |
1002 | fprintf(fp, " %*s", width, se->header); | |
b8e6d829 | 1003 | } |
ca8cdeef PZ |
1004 | fprintf(fp, "\n"); |
1005 | ||
52d422de ACM |
1006 | if (field_sep) |
1007 | goto print_entries; | |
1008 | ||
ca8cdeef | 1009 | fprintf(fp, "# ........"); |
e3d7e183 ACM |
1010 | if (show_nr_samples) |
1011 | fprintf(fp, " .........."); | |
2d65537e | 1012 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
f37a291c | 1013 | unsigned int i; |
ca8cdeef | 1014 | |
021191b3 | 1015 | if (se->elide) |
b8e6d829 IM |
1016 | continue; |
1017 | ||
4593bba8 | 1018 | fprintf(fp, " "); |
52d422de ACM |
1019 | if (se->width) |
1020 | width = *se->width; | |
1021 | else | |
1022 | width = strlen(se->header); | |
1023 | for (i = 0; i < width; i++) | |
ca8cdeef | 1024 | fprintf(fp, "."); |
2d65537e | 1025 | } |
ca8cdeef PZ |
1026 | fprintf(fp, "\n"); |
1027 | ||
1028 | fprintf(fp, "#\n"); | |
2d65537e | 1029 | |
52d422de | 1030 | print_entries: |
e7fb08b1 PZ |
1031 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { |
1032 | pos = rb_entry(nd, struct hist_entry, rb_node); | |
1033 | ret += hist_entry__fprintf(fp, pos, total_samples); | |
3a4b8cc7 ACM |
1034 | } |
1035 | ||
b8e6d829 IM |
1036 | if (sort_order == default_sort_order && |
1037 | parent_pattern == default_parent_pattern) { | |
bd74137e | 1038 | fprintf(fp, "#\n"); |
114cfab2 | 1039 | fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n"); |
bd74137e IM |
1040 | fprintf(fp, "#\n"); |
1041 | } | |
71dd8945 | 1042 | fprintf(fp, "\n"); |
bd74137e | 1043 | |
25446036 FW |
1044 | free(rem_sq_bracket); |
1045 | ||
8d513270 | 1046 | if (show_threads) |
9f866697 BG |
1047 | perf_read_values_display(fp, &show_threads_values, |
1048 | raw_printing_style); | |
8d513270 | 1049 | |
3a4b8cc7 ACM |
1050 | return ret; |
1051 | } | |
1052 | ||
436224a6 PZ |
1053 | static void register_idle_thread(void) |
1054 | { | |
6baa0a5a | 1055 | struct thread *thread = threads__findnew(0, &threads, &last_match); |
436224a6 PZ |
1056 | |
1057 | if (thread == NULL || | |
1058 | thread__set_comm(thread, "[idle]")) { | |
1059 | fprintf(stderr, "problem inserting idle task.\n"); | |
1060 | exit(-1); | |
1061 | } | |
1062 | } | |
1063 | ||
62fc4453 PZ |
1064 | static unsigned long total = 0, |
1065 | total_mmap = 0, | |
1066 | total_comm = 0, | |
1067 | total_fork = 0, | |
9d91a6f7 PZ |
1068 | total_unknown = 0, |
1069 | total_lost = 0; | |
e7fb08b1 | 1070 | |
2a0a50fe | 1071 | static int validate_chain(struct ip_callchain *chain, event_t *event) |
7522060c IM |
1072 | { |
1073 | unsigned int chain_size; | |
1074 | ||
7522060c IM |
1075 | chain_size = event->header.size; |
1076 | chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; | |
1077 | ||
9cffa8d5 | 1078 | if (chain->nr*sizeof(u64) > chain_size) |
7522060c IM |
1079 | return -1; |
1080 | ||
1081 | return 0; | |
1082 | } | |
1083 | ||
d80d338d | 1084 | static int |
e6e18ec7 | 1085 | process_sample_event(event_t *event, unsigned long offset, unsigned long head) |
75051724 IM |
1086 | { |
1087 | char level; | |
1088 | int show = 0; | |
1089 | struct dso *dso = NULL; | |
6baa0a5a | 1090 | struct thread *thread; |
9cffa8d5 PM |
1091 | u64 ip = event->ip.ip; |
1092 | u64 period = 1; | |
75051724 | 1093 | struct map *map = NULL; |
3efa1cc9 | 1094 | void *more_data = event->ip.__more_data; |
2a0a50fe | 1095 | struct ip_callchain *chain = NULL; |
d8db1b57 | 1096 | int cpumode; |
75051724 | 1097 | |
6baa0a5a FW |
1098 | thread = threads__findnew(event->ip.pid, &threads, &last_match); |
1099 | ||
e6e18ec7 | 1100 | if (sample_type & PERF_SAMPLE_PERIOD) { |
9cffa8d5 PM |
1101 | period = *(u64 *)more_data; |
1102 | more_data += sizeof(u64); | |
3efa1cc9 | 1103 | } |
ea1900e5 | 1104 | |
2cec19d9 | 1105 | dump_printf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", |
75051724 IM |
1106 | (void *)(offset + head), |
1107 | (void *)(long)(event->header.size), | |
1108 | event->header.misc, | |
94a24752 | 1109 | event->ip.pid, event->ip.tid, |
4502d77c | 1110 | (void *)(long)ip, |
ea1900e5 | 1111 | (long long)period); |
75051724 | 1112 | |
e6e18ec7 | 1113 | if (sample_type & PERF_SAMPLE_CALLCHAIN) { |
f37a291c | 1114 | unsigned int i; |
3efa1cc9 IM |
1115 | |
1116 | chain = (void *)more_data; | |
1117 | ||
2cec19d9 | 1118 | dump_printf("... chain: nr:%Lu\n", chain->nr); |
3efa1cc9 | 1119 | |
7522060c IM |
1120 | if (validate_chain(chain, event) < 0) { |
1121 | eprintf("call-chain problem with event, skipping it.\n"); | |
1122 | return 0; | |
1123 | } | |
1124 | ||
1125 | if (dump_trace) { | |
3efa1cc9 | 1126 | for (i = 0; i < chain->nr; i++) |
2cec19d9 | 1127 | dump_printf("..... %2d: %016Lx\n", i, chain->ips[i]); |
3efa1cc9 IM |
1128 | } |
1129 | } | |
1130 | ||
2cec19d9 | 1131 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); |
75051724 IM |
1132 | |
1133 | if (thread == NULL) { | |
7522060c | 1134 | eprintf("problem processing %d event, skipping it.\n", |
75051724 IM |
1135 | event->header.type); |
1136 | return -1; | |
1137 | } | |
e7fb08b1 | 1138 | |
cc8b88b1 ACM |
1139 | if (comm_list && !strlist__has_entry(comm_list, thread->comm)) |
1140 | return 0; | |
1141 | ||
d8db1b57 AB |
1142 | cpumode = event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK; |
1143 | ||
1144 | if (cpumode == PERF_EVENT_MISC_KERNEL) { | |
75051724 IM |
1145 | show = SHOW_KERNEL; |
1146 | level = 'k'; | |
e7fb08b1 | 1147 | |
75051724 | 1148 | dso = kernel_dso; |
ed966aac | 1149 | |
2cec19d9 | 1150 | dump_printf(" ...... dso: %s\n", dso->name); |
16f762a2 | 1151 | |
d8db1b57 | 1152 | } else if (cpumode == PERF_EVENT_MISC_USER) { |
16f762a2 | 1153 | |
75051724 IM |
1154 | show = SHOW_USER; |
1155 | level = '.'; | |
e7fb08b1 | 1156 | |
75051724 IM |
1157 | } else { |
1158 | show = SHOW_HV; | |
1159 | level = 'H'; | |
fb9c8188 AB |
1160 | |
1161 | dso = hypervisor_dso; | |
1162 | ||
2cec19d9 | 1163 | dump_printf(" ...... dso: [hypervisor]\n"); |
75051724 | 1164 | } |
8fa66bdc | 1165 | |
75051724 | 1166 | if (show & show_mask) { |
6e7d6fdc | 1167 | struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip); |
8fa66bdc | 1168 | |
8fd101f2 ACM |
1169 | if (dso_list && (!dso || !dso->name || |
1170 | !strlist__has_entry(dso_list, dso->name))) | |
25903407 ACM |
1171 | return 0; |
1172 | ||
8fd101f2 | 1173 | if (sym_list && (!sym || !strlist__has_entry(sym_list, sym->name))) |
7bec7a91 ACM |
1174 | return 0; |
1175 | ||
6e7d6fdc | 1176 | if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) { |
7522060c | 1177 | eprintf("problem incrementing symbol count, skipping event\n"); |
d80d338d | 1178 | return -1; |
ce7e4365 | 1179 | } |
8fa66bdc | 1180 | } |
ea1900e5 | 1181 | total += period; |
8fa66bdc | 1182 | |
75051724 IM |
1183 | return 0; |
1184 | } | |
3502973d | 1185 | |
75051724 IM |
1186 | static int |
1187 | process_mmap_event(event_t *event, unsigned long offset, unsigned long head) | |
1188 | { | |
6baa0a5a | 1189 | struct thread *thread; |
66e274f3 | 1190 | struct map *map = map__new(&event->mmap, cwd, cwdlen); |
75051724 | 1191 | |
6baa0a5a FW |
1192 | thread = threads__findnew(event->mmap.pid, &threads, &last_match); |
1193 | ||
2cec19d9 | 1194 | dump_printf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n", |
75051724 IM |
1195 | (void *)(offset + head), |
1196 | (void *)(long)(event->header.size), | |
62fc4453 | 1197 | event->mmap.pid, |
94a24752 | 1198 | event->mmap.tid, |
75051724 IM |
1199 | (void *)(long)event->mmap.start, |
1200 | (void *)(long)event->mmap.len, | |
1201 | (void *)(long)event->mmap.pgoff, | |
1202 | event->mmap.filename); | |
1203 | ||
1204 | if (thread == NULL || map == NULL) { | |
2cec19d9 | 1205 | dump_printf("problem processing PERF_EVENT_MMAP, skipping event.\n"); |
df97992c | 1206 | return 0; |
75051724 IM |
1207 | } |
1208 | ||
1209 | thread__insert_map(thread, map); | |
1210 | total_mmap++; | |
1211 | ||
1212 | return 0; | |
1213 | } | |
1214 | ||
1215 | static int | |
1216 | process_comm_event(event_t *event, unsigned long offset, unsigned long head) | |
1217 | { | |
6baa0a5a FW |
1218 | struct thread *thread; |
1219 | ||
1220 | thread = threads__findnew(event->comm.pid, &threads, &last_match); | |
75051724 | 1221 | |
2cec19d9 | 1222 | dump_printf("%p [%p]: PERF_EVENT_COMM: %s:%d\n", |
75051724 IM |
1223 | (void *)(offset + head), |
1224 | (void *)(long)(event->header.size), | |
1225 | event->comm.comm, event->comm.pid); | |
1226 | ||
1227 | if (thread == NULL || | |
1228 | thread__set_comm(thread, event->comm.comm)) { | |
2cec19d9 | 1229 | dump_printf("problem processing PERF_EVENT_COMM, skipping event.\n"); |
75051724 | 1230 | return -1; |
8fa66bdc | 1231 | } |
75051724 IM |
1232 | total_comm++; |
1233 | ||
1234 | return 0; | |
1235 | } | |
1236 | ||
62fc4453 | 1237 | static int |
27d028de | 1238 | process_task_event(event_t *event, unsigned long offset, unsigned long head) |
62fc4453 | 1239 | { |
6baa0a5a FW |
1240 | struct thread *thread; |
1241 | struct thread *parent; | |
1242 | ||
1243 | thread = threads__findnew(event->fork.pid, &threads, &last_match); | |
1244 | parent = threads__findnew(event->fork.ppid, &threads, &last_match); | |
62fc4453 | 1245 | |
2cec19d9 | 1246 | dump_printf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n", |
62fc4453 PZ |
1247 | (void *)(offset + head), |
1248 | (void *)(long)(event->header.size), | |
27d028de PZ |
1249 | event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT", |
1250 | event->fork.pid, event->fork.tid, | |
1251 | event->fork.ppid, event->fork.ptid); | |
1252 | ||
1253 | /* | |
1254 | * A thread clone will have the same PID for both | |
1255 | * parent and child. | |
1256 | */ | |
1257 | if (thread == parent) | |
1258 | return 0; | |
1259 | ||
1260 | if (event->header.type == PERF_EVENT_EXIT) | |
1261 | return 0; | |
62fc4453 PZ |
1262 | |
1263 | if (!thread || !parent || thread__fork(thread, parent)) { | |
2cec19d9 | 1264 | dump_printf("problem processing PERF_EVENT_FORK, skipping event.\n"); |
62fc4453 PZ |
1265 | return -1; |
1266 | } | |
1267 | total_fork++; | |
1268 | ||
1269 | return 0; | |
1270 | } | |
1271 | ||
9d91a6f7 PZ |
1272 | static int |
1273 | process_lost_event(event_t *event, unsigned long offset, unsigned long head) | |
1274 | { | |
2cec19d9 | 1275 | dump_printf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", |
9d91a6f7 PZ |
1276 | (void *)(offset + head), |
1277 | (void *)(long)(event->header.size), | |
1278 | event->lost.id, | |
1279 | event->lost.lost); | |
1280 | ||
1281 | total_lost += event->lost.lost; | |
1282 | ||
1283 | return 0; | |
1284 | } | |
1285 | ||
8465b050 IM |
1286 | static void trace_event(event_t *event) |
1287 | { | |
1288 | unsigned char *raw_event = (void *)event; | |
83a0944f | 1289 | const char *color = PERF_COLOR_BLUE; |
8465b050 IM |
1290 | int i, j; |
1291 | ||
1292 | if (!dump_trace) | |
1293 | return; | |
1294 | ||
2cec19d9 | 1295 | dump_printf("."); |
3efa1cc9 | 1296 | cdprintf("\n. ... raw event: size %d bytes\n", event->header.size); |
8465b050 IM |
1297 | |
1298 | for (i = 0; i < event->header.size; i++) { | |
3efa1cc9 | 1299 | if ((i & 15) == 0) { |
2cec19d9 | 1300 | dump_printf("."); |
3efa1cc9 IM |
1301 | cdprintf(" %04x: ", i); |
1302 | } | |
8465b050 | 1303 | |
3efa1cc9 | 1304 | cdprintf(" %02x", raw_event[i]); |
8465b050 IM |
1305 | |
1306 | if (((i & 15) == 15) || i == event->header.size-1) { | |
3efa1cc9 | 1307 | cdprintf(" "); |
8465b050 | 1308 | for (j = 0; j < 15-(i & 15); j++) |
3efa1cc9 | 1309 | cdprintf(" "); |
8465b050 | 1310 | for (j = 0; j < (i & 15); j++) { |
a73c7d84 | 1311 | if (isprint(raw_event[i-15+j])) |
3efa1cc9 | 1312 | cdprintf("%c", raw_event[i-15+j]); |
8465b050 | 1313 | else |
3efa1cc9 | 1314 | cdprintf("."); |
8465b050 | 1315 | } |
3efa1cc9 | 1316 | cdprintf("\n"); |
8465b050 IM |
1317 | } |
1318 | } | |
2cec19d9 | 1319 | dump_printf(".\n"); |
8465b050 IM |
1320 | } |
1321 | ||
8f18aec5 PZ |
1322 | static struct perf_header *header; |
1323 | ||
1324 | static struct perf_counter_attr *perf_header__find_attr(u64 id) | |
1325 | { | |
1326 | int i; | |
1327 | ||
1328 | for (i = 0; i < header->attrs; i++) { | |
1329 | struct perf_header_attr *attr = header->attr[i]; | |
1330 | int j; | |
1331 | ||
1332 | for (j = 0; j < attr->ids; j++) { | |
1333 | if (attr->id[j] == id) | |
1334 | return &attr->attr; | |
1335 | } | |
1336 | } | |
1337 | ||
1338 | return NULL; | |
1339 | } | |
1340 | ||
e9ea2fde PZ |
1341 | static int |
1342 | process_read_event(event_t *event, unsigned long offset, unsigned long head) | |
1343 | { | |
8f18aec5 PZ |
1344 | struct perf_counter_attr *attr = perf_header__find_attr(event->read.id); |
1345 | ||
8d513270 | 1346 | if (show_threads) { |
83a0944f | 1347 | const char *name = attr ? __event_name(attr->type, attr->config) |
8d513270 BG |
1348 | : "unknown"; |
1349 | perf_read_values_add_value(&show_threads_values, | |
1350 | event->read.pid, event->read.tid, | |
1351 | event->read.id, | |
1352 | name, | |
1353 | event->read.value); | |
1354 | } | |
1355 | ||
2cec19d9 | 1356 | dump_printf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n", |
e9ea2fde PZ |
1357 | (void *)(offset + head), |
1358 | (void *)(long)(event->header.size), | |
1359 | event->read.pid, | |
1360 | event->read.tid, | |
8f18aec5 PZ |
1361 | attr ? __event_name(attr->type, attr->config) |
1362 | : "FAIL", | |
e9ea2fde PZ |
1363 | event->read.value); |
1364 | ||
1365 | return 0; | |
1366 | } | |
1367 | ||
75051724 IM |
1368 | static int |
1369 | process_event(event_t *event, unsigned long offset, unsigned long head) | |
1370 | { | |
8465b050 IM |
1371 | trace_event(event); |
1372 | ||
75051724 | 1373 | switch (event->header.type) { |
e6e18ec7 PZ |
1374 | case PERF_EVENT_SAMPLE: |
1375 | return process_sample_event(event, offset, head); | |
1376 | ||
75051724 IM |
1377 | case PERF_EVENT_MMAP: |
1378 | return process_mmap_event(event, offset, head); | |
1379 | ||
1380 | case PERF_EVENT_COMM: | |
1381 | return process_comm_event(event, offset, head); | |
1382 | ||
62fc4453 | 1383 | case PERF_EVENT_FORK: |
27d028de PZ |
1384 | case PERF_EVENT_EXIT: |
1385 | return process_task_event(event, offset, head); | |
62fc4453 | 1386 | |
9d91a6f7 PZ |
1387 | case PERF_EVENT_LOST: |
1388 | return process_lost_event(event, offset, head); | |
1389 | ||
e9ea2fde PZ |
1390 | case PERF_EVENT_READ: |
1391 | return process_read_event(event, offset, head); | |
1392 | ||
d11444df IM |
1393 | /* |
1394 | * We dont process them right now but they are fine: | |
1395 | */ | |
62fc4453 | 1396 | |
d11444df IM |
1397 | case PERF_EVENT_THROTTLE: |
1398 | case PERF_EVENT_UNTHROTTLE: | |
1399 | return 0; | |
1400 | ||
d80d338d IM |
1401 | default: |
1402 | return -1; | |
1403 | } | |
1404 | ||
1405 | return 0; | |
1406 | } | |
1407 | ||
e6e18ec7 | 1408 | static u64 perf_header__sample_type(void) |
7c6a1c65 | 1409 | { |
83a0944f | 1410 | u64 type = 0; |
7c6a1c65 PZ |
1411 | int i; |
1412 | ||
1413 | for (i = 0; i < header->attrs; i++) { | |
1414 | struct perf_header_attr *attr = header->attr[i]; | |
1415 | ||
83a0944f IM |
1416 | if (!type) |
1417 | type = attr->attr.sample_type; | |
1418 | else if (type != attr->attr.sample_type) | |
e6e18ec7 | 1419 | die("non matching sample_type"); |
7c6a1c65 PZ |
1420 | } |
1421 | ||
83a0944f | 1422 | return type; |
7c6a1c65 | 1423 | } |
f5970550 | 1424 | |
d80d338d IM |
1425 | static int __cmd_report(void) |
1426 | { | |
75051724 | 1427 | int ret, rc = EXIT_FAILURE; |
d80d338d | 1428 | unsigned long offset = 0; |
7c6a1c65 | 1429 | unsigned long head, shift; |
83a0944f | 1430 | struct stat input_stat; |
d80d338d | 1431 | event_t *event; |
d80d338d | 1432 | uint32_t size; |
75051724 | 1433 | char *buf; |
d80d338d IM |
1434 | |
1435 | register_idle_thread(); | |
1436 | ||
8d513270 BG |
1437 | if (show_threads) |
1438 | perf_read_values_init(&show_threads_values); | |
1439 | ||
d80d338d IM |
1440 | input = open(input_name, O_RDONLY); |
1441 | if (input < 0) { | |
a14832ff IM |
1442 | fprintf(stderr, " failed to open file: %s", input_name); |
1443 | if (!strcmp(input_name, "perf.data")) | |
1444 | fprintf(stderr, " (try 'perf record' first)"); | |
1445 | fprintf(stderr, "\n"); | |
d80d338d IM |
1446 | exit(-1); |
1447 | } | |
1448 | ||
83a0944f | 1449 | ret = fstat(input, &input_stat); |
d80d338d IM |
1450 | if (ret < 0) { |
1451 | perror("failed to stat file"); | |
1452 | exit(-1); | |
1453 | } | |
1454 | ||
83a0944f | 1455 | if (!input_stat.st_size) { |
d80d338d IM |
1456 | fprintf(stderr, "zero-sized file, nothing to do!\n"); |
1457 | exit(0); | |
1458 | } | |
1459 | ||
7c6a1c65 PZ |
1460 | header = perf_header__read(input); |
1461 | head = header->data_offset; | |
f5970550 | 1462 | |
e6e18ec7 PZ |
1463 | sample_type = perf_header__sample_type(); |
1464 | ||
91b4eaea FW |
1465 | if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) { |
1466 | if (sort__has_parent) { | |
1467 | fprintf(stderr, "selected --sort parent, but no" | |
1468 | " callchain data. Did you call" | |
1469 | " perf record without -g?\n"); | |
1470 | exit(-1); | |
1471 | } | |
1472 | if (callchain) { | |
1473 | fprintf(stderr, "selected -c but no callchain data." | |
1474 | " Did you call perf record without" | |
1475 | " -g?\n"); | |
1476 | exit(-1); | |
1477 | } | |
b1a88349 FW |
1478 | } else if (callchain_param.mode != CHAIN_NONE && !callchain) { |
1479 | callchain = 1; | |
1480 | if (register_callchain_param(&callchain_param) < 0) { | |
1481 | fprintf(stderr, "Can't register callchain" | |
1482 | " params\n"); | |
1483 | exit(-1); | |
1484 | } | |
f5970550 PZ |
1485 | } |
1486 | ||
d80d338d IM |
1487 | if (load_kernel() < 0) { |
1488 | perror("failed to load kernel symbols"); | |
1489 | return EXIT_FAILURE; | |
1490 | } | |
1491 | ||
1492 | if (!full_paths) { | |
1493 | if (getcwd(__cwd, sizeof(__cwd)) == NULL) { | |
1494 | perror("failed to get the current directory"); | |
1495 | return EXIT_FAILURE; | |
1496 | } | |
1497 | cwdlen = strlen(cwd); | |
1498 | } else { | |
1499 | cwd = NULL; | |
1500 | cwdlen = 0; | |
1501 | } | |
7c6a1c65 PZ |
1502 | |
1503 | shift = page_size * (head / page_size); | |
1504 | offset += shift; | |
1505 | head -= shift; | |
1506 | ||
d80d338d IM |
1507 | remap: |
1508 | buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ, | |
1509 | MAP_SHARED, input, offset); | |
1510 | if (buf == MAP_FAILED) { | |
1511 | perror("failed to mmap file"); | |
1512 | exit(-1); | |
1513 | } | |
1514 | ||
1515 | more: | |
1516 | event = (event_t *)(buf + head); | |
1517 | ||
1518 | size = event->header.size; | |
1519 | if (!size) | |
1520 | size = 8; | |
1521 | ||
1522 | if (head + event->header.size >= page_size * mmap_window) { | |
83a0944f | 1523 | int munmap_ret; |
d80d338d | 1524 | |
7c6a1c65 PZ |
1525 | shift = page_size * (head / page_size); |
1526 | ||
83a0944f IM |
1527 | munmap_ret = munmap(buf, page_size * mmap_window); |
1528 | assert(munmap_ret == 0); | |
d80d338d IM |
1529 | |
1530 | offset += shift; | |
1531 | head -= shift; | |
1532 | goto remap; | |
1533 | } | |
1534 | ||
1535 | size = event->header.size; | |
1536 | ||
2cec19d9 | 1537 | dump_printf("\n%p [%p]: event: %d\n", |
b2fef076 IM |
1538 | (void *)(offset + head), |
1539 | (void *)(long)event->header.size, | |
1540 | event->header.type); | |
1541 | ||
d80d338d IM |
1542 | if (!size || process_event(event, offset, head) < 0) { |
1543 | ||
2cec19d9 | 1544 | dump_printf("%p [%p]: skipping unknown header type: %d\n", |
3502973d IM |
1545 | (void *)(offset + head), |
1546 | (void *)(long)(event->header.size), | |
1547 | event->header.type); | |
b7a16eac | 1548 | |
3e706114 | 1549 | total_unknown++; |
6142f9ec PZ |
1550 | |
1551 | /* | |
1552 | * assume we lost track of the stream, check alignment, and | |
1553 | * increment a single u64 in the hope to catch on again 'soon'. | |
1554 | */ | |
1555 | ||
1556 | if (unlikely(head & 7)) | |
1557 | head &= ~7ULL; | |
1558 | ||
1559 | size = 8; | |
97b07b69 | 1560 | } |
8fa66bdc | 1561 | |
6142f9ec | 1562 | head += size; |
f49515b1 | 1563 | |
7c6a1c65 | 1564 | if (offset + head >= header->data_offset + header->data_size) |
f5970550 PZ |
1565 | goto done; |
1566 | ||
83a0944f | 1567 | if (offset + head < (unsigned long)input_stat.st_size) |
8fa66bdc ACM |
1568 | goto more; |
1569 | ||
f5970550 | 1570 | done: |
8fa66bdc | 1571 | rc = EXIT_SUCCESS; |
8fa66bdc | 1572 | close(input); |
97b07b69 | 1573 | |
2cec19d9 FW |
1574 | dump_printf(" IP events: %10ld\n", total); |
1575 | dump_printf(" mmap events: %10ld\n", total_mmap); | |
1576 | dump_printf(" comm events: %10ld\n", total_comm); | |
1577 | dump_printf(" fork events: %10ld\n", total_fork); | |
1578 | dump_printf(" lost events: %10ld\n", total_lost); | |
1579 | dump_printf(" unknown events: %10ld\n", total_unknown); | |
97b07b69 | 1580 | |
3502973d | 1581 | if (dump_trace) |
97b07b69 | 1582 | return 0; |
97b07b69 | 1583 | |
9ac99545 | 1584 | if (verbose >= 3) |
6baa0a5a | 1585 | threads__fprintf(stdout, &threads); |
9ac99545 | 1586 | |
e7fb08b1 | 1587 | if (verbose >= 2) |
16f762a2 | 1588 | dsos__fprintf(stdout); |
16f762a2 | 1589 | |
8229289b | 1590 | collapse__resort(); |
c20ab37e | 1591 | output__resort(total); |
e7fb08b1 | 1592 | output__fprintf(stdout, total); |
8fa66bdc | 1593 | |
8d513270 BG |
1594 | if (show_threads) |
1595 | perf_read_values_destroy(&show_threads_values); | |
1596 | ||
8fa66bdc ACM |
1597 | return rc; |
1598 | } | |
1599 | ||
4eb3e478 FW |
1600 | static int |
1601 | parse_callchain_opt(const struct option *opt __used, const char *arg, | |
1602 | int unset __used) | |
1603 | { | |
c20ab37e FW |
1604 | char *tok; |
1605 | char *endptr; | |
1606 | ||
4eb3e478 FW |
1607 | callchain = 1; |
1608 | ||
1609 | if (!arg) | |
1610 | return 0; | |
1611 | ||
c20ab37e FW |
1612 | tok = strtok((char *)arg, ","); |
1613 | if (!tok) | |
1614 | return -1; | |
1615 | ||
1616 | /* get the output mode */ | |
1617 | if (!strncmp(tok, "graph", strlen(arg))) | |
805d127d | 1618 | callchain_param.mode = CHAIN_GRAPH_ABS; |
4eb3e478 | 1619 | |
c20ab37e | 1620 | else if (!strncmp(tok, "flat", strlen(arg))) |
805d127d FW |
1621 | callchain_param.mode = CHAIN_FLAT; |
1622 | ||
1623 | else if (!strncmp(tok, "fractal", strlen(arg))) | |
1624 | callchain_param.mode = CHAIN_GRAPH_REL; | |
1625 | ||
b1a88349 FW |
1626 | else if (!strncmp(tok, "none", strlen(arg))) { |
1627 | callchain_param.mode = CHAIN_NONE; | |
1628 | callchain = 0; | |
1629 | ||
1630 | return 0; | |
1631 | } | |
1632 | ||
4eb3e478 FW |
1633 | else |
1634 | return -1; | |
1635 | ||
c20ab37e FW |
1636 | /* get the min percentage */ |
1637 | tok = strtok(NULL, ","); | |
1638 | if (!tok) | |
805d127d | 1639 | goto setup; |
c20ab37e | 1640 | |
805d127d | 1641 | callchain_param.min_percent = strtod(tok, &endptr); |
c20ab37e FW |
1642 | if (tok == endptr) |
1643 | return -1; | |
1644 | ||
805d127d FW |
1645 | setup: |
1646 | if (register_callchain_param(&callchain_param) < 0) { | |
1647 | fprintf(stderr, "Can't register callchain params\n"); | |
1648 | return -1; | |
1649 | } | |
4eb3e478 FW |
1650 | return 0; |
1651 | } | |
1652 | ||
53cb8bc2 IM |
1653 | static const char * const report_usage[] = { |
1654 | "perf report [<options>] <command>", | |
1655 | NULL | |
1656 | }; | |
1657 | ||
1658 | static const struct option options[] = { | |
1659 | OPT_STRING('i', "input", &input_name, "file", | |
1660 | "input file name"), | |
815e777f ACM |
1661 | OPT_BOOLEAN('v', "verbose", &verbose, |
1662 | "be more verbose (show symbol address, etc)"), | |
97b07b69 IM |
1663 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
1664 | "dump raw trace in ASCII"), | |
83a0944f | 1665 | OPT_STRING('k', "vmlinux", &vmlinux_name, "file", "vmlinux pathname"), |
42976487 MG |
1666 | OPT_BOOLEAN('m', "modules", &modules, |
1667 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | |
e3d7e183 ACM |
1668 | OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, |
1669 | "Show a column with the number of samples"), | |
8d513270 BG |
1670 | OPT_BOOLEAN('T', "threads", &show_threads, |
1671 | "Show per-thread event counters"), | |
9f866697 BG |
1672 | OPT_STRING(0, "pretty", &pretty_printing_style, "key", |
1673 | "pretty printing style key: normal raw"), | |
63299f05 | 1674 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
b25bcf2f | 1675 | "sort by key(s): pid, comm, dso, symbol, parent"), |
b78c07d4 ACM |
1676 | OPT_BOOLEAN('P', "full-paths", &full_paths, |
1677 | "Don't shorten the pathnames taking into account the cwd"), | |
b25bcf2f IM |
1678 | OPT_STRING('p', "parent", &parent_pattern, "regex", |
1679 | "regex filter to identify parent, see: '--sort parent'"), | |
b8e6d829 IM |
1680 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, |
1681 | "Only display entries with parent-match"), | |
1483b19f | 1682 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", |
c20ab37e | 1683 | "Display callchains using output_type and min percent threshold. " |
1483b19f | 1684 | "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), |
25903407 ACM |
1685 | OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", |
1686 | "only consider symbols in these dsos"), | |
cc8b88b1 ACM |
1687 | OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", |
1688 | "only consider symbols in these comms"), | |
7bec7a91 ACM |
1689 | OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", |
1690 | "only consider these symbols"), | |
52d422de ACM |
1691 | OPT_STRING('w', "column-widths", &col_width_list_str, |
1692 | "width[,width...]", | |
1693 | "don't try to adjust column width, use these fixed values"), | |
1694 | OPT_STRING('t', "field-separator", &field_sep, "separator", | |
1695 | "separator for columns, no spaces will be added between " | |
1696 | "columns '.' is reserved."), | |
53cb8bc2 IM |
1697 | OPT_END() |
1698 | }; | |
1699 | ||
5352f35d IM |
1700 | static void setup_sorting(void) |
1701 | { | |
1702 | char *tmp, *tok, *str = strdup(sort_order); | |
1703 | ||
1704 | for (tok = strtok_r(str, ", ", &tmp); | |
1705 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | |
1706 | if (sort_dimension__add(tok) < 0) { | |
1707 | error("Unknown --sort key: `%s'", tok); | |
1708 | usage_with_options(report_usage, options); | |
1709 | } | |
1710 | } | |
1711 | ||
1712 | free(str); | |
1713 | } | |
1714 | ||
cc8b88b1 | 1715 | static void setup_list(struct strlist **list, const char *list_str, |
021191b3 ACM |
1716 | struct sort_entry *se, const char *list_name, |
1717 | FILE *fp) | |
cc8b88b1 ACM |
1718 | { |
1719 | if (list_str) { | |
1720 | *list = strlist__new(true, list_str); | |
1721 | if (!*list) { | |
1722 | fprintf(stderr, "problems parsing %s list\n", | |
1723 | list_name); | |
1724 | exit(129); | |
1725 | } | |
021191b3 ACM |
1726 | if (strlist__nr_entries(*list) == 1) { |
1727 | fprintf(fp, "# %s: %s\n", list_name, | |
1728 | strlist__entry(*list, 0)->s); | |
1729 | se->elide = true; | |
1730 | } | |
cc8b88b1 ACM |
1731 | } |
1732 | } | |
1733 | ||
f37a291c | 1734 | int cmd_report(int argc, const char **argv, const char *prefix __used) |
53cb8bc2 | 1735 | { |
a2928c42 | 1736 | symbol__init(); |
53cb8bc2 IM |
1737 | |
1738 | page_size = getpagesize(); | |
1739 | ||
edc52dea | 1740 | argc = parse_options(argc, argv, options, report_usage, 0); |
53cb8bc2 | 1741 | |
1aa16738 PZ |
1742 | setup_sorting(); |
1743 | ||
021191b3 | 1744 | if (parent_pattern != default_parent_pattern) { |
b8e6d829 | 1745 | sort_dimension__add("parent"); |
021191b3 ACM |
1746 | sort_parent.elide = 1; |
1747 | } else | |
b8e6d829 IM |
1748 | exclude_other = 0; |
1749 | ||
edc52dea IM |
1750 | /* |
1751 | * Any (unrecognized) arguments left? | |
1752 | */ | |
1753 | if (argc) | |
1754 | usage_with_options(report_usage, options); | |
1755 | ||
a930d2c0 IM |
1756 | setup_pager(); |
1757 | ||
021191b3 ACM |
1758 | setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout); |
1759 | setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout); | |
1760 | setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout); | |
25903407 | 1761 | |
52d422de ACM |
1762 | if (field_sep && *field_sep == '.') { |
1763 | fputs("'.' is the only non valid --field-separator argument\n", | |
1764 | stderr); | |
1765 | exit(129); | |
1766 | } | |
1767 | ||
53cb8bc2 IM |
1768 | return __cmd_report(); |
1769 | } |