| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Parts came from builtin-annotate.c, see those files for further |
|---|
| 5 | 6 | * copyright notes. |
|---|
| 6 | | - * |
|---|
| 7 | | - * Released under the GPL v2. (and only v2, not any later version) |
|---|
| 8 | 7 | */ |
|---|
| 9 | 8 | |
|---|
| 10 | 9 | #include <errno.h> |
|---|
| 11 | 10 | #include <inttypes.h> |
|---|
| 12 | | -#include "util.h" |
|---|
| 11 | +#include <libgen.h> |
|---|
| 12 | +#include <stdlib.h> |
|---|
| 13 | +#include <bpf/bpf.h> |
|---|
| 14 | +#include <bpf/btf.h> |
|---|
| 15 | +#include <bpf/libbpf.h> |
|---|
| 16 | +#include <linux/btf.h> |
|---|
| 17 | +#include "util.h" // hex_width() |
|---|
| 13 | 18 | #include "ui/ui.h" |
|---|
| 14 | 19 | #include "sort.h" |
|---|
| 15 | 20 | #include "build-id.h" |
|---|
| 16 | 21 | #include "color.h" |
|---|
| 17 | 22 | #include "config.h" |
|---|
| 18 | | -#include "cache.h" |
|---|
| 23 | +#include "dso.h" |
|---|
| 24 | +#include "env.h" |
|---|
| 25 | +#include "map.h" |
|---|
| 26 | +#include "maps.h" |
|---|
| 19 | 27 | #include "symbol.h" |
|---|
| 28 | +#include "srcline.h" |
|---|
| 20 | 29 | #include "units.h" |
|---|
| 21 | 30 | #include "debug.h" |
|---|
| 22 | 31 | #include "annotate.h" |
|---|
| 23 | 32 | #include "evsel.h" |
|---|
| 24 | 33 | #include "evlist.h" |
|---|
| 34 | +#include "bpf-event.h" |
|---|
| 25 | 35 | #include "block-range.h" |
|---|
| 26 | 36 | #include "string2.h" |
|---|
| 37 | +#include "util/event.h" |
|---|
| 27 | 38 | #include "arch/common.h" |
|---|
| 28 | 39 | #include <regex.h> |
|---|
| 29 | 40 | #include <pthread.h> |
|---|
| 30 | 41 | #include <linux/bitops.h> |
|---|
| 31 | 42 | #include <linux/kernel.h> |
|---|
| 43 | +#include <linux/string.h> |
|---|
| 44 | +#include <subcmd/parse-options.h> |
|---|
| 45 | +#include <subcmd/run-command.h> |
|---|
| 32 | 46 | |
|---|
| 33 | 47 | /* FIXME: For the HE_COLORSET */ |
|---|
| 34 | 48 | #include "ui/browser.h" |
|---|
| .. | .. |
|---|
| 42 | 56 | #define DARROW_CHAR ((unsigned char)'.') |
|---|
| 43 | 57 | #define UARROW_CHAR ((unsigned char)'-') |
|---|
| 44 | 58 | |
|---|
| 45 | | -#include "sane_ctype.h" |
|---|
| 59 | +#include <linux/ctype.h> |
|---|
| 46 | 60 | |
|---|
| 47 | 61 | struct annotation_options annotation__default_options = { |
|---|
| 48 | 62 | .use_offset = true, |
|---|
| .. | .. |
|---|
| 134 | 148 | return 0; |
|---|
| 135 | 149 | } |
|---|
| 136 | 150 | |
|---|
| 151 | +#include "arch/arc/annotate/instructions.c" |
|---|
| 137 | 152 | #include "arch/arm/annotate/instructions.c" |
|---|
| 138 | 153 | #include "arch/arm64/annotate/instructions.c" |
|---|
| 154 | +#include "arch/csky/annotate/instructions.c" |
|---|
| 139 | 155 | #include "arch/x86/annotate/instructions.c" |
|---|
| 140 | 156 | #include "arch/powerpc/annotate/instructions.c" |
|---|
| 141 | 157 | #include "arch/s390/annotate/instructions.c" |
|---|
| 158 | +#include "arch/sparc/annotate/instructions.c" |
|---|
| 142 | 159 | |
|---|
| 143 | 160 | static struct arch architectures[] = { |
|---|
| 161 | + { |
|---|
| 162 | + .name = "arc", |
|---|
| 163 | + .init = arc__annotate_init, |
|---|
| 164 | + }, |
|---|
| 144 | 165 | { |
|---|
| 145 | 166 | .name = "arm", |
|---|
| 146 | 167 | .init = arm__annotate_init, |
|---|
| .. | .. |
|---|
| 148 | 169 | { |
|---|
| 149 | 170 | .name = "arm64", |
|---|
| 150 | 171 | .init = arm64__annotate_init, |
|---|
| 172 | + }, |
|---|
| 173 | + { |
|---|
| 174 | + .name = "csky", |
|---|
| 175 | + .init = csky__annotate_init, |
|---|
| 151 | 176 | }, |
|---|
| 152 | 177 | { |
|---|
| 153 | 178 | .name = "x86", |
|---|
| .. | .. |
|---|
| 170 | 195 | .comment_char = '#', |
|---|
| 171 | 196 | }, |
|---|
| 172 | 197 | }, |
|---|
| 198 | + { |
|---|
| 199 | + .name = "sparc", |
|---|
| 200 | + .init = sparc__annotate_init, |
|---|
| 201 | + .objdump = { |
|---|
| 202 | + .comment_char = '#', |
|---|
| 203 | + }, |
|---|
| 204 | + }, |
|---|
| 173 | 205 | }; |
|---|
| 174 | 206 | |
|---|
| 175 | 207 | static void ins__delete(struct ins_operands *ops) |
|---|
| .. | .. |
|---|
| 183 | 215 | } |
|---|
| 184 | 216 | |
|---|
| 185 | 217 | static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size, |
|---|
| 186 | | - struct ins_operands *ops) |
|---|
| 218 | + struct ins_operands *ops, int max_ins_name) |
|---|
| 187 | 219 | { |
|---|
| 188 | | - return scnprintf(bf, size, "%-6s %s", ins->name, ops->raw); |
|---|
| 220 | + return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->raw); |
|---|
| 189 | 221 | } |
|---|
| 190 | 222 | |
|---|
| 191 | 223 | int ins__scnprintf(struct ins *ins, char *bf, size_t size, |
|---|
| 192 | | - struct ins_operands *ops) |
|---|
| 224 | + struct ins_operands *ops, int max_ins_name) |
|---|
| 193 | 225 | { |
|---|
| 194 | 226 | if (ins->ops->scnprintf) |
|---|
| 195 | | - return ins->ops->scnprintf(ins, bf, size, ops); |
|---|
| 227 | + return ins->ops->scnprintf(ins, bf, size, ops, max_ins_name); |
|---|
| 196 | 228 | |
|---|
| 197 | | - return ins__raw_scnprintf(ins, bf, size, ops); |
|---|
| 229 | + return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name); |
|---|
| 198 | 230 | } |
|---|
| 199 | 231 | |
|---|
| 200 | 232 | bool ins__is_fused(struct arch *arch, const char *ins1, const char *ins2) |
|---|
| .. | .. |
|---|
| 210 | 242 | char *endptr, *tok, *name; |
|---|
| 211 | 243 | struct map *map = ms->map; |
|---|
| 212 | 244 | struct addr_map_symbol target = { |
|---|
| 213 | | - .map = map, |
|---|
| 245 | + .ms = { .map = map, }, |
|---|
| 214 | 246 | }; |
|---|
| 215 | 247 | |
|---|
| 216 | 248 | ops->target.addr = strtoull(ops->raw, &endptr, 16); |
|---|
| .. | .. |
|---|
| 238 | 270 | find_target: |
|---|
| 239 | 271 | target.addr = map__objdump_2mem(map, ops->target.addr); |
|---|
| 240 | 272 | |
|---|
| 241 | | - if (map_groups__find_ams(&target) == 0 && |
|---|
| 242 | | - map__rip_2objdump(target.map, map->map_ip(target.map, target.addr)) == ops->target.addr) |
|---|
| 243 | | - ops->target.sym = target.sym; |
|---|
| 273 | + if (maps__find_ams(ms->maps, &target) == 0 && |
|---|
| 274 | + map__rip_2objdump(target.ms.map, map->map_ip(target.ms.map, target.addr)) == ops->target.addr) |
|---|
| 275 | + ops->target.sym = target.ms.sym; |
|---|
| 244 | 276 | |
|---|
| 245 | 277 | return 0; |
|---|
| 246 | 278 | |
|---|
| .. | .. |
|---|
| 258 | 290 | } |
|---|
| 259 | 291 | |
|---|
| 260 | 292 | static int call__scnprintf(struct ins *ins, char *bf, size_t size, |
|---|
| 261 | | - struct ins_operands *ops) |
|---|
| 293 | + struct ins_operands *ops, int max_ins_name) |
|---|
| 262 | 294 | { |
|---|
| 263 | 295 | if (ops->target.sym) |
|---|
| 264 | | - return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name); |
|---|
| 296 | + return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name); |
|---|
| 265 | 297 | |
|---|
| 266 | 298 | if (ops->target.addr == 0) |
|---|
| 267 | | - return ins__raw_scnprintf(ins, bf, size, ops); |
|---|
| 299 | + return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name); |
|---|
| 268 | 300 | |
|---|
| 269 | 301 | if (ops->target.name) |
|---|
| 270 | | - return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.name); |
|---|
| 302 | + return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.name); |
|---|
| 271 | 303 | |
|---|
| 272 | | - return scnprintf(bf, size, "%-6s *%" PRIx64, ins->name, ops->target.addr); |
|---|
| 304 | + return scnprintf(bf, size, "%-*s *%" PRIx64, max_ins_name, ins->name, ops->target.addr); |
|---|
| 273 | 305 | } |
|---|
| 274 | 306 | |
|---|
| 275 | 307 | static struct ins_ops call_ops = { |
|---|
| .. | .. |
|---|
| 285 | 317 | /* |
|---|
| 286 | 318 | * Prevents from matching commas in the comment section, e.g.: |
|---|
| 287 | 319 | * ffff200008446e70: b.cs ffff2000084470f4 <generic_exec_single+0x314> // b.hs, b.nlast |
|---|
| 320 | + * |
|---|
| 321 | + * and skip comma as part of function arguments, e.g.: |
|---|
| 322 | + * 1d8b4ac <linemap_lookup(line_maps const*, unsigned int)+0xcc> |
|---|
| 288 | 323 | */ |
|---|
| 289 | 324 | static inline const char *validate_comma(const char *c, struct ins_operands *ops) |
|---|
| 290 | 325 | { |
|---|
| 291 | 326 | if (ops->raw_comment && c > ops->raw_comment) |
|---|
| 327 | + return NULL; |
|---|
| 328 | + |
|---|
| 329 | + if (ops->raw_func_start && c > ops->raw_func_start) |
|---|
| 292 | 330 | return NULL; |
|---|
| 293 | 331 | |
|---|
| 294 | 332 | return c; |
|---|
| .. | .. |
|---|
| 299 | 337 | struct map *map = ms->map; |
|---|
| 300 | 338 | struct symbol *sym = ms->sym; |
|---|
| 301 | 339 | struct addr_map_symbol target = { |
|---|
| 302 | | - .map = map, |
|---|
| 340 | + .ms = { .map = map, }, |
|---|
| 303 | 341 | }; |
|---|
| 304 | 342 | const char *c = strchr(ops->raw, ','); |
|---|
| 305 | 343 | u64 start, end; |
|---|
| 306 | 344 | |
|---|
| 307 | 345 | ops->raw_comment = strchr(ops->raw, arch->objdump.comment_char); |
|---|
| 346 | + ops->raw_func_start = strchr(ops->raw, '<'); |
|---|
| 347 | + |
|---|
| 308 | 348 | c = validate_comma(c, ops); |
|---|
| 309 | 349 | |
|---|
| 310 | 350 | /* |
|---|
| .. | .. |
|---|
| 358 | 398 | * Actual navigation will come next, with further understanding of how |
|---|
| 359 | 399 | * the symbol searching and disassembly should be done. |
|---|
| 360 | 400 | */ |
|---|
| 361 | | - if (map_groups__find_ams(&target) == 0 && |
|---|
| 362 | | - map__rip_2objdump(target.map, map->map_ip(target.map, target.addr)) == ops->target.addr) |
|---|
| 363 | | - ops->target.sym = target.sym; |
|---|
| 401 | + if (maps__find_ams(ms->maps, &target) == 0 && |
|---|
| 402 | + map__rip_2objdump(target.ms.map, map->map_ip(target.ms.map, target.addr)) == ops->target.addr) |
|---|
| 403 | + ops->target.sym = target.ms.sym; |
|---|
| 364 | 404 | |
|---|
| 365 | 405 | if (!ops->target.outside) { |
|---|
| 366 | 406 | ops->target.offset = target.addr - start; |
|---|
| .. | .. |
|---|
| 373 | 413 | } |
|---|
| 374 | 414 | |
|---|
| 375 | 415 | static int jump__scnprintf(struct ins *ins, char *bf, size_t size, |
|---|
| 376 | | - struct ins_operands *ops) |
|---|
| 416 | + struct ins_operands *ops, int max_ins_name) |
|---|
| 377 | 417 | { |
|---|
| 378 | 418 | const char *c; |
|---|
| 379 | 419 | |
|---|
| 380 | 420 | if (!ops->target.addr || ops->target.offset < 0) |
|---|
| 381 | | - return ins__raw_scnprintf(ins, bf, size, ops); |
|---|
| 421 | + return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name); |
|---|
| 382 | 422 | |
|---|
| 383 | 423 | if (ops->target.outside && ops->target.sym != NULL) |
|---|
| 384 | | - return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name); |
|---|
| 424 | + return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name); |
|---|
| 385 | 425 | |
|---|
| 386 | 426 | c = strchr(ops->raw, ','); |
|---|
| 387 | 427 | c = validate_comma(c, ops); |
|---|
| .. | .. |
|---|
| 400 | 440 | c++; |
|---|
| 401 | 441 | } |
|---|
| 402 | 442 | |
|---|
| 403 | | - return scnprintf(bf, size, "%-6s %.*s%" PRIx64, |
|---|
| 443 | + return scnprintf(bf, size, "%-*s %.*s%" PRIx64, max_ins_name, |
|---|
| 404 | 444 | ins->name, c ? c - ops->raw : 0, ops->raw, |
|---|
| 405 | 445 | ops->target.offset); |
|---|
| 406 | 446 | } |
|---|
| .. | .. |
|---|
| 468 | 508 | } |
|---|
| 469 | 509 | |
|---|
| 470 | 510 | static int lock__scnprintf(struct ins *ins, char *bf, size_t size, |
|---|
| 471 | | - struct ins_operands *ops) |
|---|
| 511 | + struct ins_operands *ops, int max_ins_name) |
|---|
| 472 | 512 | { |
|---|
| 473 | 513 | int printed; |
|---|
| 474 | 514 | |
|---|
| 475 | 515 | if (ops->locked.ins.ops == NULL) |
|---|
| 476 | | - return ins__raw_scnprintf(ins, bf, size, ops); |
|---|
| 516 | + return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name); |
|---|
| 477 | 517 | |
|---|
| 478 | | - printed = scnprintf(bf, size, "%-6s ", ins->name); |
|---|
| 518 | + printed = scnprintf(bf, size, "%-*s ", max_ins_name, ins->name); |
|---|
| 479 | 519 | return printed + ins__scnprintf(&ops->locked.ins, bf + printed, |
|---|
| 480 | | - size - printed, ops->locked.ops); |
|---|
| 520 | + size - printed, ops->locked.ops, max_ins_name); |
|---|
| 481 | 521 | } |
|---|
| 482 | 522 | |
|---|
| 483 | 523 | static void lock__delete(struct ins_operands *ops) |
|---|
| .. | .. |
|---|
| 537 | 577 | if (comment == NULL) |
|---|
| 538 | 578 | return 0; |
|---|
| 539 | 579 | |
|---|
| 540 | | - comment = ltrim(comment); |
|---|
| 580 | + comment = skip_spaces(comment); |
|---|
| 541 | 581 | comment__symbol(ops->source.raw, comment + 1, &ops->source.addr, &ops->source.name); |
|---|
| 542 | 582 | comment__symbol(ops->target.raw, comment + 1, &ops->target.addr, &ops->target.name); |
|---|
| 543 | 583 | |
|---|
| .. | .. |
|---|
| 549 | 589 | } |
|---|
| 550 | 590 | |
|---|
| 551 | 591 | static int mov__scnprintf(struct ins *ins, char *bf, size_t size, |
|---|
| 552 | | - struct ins_operands *ops) |
|---|
| 592 | + struct ins_operands *ops, int max_ins_name) |
|---|
| 553 | 593 | { |
|---|
| 554 | | - return scnprintf(bf, size, "%-6s %s,%s", ins->name, |
|---|
| 594 | + return scnprintf(bf, size, "%-*s %s,%s", max_ins_name, ins->name, |
|---|
| 555 | 595 | ops->source.name ?: ops->source.raw, |
|---|
| 556 | 596 | ops->target.name ?: ops->target.raw); |
|---|
| 557 | 597 | } |
|---|
| .. | .. |
|---|
| 582 | 622 | if (comment == NULL) |
|---|
| 583 | 623 | return 0; |
|---|
| 584 | 624 | |
|---|
| 585 | | - comment = ltrim(comment); |
|---|
| 625 | + comment = skip_spaces(comment); |
|---|
| 586 | 626 | comment__symbol(ops->target.raw, comment + 1, &ops->target.addr, &ops->target.name); |
|---|
| 587 | 627 | |
|---|
| 588 | 628 | return 0; |
|---|
| 589 | 629 | } |
|---|
| 590 | 630 | |
|---|
| 591 | 631 | static int dec__scnprintf(struct ins *ins, char *bf, size_t size, |
|---|
| 592 | | - struct ins_operands *ops) |
|---|
| 632 | + struct ins_operands *ops, int max_ins_name) |
|---|
| 593 | 633 | { |
|---|
| 594 | | - return scnprintf(bf, size, "%-6s %s", ins->name, |
|---|
| 634 | + return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, |
|---|
| 595 | 635 | ops->target.name ?: ops->target.raw); |
|---|
| 596 | 636 | } |
|---|
| 597 | 637 | |
|---|
| .. | .. |
|---|
| 601 | 641 | }; |
|---|
| 602 | 642 | |
|---|
| 603 | 643 | static int nop__scnprintf(struct ins *ins __maybe_unused, char *bf, size_t size, |
|---|
| 604 | | - struct ins_operands *ops __maybe_unused) |
|---|
| 644 | + struct ins_operands *ops __maybe_unused, int max_ins_name) |
|---|
| 605 | 645 | { |
|---|
| 606 | | - return scnprintf(bf, size, "%-6s", "nop"); |
|---|
| 646 | + return scnprintf(bf, size, "%-*s", max_ins_name, "nop"); |
|---|
| 607 | 647 | } |
|---|
| 608 | 648 | |
|---|
| 609 | 649 | static struct ins_ops nop_ops = { |
|---|
| .. | .. |
|---|
| 821 | 861 | ch[offset].start < start) |
|---|
| 822 | 862 | return 0; |
|---|
| 823 | 863 | } |
|---|
| 864 | + |
|---|
| 865 | + if (ch[offset].num < NUM_SPARKS) |
|---|
| 866 | + ch[offset].cycles_spark[ch[offset].num] = cycles; |
|---|
| 867 | + |
|---|
| 824 | 868 | ch[offset].have_start = have_start; |
|---|
| 825 | 869 | ch[offset].start = start; |
|---|
| 826 | 870 | ch[offset].cycles += cycles; |
|---|
| .. | .. |
|---|
| 828 | 872 | return 0; |
|---|
| 829 | 873 | } |
|---|
| 830 | 874 | |
|---|
| 831 | | -static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, |
|---|
| 875 | +static int __symbol__inc_addr_samples(struct map_symbol *ms, |
|---|
| 832 | 876 | struct annotated_source *src, int evidx, u64 addr, |
|---|
| 833 | 877 | struct perf_sample *sample) |
|---|
| 834 | 878 | { |
|---|
| 879 | + struct symbol *sym = ms->sym; |
|---|
| 835 | 880 | unsigned offset; |
|---|
| 836 | 881 | struct sym_hist *h; |
|---|
| 837 | 882 | |
|---|
| 838 | | - pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); |
|---|
| 883 | + pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, ms->map->unmap_ip(ms->map, addr)); |
|---|
| 839 | 884 | |
|---|
| 840 | 885 | if ((addr < sym->start || addr >= sym->end) && |
|---|
| 841 | 886 | (addr != sym->end || sym->start != sym->end)) { |
|---|
| .. | .. |
|---|
| 902 | 947 | return notes->src; |
|---|
| 903 | 948 | } |
|---|
| 904 | 949 | |
|---|
| 905 | | -static int symbol__inc_addr_samples(struct symbol *sym, struct map *map, |
|---|
| 906 | | - struct perf_evsel *evsel, u64 addr, |
|---|
| 950 | +static int symbol__inc_addr_samples(struct map_symbol *ms, |
|---|
| 951 | + struct evsel *evsel, u64 addr, |
|---|
| 907 | 952 | struct perf_sample *sample) |
|---|
| 908 | 953 | { |
|---|
| 954 | + struct symbol *sym = ms->sym; |
|---|
| 909 | 955 | struct annotated_source *src; |
|---|
| 910 | 956 | |
|---|
| 911 | 957 | if (sym == NULL) |
|---|
| 912 | 958 | return 0; |
|---|
| 913 | | - src = symbol__hists(sym, evsel->evlist->nr_entries); |
|---|
| 914 | | - return (src) ? __symbol__inc_addr_samples(sym, map, src, evsel->idx, |
|---|
| 915 | | - addr, sample) : 0; |
|---|
| 959 | + src = symbol__hists(sym, evsel->evlist->core.nr_entries); |
|---|
| 960 | + return src ? __symbol__inc_addr_samples(ms, src, evsel->idx, addr, sample) : 0; |
|---|
| 916 | 961 | } |
|---|
| 917 | 962 | |
|---|
| 918 | 963 | static int symbol__account_cycles(u64 addr, u64 start, |
|---|
| .. | .. |
|---|
| 960 | 1005 | * it starts on the function start. |
|---|
| 961 | 1006 | */ |
|---|
| 962 | 1007 | if (start && |
|---|
| 963 | | - (start->sym == ams->sym || |
|---|
| 964 | | - (ams->sym && |
|---|
| 965 | | - start->addr == ams->sym->start + ams->map->start))) |
|---|
| 1008 | + (start->ms.sym == ams->ms.sym || |
|---|
| 1009 | + (ams->ms.sym && |
|---|
| 1010 | + start->addr == ams->ms.sym->start + ams->ms.map->start))) |
|---|
| 966 | 1011 | saddr = start->al_addr; |
|---|
| 967 | 1012 | if (saddr == 0) |
|---|
| 968 | 1013 | pr_debug2("BB with bad start: addr %"PRIx64" start %"PRIx64" sym %"PRIx64" saddr %"PRIx64"\n", |
|---|
| 969 | 1014 | ams->addr, |
|---|
| 970 | 1015 | start ? start->addr : 0, |
|---|
| 971 | | - ams->sym ? ams->sym->start + ams->map->start : 0, |
|---|
| 1016 | + ams->ms.sym ? ams->ms.sym->start + ams->ms.map->start : 0, |
|---|
| 972 | 1017 | saddr); |
|---|
| 973 | | - err = symbol__account_cycles(ams->al_addr, saddr, ams->sym, cycles); |
|---|
| 1018 | + err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles); |
|---|
| 974 | 1019 | if (err) |
|---|
| 975 | 1020 | pr_debug2("account_cycles failed %d\n", err); |
|---|
| 976 | 1021 | return err; |
|---|
| .. | .. |
|---|
| 991 | 1036 | static void annotation__count_and_fill(struct annotation *notes, u64 start, u64 end, struct cyc_hist *ch) |
|---|
| 992 | 1037 | { |
|---|
| 993 | 1038 | unsigned n_insn; |
|---|
| 1039 | + unsigned int cover_insn = 0; |
|---|
| 994 | 1040 | u64 offset; |
|---|
| 995 | 1041 | |
|---|
| 996 | 1042 | n_insn = annotation__count_insn(notes, start, end); |
|---|
| .. | .. |
|---|
| 998 | 1044 | float ipc = n_insn / ((double)ch->cycles / (double)ch->num); |
|---|
| 999 | 1045 | |
|---|
| 1000 | 1046 | /* Hide data when there are too many overlaps. */ |
|---|
| 1001 | | - if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2) |
|---|
| 1047 | + if (ch->reset >= 0x7fff) |
|---|
| 1002 | 1048 | return; |
|---|
| 1003 | 1049 | |
|---|
| 1004 | 1050 | for (offset = start; offset <= end; offset++) { |
|---|
| 1005 | 1051 | struct annotation_line *al = notes->offsets[offset]; |
|---|
| 1006 | 1052 | |
|---|
| 1007 | | - if (al) |
|---|
| 1053 | + if (al && al->ipc == 0.0) { |
|---|
| 1008 | 1054 | al->ipc = ipc; |
|---|
| 1055 | + cover_insn++; |
|---|
| 1056 | + } |
|---|
| 1057 | + } |
|---|
| 1058 | + |
|---|
| 1059 | + if (cover_insn) { |
|---|
| 1060 | + notes->hit_cycles += ch->cycles; |
|---|
| 1061 | + notes->hit_insn += n_insn * ch->num; |
|---|
| 1062 | + notes->cover_insn += cover_insn; |
|---|
| 1009 | 1063 | } |
|---|
| 1010 | 1064 | } |
|---|
| 1011 | 1065 | } |
|---|
| 1012 | 1066 | |
|---|
| 1013 | 1067 | void annotation__compute_ipc(struct annotation *notes, size_t size) |
|---|
| 1014 | 1068 | { |
|---|
| 1015 | | - u64 offset; |
|---|
| 1069 | + s64 offset; |
|---|
| 1016 | 1070 | |
|---|
| 1017 | 1071 | if (!notes->src || !notes->src->cycles_hist) |
|---|
| 1018 | 1072 | return; |
|---|
| 1019 | 1073 | |
|---|
| 1074 | + notes->total_insn = annotation__count_insn(notes, 0, size - 1); |
|---|
| 1075 | + notes->hit_cycles = 0; |
|---|
| 1076 | + notes->hit_insn = 0; |
|---|
| 1077 | + notes->cover_insn = 0; |
|---|
| 1078 | + |
|---|
| 1020 | 1079 | pthread_mutex_lock(¬es->lock); |
|---|
| 1021 | | - for (offset = 0; offset < size; ++offset) { |
|---|
| 1080 | + for (offset = size - 1; offset >= 0; --offset) { |
|---|
| 1022 | 1081 | struct cyc_hist *ch; |
|---|
| 1023 | 1082 | |
|---|
| 1024 | 1083 | ch = ¬es->src->cycles_hist[offset]; |
|---|
| .. | .. |
|---|
| 1040 | 1099 | } |
|---|
| 1041 | 1100 | |
|---|
| 1042 | 1101 | int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample, |
|---|
| 1043 | | - struct perf_evsel *evsel) |
|---|
| 1102 | + struct evsel *evsel) |
|---|
| 1044 | 1103 | { |
|---|
| 1045 | | - return symbol__inc_addr_samples(ams->sym, ams->map, evsel, ams->al_addr, sample); |
|---|
| 1104 | + return symbol__inc_addr_samples(&ams->ms, evsel, ams->al_addr, sample); |
|---|
| 1046 | 1105 | } |
|---|
| 1047 | 1106 | |
|---|
| 1048 | 1107 | int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *sample, |
|---|
| 1049 | | - struct perf_evsel *evsel, u64 ip) |
|---|
| 1108 | + struct evsel *evsel, u64 ip) |
|---|
| 1050 | 1109 | { |
|---|
| 1051 | | - return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evsel, ip, sample); |
|---|
| 1110 | + return symbol__inc_addr_samples(&he->ms, evsel, ip, sample); |
|---|
| 1052 | 1111 | } |
|---|
| 1053 | 1112 | |
|---|
| 1054 | 1113 | static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map_symbol *ms) |
|---|
| .. | .. |
|---|
| 1064 | 1123 | |
|---|
| 1065 | 1124 | static int disasm_line__parse(char *line, const char **namep, char **rawp) |
|---|
| 1066 | 1125 | { |
|---|
| 1067 | | - char tmp, *name = ltrim(line); |
|---|
| 1126 | + char tmp, *name = skip_spaces(line); |
|---|
| 1068 | 1127 | |
|---|
| 1069 | 1128 | if (name[0] == '\0') |
|---|
| 1070 | 1129 | return -1; |
|---|
| .. | .. |
|---|
| 1082 | 1141 | goto out; |
|---|
| 1083 | 1142 | |
|---|
| 1084 | 1143 | (*rawp)[0] = tmp; |
|---|
| 1085 | | - *rawp = ltrim(*rawp); |
|---|
| 1144 | + *rawp = strim(*rawp); |
|---|
| 1086 | 1145 | |
|---|
| 1087 | 1146 | return 0; |
|---|
| 1088 | 1147 | |
|---|
| .. | .. |
|---|
| 1091 | 1150 | } |
|---|
| 1092 | 1151 | |
|---|
| 1093 | 1152 | struct annotate_args { |
|---|
| 1094 | | - size_t privsize; |
|---|
| 1095 | | - struct arch *arch; |
|---|
| 1096 | | - struct map_symbol ms; |
|---|
| 1097 | | - struct perf_evsel *evsel; |
|---|
| 1153 | + struct arch *arch; |
|---|
| 1154 | + struct map_symbol ms; |
|---|
| 1155 | + struct evsel *evsel; |
|---|
| 1098 | 1156 | struct annotation_options *options; |
|---|
| 1099 | | - s64 offset; |
|---|
| 1100 | | - char *line; |
|---|
| 1101 | | - int line_nr; |
|---|
| 1157 | + s64 offset; |
|---|
| 1158 | + char *line; |
|---|
| 1159 | + int line_nr; |
|---|
| 1102 | 1160 | }; |
|---|
| 1103 | 1161 | |
|---|
| 1104 | | -static void annotation_line__delete(struct annotation_line *al) |
|---|
| 1162 | +static void annotation_line__init(struct annotation_line *al, |
|---|
| 1163 | + struct annotate_args *args, |
|---|
| 1164 | + int nr) |
|---|
| 1105 | 1165 | { |
|---|
| 1106 | | - void *ptr = (void *) al - al->privsize; |
|---|
| 1107 | | - |
|---|
| 1108 | | - free_srcline(al->path); |
|---|
| 1109 | | - zfree(&al->line); |
|---|
| 1110 | | - free(ptr); |
|---|
| 1166 | + al->offset = args->offset; |
|---|
| 1167 | + al->line = strdup(args->line); |
|---|
| 1168 | + al->line_nr = args->line_nr; |
|---|
| 1169 | + al->data_nr = nr; |
|---|
| 1111 | 1170 | } |
|---|
| 1112 | 1171 | |
|---|
| 1113 | | -/* |
|---|
| 1114 | | - * Allocating the annotation line data with following |
|---|
| 1115 | | - * structure: |
|---|
| 1116 | | - * |
|---|
| 1117 | | - * -------------------------------------- |
|---|
| 1118 | | - * private space | struct annotation_line |
|---|
| 1119 | | - * -------------------------------------- |
|---|
| 1120 | | - * |
|---|
| 1121 | | - * Size of the private space is stored in 'struct annotation_line'. |
|---|
| 1122 | | - * |
|---|
| 1123 | | - */ |
|---|
| 1124 | | -static struct annotation_line * |
|---|
| 1125 | | -annotation_line__new(struct annotate_args *args, size_t privsize) |
|---|
| 1172 | +static void annotation_line__exit(struct annotation_line *al) |
|---|
| 1173 | +{ |
|---|
| 1174 | + free_srcline(al->path); |
|---|
| 1175 | + zfree(&al->line); |
|---|
| 1176 | +} |
|---|
| 1177 | + |
|---|
| 1178 | +static size_t disasm_line_size(int nr) |
|---|
| 1126 | 1179 | { |
|---|
| 1127 | 1180 | struct annotation_line *al; |
|---|
| 1128 | | - struct perf_evsel *evsel = args->evsel; |
|---|
| 1129 | | - size_t size = privsize + sizeof(*al); |
|---|
| 1130 | | - int nr = 1; |
|---|
| 1131 | 1181 | |
|---|
| 1132 | | - if (perf_evsel__is_group_event(evsel)) |
|---|
| 1133 | | - nr = evsel->nr_members; |
|---|
| 1134 | | - |
|---|
| 1135 | | - size += sizeof(al->data[0]) * nr; |
|---|
| 1136 | | - |
|---|
| 1137 | | - al = zalloc(size); |
|---|
| 1138 | | - if (al) { |
|---|
| 1139 | | - al = (void *) al + privsize; |
|---|
| 1140 | | - al->privsize = privsize; |
|---|
| 1141 | | - al->offset = args->offset; |
|---|
| 1142 | | - al->line = strdup(args->line); |
|---|
| 1143 | | - al->line_nr = args->line_nr; |
|---|
| 1144 | | - al->data_nr = nr; |
|---|
| 1145 | | - } |
|---|
| 1146 | | - |
|---|
| 1147 | | - return al; |
|---|
| 1182 | + return (sizeof(struct disasm_line) + (sizeof(al->data[0]) * nr)); |
|---|
| 1148 | 1183 | } |
|---|
| 1149 | 1184 | |
|---|
| 1150 | 1185 | /* |
|---|
| 1151 | 1186 | * Allocating the disasm annotation line data with |
|---|
| 1152 | 1187 | * following structure: |
|---|
| 1153 | 1188 | * |
|---|
| 1154 | | - * ------------------------------------------------------------ |
|---|
| 1155 | | - * privsize space | struct disasm_line | struct annotation_line |
|---|
| 1156 | | - * ------------------------------------------------------------ |
|---|
| 1189 | + * ------------------------------------------- |
|---|
| 1190 | + * struct disasm_line | struct annotation_line |
|---|
| 1191 | + * ------------------------------------------- |
|---|
| 1157 | 1192 | * |
|---|
| 1158 | 1193 | * We have 'struct annotation_line' member as last member |
|---|
| 1159 | 1194 | * of 'struct disasm_line' to have an easy access. |
|---|
| 1160 | | - * |
|---|
| 1161 | 1195 | */ |
|---|
| 1162 | 1196 | static struct disasm_line *disasm_line__new(struct annotate_args *args) |
|---|
| 1163 | 1197 | { |
|---|
| 1164 | 1198 | struct disasm_line *dl = NULL; |
|---|
| 1165 | | - struct annotation_line *al; |
|---|
| 1166 | | - size_t privsize = args->privsize + offsetof(struct disasm_line, al); |
|---|
| 1199 | + int nr = 1; |
|---|
| 1167 | 1200 | |
|---|
| 1168 | | - al = annotation_line__new(args, privsize); |
|---|
| 1169 | | - if (al != NULL) { |
|---|
| 1170 | | - dl = disasm_line(al); |
|---|
| 1201 | + if (evsel__is_group_event(args->evsel)) |
|---|
| 1202 | + nr = args->evsel->core.nr_members; |
|---|
| 1171 | 1203 | |
|---|
| 1172 | | - if (dl->al.line == NULL) |
|---|
| 1173 | | - goto out_delete; |
|---|
| 1204 | + dl = zalloc(disasm_line_size(nr)); |
|---|
| 1205 | + if (!dl) |
|---|
| 1206 | + return NULL; |
|---|
| 1174 | 1207 | |
|---|
| 1175 | | - if (args->offset != -1) { |
|---|
| 1176 | | - if (disasm_line__parse(dl->al.line, &dl->ins.name, &dl->ops.raw) < 0) |
|---|
| 1177 | | - goto out_free_line; |
|---|
| 1208 | + annotation_line__init(&dl->al, args, nr); |
|---|
| 1209 | + if (dl->al.line == NULL) |
|---|
| 1210 | + goto out_delete; |
|---|
| 1178 | 1211 | |
|---|
| 1179 | | - disasm_line__init_ins(dl, args->arch, &args->ms); |
|---|
| 1180 | | - } |
|---|
| 1212 | + if (args->offset != -1) { |
|---|
| 1213 | + if (disasm_line__parse(dl->al.line, &dl->ins.name, &dl->ops.raw) < 0) |
|---|
| 1214 | + goto out_free_line; |
|---|
| 1215 | + |
|---|
| 1216 | + disasm_line__init_ins(dl, args->arch, &args->ms); |
|---|
| 1181 | 1217 | } |
|---|
| 1182 | 1218 | |
|---|
| 1183 | 1219 | return dl; |
|---|
| .. | .. |
|---|
| 1195 | 1231 | dl->ins.ops->free(&dl->ops); |
|---|
| 1196 | 1232 | else |
|---|
| 1197 | 1233 | ins__delete(&dl->ops); |
|---|
| 1198 | | - free((void *)dl->ins.name); |
|---|
| 1199 | | - dl->ins.name = NULL; |
|---|
| 1200 | | - annotation_line__delete(&dl->al); |
|---|
| 1234 | + zfree(&dl->ins.name); |
|---|
| 1235 | + annotation_line__exit(&dl->al); |
|---|
| 1236 | + free(dl); |
|---|
| 1201 | 1237 | } |
|---|
| 1202 | 1238 | |
|---|
| 1203 | | -int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw) |
|---|
| 1239 | +int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw, int max_ins_name) |
|---|
| 1204 | 1240 | { |
|---|
| 1205 | 1241 | if (raw || !dl->ins.ops) |
|---|
| 1206 | | - return scnprintf(bf, size, "%-6s %s", dl->ins.name, dl->ops.raw); |
|---|
| 1242 | + return scnprintf(bf, size, "%-*s %s", max_ins_name, dl->ins.name, dl->ops.raw); |
|---|
| 1207 | 1243 | |
|---|
| 1208 | | - return ins__scnprintf(&dl->ins, bf, size, &dl->ops); |
|---|
| 1244 | + return ins__scnprintf(&dl->ins, bf, size, &dl->ops, max_ins_name); |
|---|
| 1209 | 1245 | } |
|---|
| 1210 | 1246 | |
|---|
| 1211 | 1247 | static void annotation_line__add(struct annotation_line *al, struct list_head *head) |
|---|
| .. | .. |
|---|
| 1320 | 1356 | |
|---|
| 1321 | 1357 | static int |
|---|
| 1322 | 1358 | annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start, |
|---|
| 1323 | | - struct perf_evsel *evsel, u64 len, int min_pcnt, int printed, |
|---|
| 1359 | + struct evsel *evsel, u64 len, int min_pcnt, int printed, |
|---|
| 1324 | 1360 | int max_lines, struct annotation_line *queue, int addr_fmt_width, |
|---|
| 1325 | 1361 | int percent_type) |
|---|
| 1326 | 1362 | { |
|---|
| .. | .. |
|---|
| 1408 | 1444 | if (queue) |
|---|
| 1409 | 1445 | return -1; |
|---|
| 1410 | 1446 | |
|---|
| 1411 | | - if (perf_evsel__is_group_event(evsel)) |
|---|
| 1412 | | - width *= evsel->nr_members; |
|---|
| 1447 | + if (evsel__is_group_event(evsel)) |
|---|
| 1448 | + width *= evsel->core.nr_members; |
|---|
| 1413 | 1449 | |
|---|
| 1414 | 1450 | if (!*al->line) |
|---|
| 1415 | 1451 | printf(" %*s:\n", width, " "); |
|---|
| .. | .. |
|---|
| 1440 | 1476 | * means that it's not a disassembly line so should be treated differently. |
|---|
| 1441 | 1477 | * The ops.raw part will be parsed further according to type of the instruction. |
|---|
| 1442 | 1478 | */ |
|---|
| 1443 | | -static int symbol__parse_objdump_line(struct symbol *sym, FILE *file, |
|---|
| 1479 | +static int symbol__parse_objdump_line(struct symbol *sym, |
|---|
| 1444 | 1480 | struct annotate_args *args, |
|---|
| 1445 | | - int *line_nr) |
|---|
| 1481 | + char *parsed_line, int *line_nr) |
|---|
| 1446 | 1482 | { |
|---|
| 1447 | 1483 | struct map *map = args->ms.map; |
|---|
| 1448 | 1484 | struct annotation *notes = symbol__annotation(sym); |
|---|
| 1449 | 1485 | struct disasm_line *dl; |
|---|
| 1450 | | - char *line = NULL, *parsed_line, *tmp, *tmp2; |
|---|
| 1451 | | - size_t line_len; |
|---|
| 1486 | + char *tmp; |
|---|
| 1452 | 1487 | s64 line_ip, offset = -1; |
|---|
| 1453 | 1488 | regmatch_t match[2]; |
|---|
| 1454 | | - |
|---|
| 1455 | | - if (getline(&line, &line_len, file) < 0) |
|---|
| 1456 | | - return -1; |
|---|
| 1457 | | - |
|---|
| 1458 | | - if (!line) |
|---|
| 1459 | | - return -1; |
|---|
| 1460 | | - |
|---|
| 1461 | | - line_ip = -1; |
|---|
| 1462 | | - parsed_line = rtrim(line); |
|---|
| 1463 | 1489 | |
|---|
| 1464 | 1490 | /* /filename:linenr ? Save line number and ignore. */ |
|---|
| 1465 | 1491 | if (regexec(&file_lineno, parsed_line, 2, match, 0) == 0) { |
|---|
| .. | .. |
|---|
| 1467 | 1493 | return 0; |
|---|
| 1468 | 1494 | } |
|---|
| 1469 | 1495 | |
|---|
| 1470 | | - tmp = ltrim(parsed_line); |
|---|
| 1471 | | - if (*tmp) { |
|---|
| 1472 | | - /* |
|---|
| 1473 | | - * Parse hexa addresses followed by ':' |
|---|
| 1474 | | - */ |
|---|
| 1475 | | - line_ip = strtoull(tmp, &tmp2, 16); |
|---|
| 1476 | | - if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') |
|---|
| 1477 | | - line_ip = -1; |
|---|
| 1478 | | - } |
|---|
| 1479 | | - |
|---|
| 1480 | | - if (line_ip != -1) { |
|---|
| 1496 | + /* Process hex address followed by ':'. */ |
|---|
| 1497 | + line_ip = strtoull(parsed_line, &tmp, 16); |
|---|
| 1498 | + if (parsed_line != tmp && tmp[0] == ':' && tmp[1] != '\0') { |
|---|
| 1481 | 1499 | u64 start = map__rip_2objdump(map, sym->start), |
|---|
| 1482 | 1500 | end = map__rip_2objdump(map, sym->end); |
|---|
| 1483 | 1501 | |
|---|
| .. | .. |
|---|
| 1485 | 1503 | if ((u64)line_ip < start || (u64)line_ip >= end) |
|---|
| 1486 | 1504 | offset = -1; |
|---|
| 1487 | 1505 | else |
|---|
| 1488 | | - parsed_line = tmp2 + 1; |
|---|
| 1506 | + parsed_line = tmp + 1; |
|---|
| 1489 | 1507 | } |
|---|
| 1490 | 1508 | |
|---|
| 1491 | 1509 | args->offset = offset; |
|---|
| .. | .. |
|---|
| 1494 | 1512 | args->ms.sym = sym; |
|---|
| 1495 | 1513 | |
|---|
| 1496 | 1514 | dl = disasm_line__new(args); |
|---|
| 1497 | | - free(line); |
|---|
| 1498 | 1515 | (*line_nr)++; |
|---|
| 1499 | 1516 | |
|---|
| 1500 | 1517 | if (dl == NULL) |
|---|
| .. | .. |
|---|
| 1509 | 1526 | /* kcore has no symbols, so add the call target symbol */ |
|---|
| 1510 | 1527 | if (dl->ins.ops && ins__is_call(&dl->ins) && !dl->ops.target.sym) { |
|---|
| 1511 | 1528 | struct addr_map_symbol target = { |
|---|
| 1512 | | - .map = map, |
|---|
| 1513 | 1529 | .addr = dl->ops.target.addr, |
|---|
| 1530 | + .ms = { .map = map, }, |
|---|
| 1514 | 1531 | }; |
|---|
| 1515 | 1532 | |
|---|
| 1516 | | - if (!map_groups__find_ams(&target) && |
|---|
| 1517 | | - target.sym->start == target.al_addr) |
|---|
| 1518 | | - dl->ops.target.sym = target.sym; |
|---|
| 1533 | + if (!maps__find_ams(args->ms.maps, &target) && |
|---|
| 1534 | + target.ms.sym->start == target.al_addr) |
|---|
| 1535 | + dl->ops.target.sym = target.ms.sym; |
|---|
| 1519 | 1536 | } |
|---|
| 1520 | 1537 | |
|---|
| 1521 | 1538 | annotation_line__add(&dl->al, ¬es->src->source); |
|---|
| .. | .. |
|---|
| 1547 | 1564 | return; |
|---|
| 1548 | 1565 | } |
|---|
| 1549 | 1566 | |
|---|
| 1550 | | - list_del(&dl->al.node); |
|---|
| 1567 | + list_del_init(&dl->al.node); |
|---|
| 1551 | 1568 | disasm_line__free(dl); |
|---|
| 1552 | 1569 | } |
|---|
| 1553 | 1570 | } |
|---|
| 1554 | 1571 | |
|---|
| 1555 | | -int symbol__strerror_disassemble(struct symbol *sym __maybe_unused, struct map *map, |
|---|
| 1556 | | - int errnum, char *buf, size_t buflen) |
|---|
| 1572 | +int symbol__strerror_disassemble(struct map_symbol *ms, int errnum, char *buf, size_t buflen) |
|---|
| 1557 | 1573 | { |
|---|
| 1558 | | - struct dso *dso = map->dso; |
|---|
| 1574 | + struct dso *dso = ms->map->dso; |
|---|
| 1559 | 1575 | |
|---|
| 1560 | 1576 | BUG_ON(buflen == 0); |
|---|
| 1561 | 1577 | |
|---|
| .. | .. |
|---|
| 1570 | 1586 | char *build_id_msg = NULL; |
|---|
| 1571 | 1587 | |
|---|
| 1572 | 1588 | if (dso->has_build_id) { |
|---|
| 1573 | | - build_id__sprintf(dso->build_id, |
|---|
| 1574 | | - sizeof(dso->build_id), bf + 15); |
|---|
| 1589 | + build_id__sprintf(&dso->bid, bf + 15); |
|---|
| 1575 | 1590 | build_id_msg = bf; |
|---|
| 1576 | 1591 | } |
|---|
| 1577 | 1592 | scnprintf(buf, buflen, |
|---|
| .. | .. |
|---|
| 1582 | 1597 | "or:\n\n" |
|---|
| 1583 | 1598 | " --vmlinux vmlinux\n", build_id_msg ?: ""); |
|---|
| 1584 | 1599 | } |
|---|
| 1600 | + break; |
|---|
| 1601 | + case SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF: |
|---|
| 1602 | + scnprintf(buf, buflen, "Please link with binutils's libopcode to enable BPF annotation"); |
|---|
| 1603 | + break; |
|---|
| 1604 | + case SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_REGEXP: |
|---|
| 1605 | + scnprintf(buf, buflen, "Problems with arch specific instruction name regular expressions."); |
|---|
| 1606 | + break; |
|---|
| 1607 | + case SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING: |
|---|
| 1608 | + scnprintf(buf, buflen, "Problems while parsing the CPUID in the arch specific initialization."); |
|---|
| 1609 | + break; |
|---|
| 1610 | + case SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE: |
|---|
| 1611 | + scnprintf(buf, buflen, "Invalid BPF file: %s.", dso->long_name); |
|---|
| 1612 | + break; |
|---|
| 1613 | + case SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF: |
|---|
| 1614 | + scnprintf(buf, buflen, "The %s BPF file has no BTF section, compile with -g or use pahole -J.", |
|---|
| 1615 | + dso->long_name); |
|---|
| 1585 | 1616 | break; |
|---|
| 1586 | 1617 | default: |
|---|
| 1587 | 1618 | scnprintf(buf, buflen, "Internal error: Invalid %d error code\n", errnum); |
|---|
| .. | .. |
|---|
| 1597 | 1628 | char *build_id_filename; |
|---|
| 1598 | 1629 | char *build_id_path = NULL; |
|---|
| 1599 | 1630 | char *pos; |
|---|
| 1631 | + int len; |
|---|
| 1600 | 1632 | |
|---|
| 1601 | 1633 | if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && |
|---|
| 1602 | 1634 | !dso__is_kcore(dso)) |
|---|
| .. | .. |
|---|
| 1625 | 1657 | if (pos && strlen(pos) < SBUILD_ID_SIZE - 2) |
|---|
| 1626 | 1658 | dirname(build_id_path); |
|---|
| 1627 | 1659 | |
|---|
| 1628 | | - if (dso__is_kcore(dso) || |
|---|
| 1629 | | - readlink(build_id_path, linkname, sizeof(linkname)) < 0 || |
|---|
| 1630 | | - strstr(linkname, DSO__NAME_KALLSYMS) || |
|---|
| 1631 | | - access(filename, R_OK)) { |
|---|
| 1660 | + if (dso__is_kcore(dso)) |
|---|
| 1661 | + goto fallback; |
|---|
| 1662 | + |
|---|
| 1663 | + len = readlink(build_id_path, linkname, sizeof(linkname) - 1); |
|---|
| 1664 | + if (len < 0) |
|---|
| 1665 | + goto fallback; |
|---|
| 1666 | + |
|---|
| 1667 | + linkname[len] = '\0'; |
|---|
| 1668 | + if (strstr(linkname, DSO__NAME_KALLSYMS) || |
|---|
| 1669 | + access(filename, R_OK)) { |
|---|
| 1632 | 1670 | fallback: |
|---|
| 1633 | 1671 | /* |
|---|
| 1634 | 1672 | * If we don't have build-ids or the build-id file isn't in the |
|---|
| .. | .. |
|---|
| 1642 | 1680 | return 0; |
|---|
| 1643 | 1681 | } |
|---|
| 1644 | 1682 | |
|---|
| 1683 | +#if defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) |
|---|
| 1684 | +#define PACKAGE "perf" |
|---|
| 1685 | +#include <bfd.h> |
|---|
| 1686 | +#include <dis-asm.h> |
|---|
| 1687 | + |
|---|
| 1688 | +static int symbol__disassemble_bpf(struct symbol *sym, |
|---|
| 1689 | + struct annotate_args *args) |
|---|
| 1690 | +{ |
|---|
| 1691 | + struct annotation *notes = symbol__annotation(sym); |
|---|
| 1692 | + struct annotation_options *opts = args->options; |
|---|
| 1693 | + struct bpf_prog_info_linear *info_linear; |
|---|
| 1694 | + struct bpf_prog_linfo *prog_linfo = NULL; |
|---|
| 1695 | + struct bpf_prog_info_node *info_node; |
|---|
| 1696 | + int len = sym->end - sym->start; |
|---|
| 1697 | + disassembler_ftype disassemble; |
|---|
| 1698 | + struct map *map = args->ms.map; |
|---|
| 1699 | + struct disassemble_info info; |
|---|
| 1700 | + struct dso *dso = map->dso; |
|---|
| 1701 | + int pc = 0, count, sub_id; |
|---|
| 1702 | + struct btf *btf = NULL; |
|---|
| 1703 | + char tpath[PATH_MAX]; |
|---|
| 1704 | + size_t buf_size; |
|---|
| 1705 | + int nr_skip = 0; |
|---|
| 1706 | + char *buf; |
|---|
| 1707 | + bfd *bfdf; |
|---|
| 1708 | + int ret; |
|---|
| 1709 | + FILE *s; |
|---|
| 1710 | + |
|---|
| 1711 | + if (dso->binary_type != DSO_BINARY_TYPE__BPF_PROG_INFO) |
|---|
| 1712 | + return SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE; |
|---|
| 1713 | + |
|---|
| 1714 | + pr_debug("%s: handling sym %s addr %" PRIx64 " len %" PRIx64 "\n", __func__, |
|---|
| 1715 | + sym->name, sym->start, sym->end - sym->start); |
|---|
| 1716 | + |
|---|
| 1717 | + memset(tpath, 0, sizeof(tpath)); |
|---|
| 1718 | + perf_exe(tpath, sizeof(tpath)); |
|---|
| 1719 | + |
|---|
| 1720 | + bfdf = bfd_openr(tpath, NULL); |
|---|
| 1721 | + if (bfdf == NULL) |
|---|
| 1722 | + abort(); |
|---|
| 1723 | + |
|---|
| 1724 | + if (!bfd_check_format(bfdf, bfd_object)) |
|---|
| 1725 | + abort(); |
|---|
| 1726 | + |
|---|
| 1727 | + s = open_memstream(&buf, &buf_size); |
|---|
| 1728 | + if (!s) { |
|---|
| 1729 | + ret = errno; |
|---|
| 1730 | + goto out; |
|---|
| 1731 | + } |
|---|
| 1732 | + init_disassemble_info(&info, s, |
|---|
| 1733 | + (fprintf_ftype) fprintf); |
|---|
| 1734 | + |
|---|
| 1735 | + info.arch = bfd_get_arch(bfdf); |
|---|
| 1736 | + info.mach = bfd_get_mach(bfdf); |
|---|
| 1737 | + |
|---|
| 1738 | + info_node = perf_env__find_bpf_prog_info(dso->bpf_prog.env, |
|---|
| 1739 | + dso->bpf_prog.id); |
|---|
| 1740 | + if (!info_node) { |
|---|
| 1741 | + ret = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF; |
|---|
| 1742 | + goto out; |
|---|
| 1743 | + } |
|---|
| 1744 | + info_linear = info_node->info_linear; |
|---|
| 1745 | + sub_id = dso->bpf_prog.sub_id; |
|---|
| 1746 | + |
|---|
| 1747 | + info.buffer = (void *)(uintptr_t)(info_linear->info.jited_prog_insns); |
|---|
| 1748 | + info.buffer_length = info_linear->info.jited_prog_len; |
|---|
| 1749 | + |
|---|
| 1750 | + if (info_linear->info.nr_line_info) |
|---|
| 1751 | + prog_linfo = bpf_prog_linfo__new(&info_linear->info); |
|---|
| 1752 | + |
|---|
| 1753 | + if (info_linear->info.btf_id) { |
|---|
| 1754 | + struct btf_node *node; |
|---|
| 1755 | + |
|---|
| 1756 | + node = perf_env__find_btf(dso->bpf_prog.env, |
|---|
| 1757 | + info_linear->info.btf_id); |
|---|
| 1758 | + if (node) |
|---|
| 1759 | + btf = btf__new((__u8 *)(node->data), |
|---|
| 1760 | + node->data_size); |
|---|
| 1761 | + } |
|---|
| 1762 | + |
|---|
| 1763 | + disassemble_init_for_target(&info); |
|---|
| 1764 | + |
|---|
| 1765 | +#ifdef DISASM_FOUR_ARGS_SIGNATURE |
|---|
| 1766 | + disassemble = disassembler(info.arch, |
|---|
| 1767 | + bfd_big_endian(bfdf), |
|---|
| 1768 | + info.mach, |
|---|
| 1769 | + bfdf); |
|---|
| 1770 | +#else |
|---|
| 1771 | + disassemble = disassembler(bfdf); |
|---|
| 1772 | +#endif |
|---|
| 1773 | + if (disassemble == NULL) |
|---|
| 1774 | + abort(); |
|---|
| 1775 | + |
|---|
| 1776 | + fflush(s); |
|---|
| 1777 | + do { |
|---|
| 1778 | + const struct bpf_line_info *linfo = NULL; |
|---|
| 1779 | + struct disasm_line *dl; |
|---|
| 1780 | + size_t prev_buf_size; |
|---|
| 1781 | + const char *srcline; |
|---|
| 1782 | + u64 addr; |
|---|
| 1783 | + |
|---|
| 1784 | + addr = pc + ((u64 *)(uintptr_t)(info_linear->info.jited_ksyms))[sub_id]; |
|---|
| 1785 | + count = disassemble(pc, &info); |
|---|
| 1786 | + |
|---|
| 1787 | + if (prog_linfo) |
|---|
| 1788 | + linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo, |
|---|
| 1789 | + addr, sub_id, |
|---|
| 1790 | + nr_skip); |
|---|
| 1791 | + |
|---|
| 1792 | + if (linfo && btf) { |
|---|
| 1793 | + srcline = btf__name_by_offset(btf, linfo->line_off); |
|---|
| 1794 | + nr_skip++; |
|---|
| 1795 | + } else |
|---|
| 1796 | + srcline = NULL; |
|---|
| 1797 | + |
|---|
| 1798 | + fprintf(s, "\n"); |
|---|
| 1799 | + prev_buf_size = buf_size; |
|---|
| 1800 | + fflush(s); |
|---|
| 1801 | + |
|---|
| 1802 | + if (!opts->hide_src_code && srcline) { |
|---|
| 1803 | + args->offset = -1; |
|---|
| 1804 | + args->line = strdup(srcline); |
|---|
| 1805 | + args->line_nr = 0; |
|---|
| 1806 | + args->ms.sym = sym; |
|---|
| 1807 | + dl = disasm_line__new(args); |
|---|
| 1808 | + if (dl) { |
|---|
| 1809 | + annotation_line__add(&dl->al, |
|---|
| 1810 | + ¬es->src->source); |
|---|
| 1811 | + } |
|---|
| 1812 | + } |
|---|
| 1813 | + |
|---|
| 1814 | + args->offset = pc; |
|---|
| 1815 | + args->line = buf + prev_buf_size; |
|---|
| 1816 | + args->line_nr = 0; |
|---|
| 1817 | + args->ms.sym = sym; |
|---|
| 1818 | + dl = disasm_line__new(args); |
|---|
| 1819 | + if (dl) |
|---|
| 1820 | + annotation_line__add(&dl->al, ¬es->src->source); |
|---|
| 1821 | + |
|---|
| 1822 | + pc += count; |
|---|
| 1823 | + } while (count > 0 && pc < len); |
|---|
| 1824 | + |
|---|
| 1825 | + ret = 0; |
|---|
| 1826 | +out: |
|---|
| 1827 | + free(prog_linfo); |
|---|
| 1828 | + free(btf); |
|---|
| 1829 | + fclose(s); |
|---|
| 1830 | + bfd_close(bfdf); |
|---|
| 1831 | + return ret; |
|---|
| 1832 | +} |
|---|
| 1833 | +#else // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) |
|---|
| 1834 | +static int symbol__disassemble_bpf(struct symbol *sym __maybe_unused, |
|---|
| 1835 | + struct annotate_args *args __maybe_unused) |
|---|
| 1836 | +{ |
|---|
| 1837 | + return SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF; |
|---|
| 1838 | +} |
|---|
| 1839 | +#endif // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) |
|---|
| 1840 | + |
|---|
| 1841 | +static int |
|---|
| 1842 | +symbol__disassemble_bpf_image(struct symbol *sym, |
|---|
| 1843 | + struct annotate_args *args) |
|---|
| 1844 | +{ |
|---|
| 1845 | + struct annotation *notes = symbol__annotation(sym); |
|---|
| 1846 | + struct disasm_line *dl; |
|---|
| 1847 | + |
|---|
| 1848 | + args->offset = -1; |
|---|
| 1849 | + args->line = strdup("to be implemented"); |
|---|
| 1850 | + args->line_nr = 0; |
|---|
| 1851 | + dl = disasm_line__new(args); |
|---|
| 1852 | + if (dl) |
|---|
| 1853 | + annotation_line__add(&dl->al, ¬es->src->source); |
|---|
| 1854 | + |
|---|
| 1855 | + free(args->line); |
|---|
| 1856 | + return 0; |
|---|
| 1857 | +} |
|---|
| 1858 | + |
|---|
| 1859 | +/* |
|---|
| 1860 | + * Possibly create a new version of line with tabs expanded. Returns the |
|---|
| 1861 | + * existing or new line, storage is updated if a new line is allocated. If |
|---|
| 1862 | + * allocation fails then NULL is returned. |
|---|
| 1863 | + */ |
|---|
| 1864 | +static char *expand_tabs(char *line, char **storage, size_t *storage_len) |
|---|
| 1865 | +{ |
|---|
| 1866 | + size_t i, src, dst, len, new_storage_len, num_tabs; |
|---|
| 1867 | + char *new_line; |
|---|
| 1868 | + size_t line_len = strlen(line); |
|---|
| 1869 | + |
|---|
| 1870 | + for (num_tabs = 0, i = 0; i < line_len; i++) |
|---|
| 1871 | + if (line[i] == '\t') |
|---|
| 1872 | + num_tabs++; |
|---|
| 1873 | + |
|---|
| 1874 | + if (num_tabs == 0) |
|---|
| 1875 | + return line; |
|---|
| 1876 | + |
|---|
| 1877 | + /* |
|---|
| 1878 | + * Space for the line and '\0', less the leading and trailing |
|---|
| 1879 | + * spaces. Each tab may introduce 7 additional spaces. |
|---|
| 1880 | + */ |
|---|
| 1881 | + new_storage_len = line_len + 1 + (num_tabs * 7); |
|---|
| 1882 | + |
|---|
| 1883 | + new_line = malloc(new_storage_len); |
|---|
| 1884 | + if (new_line == NULL) { |
|---|
| 1885 | + pr_err("Failure allocating memory for tab expansion\n"); |
|---|
| 1886 | + return NULL; |
|---|
| 1887 | + } |
|---|
| 1888 | + |
|---|
| 1889 | + /* |
|---|
| 1890 | + * Copy regions starting at src and expand tabs. If there are two |
|---|
| 1891 | + * adjacent tabs then 'src == i', the memcpy is of size 0 and the spaces |
|---|
| 1892 | + * are inserted. |
|---|
| 1893 | + */ |
|---|
| 1894 | + for (i = 0, src = 0, dst = 0; i < line_len && num_tabs; i++) { |
|---|
| 1895 | + if (line[i] == '\t') { |
|---|
| 1896 | + len = i - src; |
|---|
| 1897 | + memcpy(&new_line[dst], &line[src], len); |
|---|
| 1898 | + dst += len; |
|---|
| 1899 | + new_line[dst++] = ' '; |
|---|
| 1900 | + while (dst % 8 != 0) |
|---|
| 1901 | + new_line[dst++] = ' '; |
|---|
| 1902 | + src = i + 1; |
|---|
| 1903 | + num_tabs--; |
|---|
| 1904 | + } |
|---|
| 1905 | + } |
|---|
| 1906 | + |
|---|
| 1907 | + /* Expand the last region. */ |
|---|
| 1908 | + len = line_len - src; |
|---|
| 1909 | + memcpy(&new_line[dst], &line[src], len); |
|---|
| 1910 | + dst += len; |
|---|
| 1911 | + new_line[dst] = '\0'; |
|---|
| 1912 | + |
|---|
| 1913 | + free(*storage); |
|---|
| 1914 | + *storage = new_line; |
|---|
| 1915 | + *storage_len = new_storage_len; |
|---|
| 1916 | + return new_line; |
|---|
| 1917 | + |
|---|
| 1918 | +} |
|---|
| 1919 | + |
|---|
| 1645 | 1920 | static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) |
|---|
| 1646 | 1921 | { |
|---|
| 1647 | 1922 | struct annotation_options *opts = args->options; |
|---|
| .. | .. |
|---|
| 1653 | 1928 | struct kcore_extract kce; |
|---|
| 1654 | 1929 | bool delete_extract = false; |
|---|
| 1655 | 1930 | bool decomp = false; |
|---|
| 1656 | | - int stdout_fd[2]; |
|---|
| 1657 | 1931 | int lineno = 0; |
|---|
| 1658 | 1932 | int nline; |
|---|
| 1659 | | - pid_t pid; |
|---|
| 1933 | + char *line; |
|---|
| 1934 | + size_t line_len; |
|---|
| 1935 | + const char *objdump_argv[] = { |
|---|
| 1936 | + "/bin/sh", |
|---|
| 1937 | + "-c", |
|---|
| 1938 | + NULL, /* Will be the objdump command to run. */ |
|---|
| 1939 | + "--", |
|---|
| 1940 | + NULL, /* Will be the symfs path. */ |
|---|
| 1941 | + NULL, |
|---|
| 1942 | + }; |
|---|
| 1943 | + struct child_process objdump_process; |
|---|
| 1660 | 1944 | int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename)); |
|---|
| 1661 | 1945 | |
|---|
| 1662 | 1946 | if (err) |
|---|
| .. | .. |
|---|
| 1669 | 1953 | pr_debug("annotating [%p] %30s : [%p] %30s\n", |
|---|
| 1670 | 1954 | dso, dso->long_name, sym, sym->name); |
|---|
| 1671 | 1955 | |
|---|
| 1672 | | - if (dso__is_kcore(dso)) { |
|---|
| 1956 | + if (dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO) { |
|---|
| 1957 | + return symbol__disassemble_bpf(sym, args); |
|---|
| 1958 | + } else if (dso->binary_type == DSO_BINARY_TYPE__BPF_IMAGE) { |
|---|
| 1959 | + return symbol__disassemble_bpf_image(sym, args); |
|---|
| 1960 | + } else if (dso__is_kcore(dso)) { |
|---|
| 1673 | 1961 | kce.kcore_filename = symfs_filename; |
|---|
| 1674 | 1962 | kce.addr = map__rip_2objdump(map, sym->start); |
|---|
| 1675 | 1963 | kce.offs = sym->start; |
|---|
| .. | .. |
|---|
| 1684 | 1972 | |
|---|
| 1685 | 1973 | if (dso__decompress_kmodule_path(dso, symfs_filename, |
|---|
| 1686 | 1974 | tmp, sizeof(tmp)) < 0) |
|---|
| 1687 | | - goto out; |
|---|
| 1975 | + return -1; |
|---|
| 1688 | 1976 | |
|---|
| 1689 | 1977 | decomp = true; |
|---|
| 1690 | 1978 | strcpy(symfs_filename, tmp); |
|---|
| .. | .. |
|---|
| 1693 | 1981 | err = asprintf(&command, |
|---|
| 1694 | 1982 | "%s %s%s --start-address=0x%016" PRIx64 |
|---|
| 1695 | 1983 | " --stop-address=0x%016" PRIx64 |
|---|
| 1696 | | - " -l -d %s %s -C \"%s\" 2>/dev/null|grep -v \"%s:\"|expand", |
|---|
| 1984 | + " -l -d %s %s %s %c%s%c %s%s -C \"$1\"", |
|---|
| 1697 | 1985 | opts->objdump_path ?: "objdump", |
|---|
| 1698 | 1986 | opts->disassembler_style ? "-M " : "", |
|---|
| 1699 | 1987 | opts->disassembler_style ?: "", |
|---|
| 1700 | 1988 | map__rip_2objdump(map, sym->start), |
|---|
| 1701 | 1989 | map__rip_2objdump(map, sym->end), |
|---|
| 1702 | | - opts->show_asm_raw ? "" : "--no-show-raw", |
|---|
| 1990 | + opts->show_asm_raw ? "" : "--no-show-raw-insn", |
|---|
| 1703 | 1991 | opts->annotate_src ? "-S" : "", |
|---|
| 1704 | | - symfs_filename, symfs_filename); |
|---|
| 1992 | + opts->prefix ? "--prefix " : "", |
|---|
| 1993 | + opts->prefix ? '"' : ' ', |
|---|
| 1994 | + opts->prefix ?: "", |
|---|
| 1995 | + opts->prefix ? '"' : ' ', |
|---|
| 1996 | + opts->prefix_strip ? "--prefix-strip=" : "", |
|---|
| 1997 | + opts->prefix_strip ?: ""); |
|---|
| 1705 | 1998 | |
|---|
| 1706 | 1999 | if (err < 0) { |
|---|
| 1707 | 2000 | pr_err("Failure allocating memory for the command to run\n"); |
|---|
| .. | .. |
|---|
| 1710 | 2003 | |
|---|
| 1711 | 2004 | pr_debug("Executing: %s\n", command); |
|---|
| 1712 | 2005 | |
|---|
| 1713 | | - err = -1; |
|---|
| 1714 | | - if (pipe(stdout_fd) < 0) { |
|---|
| 1715 | | - pr_err("Failure creating the pipe to run %s\n", command); |
|---|
| 2006 | + objdump_argv[2] = command; |
|---|
| 2007 | + objdump_argv[4] = symfs_filename; |
|---|
| 2008 | + |
|---|
| 2009 | + /* Create a pipe to read from for stdout */ |
|---|
| 2010 | + memset(&objdump_process, 0, sizeof(objdump_process)); |
|---|
| 2011 | + objdump_process.argv = objdump_argv; |
|---|
| 2012 | + objdump_process.out = -1; |
|---|
| 2013 | + if (start_command(&objdump_process)) { |
|---|
| 2014 | + pr_err("Failure starting to run %s\n", command); |
|---|
| 2015 | + err = -1; |
|---|
| 1716 | 2016 | goto out_free_command; |
|---|
| 1717 | 2017 | } |
|---|
| 1718 | 2018 | |
|---|
| 1719 | | - pid = fork(); |
|---|
| 1720 | | - if (pid < 0) { |
|---|
| 1721 | | - pr_err("Failure forking to run %s\n", command); |
|---|
| 1722 | | - goto out_close_stdout; |
|---|
| 1723 | | - } |
|---|
| 1724 | | - |
|---|
| 1725 | | - if (pid == 0) { |
|---|
| 1726 | | - close(stdout_fd[0]); |
|---|
| 1727 | | - dup2(stdout_fd[1], 1); |
|---|
| 1728 | | - close(stdout_fd[1]); |
|---|
| 1729 | | - execl("/bin/sh", "sh", "-c", command, NULL); |
|---|
| 1730 | | - perror(command); |
|---|
| 1731 | | - exit(-1); |
|---|
| 1732 | | - } |
|---|
| 1733 | | - |
|---|
| 1734 | | - close(stdout_fd[1]); |
|---|
| 1735 | | - |
|---|
| 1736 | | - file = fdopen(stdout_fd[0], "r"); |
|---|
| 2019 | + file = fdopen(objdump_process.out, "r"); |
|---|
| 1737 | 2020 | if (!file) { |
|---|
| 1738 | 2021 | pr_err("Failure creating FILE stream for %s\n", command); |
|---|
| 1739 | 2022 | /* |
|---|
| 1740 | 2023 | * If we were using debug info should retry with |
|---|
| 1741 | 2024 | * original binary. |
|---|
| 1742 | 2025 | */ |
|---|
| 1743 | | - goto out_free_command; |
|---|
| 2026 | + err = -1; |
|---|
| 2027 | + goto out_close_stdout; |
|---|
| 1744 | 2028 | } |
|---|
| 2029 | + |
|---|
| 2030 | + /* Storage for getline. */ |
|---|
| 2031 | + line = NULL; |
|---|
| 2032 | + line_len = 0; |
|---|
| 1745 | 2033 | |
|---|
| 1746 | 2034 | nline = 0; |
|---|
| 1747 | 2035 | while (!feof(file)) { |
|---|
| 2036 | + const char *match; |
|---|
| 2037 | + char *expanded_line; |
|---|
| 2038 | + |
|---|
| 2039 | + if (getline(&line, &line_len, file) < 0 || !line) |
|---|
| 2040 | + break; |
|---|
| 2041 | + |
|---|
| 2042 | + /* Skip lines containing "filename:" */ |
|---|
| 2043 | + match = strstr(line, symfs_filename); |
|---|
| 2044 | + if (match && match[strlen(symfs_filename)] == ':') |
|---|
| 2045 | + continue; |
|---|
| 2046 | + |
|---|
| 2047 | + expanded_line = strim(line); |
|---|
| 2048 | + expanded_line = expand_tabs(expanded_line, &line, &line_len); |
|---|
| 2049 | + if (!expanded_line) |
|---|
| 2050 | + break; |
|---|
| 2051 | + |
|---|
| 1748 | 2052 | /* |
|---|
| 1749 | 2053 | * The source code line number (lineno) needs to be kept in |
|---|
| 1750 | | - * accross calls to symbol__parse_objdump_line(), so that it |
|---|
| 2054 | + * across calls to symbol__parse_objdump_line(), so that it |
|---|
| 1751 | 2055 | * can associate it with the instructions till the next one. |
|---|
| 1752 | 2056 | * See disasm_line__new() and struct disasm_line::line_nr. |
|---|
| 1753 | 2057 | */ |
|---|
| 1754 | | - if (symbol__parse_objdump_line(sym, file, args, &lineno) < 0) |
|---|
| 2058 | + if (symbol__parse_objdump_line(sym, args, expanded_line, |
|---|
| 2059 | + &lineno) < 0) |
|---|
| 1755 | 2060 | break; |
|---|
| 1756 | 2061 | nline++; |
|---|
| 1757 | 2062 | } |
|---|
| 2063 | + free(line); |
|---|
| 1758 | 2064 | |
|---|
| 1759 | | - if (nline == 0) |
|---|
| 2065 | + err = finish_command(&objdump_process); |
|---|
| 2066 | + if (err) |
|---|
| 2067 | + pr_err("Error running %s\n", command); |
|---|
| 2068 | + |
|---|
| 2069 | + if (nline == 0) { |
|---|
| 2070 | + err = -1; |
|---|
| 1760 | 2071 | pr_err("No output from %s\n", command); |
|---|
| 2072 | + } |
|---|
| 1761 | 2073 | |
|---|
| 1762 | 2074 | /* |
|---|
| 1763 | 2075 | * kallsyms does not have symbol sizes so there may a nop at the end. |
|---|
| .. | .. |
|---|
| 1767 | 2079 | delete_last_nop(sym); |
|---|
| 1768 | 2080 | |
|---|
| 1769 | 2081 | fclose(file); |
|---|
| 1770 | | - err = 0; |
|---|
| 2082 | + |
|---|
| 2083 | +out_close_stdout: |
|---|
| 2084 | + close(objdump_process.out); |
|---|
| 2085 | + |
|---|
| 1771 | 2086 | out_free_command: |
|---|
| 1772 | 2087 | free(command); |
|---|
| 1773 | | -out_remove_tmp: |
|---|
| 1774 | | - close(stdout_fd[0]); |
|---|
| 1775 | 2088 | |
|---|
| 2089 | +out_remove_tmp: |
|---|
| 1776 | 2090 | if (decomp) |
|---|
| 1777 | 2091 | unlink(symfs_filename); |
|---|
| 1778 | 2092 | |
|---|
| 1779 | 2093 | if (delete_extract) |
|---|
| 1780 | 2094 | kcore_extract__delete(&kce); |
|---|
| 1781 | | -out: |
|---|
| 1782 | | - return err; |
|---|
| 1783 | 2095 | |
|---|
| 1784 | | -out_close_stdout: |
|---|
| 1785 | | - close(stdout_fd[1]); |
|---|
| 1786 | | - goto out_free_command; |
|---|
| 2096 | + return err; |
|---|
| 1787 | 2097 | } |
|---|
| 1788 | 2098 | |
|---|
| 1789 | 2099 | static void calc_percent(struct sym_hist *sym_hist, |
|---|
| .. | .. |
|---|
| 1817 | 2127 | } |
|---|
| 1818 | 2128 | |
|---|
| 1819 | 2129 | static void annotation__calc_percent(struct annotation *notes, |
|---|
| 1820 | | - struct perf_evsel *leader, s64 len) |
|---|
| 2130 | + struct evsel *leader, s64 len) |
|---|
| 1821 | 2131 | { |
|---|
| 1822 | 2132 | struct annotation_line *al, *next; |
|---|
| 1823 | | - struct perf_evsel *evsel; |
|---|
| 2133 | + struct evsel *evsel; |
|---|
| 1824 | 2134 | |
|---|
| 1825 | 2135 | list_for_each_entry(al, ¬es->src->source, node) { |
|---|
| 1826 | 2136 | s64 end; |
|---|
| .. | .. |
|---|
| 1847 | 2157 | } |
|---|
| 1848 | 2158 | } |
|---|
| 1849 | 2159 | |
|---|
| 1850 | | -void symbol__calc_percent(struct symbol *sym, struct perf_evsel *evsel) |
|---|
| 2160 | +void symbol__calc_percent(struct symbol *sym, struct evsel *evsel) |
|---|
| 1851 | 2161 | { |
|---|
| 1852 | 2162 | struct annotation *notes = symbol__annotation(sym); |
|---|
| 1853 | 2163 | |
|---|
| 1854 | 2164 | annotation__calc_percent(notes, evsel, symbol__size(sym)); |
|---|
| 1855 | 2165 | } |
|---|
| 1856 | 2166 | |
|---|
| 1857 | | -int symbol__annotate(struct symbol *sym, struct map *map, |
|---|
| 1858 | | - struct perf_evsel *evsel, size_t privsize, |
|---|
| 1859 | | - struct annotation_options *options, |
|---|
| 1860 | | - struct arch **parch) |
|---|
| 2167 | +int symbol__annotate(struct map_symbol *ms, struct evsel *evsel, |
|---|
| 2168 | + struct annotation_options *options, struct arch **parch) |
|---|
| 1861 | 2169 | { |
|---|
| 2170 | + struct symbol *sym = ms->sym; |
|---|
| 1862 | 2171 | struct annotation *notes = symbol__annotation(sym); |
|---|
| 1863 | 2172 | struct annotate_args args = { |
|---|
| 1864 | | - .privsize = privsize, |
|---|
| 1865 | 2173 | .evsel = evsel, |
|---|
| 1866 | 2174 | .options = options, |
|---|
| 1867 | 2175 | }; |
|---|
| 1868 | | - struct perf_env *env = perf_evsel__env(evsel); |
|---|
| 2176 | + struct perf_env *env = evsel__env(evsel); |
|---|
| 1869 | 2177 | const char *arch_name = perf_env__arch(env); |
|---|
| 1870 | 2178 | struct arch *arch; |
|---|
| 1871 | 2179 | int err; |
|---|
| .. | .. |
|---|
| 1888 | 2196 | } |
|---|
| 1889 | 2197 | } |
|---|
| 1890 | 2198 | |
|---|
| 1891 | | - args.ms.map = map; |
|---|
| 1892 | | - args.ms.sym = sym; |
|---|
| 1893 | | - notes->start = map__rip_2objdump(map, sym->start); |
|---|
| 2199 | + args.ms = *ms; |
|---|
| 2200 | + notes->start = map__rip_2objdump(ms->map, sym->start); |
|---|
| 1894 | 2201 | |
|---|
| 1895 | 2202 | return symbol__disassemble(sym, &args); |
|---|
| 1896 | 2203 | } |
|---|
| .. | .. |
|---|
| 2020 | 2327 | } |
|---|
| 2021 | 2328 | } |
|---|
| 2022 | 2329 | |
|---|
| 2023 | | -static void symbol__annotate_hits(struct symbol *sym, struct perf_evsel *evsel) |
|---|
| 2330 | +static void symbol__annotate_hits(struct symbol *sym, struct evsel *evsel) |
|---|
| 2024 | 2331 | { |
|---|
| 2025 | 2332 | struct annotation *notes = symbol__annotation(sym); |
|---|
| 2026 | 2333 | struct sym_hist *h = annotation__histogram(notes, evsel->idx); |
|---|
| .. | .. |
|---|
| 2046 | 2353 | return 0; |
|---|
| 2047 | 2354 | } |
|---|
| 2048 | 2355 | |
|---|
| 2049 | | -int symbol__annotate_printf(struct symbol *sym, struct map *map, |
|---|
| 2050 | | - struct perf_evsel *evsel, |
|---|
| 2356 | +int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel, |
|---|
| 2051 | 2357 | struct annotation_options *opts) |
|---|
| 2052 | 2358 | { |
|---|
| 2359 | + struct map *map = ms->map; |
|---|
| 2360 | + struct symbol *sym = ms->sym; |
|---|
| 2053 | 2361 | struct dso *dso = map->dso; |
|---|
| 2054 | 2362 | char *filename; |
|---|
| 2055 | 2363 | const char *d_filename; |
|---|
| 2056 | | - const char *evsel_name = perf_evsel__name(evsel); |
|---|
| 2364 | + const char *evsel_name = evsel__name(evsel); |
|---|
| 2057 | 2365 | struct annotation *notes = symbol__annotation(sym); |
|---|
| 2058 | 2366 | struct sym_hist *h = annotation__histogram(notes, evsel->idx); |
|---|
| 2059 | 2367 | struct annotation_line *pos, *queue = NULL; |
|---|
| .. | .. |
|---|
| 2077 | 2385 | |
|---|
| 2078 | 2386 | len = symbol__size(sym); |
|---|
| 2079 | 2387 | |
|---|
| 2080 | | - if (perf_evsel__is_group_event(evsel)) { |
|---|
| 2081 | | - width *= evsel->nr_members; |
|---|
| 2082 | | - perf_evsel__group_desc(evsel, buf, sizeof(buf)); |
|---|
| 2388 | + if (evsel__is_group_event(evsel)) { |
|---|
| 2389 | + width *= evsel->core.nr_members; |
|---|
| 2390 | + evsel__group_desc(evsel, buf, sizeof(buf)); |
|---|
| 2083 | 2391 | evsel_name = buf; |
|---|
| 2084 | 2392 | } |
|---|
| 2085 | 2393 | |
|---|
| .. | .. |
|---|
| 2211 | 2519 | return 0; |
|---|
| 2212 | 2520 | } |
|---|
| 2213 | 2521 | |
|---|
| 2214 | | -int map_symbol__annotation_dump(struct map_symbol *ms, struct perf_evsel *evsel, |
|---|
| 2522 | +int map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel, |
|---|
| 2215 | 2523 | struct annotation_options *opts) |
|---|
| 2216 | 2524 | { |
|---|
| 2217 | | - const char *ev_name = perf_evsel__name(evsel); |
|---|
| 2525 | + const char *ev_name = evsel__name(evsel); |
|---|
| 2218 | 2526 | char buf[1024]; |
|---|
| 2219 | 2527 | char *filename; |
|---|
| 2220 | 2528 | int err = -1; |
|---|
| .. | .. |
|---|
| 2227 | 2535 | if (fp == NULL) |
|---|
| 2228 | 2536 | goto out_free_filename; |
|---|
| 2229 | 2537 | |
|---|
| 2230 | | - if (perf_evsel__is_group_event(evsel)) { |
|---|
| 2231 | | - perf_evsel__group_desc(evsel, buf, sizeof(buf)); |
|---|
| 2538 | + if (evsel__is_group_event(evsel)) { |
|---|
| 2539 | + evsel__group_desc(evsel, buf, sizeof(buf)); |
|---|
| 2232 | 2540 | ev_name = buf; |
|---|
| 2233 | 2541 | } |
|---|
| 2234 | 2542 | |
|---|
| .. | .. |
|---|
| 2269 | 2577 | struct annotation_line *al, *n; |
|---|
| 2270 | 2578 | |
|---|
| 2271 | 2579 | list_for_each_entry_safe(al, n, &as->source, node) { |
|---|
| 2272 | | - list_del(&al->node); |
|---|
| 2580 | + list_del_init(&al->node); |
|---|
| 2273 | 2581 | disasm_line__free(disasm_line(al)); |
|---|
| 2274 | 2582 | } |
|---|
| 2275 | 2583 | } |
|---|
| .. | .. |
|---|
| 2340 | 2648 | |
|---|
| 2341 | 2649 | if (++al->jump_sources > notes->max_jump_sources) |
|---|
| 2342 | 2650 | notes->max_jump_sources = al->jump_sources; |
|---|
| 2343 | | - |
|---|
| 2344 | | - ++notes->nr_jumps; |
|---|
| 2345 | 2651 | } |
|---|
| 2346 | 2652 | } |
|---|
| 2347 | 2653 | |
|---|
| .. | .. |
|---|
| 2350 | 2656 | struct annotation_line *al; |
|---|
| 2351 | 2657 | |
|---|
| 2352 | 2658 | notes->max_line_len = 0; |
|---|
| 2659 | + notes->nr_entries = 0; |
|---|
| 2660 | + notes->nr_asm_entries = 0; |
|---|
| 2353 | 2661 | |
|---|
| 2354 | 2662 | list_for_each_entry(al, ¬es->src->source, node) { |
|---|
| 2355 | 2663 | size_t line_len = strlen(al->line); |
|---|
| .. | .. |
|---|
| 2382 | 2690 | return 1; |
|---|
| 2383 | 2691 | } |
|---|
| 2384 | 2692 | |
|---|
| 2693 | +static int annotation__max_ins_name(struct annotation *notes) |
|---|
| 2694 | +{ |
|---|
| 2695 | + int max_name = 0, len; |
|---|
| 2696 | + struct annotation_line *al; |
|---|
| 2697 | + |
|---|
| 2698 | + list_for_each_entry(al, ¬es->src->source, node) { |
|---|
| 2699 | + if (al->offset == -1) |
|---|
| 2700 | + continue; |
|---|
| 2701 | + |
|---|
| 2702 | + len = strlen(disasm_line(al)->ins.name); |
|---|
| 2703 | + if (max_name < len) |
|---|
| 2704 | + max_name = len; |
|---|
| 2705 | + } |
|---|
| 2706 | + |
|---|
| 2707 | + return max_name; |
|---|
| 2708 | +} |
|---|
| 2709 | + |
|---|
| 2385 | 2710 | void annotation__init_column_widths(struct annotation *notes, struct symbol *sym) |
|---|
| 2386 | 2711 | { |
|---|
| 2387 | 2712 | notes->widths.addr = notes->widths.target = |
|---|
| 2388 | 2713 | notes->widths.min_addr = hex_width(symbol__size(sym)); |
|---|
| 2389 | 2714 | notes->widths.max_addr = hex_width(sym->end); |
|---|
| 2390 | 2715 | notes->widths.jumps = width_jumps(notes->max_jump_sources); |
|---|
| 2716 | + notes->widths.max_ins_name = annotation__max_ins_name(notes); |
|---|
| 2391 | 2717 | } |
|---|
| 2392 | 2718 | |
|---|
| 2393 | 2719 | void annotation__update_column_widths(struct annotation *notes) |
|---|
| .. | .. |
|---|
| 2435 | 2761 | resort_source_line(root, &tmp_root); |
|---|
| 2436 | 2762 | } |
|---|
| 2437 | 2763 | |
|---|
| 2438 | | -static void symbol__calc_lines(struct symbol *sym, struct map *map, |
|---|
| 2439 | | - struct rb_root *root, |
|---|
| 2764 | +static void symbol__calc_lines(struct map_symbol *ms, struct rb_root *root, |
|---|
| 2440 | 2765 | struct annotation_options *opts) |
|---|
| 2441 | 2766 | { |
|---|
| 2442 | | - struct annotation *notes = symbol__annotation(sym); |
|---|
| 2767 | + struct annotation *notes = symbol__annotation(ms->sym); |
|---|
| 2443 | 2768 | |
|---|
| 2444 | | - annotation__calc_lines(notes, map, root, opts); |
|---|
| 2769 | + annotation__calc_lines(notes, ms->map, root, opts); |
|---|
| 2445 | 2770 | } |
|---|
| 2446 | 2771 | |
|---|
| 2447 | | -int symbol__tty_annotate2(struct symbol *sym, struct map *map, |
|---|
| 2448 | | - struct perf_evsel *evsel, |
|---|
| 2772 | +int symbol__tty_annotate2(struct map_symbol *ms, struct evsel *evsel, |
|---|
| 2449 | 2773 | struct annotation_options *opts) |
|---|
| 2450 | 2774 | { |
|---|
| 2451 | | - struct dso *dso = map->dso; |
|---|
| 2775 | + struct dso *dso = ms->map->dso; |
|---|
| 2776 | + struct symbol *sym = ms->sym; |
|---|
| 2452 | 2777 | struct rb_root source_line = RB_ROOT; |
|---|
| 2453 | 2778 | struct hists *hists = evsel__hists(evsel); |
|---|
| 2454 | 2779 | char buf[1024]; |
|---|
| 2455 | 2780 | |
|---|
| 2456 | | - if (symbol__annotate2(sym, map, evsel, opts, NULL) < 0) |
|---|
| 2781 | + if (symbol__annotate2(ms, evsel, opts, NULL) < 0) |
|---|
| 2457 | 2782 | return -1; |
|---|
| 2458 | 2783 | |
|---|
| 2459 | 2784 | if (opts->print_lines) { |
|---|
| 2460 | 2785 | srcline_full_filename = opts->full_path; |
|---|
| 2461 | | - symbol__calc_lines(sym, map, &source_line, opts); |
|---|
| 2786 | + symbol__calc_lines(ms, &source_line, opts); |
|---|
| 2462 | 2787 | print_summary(&source_line, dso->long_name); |
|---|
| 2463 | 2788 | } |
|---|
| 2464 | 2789 | |
|---|
| .. | .. |
|---|
| 2472 | 2797 | return 0; |
|---|
| 2473 | 2798 | } |
|---|
| 2474 | 2799 | |
|---|
| 2475 | | -int symbol__tty_annotate(struct symbol *sym, struct map *map, |
|---|
| 2476 | | - struct perf_evsel *evsel, |
|---|
| 2800 | +int symbol__tty_annotate(struct map_symbol *ms, struct evsel *evsel, |
|---|
| 2477 | 2801 | struct annotation_options *opts) |
|---|
| 2478 | 2802 | { |
|---|
| 2479 | | - struct dso *dso = map->dso; |
|---|
| 2803 | + struct dso *dso = ms->map->dso; |
|---|
| 2804 | + struct symbol *sym = ms->sym; |
|---|
| 2480 | 2805 | struct rb_root source_line = RB_ROOT; |
|---|
| 2481 | 2806 | |
|---|
| 2482 | | - if (symbol__annotate(sym, map, evsel, 0, opts, NULL) < 0) |
|---|
| 2807 | + if (symbol__annotate(ms, evsel, opts, NULL) < 0) |
|---|
| 2483 | 2808 | return -1; |
|---|
| 2484 | 2809 | |
|---|
| 2485 | 2810 | symbol__calc_percent(sym, evsel); |
|---|
| 2486 | 2811 | |
|---|
| 2487 | 2812 | if (opts->print_lines) { |
|---|
| 2488 | 2813 | srcline_full_filename = opts->full_path; |
|---|
| 2489 | | - symbol__calc_lines(sym, map, &source_line, opts); |
|---|
| 2814 | + symbol__calc_lines(ms, &source_line, opts); |
|---|
| 2490 | 2815 | print_summary(&source_line, dso->long_name); |
|---|
| 2491 | 2816 | } |
|---|
| 2492 | 2817 | |
|---|
| 2493 | | - symbol__annotate_printf(sym, map, evsel, opts); |
|---|
| 2818 | + symbol__annotate_printf(ms, evsel, opts); |
|---|
| 2494 | 2819 | |
|---|
| 2495 | 2820 | annotated_source__purge(symbol__annotation(sym)->src); |
|---|
| 2496 | 2821 | |
|---|
| .. | .. |
|---|
| 2551 | 2876 | obj__printf(obj, " "); |
|---|
| 2552 | 2877 | } |
|---|
| 2553 | 2878 | |
|---|
| 2554 | | - disasm_line__scnprintf(dl, bf, size, !notes->options->use_offset); |
|---|
| 2879 | + disasm_line__scnprintf(dl, bf, size, !notes->options->use_offset, notes->widths.max_ins_name); |
|---|
| 2880 | +} |
|---|
| 2881 | + |
|---|
| 2882 | +static void ipc_coverage_string(char *bf, int size, struct annotation *notes) |
|---|
| 2883 | +{ |
|---|
| 2884 | + double ipc = 0.0, coverage = 0.0; |
|---|
| 2885 | + |
|---|
| 2886 | + if (notes->hit_cycles) |
|---|
| 2887 | + ipc = notes->hit_insn / ((double)notes->hit_cycles); |
|---|
| 2888 | + |
|---|
| 2889 | + if (notes->total_insn) { |
|---|
| 2890 | + coverage = notes->cover_insn * 100.0 / |
|---|
| 2891 | + ((double)notes->total_insn); |
|---|
| 2892 | + } |
|---|
| 2893 | + |
|---|
| 2894 | + scnprintf(bf, size, "(Average IPC: %.2f, IPC Coverage: %.1f%%)", |
|---|
| 2895 | + ipc, coverage); |
|---|
| 2555 | 2896 | } |
|---|
| 2556 | 2897 | |
|---|
| 2557 | 2898 | static void __annotation_line__write(struct annotation_line *al, struct annotation *notes, |
|---|
| .. | .. |
|---|
| 2588 | 2929 | percent = annotation_data__percent(&al->data[i], percent_type); |
|---|
| 2589 | 2930 | |
|---|
| 2590 | 2931 | obj__set_percent_color(obj, percent, current_entry); |
|---|
| 2591 | | - if (notes->options->show_total_period) { |
|---|
| 2932 | + if (symbol_conf.show_total_period) { |
|---|
| 2592 | 2933 | obj__printf(obj, "%11" PRIu64 " ", al->data[i].he.period); |
|---|
| 2593 | | - } else if (notes->options->show_nr_samples) { |
|---|
| 2934 | + } else if (symbol_conf.show_nr_samples) { |
|---|
| 2594 | 2935 | obj__printf(obj, "%6" PRIu64 " ", |
|---|
| 2595 | 2936 | al->data[i].he.nr_samples); |
|---|
| 2596 | 2937 | } else { |
|---|
| .. | .. |
|---|
| 2604 | 2945 | obj__printf(obj, "%-*s", pcnt_width, " "); |
|---|
| 2605 | 2946 | else { |
|---|
| 2606 | 2947 | obj__printf(obj, "%-*s", pcnt_width, |
|---|
| 2607 | | - notes->options->show_total_period ? "Period" : |
|---|
| 2608 | | - notes->options->show_nr_samples ? "Samples" : "Percent"); |
|---|
| 2948 | + symbol_conf.show_total_period ? "Period" : |
|---|
| 2949 | + symbol_conf.show_nr_samples ? "Samples" : "Percent"); |
|---|
| 2609 | 2950 | } |
|---|
| 2610 | 2951 | } |
|---|
| 2611 | 2952 | |
|---|
| .. | .. |
|---|
| 2648 | 2989 | obj__printf(obj, "%*s ", |
|---|
| 2649 | 2990 | ANNOTATION__MINMAX_CYCLES_WIDTH - 1, |
|---|
| 2650 | 2991 | "Cycle(min/max)"); |
|---|
| 2992 | + } |
|---|
| 2993 | + |
|---|
| 2994 | + if (show_title && !*al->line) { |
|---|
| 2995 | + ipc_coverage_string(bf, sizeof(bf), notes); |
|---|
| 2996 | + obj__printf(obj, "%*s", ANNOTATION__AVG_IPC_WIDTH, bf); |
|---|
| 2651 | 2997 | } |
|---|
| 2652 | 2998 | } |
|---|
| 2653 | 2999 | |
|---|
| .. | .. |
|---|
| 2723 | 3069 | wops->write_graph); |
|---|
| 2724 | 3070 | } |
|---|
| 2725 | 3071 | |
|---|
| 2726 | | -int symbol__annotate2(struct symbol *sym, struct map *map, struct perf_evsel *evsel, |
|---|
| 3072 | +int symbol__annotate2(struct map_symbol *ms, struct evsel *evsel, |
|---|
| 2727 | 3073 | struct annotation_options *options, struct arch **parch) |
|---|
| 2728 | 3074 | { |
|---|
| 3075 | + struct symbol *sym = ms->sym; |
|---|
| 2729 | 3076 | struct annotation *notes = symbol__annotation(sym); |
|---|
| 2730 | 3077 | size_t size = symbol__size(sym); |
|---|
| 2731 | 3078 | int nr_pcnt = 1, err; |
|---|
| .. | .. |
|---|
| 2734 | 3081 | if (notes->offsets == NULL) |
|---|
| 2735 | 3082 | return ENOMEM; |
|---|
| 2736 | 3083 | |
|---|
| 2737 | | - if (perf_evsel__is_group_event(evsel)) |
|---|
| 2738 | | - nr_pcnt = evsel->nr_members; |
|---|
| 3084 | + if (evsel__is_group_event(evsel)) |
|---|
| 3085 | + nr_pcnt = evsel->core.nr_members; |
|---|
| 2739 | 3086 | |
|---|
| 2740 | | - err = symbol__annotate(sym, map, evsel, 0, options, parch); |
|---|
| 3087 | + err = symbol__annotate(ms, evsel, options, parch); |
|---|
| 2741 | 3088 | if (err) |
|---|
| 2742 | 3089 | goto out_free_offsets; |
|---|
| 2743 | 3090 | |
|---|
| .. | .. |
|---|
| 2752 | 3099 | notes->nr_events = nr_pcnt; |
|---|
| 2753 | 3100 | |
|---|
| 2754 | 3101 | annotation__update_column_widths(notes); |
|---|
| 3102 | + sym->annotate2 = true; |
|---|
| 2755 | 3103 | |
|---|
| 2756 | 3104 | return 0; |
|---|
| 2757 | 3105 | |
|---|
| .. | .. |
|---|
| 2760 | 3108 | return err; |
|---|
| 2761 | 3109 | } |
|---|
| 2762 | 3110 | |
|---|
| 2763 | | -#define ANNOTATION__CFG(n) \ |
|---|
| 2764 | | - { .name = #n, .value = &annotation__default_options.n, } |
|---|
| 2765 | | - |
|---|
| 2766 | | -/* |
|---|
| 2767 | | - * Keep the entries sorted, they are bsearch'ed |
|---|
| 2768 | | - */ |
|---|
| 2769 | | -static struct annotation_config { |
|---|
| 2770 | | - const char *name; |
|---|
| 2771 | | - void *value; |
|---|
| 2772 | | -} annotation__configs[] = { |
|---|
| 2773 | | - ANNOTATION__CFG(hide_src_code), |
|---|
| 2774 | | - ANNOTATION__CFG(jump_arrows), |
|---|
| 2775 | | - ANNOTATION__CFG(offset_level), |
|---|
| 2776 | | - ANNOTATION__CFG(show_linenr), |
|---|
| 2777 | | - ANNOTATION__CFG(show_nr_jumps), |
|---|
| 2778 | | - ANNOTATION__CFG(show_nr_samples), |
|---|
| 2779 | | - ANNOTATION__CFG(show_total_period), |
|---|
| 2780 | | - ANNOTATION__CFG(use_offset), |
|---|
| 2781 | | -}; |
|---|
| 2782 | | - |
|---|
| 2783 | | -#undef ANNOTATION__CFG |
|---|
| 2784 | | - |
|---|
| 2785 | | -static int annotation_config__cmp(const void *name, const void *cfgp) |
|---|
| 3111 | +static int annotation__config(const char *var, const char *value, void *data) |
|---|
| 2786 | 3112 | { |
|---|
| 2787 | | - const struct annotation_config *cfg = cfgp; |
|---|
| 2788 | | - |
|---|
| 2789 | | - return strcmp(name, cfg->name); |
|---|
| 2790 | | -} |
|---|
| 2791 | | - |
|---|
| 2792 | | -static int annotation__config(const char *var, const char *value, |
|---|
| 2793 | | - void *data __maybe_unused) |
|---|
| 2794 | | -{ |
|---|
| 2795 | | - struct annotation_config *cfg; |
|---|
| 2796 | | - const char *name; |
|---|
| 3113 | + struct annotation_options *opt = data; |
|---|
| 2797 | 3114 | |
|---|
| 2798 | 3115 | if (!strstarts(var, "annotate.")) |
|---|
| 2799 | 3116 | return 0; |
|---|
| 2800 | 3117 | |
|---|
| 2801 | | - name = var + 9; |
|---|
| 2802 | | - cfg = bsearch(name, annotation__configs, ARRAY_SIZE(annotation__configs), |
|---|
| 2803 | | - sizeof(struct annotation_config), annotation_config__cmp); |
|---|
| 3118 | + if (!strcmp(var, "annotate.offset_level")) { |
|---|
| 3119 | + perf_config_u8(&opt->offset_level, "offset_level", value); |
|---|
| 2804 | 3120 | |
|---|
| 2805 | | - if (cfg == NULL) |
|---|
| 2806 | | - pr_debug("%s variable unknown, ignoring...", var); |
|---|
| 2807 | | - else if (strcmp(var, "annotate.offset_level") == 0) { |
|---|
| 2808 | | - perf_config_int(cfg->value, name, value); |
|---|
| 2809 | | - |
|---|
| 2810 | | - if (*(int *)cfg->value > ANNOTATION__MAX_OFFSET_LEVEL) |
|---|
| 2811 | | - *(int *)cfg->value = ANNOTATION__MAX_OFFSET_LEVEL; |
|---|
| 2812 | | - else if (*(int *)cfg->value < ANNOTATION__MIN_OFFSET_LEVEL) |
|---|
| 2813 | | - *(int *)cfg->value = ANNOTATION__MIN_OFFSET_LEVEL; |
|---|
| 3121 | + if (opt->offset_level > ANNOTATION__MAX_OFFSET_LEVEL) |
|---|
| 3122 | + opt->offset_level = ANNOTATION__MAX_OFFSET_LEVEL; |
|---|
| 3123 | + else if (opt->offset_level < ANNOTATION__MIN_OFFSET_LEVEL) |
|---|
| 3124 | + opt->offset_level = ANNOTATION__MIN_OFFSET_LEVEL; |
|---|
| 3125 | + } else if (!strcmp(var, "annotate.hide_src_code")) { |
|---|
| 3126 | + opt->hide_src_code = perf_config_bool("hide_src_code", value); |
|---|
| 3127 | + } else if (!strcmp(var, "annotate.jump_arrows")) { |
|---|
| 3128 | + opt->jump_arrows = perf_config_bool("jump_arrows", value); |
|---|
| 3129 | + } else if (!strcmp(var, "annotate.show_linenr")) { |
|---|
| 3130 | + opt->show_linenr = perf_config_bool("show_linenr", value); |
|---|
| 3131 | + } else if (!strcmp(var, "annotate.show_nr_jumps")) { |
|---|
| 3132 | + opt->show_nr_jumps = perf_config_bool("show_nr_jumps", value); |
|---|
| 3133 | + } else if (!strcmp(var, "annotate.show_nr_samples")) { |
|---|
| 3134 | + symbol_conf.show_nr_samples = perf_config_bool("show_nr_samples", |
|---|
| 3135 | + value); |
|---|
| 3136 | + } else if (!strcmp(var, "annotate.show_total_period")) { |
|---|
| 3137 | + symbol_conf.show_total_period = perf_config_bool("show_total_period", |
|---|
| 3138 | + value); |
|---|
| 3139 | + } else if (!strcmp(var, "annotate.use_offset")) { |
|---|
| 3140 | + opt->use_offset = perf_config_bool("use_offset", value); |
|---|
| 3141 | + } else if (!strcmp(var, "annotate.disassembler_style")) { |
|---|
| 3142 | + opt->disassembler_style = value; |
|---|
| 2814 | 3143 | } else { |
|---|
| 2815 | | - *(bool *)cfg->value = perf_config_bool(name, value); |
|---|
| 3144 | + pr_debug("%s variable unknown, ignoring...", var); |
|---|
| 2816 | 3145 | } |
|---|
| 3146 | + |
|---|
| 2817 | 3147 | return 0; |
|---|
| 2818 | 3148 | } |
|---|
| 2819 | 3149 | |
|---|
| 2820 | | -void annotation_config__init(void) |
|---|
| 3150 | +void annotation_config__init(struct annotation_options *opt) |
|---|
| 2821 | 3151 | { |
|---|
| 2822 | | - perf_config(annotation__config, NULL); |
|---|
| 2823 | | - |
|---|
| 2824 | | - annotation__default_options.show_total_period = symbol_conf.show_total_period; |
|---|
| 2825 | | - annotation__default_options.show_nr_samples = symbol_conf.show_nr_samples; |
|---|
| 3152 | + perf_config(annotation__config, opt); |
|---|
| 2826 | 3153 | } |
|---|
| 2827 | 3154 | |
|---|
| 2828 | 3155 | static unsigned int parse_percent_type(char *str1, char *str2) |
|---|
| .. | .. |
|---|
| 2876 | 3203 | free(str1); |
|---|
| 2877 | 3204 | return err; |
|---|
| 2878 | 3205 | } |
|---|
| 3206 | + |
|---|
| 3207 | +int annotate_check_args(struct annotation_options *args) |
|---|
| 3208 | +{ |
|---|
| 3209 | + if (args->prefix_strip && !args->prefix) { |
|---|
| 3210 | + pr_err("--prefix-strip requires --prefix\n"); |
|---|
| 3211 | + return -1; |
|---|
| 3212 | + } |
|---|
| 3213 | + return 0; |
|---|
| 3214 | +} |
|---|