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/event.h" |
24 | #include <subcmd/parse-options.h> |
25 | #include "util/parse-events.h" |
26 | #include "util/sort.h" |
27 | #include "util/hist.h" |
28 | #include "util/dso.h" |
29 | #include "util/machine.h" |
30 | #include "util/map.h" |
31 | #include "util/session.h" |
32 | #include "util/tool.h" |
33 | #include "util/data.h" |
34 | #include "arch/common.h" |
35 | #include "util/block-range.h" |
36 | #include "util/map_symbol.h" |
37 | #include "util/branch.h" |
38 | #include "util/util.h" |
39 | |
40 | #include <dlfcn.h> |
41 | #include <errno.h> |
42 | #include <linux/bitmap.h> |
43 | #include <linux/err.h> |
44 | |
45 | struct perf_annotate { |
46 | struct perf_tool tool; |
47 | struct perf_session *session; |
48 | struct annotation_options opts; |
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 | float min_percent; |
60 | const char *sym_hist_filter; |
61 | const char *cpu_list; |
62 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
63 | }; |
64 | |
65 | /* |
66 | * Given one basic block: |
67 | * |
68 | * from to branch_i |
69 | * * ----> * |
70 | * | |
71 | * | block |
72 | * v |
73 | * * ----> * |
74 | * from to branch_i+1 |
75 | * |
76 | * where the horizontal are the branches and the vertical is the executed |
77 | * block of instructions. |
78 | * |
79 | * We count, for each 'instruction', the number of blocks that covered it as |
80 | * well as count the ratio each branch is taken. |
81 | * |
82 | * We can do this without knowing the actual instruction stream by keeping |
83 | * track of the address ranges. We break down ranges such that there is no |
84 | * overlap and iterate from the start until the end. |
85 | * |
86 | * @acme: once we parse the objdump output _before_ processing the samples, |
87 | * we can easily fold the branch.cycles IPC bits in. |
88 | */ |
89 | static void process_basic_block(struct addr_map_symbol *start, |
90 | struct addr_map_symbol *end, |
91 | struct branch_flags *flags) |
92 | { |
93 | struct symbol *sym = start->ms.sym; |
94 | struct annotation *notes = sym ? symbol__annotation(sym) : NULL; |
95 | struct block_range_iter iter; |
96 | struct block_range *entry; |
97 | |
98 | /* |
99 | * Sanity; NULL isn't executable and the CPU cannot execute backwards |
100 | */ |
101 | if (!start->addr || start->addr > end->addr) |
102 | return; |
103 | |
104 | iter = block_range__create(start: start->addr, end: end->addr); |
105 | if (!block_range_iter__valid(iter: &iter)) |
106 | return; |
107 | |
108 | /* |
109 | * First block in range is a branch target. |
110 | */ |
111 | entry = block_range_iter(iter: &iter); |
112 | assert(entry->is_target); |
113 | entry->entry++; |
114 | |
115 | do { |
116 | entry = block_range_iter(iter: &iter); |
117 | |
118 | entry->coverage++; |
119 | entry->sym = sym; |
120 | |
121 | if (notes) |
122 | notes->max_coverage = max(notes->max_coverage, entry->coverage); |
123 | |
124 | } while (block_range_iter__next(iter: &iter)); |
125 | |
126 | /* |
127 | * Last block in rage is a branch. |
128 | */ |
129 | entry = block_range_iter(iter: &iter); |
130 | assert(entry->is_branch); |
131 | entry->taken++; |
132 | if (flags->predicted) |
133 | entry->pred++; |
134 | } |
135 | |
136 | static void process_branch_stack(struct branch_stack *bs, struct addr_location *al, |
137 | struct perf_sample *sample) |
138 | { |
139 | struct addr_map_symbol *prev = NULL; |
140 | struct branch_info *bi; |
141 | int i; |
142 | |
143 | if (!bs || !bs->nr) |
144 | return; |
145 | |
146 | bi = sample__resolve_bstack(sample, al); |
147 | if (!bi) |
148 | return; |
149 | |
150 | for (i = bs->nr - 1; i >= 0; i--) { |
151 | /* |
152 | * XXX filter against symbol |
153 | */ |
154 | if (prev) |
155 | process_basic_block(start: prev, end: &bi[i].from, flags: &bi[i].flags); |
156 | prev = &bi[i].to; |
157 | } |
158 | |
159 | free(bi); |
160 | } |
161 | |
162 | static int hist_iter__branch_callback(struct hist_entry_iter *iter, |
163 | struct addr_location *al __maybe_unused, |
164 | bool single __maybe_unused, |
165 | void *arg __maybe_unused) |
166 | { |
167 | struct hist_entry *he = iter->he; |
168 | struct branch_info *bi; |
169 | struct perf_sample *sample = iter->sample; |
170 | struct evsel *evsel = iter->evsel; |
171 | int err; |
172 | |
173 | bi = he->branch_info; |
174 | err = addr_map_symbol__inc_samples(ams: &bi->from, sample, evsel); |
175 | |
176 | if (err) |
177 | goto out; |
178 | |
179 | err = addr_map_symbol__inc_samples(ams: &bi->to, sample, evsel); |
180 | |
181 | out: |
182 | return err; |
183 | } |
184 | |
185 | static int process_branch_callback(struct evsel *evsel, |
186 | struct perf_sample *sample, |
187 | struct addr_location *al, |
188 | struct perf_annotate *ann, |
189 | struct machine *machine) |
190 | { |
191 | struct hist_entry_iter iter = { |
192 | .evsel = evsel, |
193 | .sample = sample, |
194 | .add_entry_cb = hist_iter__branch_callback, |
195 | .hide_unresolved = symbol_conf.hide_unresolved, |
196 | .ops = &hist_iter_branch, |
197 | }; |
198 | struct addr_location a; |
199 | int ret; |
200 | |
201 | addr_location__init(al: &a); |
202 | if (machine__resolve(machine, al: &a, sample) < 0) { |
203 | ret = -1; |
204 | goto out; |
205 | } |
206 | |
207 | if (a.sym == NULL) { |
208 | ret = 0; |
209 | goto out; |
210 | } |
211 | |
212 | if (a.map != NULL) |
213 | map__dso(map: a.map)->hit = 1; |
214 | |
215 | hist__account_cycles(bs: sample->branch_stack, al, sample, nonany_branch_mode: false, NULL); |
216 | |
217 | ret = hist_entry_iter__add(iter: &iter, al: &a, PERF_MAX_STACK_DEPTH, arg: ann); |
218 | out: |
219 | addr_location__exit(al: &a); |
220 | return ret; |
221 | } |
222 | |
223 | static bool has_annotation(struct perf_annotate *ann) |
224 | { |
225 | return ui__has_annotation() || ann->use_stdio2; |
226 | } |
227 | |
228 | static int evsel__add_sample(struct evsel *evsel, struct perf_sample *sample, |
229 | struct addr_location *al, struct perf_annotate *ann, |
230 | struct machine *machine) |
231 | { |
232 | struct hists *hists = evsel__hists(evsel); |
233 | struct hist_entry *he; |
234 | int ret; |
235 | |
236 | if ((!ann->has_br_stack || !has_annotation(ann)) && |
237 | ann->sym_hist_filter != NULL && |
238 | (al->sym == NULL || |
239 | strcmp(ann->sym_hist_filter, al->sym->name) != 0)) { |
240 | /* We're only interested in a symbol named sym_hist_filter */ |
241 | /* |
242 | * FIXME: why isn't this done in the symbol_filter when loading |
243 | * the DSO? |
244 | */ |
245 | if (al->sym != NULL) { |
246 | struct dso *dso = map__dso(map: al->map); |
247 | |
248 | rb_erase_cached(node: &al->sym->rb_node, root: &dso->symbols); |
249 | symbol__delete(sym: al->sym); |
250 | dso__reset_find_symbol_cache(dso); |
251 | } |
252 | return 0; |
253 | } |
254 | |
255 | /* |
256 | * XXX filtered samples can still have branch entries pointing into our |
257 | * symbol and are missed. |
258 | */ |
259 | process_branch_stack(bs: sample->branch_stack, al, sample); |
260 | |
261 | if (ann->has_br_stack && has_annotation(ann)) |
262 | return process_branch_callback(evsel, sample, al, ann, machine); |
263 | |
264 | he = hists__add_entry(hists, al, NULL, NULL, NULL, NULL, sample, sample_self: true); |
265 | if (he == NULL) |
266 | return -ENOMEM; |
267 | |
268 | ret = hist_entry__inc_addr_samples(he, sample, evsel, addr: al->addr); |
269 | hists__inc_nr_samples(hists, filtered: true); |
270 | return ret; |
271 | } |
272 | |
273 | static int process_sample_event(struct perf_tool *tool, |
274 | union perf_event *event, |
275 | struct perf_sample *sample, |
276 | struct evsel *evsel, |
277 | struct machine *machine) |
278 | { |
279 | struct perf_annotate *ann = container_of(tool, struct perf_annotate, tool); |
280 | struct addr_location al; |
281 | int ret = 0; |
282 | |
283 | addr_location__init(al: &al); |
284 | if (machine__resolve(machine, al: &al, sample) < 0) { |
285 | pr_warning("problem processing %d event, skipping it.\n" , |
286 | event->header.type); |
287 | ret = -1; |
288 | goto out_put; |
289 | } |
290 | |
291 | if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap)) |
292 | goto out_put; |
293 | |
294 | if (!al.filtered && |
295 | evsel__add_sample(evsel, sample, al: &al, ann, machine)) { |
296 | pr_warning("problem incrementing symbol count, " |
297 | "skipping event\n" ); |
298 | ret = -1; |
299 | } |
300 | out_put: |
301 | addr_location__exit(al: &al); |
302 | return ret; |
303 | } |
304 | |
305 | static int process_feature_event(struct perf_session *session, |
306 | union perf_event *event) |
307 | { |
308 | if (event->feat.feat_id < HEADER_LAST_FEATURE) |
309 | return perf_event__process_feature(session, event); |
310 | return 0; |
311 | } |
312 | |
313 | static int hist_entry__tty_annotate(struct hist_entry *he, |
314 | struct evsel *evsel, |
315 | struct perf_annotate *ann) |
316 | { |
317 | if (!ann->use_stdio2) |
318 | return symbol__tty_annotate(ms: &he->ms, evsel, opts: &ann->opts); |
319 | |
320 | return symbol__tty_annotate2(ms: &he->ms, evsel, opts: &ann->opts); |
321 | } |
322 | |
323 | static void hists__find_annotations(struct hists *hists, |
324 | struct evsel *evsel, |
325 | struct perf_annotate *ann) |
326 | { |
327 | struct rb_node *nd = rb_first_cached(&hists->entries), *next; |
328 | int key = K_RIGHT; |
329 | |
330 | while (nd) { |
331 | struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node); |
332 | struct annotation *notes; |
333 | |
334 | if (he->ms.sym == NULL || map__dso(map: he->ms.map)->annotate_warned) |
335 | goto find_next; |
336 | |
337 | if (ann->sym_hist_filter && |
338 | (strcmp(he->ms.sym->name, ann->sym_hist_filter) != 0)) |
339 | goto find_next; |
340 | |
341 | if (ann->min_percent) { |
342 | float percent = 0; |
343 | u64 total = hists__total_period(hists); |
344 | |
345 | if (total) |
346 | percent = 100.0 * he->stat.period / total; |
347 | |
348 | if (percent < ann->min_percent) |
349 | goto find_next; |
350 | } |
351 | |
352 | notes = symbol__annotation(sym: he->ms.sym); |
353 | if (notes->src == NULL) { |
354 | find_next: |
355 | if (key == K_LEFT || key == '<') |
356 | nd = rb_prev(nd); |
357 | else |
358 | nd = rb_next(nd); |
359 | continue; |
360 | } |
361 | |
362 | if (use_browser == 2) { |
363 | int ret; |
364 | int (*annotate)(struct hist_entry *he, |
365 | struct evsel *evsel, |
366 | struct annotation_options *options, |
367 | struct hist_browser_timer *hbt); |
368 | |
369 | annotate = dlsym(perf_gtk_handle, |
370 | "hist_entry__gtk_annotate" ); |
371 | if (annotate == NULL) { |
372 | ui__error(format: "GTK browser not found!\n" ); |
373 | return; |
374 | } |
375 | |
376 | ret = annotate(he, evsel, &ann->opts, NULL); |
377 | if (!ret || !ann->skip_missing) |
378 | return; |
379 | |
380 | /* skip missing symbols */ |
381 | nd = rb_next(nd); |
382 | } else if (use_browser == 1) { |
383 | key = hist_entry__tui_annotate(he, evsel, NULL, annotation_opts: &ann->opts); |
384 | |
385 | switch (key) { |
386 | case -1: |
387 | if (!ann->skip_missing) |
388 | return; |
389 | /* fall through */ |
390 | case K_RIGHT: |
391 | case '>': |
392 | next = rb_next(nd); |
393 | break; |
394 | case K_LEFT: |
395 | case '<': |
396 | next = rb_prev(nd); |
397 | break; |
398 | default: |
399 | return; |
400 | } |
401 | |
402 | if (next != NULL) |
403 | nd = next; |
404 | } else { |
405 | hist_entry__tty_annotate(he, evsel, ann); |
406 | nd = rb_next(nd); |
407 | } |
408 | } |
409 | } |
410 | |
411 | static int __cmd_annotate(struct perf_annotate *ann) |
412 | { |
413 | int ret; |
414 | struct perf_session *session = ann->session; |
415 | struct evsel *pos; |
416 | u64 total_nr_samples; |
417 | |
418 | if (ann->cpu_list) { |
419 | ret = perf_session__cpu_bitmap(session, cpu_list: ann->cpu_list, |
420 | cpu_bitmap: ann->cpu_bitmap); |
421 | if (ret) |
422 | goto out; |
423 | } |
424 | |
425 | if (!ann->opts.objdump_path) { |
426 | ret = perf_env__lookup_objdump(env: &session->header.env, |
427 | path: &ann->opts.objdump_path); |
428 | if (ret) |
429 | goto out; |
430 | } |
431 | |
432 | ret = perf_session__process_events(session); |
433 | if (ret) |
434 | goto out; |
435 | |
436 | if (dump_trace) { |
437 | perf_session__fprintf_nr_events(session, stdout, false); |
438 | evlist__fprintf_nr_events(session->evlist, stdout, false); |
439 | goto out; |
440 | } |
441 | |
442 | if (verbose > 3) |
443 | perf_session__fprintf(session, stdout); |
444 | |
445 | if (verbose > 2) |
446 | perf_session__fprintf_dsos(session, stdout); |
447 | |
448 | total_nr_samples = 0; |
449 | evlist__for_each_entry(session->evlist, pos) { |
450 | struct hists *hists = evsel__hists(evsel: pos); |
451 | u32 nr_samples = hists->stats.nr_samples; |
452 | |
453 | if (nr_samples > 0) { |
454 | total_nr_samples += nr_samples; |
455 | hists__collapse_resort(hists, NULL); |
456 | /* Don't sort callchain */ |
457 | evsel__reset_sample_bit(pos, CALLCHAIN); |
458 | evsel__output_resort(evsel: pos, NULL); |
459 | |
460 | if (symbol_conf.event_group && !evsel__is_group_leader(evsel: pos)) |
461 | continue; |
462 | |
463 | hists__find_annotations(hists, evsel: pos, ann); |
464 | } |
465 | } |
466 | |
467 | if (total_nr_samples == 0) { |
468 | ui__error(format: "The %s data has no samples!\n" , session->data->path); |
469 | goto out; |
470 | } |
471 | |
472 | if (use_browser == 2) { |
473 | void (*show_annotations)(void); |
474 | |
475 | show_annotations = dlsym(perf_gtk_handle, |
476 | "perf_gtk__show_annotations" ); |
477 | if (show_annotations == NULL) { |
478 | ui__error(format: "GTK browser not found!\n" ); |
479 | goto out; |
480 | } |
481 | show_annotations(); |
482 | } |
483 | |
484 | out: |
485 | return ret; |
486 | } |
487 | |
488 | static int parse_percent_limit(const struct option *opt, const char *str, |
489 | int unset __maybe_unused) |
490 | { |
491 | struct perf_annotate *ann = opt->value; |
492 | double pcnt = strtof(str, NULL); |
493 | |
494 | ann->min_percent = pcnt; |
495 | return 0; |
496 | } |
497 | |
498 | static const char * const annotate_usage[] = { |
499 | "perf annotate [<options>]" , |
500 | NULL |
501 | }; |
502 | |
503 | int cmd_annotate(int argc, const char **argv) |
504 | { |
505 | struct perf_annotate annotate = { |
506 | .tool = { |
507 | .sample = process_sample_event, |
508 | .mmap = perf_event__process_mmap, |
509 | .mmap2 = perf_event__process_mmap2, |
510 | .comm = perf_event__process_comm, |
511 | .exit = perf_event__process_exit, |
512 | .fork = perf_event__process_fork, |
513 | .namespaces = perf_event__process_namespaces, |
514 | .attr = perf_event__process_attr, |
515 | .build_id = perf_event__process_build_id, |
516 | #ifdef HAVE_LIBTRACEEVENT |
517 | .tracing_data = perf_event__process_tracing_data, |
518 | #endif |
519 | .id_index = perf_event__process_id_index, |
520 | .auxtrace_info = perf_event__process_auxtrace_info, |
521 | .auxtrace = perf_event__process_auxtrace, |
522 | .feature = process_feature_event, |
523 | .ordered_events = true, |
524 | .ordering_requires_timestamps = true, |
525 | }, |
526 | }; |
527 | struct perf_data data = { |
528 | .mode = PERF_DATA_MODE_READ, |
529 | }; |
530 | struct itrace_synth_opts itrace_synth_opts = { |
531 | .set = 0, |
532 | }; |
533 | const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL; |
534 | struct option options[] = { |
535 | OPT_STRING('i', "input" , &input_name, "file" , |
536 | "input file name" ), |
537 | OPT_STRING('d', "dsos" , &symbol_conf.dso_list_str, "dso[,dso...]" , |
538 | "only consider symbols in these dsos" ), |
539 | OPT_STRING('s', "symbol" , &annotate.sym_hist_filter, "symbol" , |
540 | "symbol to annotate" ), |
541 | OPT_BOOLEAN('f', "force" , &data.force, "don't complain, do it" ), |
542 | OPT_INCR('v', "verbose" , &verbose, |
543 | "be more verbose (show symbol address, etc)" ), |
544 | OPT_BOOLEAN('q', "quiet" , &quiet, "do now show any warnings or messages" ), |
545 | OPT_BOOLEAN('D', "dump-raw-trace" , &dump_trace, |
546 | "dump raw trace in ASCII" ), |
547 | #ifdef HAVE_GTK2_SUPPORT |
548 | OPT_BOOLEAN(0, "gtk" , &annotate.use_gtk, "Use the GTK interface" ), |
549 | #endif |
550 | #ifdef HAVE_SLANG_SUPPORT |
551 | OPT_BOOLEAN(0, "tui" , &annotate.use_tui, "Use the TUI interface" ), |
552 | #endif |
553 | OPT_BOOLEAN(0, "stdio" , &annotate.use_stdio, "Use the stdio interface" ), |
554 | OPT_BOOLEAN(0, "stdio2" , &annotate.use_stdio2, "Use the stdio interface" ), |
555 | OPT_BOOLEAN(0, "ignore-vmlinux" , &symbol_conf.ignore_vmlinux, |
556 | "don't load vmlinux even if found" ), |
557 | OPT_STRING('k', "vmlinux" , &symbol_conf.vmlinux_name, |
558 | "file" , "vmlinux pathname" ), |
559 | OPT_BOOLEAN('m', "modules" , &symbol_conf.use_modules, |
560 | "load module symbols - WARNING: use only with -k and LIVE kernel" ), |
561 | OPT_BOOLEAN('l', "print-line" , &annotate.opts.print_lines, |
562 | "print matching source lines (may be slow)" ), |
563 | OPT_BOOLEAN('P', "full-paths" , &annotate.opts.full_path, |
564 | "Don't shorten the displayed pathnames" ), |
565 | OPT_BOOLEAN(0, "skip-missing" , &annotate.skip_missing, |
566 | "Skip symbols that cannot be annotated" ), |
567 | OPT_BOOLEAN_SET(0, "group" , &symbol_conf.event_group, |
568 | &annotate.group_set, |
569 | "Show event group information together" ), |
570 | OPT_STRING('C', "cpu" , &annotate.cpu_list, "cpu" , "list of cpus to profile" ), |
571 | OPT_CALLBACK(0, "symfs" , NULL, "directory" , |
572 | "Look for files with symbols relative to this directory" , |
573 | symbol__config_symfs), |
574 | OPT_BOOLEAN(0, "source" , &annotate.opts.annotate_src, |
575 | "Interleave source code with assembly code (default)" ), |
576 | OPT_BOOLEAN(0, "asm-raw" , &annotate.opts.show_asm_raw, |
577 | "Display raw encoding of assembly instructions (default)" ), |
578 | OPT_STRING('M', "disassembler-style" , &disassembler_style, "disassembler style" , |
579 | "Specify disassembler style (e.g. -M intel for intel syntax)" ), |
580 | OPT_STRING(0, "prefix" , &annotate.opts.prefix, "prefix" , |
581 | "Add prefix to source file path names in programs (with --prefix-strip)" ), |
582 | OPT_STRING(0, "prefix-strip" , &annotate.opts.prefix_strip, "N" , |
583 | "Strip first N entries of source file path name in programs (with --prefix)" ), |
584 | OPT_STRING(0, "objdump" , &objdump_path, "path" , |
585 | "objdump binary to use for disassembly and annotations" ), |
586 | OPT_STRING(0, "addr2line" , &addr2line_path, "path" , |
587 | "addr2line binary to use for line numbers" ), |
588 | OPT_BOOLEAN(0, "demangle" , &symbol_conf.demangle, |
589 | "Enable symbol demangling" ), |
590 | OPT_BOOLEAN(0, "demangle-kernel" , &symbol_conf.demangle_kernel, |
591 | "Enable kernel symbol demangling" ), |
592 | OPT_BOOLEAN(0, "group" , &symbol_conf.event_group, |
593 | "Show event group information together" ), |
594 | OPT_BOOLEAN(0, "show-total-period" , &symbol_conf.show_total_period, |
595 | "Show a column with the sum of periods" ), |
596 | OPT_BOOLEAN('n', "show-nr-samples" , &symbol_conf.show_nr_samples, |
597 | "Show a column with the number of samples" ), |
598 | OPT_CALLBACK_DEFAULT(0, "stdio-color" , NULL, "mode" , |
599 | "'always' (default), 'never' or 'auto' only applicable to --stdio mode" , |
600 | stdio__config_color, "always" ), |
601 | OPT_CALLBACK(0, "percent-type" , &annotate.opts, "local-period" , |
602 | "Set percent type local/global-period/hits" , |
603 | annotate_parse_percent_type), |
604 | OPT_CALLBACK(0, "percent-limit" , &annotate, "percent" , |
605 | "Don't show entries under that percent" , parse_percent_limit), |
606 | OPT_CALLBACK_OPTARG(0, "itrace" , &itrace_synth_opts, NULL, "opts" , |
607 | "Instruction Tracing options\n" ITRACE_HELP, |
608 | itrace_parse_synth_opts), |
609 | |
610 | OPT_END() |
611 | }; |
612 | int ret; |
613 | |
614 | set_option_flag(options, 0, "show-total-period" , PARSE_OPT_EXCLUSIVE); |
615 | set_option_flag(options, 0, "show-nr-samples" , PARSE_OPT_EXCLUSIVE); |
616 | |
617 | annotation_options__init(opt: &annotate.opts); |
618 | |
619 | ret = hists__init(); |
620 | if (ret < 0) |
621 | return ret; |
622 | |
623 | annotation_config__init(opt: &annotate.opts); |
624 | |
625 | argc = parse_options(argc, argv, options, annotate_usage, 0); |
626 | if (argc) { |
627 | /* |
628 | * Special case: if there's an argument left then assume that |
629 | * it's a symbol filter: |
630 | */ |
631 | if (argc > 1) |
632 | usage_with_options(annotate_usage, options); |
633 | |
634 | annotate.sym_hist_filter = argv[0]; |
635 | } |
636 | |
637 | if (disassembler_style) { |
638 | annotate.opts.disassembler_style = strdup(disassembler_style); |
639 | if (!annotate.opts.disassembler_style) |
640 | return -ENOMEM; |
641 | } |
642 | if (objdump_path) { |
643 | annotate.opts.objdump_path = strdup(objdump_path); |
644 | if (!annotate.opts.objdump_path) |
645 | return -ENOMEM; |
646 | } |
647 | if (addr2line_path) { |
648 | symbol_conf.addr2line_path = strdup(addr2line_path); |
649 | if (!symbol_conf.addr2line_path) |
650 | return -ENOMEM; |
651 | } |
652 | |
653 | if (annotate_check_args(args: &annotate.opts) < 0) |
654 | return -EINVAL; |
655 | |
656 | #ifdef HAVE_GTK2_SUPPORT |
657 | if (symbol_conf.show_nr_samples && annotate.use_gtk) { |
658 | pr_err("--show-nr-samples is not available in --gtk mode at this time\n" ); |
659 | return ret; |
660 | } |
661 | #endif |
662 | |
663 | ret = symbol__validate_sym_arguments(); |
664 | if (ret) |
665 | return ret; |
666 | |
667 | if (quiet) |
668 | perf_quiet_option(); |
669 | |
670 | data.path = input_name; |
671 | |
672 | annotate.session = perf_session__new(data: &data, tool: &annotate.tool); |
673 | if (IS_ERR(ptr: annotate.session)) |
674 | return PTR_ERR(ptr: annotate.session); |
675 | |
676 | annotate.session->itrace_synth_opts = &itrace_synth_opts; |
677 | |
678 | annotate.has_br_stack = perf_header__has_feat(header: &annotate.session->header, |
679 | feat: HEADER_BRANCH_STACK); |
680 | |
681 | if (annotate.group_set) |
682 | evlist__force_leader(evlist: annotate.session->evlist); |
683 | |
684 | ret = symbol__annotation_init(); |
685 | if (ret < 0) |
686 | goto out_delete; |
687 | |
688 | symbol_conf.try_vmlinux_path = true; |
689 | |
690 | ret = symbol__init(env: &annotate.session->header.env); |
691 | if (ret < 0) |
692 | goto out_delete; |
693 | |
694 | if (annotate.use_stdio || annotate.use_stdio2) |
695 | use_browser = 0; |
696 | #ifdef HAVE_SLANG_SUPPORT |
697 | else if (annotate.use_tui) |
698 | use_browser = 1; |
699 | #endif |
700 | #ifdef HAVE_GTK2_SUPPORT |
701 | else if (annotate.use_gtk) |
702 | use_browser = 2; |
703 | #endif |
704 | |
705 | setup_browser(true); |
706 | |
707 | /* |
708 | * Events of different processes may correspond to the same |
709 | * symbol, we do not care about the processes in annotate, |
710 | * set sort order to avoid repeated output. |
711 | */ |
712 | sort_order = "dso,symbol" ; |
713 | |
714 | /* |
715 | * Set SORT_MODE__BRANCH so that annotate display IPC/Cycle |
716 | * if branch info is in perf data in TUI mode. |
717 | */ |
718 | if ((use_browser == 1 || annotate.use_stdio2) && annotate.has_br_stack) |
719 | sort__mode = SORT_MODE__BRANCH; |
720 | |
721 | if (setup_sorting(NULL) < 0) |
722 | usage_with_options(annotate_usage, options); |
723 | |
724 | ret = __cmd_annotate(ann: &annotate); |
725 | |
726 | out_delete: |
727 | /* |
728 | * Speed up the exit process by only deleting for debug builds. For |
729 | * large files this can save time. |
730 | */ |
731 | #ifndef NDEBUG |
732 | perf_session__delete(session: annotate.session); |
733 | #endif |
734 | annotation_options__exit(opt: &annotate.opts); |
735 | |
736 | return ret; |
737 | } |
738 | |