1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * builtin-annotate.c |
4 | * |
5 | * Builtin annotate command: Analyze the perf.data input file, |
6 | * look up and read DSOs and symbol information and display |
7 | * a histogram of results, along various sorting keys. |
8 | */ |
9 | #include "builtin.h" |
10 | |
11 | #include "util/color.h" |
12 | #include <linux/list.h> |
13 | #include "util/cache.h" |
14 | #include <linux/rbtree.h> |
15 | #include <linux/zalloc.h> |
16 | #include "util/symbol.h" |
17 | |
18 | #include "util/debug.h" |
19 | |
20 | #include "util/evlist.h" |
21 | #include "util/evsel.h" |
22 | #include "util/annotate.h" |
23 | #include "util/annotate-data.h" |
24 | #include "util/event.h" |
25 | #include <subcmd/parse-options.h> |
26 | #include "util/parse-events.h" |
27 | #include "util/sort.h" |
28 | #include "util/hist.h" |
29 | #include "util/dso.h" |
30 | #include "util/machine.h" |
31 | #include "util/map.h" |
32 | #include "util/session.h" |
33 | #include "util/tool.h" |
34 | #include "util/data.h" |
35 | #include "arch/common.h" |
36 | #include "util/block-range.h" |
37 | #include "util/map_symbol.h" |
38 | #include "util/branch.h" |
39 | #include "util/util.h" |
40 | |
41 | #include <dlfcn.h> |
42 | #include <errno.h> |
43 | #include <linux/bitmap.h> |
44 | #include <linux/err.h> |
45 | |
46 | struct perf_annotate { |
47 | struct perf_tool tool; |
48 | struct perf_session *session; |
49 | #ifdef HAVE_SLANG_SUPPORT |
50 | bool use_tui; |
51 | #endif |
52 | bool use_stdio, use_stdio2; |
53 | #ifdef HAVE_GTK2_SUPPORT |
54 | bool use_gtk; |
55 | #endif |
56 | bool skip_missing; |
57 | bool has_br_stack; |
58 | bool group_set; |
59 | bool data_type; |
60 | bool type_stat; |
61 | bool insn_stat; |
62 | float min_percent; |
63 | const char *sym_hist_filter; |
64 | const char *cpu_list; |
65 | const char *target_data_type; |
66 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
67 | }; |
68 | |
69 | /* |
70 | * Given one basic block: |
71 | * |
72 | * from to branch_i |
73 | * * ----> * |
74 | * | |
75 | * | block |
76 | * v |
77 | * * ----> * |
78 | * from to branch_i+1 |
79 | * |
80 | * where the horizontal are the branches and the vertical is the executed |
81 | * block of instructions. |
82 | * |
83 | * We count, for each 'instruction', the number of blocks that covered it as |
84 | * well as count the ratio each branch is taken. |
85 | * |
86 | * We can do this without knowing the actual instruction stream by keeping |
87 | * track of the address ranges. We break down ranges such that there is no |
88 | * overlap and iterate from the start until the end. |
89 | * |
90 | * @acme: once we parse the objdump output _before_ processing the samples, |
91 | * we can easily fold the branch.cycles IPC bits in. |
92 | */ |
93 | static void process_basic_block(struct addr_map_symbol *start, |
94 | struct addr_map_symbol *end, |
95 | struct branch_flags *flags) |
96 | { |
97 | struct symbol *sym = start->ms.sym; |
98 | struct annotation *notes = sym ? symbol__annotation(sym) : NULL; |
99 | struct block_range_iter iter; |
100 | struct block_range *entry; |
101 | struct annotated_branch *branch; |
102 | |
103 | /* |
104 | * Sanity; NULL isn't executable and the CPU cannot execute backwards |
105 | */ |
106 | if (!start->addr || start->addr > end->addr) |
107 | return; |
108 | |
109 | iter = block_range__create(start: start->addr, end: end->addr); |
110 | if (!block_range_iter__valid(iter: &iter)) |
111 | return; |
112 | |
113 | branch = annotation__get_branch(notes); |
114 | |
115 | /* |
116 | * First block in range is a branch target. |
117 | */ |
118 | entry = block_range_iter(iter: &iter); |
119 | assert(entry->is_target); |
120 | entry->entry++; |
121 | |
122 | do { |
123 | entry = block_range_iter(iter: &iter); |
124 | |
125 | entry->coverage++; |
126 | entry->sym = sym; |
127 | |
128 | if (branch) |
129 | branch->max_coverage = max(branch->max_coverage, entry->coverage); |
130 | |
131 | } while (block_range_iter__next(iter: &iter)); |
132 | |
133 | /* |
134 | * Last block in rage is a branch. |
135 | */ |
136 | entry = block_range_iter(iter: &iter); |
137 | assert(entry->is_branch); |
138 | entry->taken++; |
139 | if (flags->predicted) |
140 | entry->pred++; |
141 | } |
142 | |
143 | static void process_branch_stack(struct branch_stack *bs, struct addr_location *al, |
144 | struct perf_sample *sample) |
145 | { |
146 | struct addr_map_symbol *prev = NULL; |
147 | struct branch_info *bi; |
148 | int i; |
149 | |
150 | if (!bs || !bs->nr) |
151 | return; |
152 | |
153 | bi = sample__resolve_bstack(sample, al); |
154 | if (!bi) |
155 | return; |
156 | |
157 | for (i = bs->nr - 1; i >= 0; i--) { |
158 | /* |
159 | * XXX filter against symbol |
160 | */ |
161 | if (prev) |
162 | process_basic_block(start: prev, end: &bi[i].from, flags: &bi[i].flags); |
163 | prev = &bi[i].to; |
164 | } |
165 | |
166 | free(bi); |
167 | } |
168 | |
169 | static int hist_iter__branch_callback(struct hist_entry_iter *iter, |
170 | struct addr_location *al __maybe_unused, |
171 | bool single __maybe_unused, |
172 | void *arg __maybe_unused) |
173 | { |
174 | struct hist_entry *he = iter->he; |
175 | struct branch_info *bi; |
176 | struct perf_sample *sample = iter->sample; |
177 | struct evsel *evsel = iter->evsel; |
178 | int err; |
179 | |
180 | bi = he->branch_info; |
181 | err = addr_map_symbol__inc_samples(ams: &bi->from, sample, evsel); |
182 | |
183 | if (err) |
184 | goto out; |
185 | |
186 | err = addr_map_symbol__inc_samples(ams: &bi->to, sample, evsel); |
187 | |
188 | out: |
189 | return err; |
190 | } |
191 | |
192 | static int process_branch_callback(struct evsel *evsel, |
193 | struct perf_sample *sample, |
194 | struct addr_location *al, |
195 | struct perf_annotate *ann, |
196 | struct machine *machine) |
197 | { |
198 | struct hist_entry_iter iter = { |
199 | .evsel = evsel, |
200 | .sample = sample, |
201 | .add_entry_cb = hist_iter__branch_callback, |
202 | .hide_unresolved = symbol_conf.hide_unresolved, |
203 | .ops = &hist_iter_branch, |
204 | }; |
205 | struct addr_location a; |
206 | int ret; |
207 | |
208 | addr_location__init(al: &a); |
209 | if (machine__resolve(machine, al: &a, sample) < 0) { |
210 | ret = -1; |
211 | goto out; |
212 | } |
213 | |
214 | if (a.sym == NULL) { |
215 | ret = 0; |
216 | goto out; |
217 | } |
218 | |
219 | if (a.map != NULL) |
220 | map__dso(map: a.map)->hit = 1; |
221 | |
222 | hist__account_cycles(bs: sample->branch_stack, al, sample, nonany_branch_mode: false, NULL); |
223 | |
224 | ret = hist_entry_iter__add(iter: &iter, al: &a, PERF_MAX_STACK_DEPTH, arg: ann); |
225 | out: |
226 | addr_location__exit(al: &a); |
227 | return ret; |
228 | } |
229 | |
230 | static bool has_annotation(struct perf_annotate *ann) |
231 | { |
232 | return ui__has_annotation() || ann->use_stdio2; |
233 | } |
234 | |
235 | static int evsel__add_sample(struct evsel *evsel, struct perf_sample *sample, |
236 | struct addr_location *al, struct perf_annotate *ann, |
237 | struct machine *machine) |
238 | { |
239 | struct hists *hists = evsel__hists(evsel); |
240 | struct hist_entry *he; |
241 | int ret; |
242 | |
243 | if ((!ann->has_br_stack || !has_annotation(ann)) && |
244 | ann->sym_hist_filter != NULL && |
245 | (al->sym == NULL || |
246 | strcmp(ann->sym_hist_filter, al->sym->name) != 0)) { |
247 | /* We're only interested in a symbol named sym_hist_filter */ |
248 | /* |
249 | * FIXME: why isn't this done in the symbol_filter when loading |
250 | * the DSO? |
251 | */ |
252 | if (al->sym != NULL) { |
253 | struct dso *dso = map__dso(map: al->map); |
254 | |
255 | rb_erase_cached(node: &al->sym->rb_node, root: &dso->symbols); |
256 | symbol__delete(sym: al->sym); |
257 | dso__reset_find_symbol_cache(dso); |
258 | } |
259 | return 0; |
260 | } |
261 | |
262 | /* |
263 | * XXX filtered samples can still have branch entries pointing into our |
264 | * symbol and are missed. |
265 | */ |
266 | process_branch_stack(bs: sample->branch_stack, al, sample); |
267 | |
268 | if (ann->has_br_stack && has_annotation(ann)) |
269 | return process_branch_callback(evsel, sample, al, ann, machine); |
270 | |
271 | he = hists__add_entry(hists, al, NULL, NULL, NULL, NULL, sample, sample_self: true); |
272 | if (he == NULL) |
273 | return -ENOMEM; |
274 | |
275 | ret = hist_entry__inc_addr_samples(he, sample, evsel, addr: al->addr); |
276 | hists__inc_nr_samples(hists, filtered: true); |
277 | return ret; |
278 | } |
279 | |
280 | static int process_sample_event(struct perf_tool *tool, |
281 | union perf_event *event, |
282 | struct perf_sample *sample, |
283 | struct evsel *evsel, |
284 | struct machine *machine) |
285 | { |
286 | struct perf_annotate *ann = container_of(tool, struct perf_annotate, tool); |
287 | struct addr_location al; |
288 | int ret = 0; |
289 | |
290 | addr_location__init(al: &al); |
291 | if (machine__resolve(machine, al: &al, sample) < 0) { |
292 | pr_warning("problem processing %d event, skipping it.\n" , |
293 | event->header.type); |
294 | ret = -1; |
295 | goto out_put; |
296 | } |
297 | |
298 | if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap)) |
299 | goto out_put; |
300 | |
301 | if (!al.filtered && |
302 | evsel__add_sample(evsel, sample, al: &al, ann, machine)) { |
303 | pr_warning("problem incrementing symbol count, " |
304 | "skipping event\n" ); |
305 | ret = -1; |
306 | } |
307 | out_put: |
308 | addr_location__exit(al: &al); |
309 | return ret; |
310 | } |
311 | |
312 | static int process_feature_event(struct perf_session *session, |
313 | union perf_event *event) |
314 | { |
315 | if (event->feat.feat_id < HEADER_LAST_FEATURE) |
316 | return perf_event__process_feature(session, event); |
317 | return 0; |
318 | } |
319 | |
320 | static int hist_entry__tty_annotate(struct hist_entry *he, |
321 | struct evsel *evsel, |
322 | struct perf_annotate *ann) |
323 | { |
324 | if (!ann->use_stdio2) |
325 | return symbol__tty_annotate(ms: &he->ms, evsel); |
326 | |
327 | return symbol__tty_annotate2(ms: &he->ms, evsel); |
328 | } |
329 | |
330 | static void (struct hist_entry *he, struct evsel *evsel) |
331 | { |
332 | struct dso *dso = map__dso(map: he->ms.map); |
333 | int nr_members = 1; |
334 | int nr_samples = he->stat.nr_events; |
335 | |
336 | if (evsel__is_group_event(evsel)) { |
337 | struct hist_entry *pair; |
338 | |
339 | list_for_each_entry(pair, &he->pairs.head, pairs.node) |
340 | nr_samples += pair->stat.nr_events; |
341 | } |
342 | |
343 | printf("Annotate type: '%s' in %s (%d samples):\n" , |
344 | he->mem_type->self.type_name, dso->name, nr_samples); |
345 | |
346 | if (evsel__is_group_event(evsel)) { |
347 | struct evsel *pos; |
348 | int i = 0; |
349 | |
350 | for_each_group_evsel(pos, evsel) |
351 | printf(" event[%d] = %s\n" , i++, pos->name); |
352 | |
353 | nr_members = evsel->core.nr_members; |
354 | } |
355 | |
356 | printf("============================================================================\n" ); |
357 | printf("%*s %10s %10s %s\n" , 11 * nr_members, "samples" , "offset" , "size" , "field" ); |
358 | } |
359 | |
360 | static void print_annotated_data_type(struct annotated_data_type *mem_type, |
361 | struct annotated_member *member, |
362 | struct evsel *evsel, int indent) |
363 | { |
364 | struct annotated_member *child; |
365 | struct type_hist *h = mem_type->histograms[evsel->core.idx]; |
366 | int i, nr_events = 1, samples = 0; |
367 | |
368 | for (i = 0; i < member->size; i++) |
369 | samples += h->addr[member->offset + i].nr_samples; |
370 | printf(" %10d" , samples); |
371 | |
372 | if (evsel__is_group_event(evsel)) { |
373 | struct evsel *pos; |
374 | |
375 | for_each_group_member(pos, evsel) { |
376 | h = mem_type->histograms[pos->core.idx]; |
377 | |
378 | samples = 0; |
379 | for (i = 0; i < member->size; i++) |
380 | samples += h->addr[member->offset + i].nr_samples; |
381 | printf(" %10d" , samples); |
382 | } |
383 | nr_events = evsel->core.nr_members; |
384 | } |
385 | |
386 | printf(" %10d %10d %*s%s\t%s" , |
387 | member->offset, member->size, indent, "" , member->type_name, |
388 | member->var_name ?: "" ); |
389 | |
390 | if (!list_empty(head: &member->children)) |
391 | printf(" {\n" ); |
392 | |
393 | list_for_each_entry(child, &member->children, node) |
394 | print_annotated_data_type(mem_type, member: child, evsel, indent: indent + 4); |
395 | |
396 | if (!list_empty(head: &member->children)) |
397 | printf("%*s}" , 11 * nr_events + 24 + indent, "" ); |
398 | printf(";\n" ); |
399 | } |
400 | |
401 | static void print_annotate_data_stat(struct annotated_data_stat *s) |
402 | { |
403 | #define PRINT_STAT(fld) if (s->fld) printf("%10d : %s\n", s->fld, #fld) |
404 | |
405 | int bad = s->no_sym + |
406 | s->no_insn + |
407 | s->no_insn_ops + |
408 | s->no_mem_ops + |
409 | s->no_reg + |
410 | s->no_dbginfo + |
411 | s->no_cuinfo + |
412 | s->no_var + |
413 | s->no_typeinfo + |
414 | s->invalid_size + |
415 | s->bad_offset; |
416 | int ok = s->total - bad; |
417 | |
418 | printf("Annotate data type stats:\n" ); |
419 | printf("total %d, ok %d (%.1f%%), bad %d (%.1f%%)\n" , |
420 | s->total, ok, 100.0 * ok / (s->total ?: 1), bad, 100.0 * bad / (s->total ?: 1)); |
421 | printf("-----------------------------------------------------------\n" ); |
422 | PRINT_STAT(no_sym); |
423 | PRINT_STAT(no_insn); |
424 | PRINT_STAT(no_insn_ops); |
425 | PRINT_STAT(no_mem_ops); |
426 | PRINT_STAT(no_reg); |
427 | PRINT_STAT(no_dbginfo); |
428 | PRINT_STAT(no_cuinfo); |
429 | PRINT_STAT(no_var); |
430 | PRINT_STAT(no_typeinfo); |
431 | PRINT_STAT(invalid_size); |
432 | PRINT_STAT(bad_offset); |
433 | printf("\n" ); |
434 | |
435 | #undef PRINT_STAT |
436 | } |
437 | |
438 | static void print_annotate_item_stat(struct list_head *head, const char *title) |
439 | { |
440 | struct annotated_item_stat *istat, *pos, *iter; |
441 | int total_good, total_bad, total; |
442 | int sum1, sum2; |
443 | LIST_HEAD(tmp); |
444 | |
445 | /* sort the list by count */ |
446 | list_splice_init(list: head, head: &tmp); |
447 | total_good = total_bad = 0; |
448 | |
449 | list_for_each_entry_safe(istat, pos, &tmp, list) { |
450 | total_good += istat->good; |
451 | total_bad += istat->bad; |
452 | sum1 = istat->good + istat->bad; |
453 | |
454 | list_for_each_entry(iter, head, list) { |
455 | sum2 = iter->good + iter->bad; |
456 | if (sum1 > sum2) |
457 | break; |
458 | } |
459 | list_move_tail(list: &istat->list, head: &iter->list); |
460 | } |
461 | total = total_good + total_bad; |
462 | |
463 | printf("Annotate %s stats\n" , title); |
464 | printf("total %d, ok %d (%.1f%%), bad %d (%.1f%%)\n\n" , total, |
465 | total_good, 100.0 * total_good / (total ?: 1), |
466 | total_bad, 100.0 * total_bad / (total ?: 1)); |
467 | printf(" %-10s: %5s %5s\n" , "Name" , "Good" , "Bad" ); |
468 | printf("-----------------------------------------------------------\n" ); |
469 | list_for_each_entry(istat, head, list) |
470 | printf(" %-10s: %5d %5d\n" , istat->name, istat->good, istat->bad); |
471 | printf("\n" ); |
472 | } |
473 | |
474 | static void hists__find_annotations(struct hists *hists, |
475 | struct evsel *evsel, |
476 | struct perf_annotate *ann) |
477 | { |
478 | struct rb_node *nd = rb_first_cached(&hists->entries), *next; |
479 | int key = K_RIGHT; |
480 | |
481 | if (ann->type_stat) |
482 | print_annotate_data_stat(s: &ann_data_stat); |
483 | if (ann->insn_stat) |
484 | print_annotate_item_stat(head: &ann_insn_stat, title: "Instruction" ); |
485 | |
486 | while (nd) { |
487 | struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); |
488 | struct annotation *notes; |
489 | |
490 | if (he->ms.sym == NULL || map__dso(map: he->ms.map)->annotate_warned) |
491 | goto find_next; |
492 | |
493 | if (ann->sym_hist_filter && |
494 | (strcmp(he->ms.sym->name, ann->sym_hist_filter) != 0)) |
495 | goto find_next; |
496 | |
497 | if (ann->min_percent) { |
498 | float percent = 0; |
499 | u64 total = hists__total_period(hists); |
500 | |
501 | if (total) |
502 | percent = 100.0 * he->stat.period / total; |
503 | |
504 | if (percent < ann->min_percent) |
505 | goto find_next; |
506 | } |
507 | |
508 | notes = symbol__annotation(sym: he->ms.sym); |
509 | if (notes->src == NULL) { |
510 | find_next: |
511 | if (key == K_LEFT || key == '<') |
512 | nd = rb_prev(nd); |
513 | else |
514 | nd = rb_next(nd); |
515 | continue; |
516 | } |
517 | |
518 | if (ann->data_type) { |
519 | /* skip unknown type */ |
520 | if (he->mem_type->histograms == NULL) |
521 | goto find_next; |
522 | |
523 | if (ann->target_data_type) { |
524 | const char *type_name = he->mem_type->self.type_name; |
525 | |
526 | /* skip 'struct ' prefix in the type name */ |
527 | if (strncmp(ann->target_data_type, "struct " , 7) && |
528 | !strncmp(type_name, "struct " , 7)) |
529 | type_name += 7; |
530 | |
531 | /* skip 'union ' prefix in the type name */ |
532 | if (strncmp(ann->target_data_type, "union " , 6) && |
533 | !strncmp(type_name, "union " , 6)) |
534 | type_name += 6; |
535 | |
536 | if (strcmp(ann->target_data_type, type_name)) |
537 | goto find_next; |
538 | } |
539 | |
540 | print_annotated_data_header(he, evsel); |
541 | print_annotated_data_type(mem_type: he->mem_type, member: &he->mem_type->self, evsel, indent: 0); |
542 | printf("\n" ); |
543 | goto find_next; |
544 | } |
545 | |
546 | if (use_browser == 2) { |
547 | int ret; |
548 | int (*annotate)(struct hist_entry *he, |
549 | struct evsel *evsel, |
550 | struct hist_browser_timer *hbt); |
551 | |
552 | annotate = dlsym(perf_gtk_handle, |
553 | "hist_entry__gtk_annotate" ); |
554 | if (annotate == NULL) { |
555 | ui__error(format: "GTK browser not found!\n" ); |
556 | return; |
557 | } |
558 | |
559 | ret = annotate(he, evsel, NULL); |
560 | if (!ret || !ann->skip_missing) |
561 | return; |
562 | |
563 | /* skip missing symbols */ |
564 | nd = rb_next(nd); |
565 | } else if (use_browser == 1) { |
566 | key = hist_entry__tui_annotate(he, evsel, NULL); |
567 | |
568 | switch (key) { |
569 | case -1: |
570 | if (!ann->skip_missing) |
571 | return; |
572 | /* fall through */ |
573 | case K_RIGHT: |
574 | case '>': |
575 | next = rb_next(nd); |
576 | break; |
577 | case K_LEFT: |
578 | case '<': |
579 | next = rb_prev(nd); |
580 | break; |
581 | default: |
582 | return; |
583 | } |
584 | |
585 | if (next != NULL) |
586 | nd = next; |
587 | } else { |
588 | hist_entry__tty_annotate(he, evsel, ann); |
589 | nd = rb_next(nd); |
590 | } |
591 | } |
592 | } |
593 | |
594 | static int __cmd_annotate(struct perf_annotate *ann) |
595 | { |
596 | int ret; |
597 | struct perf_session *session = ann->session; |
598 | struct evsel *pos; |
599 | u64 total_nr_samples; |
600 | |
601 | if (ann->cpu_list) { |
602 | ret = perf_session__cpu_bitmap(session, cpu_list: ann->cpu_list, |
603 | cpu_bitmap: ann->cpu_bitmap); |
604 | if (ret) |
605 | goto out; |
606 | } |
607 | |
608 | if (!annotate_opts.objdump_path) { |
609 | ret = perf_env__lookup_objdump(env: &session->header.env, |
610 | path: &annotate_opts.objdump_path); |
611 | if (ret) |
612 | goto out; |
613 | } |
614 | |
615 | ret = perf_session__process_events(session); |
616 | if (ret) |
617 | goto out; |
618 | |
619 | if (dump_trace) { |
620 | perf_session__fprintf_nr_events(session, stdout, false); |
621 | evlist__fprintf_nr_events(session->evlist, stdout, false); |
622 | goto out; |
623 | } |
624 | |
625 | if (verbose > 3) |
626 | perf_session__fprintf(session, stdout); |
627 | |
628 | if (verbose > 2) |
629 | perf_session__fprintf_dsos(session, stdout); |
630 | |
631 | total_nr_samples = 0; |
632 | evlist__for_each_entry(session->evlist, pos) { |
633 | struct hists *hists = evsel__hists(evsel: pos); |
634 | u32 nr_samples = hists->stats.nr_samples; |
635 | |
636 | if (nr_samples > 0) { |
637 | total_nr_samples += nr_samples; |
638 | hists__collapse_resort(hists, NULL); |
639 | /* Don't sort callchain */ |
640 | evsel__reset_sample_bit(pos, CALLCHAIN); |
641 | evsel__output_resort(evsel: pos, NULL); |
642 | |
643 | /* |
644 | * An event group needs to display other events too. |
645 | * Let's delay printing until other events are processed. |
646 | */ |
647 | if (symbol_conf.event_group) { |
648 | if (!evsel__is_group_leader(evsel: pos)) { |
649 | struct hists *leader_hists; |
650 | |
651 | leader_hists = evsel__hists(evsel: evsel__leader(evsel: pos)); |
652 | hists__match(leader: leader_hists, other: hists); |
653 | hists__link(leader: leader_hists, other: hists); |
654 | } |
655 | continue; |
656 | } |
657 | |
658 | hists__find_annotations(hists, evsel: pos, ann); |
659 | } |
660 | } |
661 | |
662 | if (total_nr_samples == 0) { |
663 | ui__error(format: "The %s data has no samples!\n" , session->data->path); |
664 | goto out; |
665 | } |
666 | |
667 | /* Display group events together */ |
668 | evlist__for_each_entry(session->evlist, pos) { |
669 | struct hists *hists = evsel__hists(evsel: pos); |
670 | u32 nr_samples = hists->stats.nr_samples; |
671 | |
672 | if (nr_samples == 0) |
673 | continue; |
674 | |
675 | if (!symbol_conf.event_group || !evsel__is_group_leader(evsel: pos)) |
676 | continue; |
677 | |
678 | hists__find_annotations(hists, evsel: pos, ann); |
679 | } |
680 | |
681 | if (use_browser == 2) { |
682 | void (*show_annotations)(void); |
683 | |
684 | show_annotations = dlsym(perf_gtk_handle, |
685 | "perf_gtk__show_annotations" ); |
686 | if (show_annotations == NULL) { |
687 | ui__error(format: "GTK browser not found!\n" ); |
688 | goto out; |
689 | } |
690 | show_annotations(); |
691 | } |
692 | |
693 | out: |
694 | return ret; |
695 | } |
696 | |
697 | static int parse_percent_limit(const struct option *opt, const char *str, |
698 | int unset __maybe_unused) |
699 | { |
700 | struct perf_annotate *ann = opt->value; |
701 | double pcnt = strtof(str, NULL); |
702 | |
703 | ann->min_percent = pcnt; |
704 | return 0; |
705 | } |
706 | |
707 | static int parse_data_type(const struct option *opt, const char *str, int unset) |
708 | { |
709 | struct perf_annotate *ann = opt->value; |
710 | |
711 | ann->data_type = !unset; |
712 | if (str) |
713 | ann->target_data_type = strdup(str); |
714 | |
715 | return 0; |
716 | } |
717 | |
718 | static const char * const annotate_usage[] = { |
719 | "perf annotate [<options>]" , |
720 | NULL |
721 | }; |
722 | |
723 | int cmd_annotate(int argc, const char **argv) |
724 | { |
725 | struct perf_annotate annotate = { |
726 | .tool = { |
727 | .sample = process_sample_event, |
728 | .mmap = perf_event__process_mmap, |
729 | .mmap2 = perf_event__process_mmap2, |
730 | .comm = perf_event__process_comm, |
731 | .exit = perf_event__process_exit, |
732 | .fork = perf_event__process_fork, |
733 | .namespaces = perf_event__process_namespaces, |
734 | .attr = perf_event__process_attr, |
735 | .build_id = perf_event__process_build_id, |
736 | #ifdef HAVE_LIBTRACEEVENT |
737 | .tracing_data = perf_event__process_tracing_data, |
738 | #endif |
739 | .id_index = perf_event__process_id_index, |
740 | .auxtrace_info = perf_event__process_auxtrace_info, |
741 | .auxtrace = perf_event__process_auxtrace, |
742 | .feature = process_feature_event, |
743 | .ordered_events = true, |
744 | .ordering_requires_timestamps = true, |
745 | }, |
746 | }; |
747 | struct perf_data data = { |
748 | .mode = PERF_DATA_MODE_READ, |
749 | }; |
750 | struct itrace_synth_opts itrace_synth_opts = { |
751 | .set = 0, |
752 | }; |
753 | const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL; |
754 | struct option options[] = { |
755 | OPT_STRING('i', "input" , &input_name, "file" , |
756 | "input file name" ), |
757 | OPT_STRING('d', "dsos" , &symbol_conf.dso_list_str, "dso[,dso...]" , |
758 | "only consider symbols in these dsos" ), |
759 | OPT_STRING('s', "symbol" , &annotate.sym_hist_filter, "symbol" , |
760 | "symbol to annotate" ), |
761 | OPT_BOOLEAN('f', "force" , &data.force, "don't complain, do it" ), |
762 | OPT_INCR('v', "verbose" , &verbose, |
763 | "be more verbose (show symbol address, etc)" ), |
764 | OPT_BOOLEAN('q', "quiet" , &quiet, "do now show any warnings or messages" ), |
765 | OPT_BOOLEAN('D', "dump-raw-trace" , &dump_trace, |
766 | "dump raw trace in ASCII" ), |
767 | #ifdef HAVE_GTK2_SUPPORT |
768 | OPT_BOOLEAN(0, "gtk" , &annotate.use_gtk, "Use the GTK interface" ), |
769 | #endif |
770 | #ifdef HAVE_SLANG_SUPPORT |
771 | OPT_BOOLEAN(0, "tui" , &annotate.use_tui, "Use the TUI interface" ), |
772 | #endif |
773 | OPT_BOOLEAN(0, "stdio" , &annotate.use_stdio, "Use the stdio interface" ), |
774 | OPT_BOOLEAN(0, "stdio2" , &annotate.use_stdio2, "Use the stdio interface" ), |
775 | OPT_BOOLEAN(0, "ignore-vmlinux" , &symbol_conf.ignore_vmlinux, |
776 | "don't load vmlinux even if found" ), |
777 | OPT_STRING('k', "vmlinux" , &symbol_conf.vmlinux_name, |
778 | "file" , "vmlinux pathname" ), |
779 | OPT_BOOLEAN('m', "modules" , &symbol_conf.use_modules, |
780 | "load module symbols - WARNING: use only with -k and LIVE kernel" ), |
781 | OPT_BOOLEAN('l', "print-line" , &annotate_opts.print_lines, |
782 | "print matching source lines (may be slow)" ), |
783 | OPT_BOOLEAN('P', "full-paths" , &annotate_opts.full_path, |
784 | "Don't shorten the displayed pathnames" ), |
785 | OPT_BOOLEAN(0, "skip-missing" , &annotate.skip_missing, |
786 | "Skip symbols that cannot be annotated" ), |
787 | OPT_BOOLEAN_SET(0, "group" , &symbol_conf.event_group, |
788 | &annotate.group_set, |
789 | "Show event group information together" ), |
790 | OPT_STRING('C', "cpu" , &annotate.cpu_list, "cpu" , "list of cpus to profile" ), |
791 | OPT_CALLBACK(0, "symfs" , NULL, "directory" , |
792 | "Look for files with symbols relative to this directory" , |
793 | symbol__config_symfs), |
794 | OPT_BOOLEAN(0, "source" , &annotate_opts.annotate_src, |
795 | "Interleave source code with assembly code (default)" ), |
796 | OPT_BOOLEAN(0, "asm-raw" , &annotate_opts.show_asm_raw, |
797 | "Display raw encoding of assembly instructions (default)" ), |
798 | OPT_STRING('M', "disassembler-style" , &disassembler_style, "disassembler style" , |
799 | "Specify disassembler style (e.g. -M intel for intel syntax)" ), |
800 | OPT_STRING(0, "prefix" , &annotate_opts.prefix, "prefix" , |
801 | "Add prefix to source file path names in programs (with --prefix-strip)" ), |
802 | OPT_STRING(0, "prefix-strip" , &annotate_opts.prefix_strip, "N" , |
803 | "Strip first N entries of source file path name in programs (with --prefix)" ), |
804 | OPT_STRING(0, "objdump" , &objdump_path, "path" , |
805 | "objdump binary to use for disassembly and annotations" ), |
806 | OPT_STRING(0, "addr2line" , &addr2line_path, "path" , |
807 | "addr2line binary to use for line numbers" ), |
808 | OPT_BOOLEAN(0, "demangle" , &symbol_conf.demangle, |
809 | "Enable symbol demangling" ), |
810 | OPT_BOOLEAN(0, "demangle-kernel" , &symbol_conf.demangle_kernel, |
811 | "Enable kernel symbol demangling" ), |
812 | OPT_BOOLEAN(0, "group" , &symbol_conf.event_group, |
813 | "Show event group information together" ), |
814 | OPT_BOOLEAN(0, "show-total-period" , &symbol_conf.show_total_period, |
815 | "Show a column with the sum of periods" ), |
816 | OPT_BOOLEAN('n', "show-nr-samples" , &symbol_conf.show_nr_samples, |
817 | "Show a column with the number of samples" ), |
818 | OPT_CALLBACK_DEFAULT(0, "stdio-color" , NULL, "mode" , |
819 | "'always' (default), 'never' or 'auto' only applicable to --stdio mode" , |
820 | stdio__config_color, "always" ), |
821 | OPT_CALLBACK(0, "percent-type" , &annotate_opts, "local-period" , |
822 | "Set percent type local/global-period/hits" , |
823 | annotate_parse_percent_type), |
824 | OPT_CALLBACK(0, "percent-limit" , &annotate, "percent" , |
825 | "Don't show entries under that percent" , parse_percent_limit), |
826 | OPT_CALLBACK_OPTARG(0, "itrace" , &itrace_synth_opts, NULL, "opts" , |
827 | "Instruction Tracing options\n" ITRACE_HELP, |
828 | itrace_parse_synth_opts), |
829 | OPT_CALLBACK_OPTARG(0, "data-type" , &annotate, NULL, "name" , |
830 | "Show data type annotate for the memory accesses" , |
831 | parse_data_type), |
832 | OPT_BOOLEAN(0, "type-stat" , &annotate.type_stat, |
833 | "Show stats for the data type annotation" ), |
834 | OPT_BOOLEAN(0, "insn-stat" , &annotate.insn_stat, |
835 | "Show instruction stats for the data type annotation" ), |
836 | OPT_END() |
837 | }; |
838 | int ret; |
839 | |
840 | set_option_flag(options, 0, "show-total-period" , PARSE_OPT_EXCLUSIVE); |
841 | set_option_flag(options, 0, "show-nr-samples" , PARSE_OPT_EXCLUSIVE); |
842 | |
843 | annotation_options__init(); |
844 | |
845 | ret = hists__init(); |
846 | if (ret < 0) |
847 | return ret; |
848 | |
849 | annotation_config__init(); |
850 | |
851 | argc = parse_options(argc, argv, options, annotate_usage, 0); |
852 | if (argc) { |
853 | /* |
854 | * Special case: if there's an argument left then assume that |
855 | * it's a symbol filter: |
856 | */ |
857 | if (argc > 1) |
858 | usage_with_options(annotate_usage, options); |
859 | |
860 | annotate.sym_hist_filter = argv[0]; |
861 | } |
862 | |
863 | if (disassembler_style) { |
864 | annotate_opts.disassembler_style = strdup(disassembler_style); |
865 | if (!annotate_opts.disassembler_style) |
866 | return -ENOMEM; |
867 | } |
868 | if (objdump_path) { |
869 | annotate_opts.objdump_path = strdup(objdump_path); |
870 | if (!annotate_opts.objdump_path) |
871 | return -ENOMEM; |
872 | } |
873 | if (addr2line_path) { |
874 | symbol_conf.addr2line_path = strdup(addr2line_path); |
875 | if (!symbol_conf.addr2line_path) |
876 | return -ENOMEM; |
877 | } |
878 | |
879 | if (annotate_check_args() < 0) |
880 | return -EINVAL; |
881 | |
882 | #ifdef HAVE_GTK2_SUPPORT |
883 | if (symbol_conf.show_nr_samples && annotate.use_gtk) { |
884 | pr_err("--show-nr-samples is not available in --gtk mode at this time\n" ); |
885 | return ret; |
886 | } |
887 | #endif |
888 | |
889 | #ifndef HAVE_DWARF_GETLOCATIONS_SUPPORT |
890 | if (annotate.data_type) { |
891 | pr_err("Error: Data type profiling is disabled due to missing DWARF support\n" ); |
892 | return -ENOTSUP; |
893 | } |
894 | #endif |
895 | |
896 | ret = symbol__validate_sym_arguments(); |
897 | if (ret) |
898 | return ret; |
899 | |
900 | if (quiet) |
901 | perf_quiet_option(); |
902 | |
903 | data.path = input_name; |
904 | |
905 | annotate.session = perf_session__new(data: &data, tool: &annotate.tool); |
906 | if (IS_ERR(ptr: annotate.session)) |
907 | return PTR_ERR(ptr: annotate.session); |
908 | |
909 | annotate.session->itrace_synth_opts = &itrace_synth_opts; |
910 | |
911 | annotate.has_br_stack = perf_header__has_feat(header: &annotate.session->header, |
912 | feat: HEADER_BRANCH_STACK); |
913 | |
914 | if (annotate.group_set) |
915 | evlist__force_leader(evlist: annotate.session->evlist); |
916 | |
917 | ret = symbol__annotation_init(); |
918 | if (ret < 0) |
919 | goto out_delete; |
920 | |
921 | symbol_conf.try_vmlinux_path = true; |
922 | |
923 | ret = symbol__init(env: &annotate.session->header.env); |
924 | if (ret < 0) |
925 | goto out_delete; |
926 | |
927 | if (annotate.use_stdio || annotate.use_stdio2) |
928 | use_browser = 0; |
929 | #ifdef HAVE_SLANG_SUPPORT |
930 | else if (annotate.use_tui) |
931 | use_browser = 1; |
932 | #endif |
933 | #ifdef HAVE_GTK2_SUPPORT |
934 | else if (annotate.use_gtk) |
935 | use_browser = 2; |
936 | #endif |
937 | |
938 | /* FIXME: only support stdio for now */ |
939 | if (annotate.data_type) { |
940 | use_browser = 0; |
941 | annotate_opts.annotate_src = false; |
942 | symbol_conf.annotate_data_member = true; |
943 | symbol_conf.annotate_data_sample = true; |
944 | } |
945 | |
946 | setup_browser(true); |
947 | |
948 | /* |
949 | * Events of different processes may correspond to the same |
950 | * symbol, we do not care about the processes in annotate, |
951 | * set sort order to avoid repeated output. |
952 | */ |
953 | if (annotate.data_type) |
954 | sort_order = "dso,type" ; |
955 | else |
956 | sort_order = "dso,symbol" ; |
957 | |
958 | /* |
959 | * Set SORT_MODE__BRANCH so that annotate display IPC/Cycle |
960 | * if branch info is in perf data in TUI mode. |
961 | */ |
962 | if ((use_browser == 1 || annotate.use_stdio2) && annotate.has_br_stack) |
963 | sort__mode = SORT_MODE__BRANCH; |
964 | |
965 | if (setup_sorting(NULL) < 0) |
966 | usage_with_options(annotate_usage, options); |
967 | |
968 | ret = __cmd_annotate(ann: &annotate); |
969 | |
970 | out_delete: |
971 | /* |
972 | * Speed up the exit process by only deleting for debug builds. For |
973 | * large files this can save time. |
974 | */ |
975 | #ifndef NDEBUG |
976 | perf_session__delete(session: annotate.session); |
977 | #endif |
978 | annotation_options__exit(); |
979 | |
980 | return ret; |
981 | } |
982 | |