162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Parts came from builtin-annotate.c, see those files for further 662306a36Sopenharmony_ci * copyright notes. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <errno.h> 1062306a36Sopenharmony_ci#include <inttypes.h> 1162306a36Sopenharmony_ci#include <libgen.h> 1262306a36Sopenharmony_ci#include <stdlib.h> 1362306a36Sopenharmony_ci#include "util.h" // hex_width() 1462306a36Sopenharmony_ci#include "ui/ui.h" 1562306a36Sopenharmony_ci#include "sort.h" 1662306a36Sopenharmony_ci#include "build-id.h" 1762306a36Sopenharmony_ci#include "color.h" 1862306a36Sopenharmony_ci#include "config.h" 1962306a36Sopenharmony_ci#include "dso.h" 2062306a36Sopenharmony_ci#include "env.h" 2162306a36Sopenharmony_ci#include "map.h" 2262306a36Sopenharmony_ci#include "maps.h" 2362306a36Sopenharmony_ci#include "symbol.h" 2462306a36Sopenharmony_ci#include "srcline.h" 2562306a36Sopenharmony_ci#include "units.h" 2662306a36Sopenharmony_ci#include "debug.h" 2762306a36Sopenharmony_ci#include "annotate.h" 2862306a36Sopenharmony_ci#include "evsel.h" 2962306a36Sopenharmony_ci#include "evlist.h" 3062306a36Sopenharmony_ci#include "bpf-event.h" 3162306a36Sopenharmony_ci#include "bpf-utils.h" 3262306a36Sopenharmony_ci#include "block-range.h" 3362306a36Sopenharmony_ci#include "string2.h" 3462306a36Sopenharmony_ci#include "util/event.h" 3562306a36Sopenharmony_ci#include "util/sharded_mutex.h" 3662306a36Sopenharmony_ci#include "arch/common.h" 3762306a36Sopenharmony_ci#include "namespaces.h" 3862306a36Sopenharmony_ci#include <regex.h> 3962306a36Sopenharmony_ci#include <linux/bitops.h> 4062306a36Sopenharmony_ci#include <linux/kernel.h> 4162306a36Sopenharmony_ci#include <linux/string.h> 4262306a36Sopenharmony_ci#include <linux/zalloc.h> 4362306a36Sopenharmony_ci#include <subcmd/parse-options.h> 4462306a36Sopenharmony_ci#include <subcmd/run-command.h> 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* FIXME: For the HE_COLORSET */ 4762306a36Sopenharmony_ci#include "ui/browser.h" 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci * FIXME: Using the same values as slang.h, 5162306a36Sopenharmony_ci * but that header may not be available everywhere 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ci#define LARROW_CHAR ((unsigned char)',') 5462306a36Sopenharmony_ci#define RARROW_CHAR ((unsigned char)'+') 5562306a36Sopenharmony_ci#define DARROW_CHAR ((unsigned char)'.') 5662306a36Sopenharmony_ci#define UARROW_CHAR ((unsigned char)'-') 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#include <linux/ctype.h> 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic regex_t file_lineno; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic struct ins_ops *ins__find(struct arch *arch, const char *name); 6362306a36Sopenharmony_cistatic void ins__sort(struct arch *arch); 6462306a36Sopenharmony_cistatic int disasm_line__parse(char *line, const char **namep, char **rawp); 6562306a36Sopenharmony_cistatic int call__scnprintf(struct ins *ins, char *bf, size_t size, 6662306a36Sopenharmony_ci struct ins_operands *ops, int max_ins_name); 6762306a36Sopenharmony_cistatic int jump__scnprintf(struct ins *ins, char *bf, size_t size, 6862306a36Sopenharmony_ci struct ins_operands *ops, int max_ins_name); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct arch { 7162306a36Sopenharmony_ci const char *name; 7262306a36Sopenharmony_ci struct ins *instructions; 7362306a36Sopenharmony_ci size_t nr_instructions; 7462306a36Sopenharmony_ci size_t nr_instructions_allocated; 7562306a36Sopenharmony_ci struct ins_ops *(*associate_instruction_ops)(struct arch *arch, const char *name); 7662306a36Sopenharmony_ci bool sorted_instructions; 7762306a36Sopenharmony_ci bool initialized; 7862306a36Sopenharmony_ci const char *insn_suffix; 7962306a36Sopenharmony_ci void *priv; 8062306a36Sopenharmony_ci unsigned int model; 8162306a36Sopenharmony_ci unsigned int family; 8262306a36Sopenharmony_ci int (*init)(struct arch *arch, char *cpuid); 8362306a36Sopenharmony_ci bool (*ins_is_fused)(struct arch *arch, const char *ins1, 8462306a36Sopenharmony_ci const char *ins2); 8562306a36Sopenharmony_ci struct { 8662306a36Sopenharmony_ci char comment_char; 8762306a36Sopenharmony_ci char skip_functions_char; 8862306a36Sopenharmony_ci } objdump; 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic struct ins_ops call_ops; 9262306a36Sopenharmony_cistatic struct ins_ops dec_ops; 9362306a36Sopenharmony_cistatic struct ins_ops jump_ops; 9462306a36Sopenharmony_cistatic struct ins_ops mov_ops; 9562306a36Sopenharmony_cistatic struct ins_ops nop_ops; 9662306a36Sopenharmony_cistatic struct ins_ops lock_ops; 9762306a36Sopenharmony_cistatic struct ins_ops ret_ops; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int arch__grow_instructions(struct arch *arch) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct ins *new_instructions; 10262306a36Sopenharmony_ci size_t new_nr_allocated; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (arch->nr_instructions_allocated == 0 && arch->instructions) 10562306a36Sopenharmony_ci goto grow_from_non_allocated_table; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci new_nr_allocated = arch->nr_instructions_allocated + 128; 10862306a36Sopenharmony_ci new_instructions = realloc(arch->instructions, new_nr_allocated * sizeof(struct ins)); 10962306a36Sopenharmony_ci if (new_instructions == NULL) 11062306a36Sopenharmony_ci return -1; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ciout_update_instructions: 11362306a36Sopenharmony_ci arch->instructions = new_instructions; 11462306a36Sopenharmony_ci arch->nr_instructions_allocated = new_nr_allocated; 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cigrow_from_non_allocated_table: 11862306a36Sopenharmony_ci new_nr_allocated = arch->nr_instructions + 128; 11962306a36Sopenharmony_ci new_instructions = calloc(new_nr_allocated, sizeof(struct ins)); 12062306a36Sopenharmony_ci if (new_instructions == NULL) 12162306a36Sopenharmony_ci return -1; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci memcpy(new_instructions, arch->instructions, arch->nr_instructions); 12462306a36Sopenharmony_ci goto out_update_instructions; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic int arch__associate_ins_ops(struct arch* arch, const char *name, struct ins_ops *ops) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct ins *ins; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (arch->nr_instructions == arch->nr_instructions_allocated && 13262306a36Sopenharmony_ci arch__grow_instructions(arch)) 13362306a36Sopenharmony_ci return -1; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci ins = &arch->instructions[arch->nr_instructions]; 13662306a36Sopenharmony_ci ins->name = strdup(name); 13762306a36Sopenharmony_ci if (!ins->name) 13862306a36Sopenharmony_ci return -1; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci ins->ops = ops; 14162306a36Sopenharmony_ci arch->nr_instructions++; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ins__sort(arch); 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci#include "arch/arc/annotate/instructions.c" 14862306a36Sopenharmony_ci#include "arch/arm/annotate/instructions.c" 14962306a36Sopenharmony_ci#include "arch/arm64/annotate/instructions.c" 15062306a36Sopenharmony_ci#include "arch/csky/annotate/instructions.c" 15162306a36Sopenharmony_ci#include "arch/loongarch/annotate/instructions.c" 15262306a36Sopenharmony_ci#include "arch/mips/annotate/instructions.c" 15362306a36Sopenharmony_ci#include "arch/x86/annotate/instructions.c" 15462306a36Sopenharmony_ci#include "arch/powerpc/annotate/instructions.c" 15562306a36Sopenharmony_ci#include "arch/riscv64/annotate/instructions.c" 15662306a36Sopenharmony_ci#include "arch/s390/annotate/instructions.c" 15762306a36Sopenharmony_ci#include "arch/sparc/annotate/instructions.c" 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic struct arch architectures[] = { 16062306a36Sopenharmony_ci { 16162306a36Sopenharmony_ci .name = "arc", 16262306a36Sopenharmony_ci .init = arc__annotate_init, 16362306a36Sopenharmony_ci }, 16462306a36Sopenharmony_ci { 16562306a36Sopenharmony_ci .name = "arm", 16662306a36Sopenharmony_ci .init = arm__annotate_init, 16762306a36Sopenharmony_ci }, 16862306a36Sopenharmony_ci { 16962306a36Sopenharmony_ci .name = "arm64", 17062306a36Sopenharmony_ci .init = arm64__annotate_init, 17162306a36Sopenharmony_ci }, 17262306a36Sopenharmony_ci { 17362306a36Sopenharmony_ci .name = "csky", 17462306a36Sopenharmony_ci .init = csky__annotate_init, 17562306a36Sopenharmony_ci }, 17662306a36Sopenharmony_ci { 17762306a36Sopenharmony_ci .name = "mips", 17862306a36Sopenharmony_ci .init = mips__annotate_init, 17962306a36Sopenharmony_ci .objdump = { 18062306a36Sopenharmony_ci .comment_char = '#', 18162306a36Sopenharmony_ci }, 18262306a36Sopenharmony_ci }, 18362306a36Sopenharmony_ci { 18462306a36Sopenharmony_ci .name = "x86", 18562306a36Sopenharmony_ci .init = x86__annotate_init, 18662306a36Sopenharmony_ci .instructions = x86__instructions, 18762306a36Sopenharmony_ci .nr_instructions = ARRAY_SIZE(x86__instructions), 18862306a36Sopenharmony_ci .insn_suffix = "bwlq", 18962306a36Sopenharmony_ci .objdump = { 19062306a36Sopenharmony_ci .comment_char = '#', 19162306a36Sopenharmony_ci }, 19262306a36Sopenharmony_ci }, 19362306a36Sopenharmony_ci { 19462306a36Sopenharmony_ci .name = "powerpc", 19562306a36Sopenharmony_ci .init = powerpc__annotate_init, 19662306a36Sopenharmony_ci }, 19762306a36Sopenharmony_ci { 19862306a36Sopenharmony_ci .name = "riscv64", 19962306a36Sopenharmony_ci .init = riscv64__annotate_init, 20062306a36Sopenharmony_ci }, 20162306a36Sopenharmony_ci { 20262306a36Sopenharmony_ci .name = "s390", 20362306a36Sopenharmony_ci .init = s390__annotate_init, 20462306a36Sopenharmony_ci .objdump = { 20562306a36Sopenharmony_ci .comment_char = '#', 20662306a36Sopenharmony_ci }, 20762306a36Sopenharmony_ci }, 20862306a36Sopenharmony_ci { 20962306a36Sopenharmony_ci .name = "sparc", 21062306a36Sopenharmony_ci .init = sparc__annotate_init, 21162306a36Sopenharmony_ci .objdump = { 21262306a36Sopenharmony_ci .comment_char = '#', 21362306a36Sopenharmony_ci }, 21462306a36Sopenharmony_ci }, 21562306a36Sopenharmony_ci { 21662306a36Sopenharmony_ci .name = "loongarch", 21762306a36Sopenharmony_ci .init = loongarch__annotate_init, 21862306a36Sopenharmony_ci .objdump = { 21962306a36Sopenharmony_ci .comment_char = '#', 22062306a36Sopenharmony_ci }, 22162306a36Sopenharmony_ci }, 22262306a36Sopenharmony_ci}; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic void ins__delete(struct ins_operands *ops) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci if (ops == NULL) 22762306a36Sopenharmony_ci return; 22862306a36Sopenharmony_ci zfree(&ops->source.raw); 22962306a36Sopenharmony_ci zfree(&ops->source.name); 23062306a36Sopenharmony_ci zfree(&ops->target.raw); 23162306a36Sopenharmony_ci zfree(&ops->target.name); 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size, 23562306a36Sopenharmony_ci struct ins_operands *ops, int max_ins_name) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->raw); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ciint ins__scnprintf(struct ins *ins, char *bf, size_t size, 24162306a36Sopenharmony_ci struct ins_operands *ops, int max_ins_name) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci if (ins->ops->scnprintf) 24462306a36Sopenharmony_ci return ins->ops->scnprintf(ins, bf, size, ops, max_ins_name); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cibool ins__is_fused(struct arch *arch, const char *ins1, const char *ins2) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci if (!arch || !arch->ins_is_fused) 25262306a36Sopenharmony_ci return false; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return arch->ins_is_fused(arch, ins1, ins2); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int call__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci char *endptr, *tok, *name; 26062306a36Sopenharmony_ci struct map *map = ms->map; 26162306a36Sopenharmony_ci struct addr_map_symbol target = { 26262306a36Sopenharmony_ci .ms = { .map = map, }, 26362306a36Sopenharmony_ci }; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci ops->target.addr = strtoull(ops->raw, &endptr, 16); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci name = strchr(endptr, '<'); 26862306a36Sopenharmony_ci if (name == NULL) 26962306a36Sopenharmony_ci goto indirect_call; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci name++; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (arch->objdump.skip_functions_char && 27462306a36Sopenharmony_ci strchr(name, arch->objdump.skip_functions_char)) 27562306a36Sopenharmony_ci return -1; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci tok = strchr(name, '>'); 27862306a36Sopenharmony_ci if (tok == NULL) 27962306a36Sopenharmony_ci return -1; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci *tok = '\0'; 28262306a36Sopenharmony_ci ops->target.name = strdup(name); 28362306a36Sopenharmony_ci *tok = '>'; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (ops->target.name == NULL) 28662306a36Sopenharmony_ci return -1; 28762306a36Sopenharmony_cifind_target: 28862306a36Sopenharmony_ci target.addr = map__objdump_2mem(map, ops->target.addr); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (maps__find_ams(ms->maps, &target) == 0 && 29162306a36Sopenharmony_ci map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr) 29262306a36Sopenharmony_ci ops->target.sym = target.ms.sym; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ciindirect_call: 29762306a36Sopenharmony_ci tok = strchr(endptr, '*'); 29862306a36Sopenharmony_ci if (tok != NULL) { 29962306a36Sopenharmony_ci endptr++; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Indirect call can use a non-rip register and offset: callq *0x8(%rbx). 30262306a36Sopenharmony_ci * Do not parse such instruction. */ 30362306a36Sopenharmony_ci if (strstr(endptr, "(%r") == NULL) 30462306a36Sopenharmony_ci ops->target.addr = strtoull(endptr, NULL, 16); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci goto find_target; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic int call__scnprintf(struct ins *ins, char *bf, size_t size, 31062306a36Sopenharmony_ci struct ins_operands *ops, int max_ins_name) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci if (ops->target.sym) 31362306a36Sopenharmony_ci return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (ops->target.addr == 0) 31662306a36Sopenharmony_ci return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (ops->target.name) 31962306a36Sopenharmony_ci return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.name); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return scnprintf(bf, size, "%-*s *%" PRIx64, max_ins_name, ins->name, ops->target.addr); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic struct ins_ops call_ops = { 32562306a36Sopenharmony_ci .parse = call__parse, 32662306a36Sopenharmony_ci .scnprintf = call__scnprintf, 32762306a36Sopenharmony_ci}; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cibool ins__is_call(const struct ins *ins) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci return ins->ops == &call_ops || ins->ops == &s390_call_ops || ins->ops == &loongarch_call_ops; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci/* 33562306a36Sopenharmony_ci * Prevents from matching commas in the comment section, e.g.: 33662306a36Sopenharmony_ci * ffff200008446e70: b.cs ffff2000084470f4 <generic_exec_single+0x314> // b.hs, b.nlast 33762306a36Sopenharmony_ci * 33862306a36Sopenharmony_ci * and skip comma as part of function arguments, e.g.: 33962306a36Sopenharmony_ci * 1d8b4ac <linemap_lookup(line_maps const*, unsigned int)+0xcc> 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_cistatic inline const char *validate_comma(const char *c, struct ins_operands *ops) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci if (ops->raw_comment && c > ops->raw_comment) 34462306a36Sopenharmony_ci return NULL; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (ops->raw_func_start && c > ops->raw_func_start) 34762306a36Sopenharmony_ci return NULL; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return c; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct map *map = ms->map; 35562306a36Sopenharmony_ci struct symbol *sym = ms->sym; 35662306a36Sopenharmony_ci struct addr_map_symbol target = { 35762306a36Sopenharmony_ci .ms = { .map = map, }, 35862306a36Sopenharmony_ci }; 35962306a36Sopenharmony_ci const char *c = strchr(ops->raw, ','); 36062306a36Sopenharmony_ci u64 start, end; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ops->raw_comment = strchr(ops->raw, arch->objdump.comment_char); 36362306a36Sopenharmony_ci ops->raw_func_start = strchr(ops->raw, '<'); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci c = validate_comma(c, ops); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* 36862306a36Sopenharmony_ci * Examples of lines to parse for the _cpp_lex_token@@Base 36962306a36Sopenharmony_ci * function: 37062306a36Sopenharmony_ci * 37162306a36Sopenharmony_ci * 1159e6c: jne 115aa32 <_cpp_lex_token@@Base+0xf92> 37262306a36Sopenharmony_ci * 1159e8b: jne c469be <cpp_named_operator2name@@Base+0xa72> 37362306a36Sopenharmony_ci * 37462306a36Sopenharmony_ci * The first is a jump to an offset inside the same function, 37562306a36Sopenharmony_ci * the second is to another function, i.e. that 0xa72 is an 37662306a36Sopenharmony_ci * offset in the cpp_named_operator2name@@base function. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci /* 37962306a36Sopenharmony_ci * skip over possible up to 2 operands to get to address, e.g.: 38062306a36Sopenharmony_ci * tbnz w0, #26, ffff0000083cd190 <security_file_permission+0xd0> 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_ci if (c++ != NULL) { 38362306a36Sopenharmony_ci ops->target.addr = strtoull(c, NULL, 16); 38462306a36Sopenharmony_ci if (!ops->target.addr) { 38562306a36Sopenharmony_ci c = strchr(c, ','); 38662306a36Sopenharmony_ci c = validate_comma(c, ops); 38762306a36Sopenharmony_ci if (c++ != NULL) 38862306a36Sopenharmony_ci ops->target.addr = strtoull(c, NULL, 16); 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci } else { 39162306a36Sopenharmony_ci ops->target.addr = strtoull(ops->raw, NULL, 16); 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci target.addr = map__objdump_2mem(map, ops->target.addr); 39562306a36Sopenharmony_ci start = map__unmap_ip(map, sym->start); 39662306a36Sopenharmony_ci end = map__unmap_ip(map, sym->end); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci ops->target.outside = target.addr < start || target.addr > end; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* 40162306a36Sopenharmony_ci * FIXME: things like this in _cpp_lex_token (gcc's cc1 program): 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci cpp_named_operator2name@@Base+0xa72 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci * Point to a place that is after the cpp_named_operator2name 40662306a36Sopenharmony_ci * boundaries, i.e. in the ELF symbol table for cc1 40762306a36Sopenharmony_ci * cpp_named_operator2name is marked as being 32-bytes long, but it in 40862306a36Sopenharmony_ci * fact is much larger than that, so we seem to need a symbols__find() 40962306a36Sopenharmony_ci * routine that looks for >= current->start and < next_symbol->start, 41062306a36Sopenharmony_ci * possibly just for C++ objects? 41162306a36Sopenharmony_ci * 41262306a36Sopenharmony_ci * For now lets just make some progress by marking jumps to outside the 41362306a36Sopenharmony_ci * current function as call like. 41462306a36Sopenharmony_ci * 41562306a36Sopenharmony_ci * Actual navigation will come next, with further understanding of how 41662306a36Sopenharmony_ci * the symbol searching and disassembly should be done. 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_ci if (maps__find_ams(ms->maps, &target) == 0 && 41962306a36Sopenharmony_ci map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr) 42062306a36Sopenharmony_ci ops->target.sym = target.ms.sym; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (!ops->target.outside) { 42362306a36Sopenharmony_ci ops->target.offset = target.addr - start; 42462306a36Sopenharmony_ci ops->target.offset_avail = true; 42562306a36Sopenharmony_ci } else { 42662306a36Sopenharmony_ci ops->target.offset_avail = false; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci return 0; 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic int jump__scnprintf(struct ins *ins, char *bf, size_t size, 43362306a36Sopenharmony_ci struct ins_operands *ops, int max_ins_name) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci const char *c; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (!ops->target.addr || ops->target.offset < 0) 43862306a36Sopenharmony_ci return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (ops->target.outside && ops->target.sym != NULL) 44162306a36Sopenharmony_ci return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci c = strchr(ops->raw, ','); 44462306a36Sopenharmony_ci c = validate_comma(c, ops); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (c != NULL) { 44762306a36Sopenharmony_ci const char *c2 = strchr(c + 1, ','); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci c2 = validate_comma(c2, ops); 45062306a36Sopenharmony_ci /* check for 3-op insn */ 45162306a36Sopenharmony_ci if (c2 != NULL) 45262306a36Sopenharmony_ci c = c2; 45362306a36Sopenharmony_ci c++; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* mirror arch objdump's space-after-comma style */ 45662306a36Sopenharmony_ci if (*c == ' ') 45762306a36Sopenharmony_ci c++; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci return scnprintf(bf, size, "%-*s %.*s%" PRIx64, max_ins_name, 46162306a36Sopenharmony_ci ins->name, c ? c - ops->raw : 0, ops->raw, 46262306a36Sopenharmony_ci ops->target.offset); 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic struct ins_ops jump_ops = { 46662306a36Sopenharmony_ci .parse = jump__parse, 46762306a36Sopenharmony_ci .scnprintf = jump__scnprintf, 46862306a36Sopenharmony_ci}; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cibool ins__is_jump(const struct ins *ins) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci return ins->ops == &jump_ops || ins->ops == &loongarch_jump_ops; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci char *endptr, *name, *t; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (strstr(raw, "(%rip)") == NULL) 48062306a36Sopenharmony_ci return 0; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci *addrp = strtoull(comment, &endptr, 16); 48362306a36Sopenharmony_ci if (endptr == comment) 48462306a36Sopenharmony_ci return 0; 48562306a36Sopenharmony_ci name = strchr(endptr, '<'); 48662306a36Sopenharmony_ci if (name == NULL) 48762306a36Sopenharmony_ci return -1; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci name++; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci t = strchr(name, '>'); 49262306a36Sopenharmony_ci if (t == NULL) 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci *t = '\0'; 49662306a36Sopenharmony_ci *namep = strdup(name); 49762306a36Sopenharmony_ci *t = '>'; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return 0; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic int lock__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci ops->locked.ops = zalloc(sizeof(*ops->locked.ops)); 50562306a36Sopenharmony_ci if (ops->locked.ops == NULL) 50662306a36Sopenharmony_ci return 0; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (disasm_line__parse(ops->raw, &ops->locked.ins.name, &ops->locked.ops->raw) < 0) 50962306a36Sopenharmony_ci goto out_free_ops; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci ops->locked.ins.ops = ins__find(arch, ops->locked.ins.name); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (ops->locked.ins.ops == NULL) 51462306a36Sopenharmony_ci goto out_free_ops; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (ops->locked.ins.ops->parse && 51762306a36Sopenharmony_ci ops->locked.ins.ops->parse(arch, ops->locked.ops, ms) < 0) 51862306a36Sopenharmony_ci goto out_free_ops; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ciout_free_ops: 52362306a36Sopenharmony_ci zfree(&ops->locked.ops); 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic int lock__scnprintf(struct ins *ins, char *bf, size_t size, 52862306a36Sopenharmony_ci struct ins_operands *ops, int max_ins_name) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci int printed; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (ops->locked.ins.ops == NULL) 53362306a36Sopenharmony_ci return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci printed = scnprintf(bf, size, "%-*s ", max_ins_name, ins->name); 53662306a36Sopenharmony_ci return printed + ins__scnprintf(&ops->locked.ins, bf + printed, 53762306a36Sopenharmony_ci size - printed, ops->locked.ops, max_ins_name); 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic void lock__delete(struct ins_operands *ops) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct ins *ins = &ops->locked.ins; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci if (ins->ops && ins->ops->free) 54562306a36Sopenharmony_ci ins->ops->free(ops->locked.ops); 54662306a36Sopenharmony_ci else 54762306a36Sopenharmony_ci ins__delete(ops->locked.ops); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci zfree(&ops->locked.ops); 55062306a36Sopenharmony_ci zfree(&ops->target.raw); 55162306a36Sopenharmony_ci zfree(&ops->target.name); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic struct ins_ops lock_ops = { 55562306a36Sopenharmony_ci .free = lock__delete, 55662306a36Sopenharmony_ci .parse = lock__parse, 55762306a36Sopenharmony_ci .scnprintf = lock__scnprintf, 55862306a36Sopenharmony_ci}; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic int mov__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms __maybe_unused) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci char *s = strchr(ops->raw, ','), *target, *comment, prev; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci if (s == NULL) 56562306a36Sopenharmony_ci return -1; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci *s = '\0'; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* 57062306a36Sopenharmony_ci * x86 SIB addressing has something like 0x8(%rax, %rcx, 1) 57162306a36Sopenharmony_ci * then it needs to have the closing parenthesis. 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_ci if (strchr(ops->raw, '(')) { 57462306a36Sopenharmony_ci *s = ','; 57562306a36Sopenharmony_ci s = strchr(ops->raw, ')'); 57662306a36Sopenharmony_ci if (s == NULL || s[1] != ',') 57762306a36Sopenharmony_ci return -1; 57862306a36Sopenharmony_ci *++s = '\0'; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci ops->source.raw = strdup(ops->raw); 58262306a36Sopenharmony_ci *s = ','; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (ops->source.raw == NULL) 58562306a36Sopenharmony_ci return -1; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci target = skip_spaces(++s); 58862306a36Sopenharmony_ci comment = strchr(s, arch->objdump.comment_char); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (comment != NULL) 59162306a36Sopenharmony_ci s = comment - 1; 59262306a36Sopenharmony_ci else 59362306a36Sopenharmony_ci s = strchr(s, '\0') - 1; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci while (s > target && isspace(s[0])) 59662306a36Sopenharmony_ci --s; 59762306a36Sopenharmony_ci s++; 59862306a36Sopenharmony_ci prev = *s; 59962306a36Sopenharmony_ci *s = '\0'; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci ops->target.raw = strdup(target); 60262306a36Sopenharmony_ci *s = prev; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (ops->target.raw == NULL) 60562306a36Sopenharmony_ci goto out_free_source; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (comment == NULL) 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci comment = skip_spaces(comment); 61162306a36Sopenharmony_ci comment__symbol(ops->source.raw, comment + 1, &ops->source.addr, &ops->source.name); 61262306a36Sopenharmony_ci comment__symbol(ops->target.raw, comment + 1, &ops->target.addr, &ops->target.name); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ciout_free_source: 61762306a36Sopenharmony_ci zfree(&ops->source.raw); 61862306a36Sopenharmony_ci return -1; 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic int mov__scnprintf(struct ins *ins, char *bf, size_t size, 62262306a36Sopenharmony_ci struct ins_operands *ops, int max_ins_name) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci return scnprintf(bf, size, "%-*s %s,%s", max_ins_name, ins->name, 62562306a36Sopenharmony_ci ops->source.name ?: ops->source.raw, 62662306a36Sopenharmony_ci ops->target.name ?: ops->target.raw); 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic struct ins_ops mov_ops = { 63062306a36Sopenharmony_ci .parse = mov__parse, 63162306a36Sopenharmony_ci .scnprintf = mov__scnprintf, 63262306a36Sopenharmony_ci}; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map_symbol *ms __maybe_unused) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci char *target, *comment, *s, prev; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci target = s = ops->raw; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci while (s[0] != '\0' && !isspace(s[0])) 64162306a36Sopenharmony_ci ++s; 64262306a36Sopenharmony_ci prev = *s; 64362306a36Sopenharmony_ci *s = '\0'; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci ops->target.raw = strdup(target); 64662306a36Sopenharmony_ci *s = prev; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (ops->target.raw == NULL) 64962306a36Sopenharmony_ci return -1; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci comment = strchr(s, arch->objdump.comment_char); 65262306a36Sopenharmony_ci if (comment == NULL) 65362306a36Sopenharmony_ci return 0; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci comment = skip_spaces(comment); 65662306a36Sopenharmony_ci comment__symbol(ops->target.raw, comment + 1, &ops->target.addr, &ops->target.name); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci return 0; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic int dec__scnprintf(struct ins *ins, char *bf, size_t size, 66262306a36Sopenharmony_ci struct ins_operands *ops, int max_ins_name) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, 66562306a36Sopenharmony_ci ops->target.name ?: ops->target.raw); 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic struct ins_ops dec_ops = { 66962306a36Sopenharmony_ci .parse = dec__parse, 67062306a36Sopenharmony_ci .scnprintf = dec__scnprintf, 67162306a36Sopenharmony_ci}; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic int nop__scnprintf(struct ins *ins __maybe_unused, char *bf, size_t size, 67462306a36Sopenharmony_ci struct ins_operands *ops __maybe_unused, int max_ins_name) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci return scnprintf(bf, size, "%-*s", max_ins_name, "nop"); 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic struct ins_ops nop_ops = { 68062306a36Sopenharmony_ci .scnprintf = nop__scnprintf, 68162306a36Sopenharmony_ci}; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic struct ins_ops ret_ops = { 68462306a36Sopenharmony_ci .scnprintf = ins__raw_scnprintf, 68562306a36Sopenharmony_ci}; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cibool ins__is_ret(const struct ins *ins) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci return ins->ops == &ret_ops; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cibool ins__is_lock(const struct ins *ins) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci return ins->ops == &lock_ops; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic int ins__key_cmp(const void *name, const void *insp) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci const struct ins *ins = insp; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci return strcmp(name, ins->name); 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic int ins__cmp(const void *a, const void *b) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci const struct ins *ia = a; 70762306a36Sopenharmony_ci const struct ins *ib = b; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci return strcmp(ia->name, ib->name); 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic void ins__sort(struct arch *arch) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci const int nmemb = arch->nr_instructions; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci qsort(arch->instructions, nmemb, sizeof(struct ins), ins__cmp); 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic struct ins_ops *__ins__find(struct arch *arch, const char *name) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct ins *ins; 72262306a36Sopenharmony_ci const int nmemb = arch->nr_instructions; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (!arch->sorted_instructions) { 72562306a36Sopenharmony_ci ins__sort(arch); 72662306a36Sopenharmony_ci arch->sorted_instructions = true; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci ins = bsearch(name, arch->instructions, nmemb, sizeof(struct ins), ins__key_cmp); 73062306a36Sopenharmony_ci if (ins) 73162306a36Sopenharmony_ci return ins->ops; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (arch->insn_suffix) { 73462306a36Sopenharmony_ci char tmp[32]; 73562306a36Sopenharmony_ci char suffix; 73662306a36Sopenharmony_ci size_t len = strlen(name); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (len == 0 || len >= sizeof(tmp)) 73962306a36Sopenharmony_ci return NULL; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci suffix = name[len - 1]; 74262306a36Sopenharmony_ci if (strchr(arch->insn_suffix, suffix) == NULL) 74362306a36Sopenharmony_ci return NULL; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci strcpy(tmp, name); 74662306a36Sopenharmony_ci tmp[len - 1] = '\0'; /* remove the suffix and check again */ 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci ins = bsearch(tmp, arch->instructions, nmemb, sizeof(struct ins), ins__key_cmp); 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci return ins ? ins->ops : NULL; 75162306a36Sopenharmony_ci} 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic struct ins_ops *ins__find(struct arch *arch, const char *name) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci struct ins_ops *ops = __ins__find(arch, name); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci if (!ops && arch->associate_instruction_ops) 75862306a36Sopenharmony_ci ops = arch->associate_instruction_ops(arch, name); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci return ops; 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic int arch__key_cmp(const void *name, const void *archp) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci const struct arch *arch = archp; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci return strcmp(name, arch->name); 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic int arch__cmp(const void *a, const void *b) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci const struct arch *aa = a; 77362306a36Sopenharmony_ci const struct arch *ab = b; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci return strcmp(aa->name, ab->name); 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic void arch__sort(void) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci const int nmemb = ARRAY_SIZE(architectures); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci qsort(architectures, nmemb, sizeof(struct arch), arch__cmp); 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic struct arch *arch__find(const char *name) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci const int nmemb = ARRAY_SIZE(architectures); 78862306a36Sopenharmony_ci static bool sorted; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (!sorted) { 79162306a36Sopenharmony_ci arch__sort(); 79262306a36Sopenharmony_ci sorted = true; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci return bsearch(name, architectures, nmemb, sizeof(struct arch), arch__key_cmp); 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_cistatic struct annotated_source *annotated_source__new(void) 79962306a36Sopenharmony_ci{ 80062306a36Sopenharmony_ci struct annotated_source *src = zalloc(sizeof(*src)); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (src != NULL) 80362306a36Sopenharmony_ci INIT_LIST_HEAD(&src->source); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci return src; 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic __maybe_unused void annotated_source__delete(struct annotated_source *src) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci if (src == NULL) 81162306a36Sopenharmony_ci return; 81262306a36Sopenharmony_ci zfree(&src->histograms); 81362306a36Sopenharmony_ci zfree(&src->cycles_hist); 81462306a36Sopenharmony_ci free(src); 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic int annotated_source__alloc_histograms(struct annotated_source *src, 81862306a36Sopenharmony_ci size_t size, int nr_hists) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci size_t sizeof_sym_hist; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci /* 82362306a36Sopenharmony_ci * Add buffer of one element for zero length symbol. 82462306a36Sopenharmony_ci * When sample is taken from first instruction of 82562306a36Sopenharmony_ci * zero length symbol, perf still resolves it and 82662306a36Sopenharmony_ci * shows symbol name in perf report and allows to 82762306a36Sopenharmony_ci * annotate it. 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_ci if (size == 0) 83062306a36Sopenharmony_ci size = 1; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci /* Check for overflow when calculating sizeof_sym_hist */ 83362306a36Sopenharmony_ci if (size > (SIZE_MAX - sizeof(struct sym_hist)) / sizeof(struct sym_hist_entry)) 83462306a36Sopenharmony_ci return -1; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(struct sym_hist_entry)); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* Check for overflow in zalloc argument */ 83962306a36Sopenharmony_ci if (sizeof_sym_hist > SIZE_MAX / nr_hists) 84062306a36Sopenharmony_ci return -1; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci src->sizeof_sym_hist = sizeof_sym_hist; 84362306a36Sopenharmony_ci src->nr_histograms = nr_hists; 84462306a36Sopenharmony_ci src->histograms = calloc(nr_hists, sizeof_sym_hist) ; 84562306a36Sopenharmony_ci return src->histograms ? 0 : -1; 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci/* The cycles histogram is lazily allocated. */ 84962306a36Sopenharmony_cistatic int symbol__alloc_hist_cycles(struct symbol *sym) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 85262306a36Sopenharmony_ci const size_t size = symbol__size(sym); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci notes->src->cycles_hist = calloc(size, sizeof(struct cyc_hist)); 85562306a36Sopenharmony_ci if (notes->src->cycles_hist == NULL) 85662306a36Sopenharmony_ci return -1; 85762306a36Sopenharmony_ci return 0; 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_civoid symbol__annotate_zero_histograms(struct symbol *sym) 86162306a36Sopenharmony_ci{ 86262306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci annotation__lock(notes); 86562306a36Sopenharmony_ci if (notes->src != NULL) { 86662306a36Sopenharmony_ci memset(notes->src->histograms, 0, 86762306a36Sopenharmony_ci notes->src->nr_histograms * notes->src->sizeof_sym_hist); 86862306a36Sopenharmony_ci if (notes->src->cycles_hist) 86962306a36Sopenharmony_ci memset(notes->src->cycles_hist, 0, 87062306a36Sopenharmony_ci symbol__size(sym) * sizeof(struct cyc_hist)); 87162306a36Sopenharmony_ci } 87262306a36Sopenharmony_ci annotation__unlock(notes); 87362306a36Sopenharmony_ci} 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cistatic int __symbol__account_cycles(struct cyc_hist *ch, 87662306a36Sopenharmony_ci u64 start, 87762306a36Sopenharmony_ci unsigned offset, unsigned cycles, 87862306a36Sopenharmony_ci unsigned have_start) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci /* 88162306a36Sopenharmony_ci * For now we can only account one basic block per 88262306a36Sopenharmony_ci * final jump. But multiple could be overlapping. 88362306a36Sopenharmony_ci * Always account the longest one. So when 88462306a36Sopenharmony_ci * a shorter one has been already seen throw it away. 88562306a36Sopenharmony_ci * 88662306a36Sopenharmony_ci * We separately always account the full cycles. 88762306a36Sopenharmony_ci */ 88862306a36Sopenharmony_ci ch[offset].num_aggr++; 88962306a36Sopenharmony_ci ch[offset].cycles_aggr += cycles; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (cycles > ch[offset].cycles_max) 89262306a36Sopenharmony_ci ch[offset].cycles_max = cycles; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci if (ch[offset].cycles_min) { 89562306a36Sopenharmony_ci if (cycles && cycles < ch[offset].cycles_min) 89662306a36Sopenharmony_ci ch[offset].cycles_min = cycles; 89762306a36Sopenharmony_ci } else 89862306a36Sopenharmony_ci ch[offset].cycles_min = cycles; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (!have_start && ch[offset].have_start) 90162306a36Sopenharmony_ci return 0; 90262306a36Sopenharmony_ci if (ch[offset].num) { 90362306a36Sopenharmony_ci if (have_start && (!ch[offset].have_start || 90462306a36Sopenharmony_ci ch[offset].start > start)) { 90562306a36Sopenharmony_ci ch[offset].have_start = 0; 90662306a36Sopenharmony_ci ch[offset].cycles = 0; 90762306a36Sopenharmony_ci ch[offset].num = 0; 90862306a36Sopenharmony_ci if (ch[offset].reset < 0xffff) 90962306a36Sopenharmony_ci ch[offset].reset++; 91062306a36Sopenharmony_ci } else if (have_start && 91162306a36Sopenharmony_ci ch[offset].start < start) 91262306a36Sopenharmony_ci return 0; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (ch[offset].num < NUM_SPARKS) 91662306a36Sopenharmony_ci ch[offset].cycles_spark[ch[offset].num] = cycles; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci ch[offset].have_start = have_start; 91962306a36Sopenharmony_ci ch[offset].start = start; 92062306a36Sopenharmony_ci ch[offset].cycles += cycles; 92162306a36Sopenharmony_ci ch[offset].num++; 92262306a36Sopenharmony_ci return 0; 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic int __symbol__inc_addr_samples(struct map_symbol *ms, 92662306a36Sopenharmony_ci struct annotated_source *src, int evidx, u64 addr, 92762306a36Sopenharmony_ci struct perf_sample *sample) 92862306a36Sopenharmony_ci{ 92962306a36Sopenharmony_ci struct symbol *sym = ms->sym; 93062306a36Sopenharmony_ci unsigned offset; 93162306a36Sopenharmony_ci struct sym_hist *h; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map__unmap_ip(ms->map, addr)); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci if ((addr < sym->start || addr >= sym->end) && 93662306a36Sopenharmony_ci (addr != sym->end || sym->start != sym->end)) { 93762306a36Sopenharmony_ci pr_debug("%s(%d): ERANGE! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 "\n", 93862306a36Sopenharmony_ci __func__, __LINE__, sym->name, sym->start, addr, sym->end); 93962306a36Sopenharmony_ci return -ERANGE; 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci offset = addr - sym->start; 94362306a36Sopenharmony_ci h = annotated_source__histogram(src, evidx); 94462306a36Sopenharmony_ci if (h == NULL) { 94562306a36Sopenharmony_ci pr_debug("%s(%d): ENOMEM! sym->name=%s, start=%#" PRIx64 ", addr=%#" PRIx64 ", end=%#" PRIx64 ", func: %d\n", 94662306a36Sopenharmony_ci __func__, __LINE__, sym->name, sym->start, addr, sym->end, sym->type == STT_FUNC); 94762306a36Sopenharmony_ci return -ENOMEM; 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci h->nr_samples++; 95062306a36Sopenharmony_ci h->addr[offset].nr_samples++; 95162306a36Sopenharmony_ci h->period += sample->period; 95262306a36Sopenharmony_ci h->addr[offset].period += sample->period; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64 95562306a36Sopenharmony_ci ", evidx=%d] => nr_samples: %" PRIu64 ", period: %" PRIu64 "\n", 95662306a36Sopenharmony_ci sym->start, sym->name, addr, addr - sym->start, evidx, 95762306a36Sopenharmony_ci h->addr[offset].nr_samples, h->addr[offset].period); 95862306a36Sopenharmony_ci return 0; 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic struct cyc_hist *symbol__cycles_hist(struct symbol *sym) 96262306a36Sopenharmony_ci{ 96362306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (notes->src == NULL) { 96662306a36Sopenharmony_ci notes->src = annotated_source__new(); 96762306a36Sopenharmony_ci if (notes->src == NULL) 96862306a36Sopenharmony_ci return NULL; 96962306a36Sopenharmony_ci goto alloc_cycles_hist; 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci if (!notes->src->cycles_hist) { 97362306a36Sopenharmony_cialloc_cycles_hist: 97462306a36Sopenharmony_ci symbol__alloc_hist_cycles(sym); 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci return notes->src->cycles_hist; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistruct annotated_source *symbol__hists(struct symbol *sym, int nr_hists) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci if (notes->src == NULL) { 98562306a36Sopenharmony_ci notes->src = annotated_source__new(); 98662306a36Sopenharmony_ci if (notes->src == NULL) 98762306a36Sopenharmony_ci return NULL; 98862306a36Sopenharmony_ci goto alloc_histograms; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci if (notes->src->histograms == NULL) { 99262306a36Sopenharmony_cialloc_histograms: 99362306a36Sopenharmony_ci annotated_source__alloc_histograms(notes->src, symbol__size(sym), 99462306a36Sopenharmony_ci nr_hists); 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci return notes->src; 99862306a36Sopenharmony_ci} 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_cistatic int symbol__inc_addr_samples(struct map_symbol *ms, 100162306a36Sopenharmony_ci struct evsel *evsel, u64 addr, 100262306a36Sopenharmony_ci struct perf_sample *sample) 100362306a36Sopenharmony_ci{ 100462306a36Sopenharmony_ci struct symbol *sym = ms->sym; 100562306a36Sopenharmony_ci struct annotated_source *src; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci if (sym == NULL) 100862306a36Sopenharmony_ci return 0; 100962306a36Sopenharmony_ci src = symbol__hists(sym, evsel->evlist->core.nr_entries); 101062306a36Sopenharmony_ci return src ? __symbol__inc_addr_samples(ms, src, evsel->core.idx, addr, sample) : 0; 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistatic int symbol__account_cycles(u64 addr, u64 start, 101462306a36Sopenharmony_ci struct symbol *sym, unsigned cycles) 101562306a36Sopenharmony_ci{ 101662306a36Sopenharmony_ci struct cyc_hist *cycles_hist; 101762306a36Sopenharmony_ci unsigned offset; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci if (sym == NULL) 102062306a36Sopenharmony_ci return 0; 102162306a36Sopenharmony_ci cycles_hist = symbol__cycles_hist(sym); 102262306a36Sopenharmony_ci if (cycles_hist == NULL) 102362306a36Sopenharmony_ci return -ENOMEM; 102462306a36Sopenharmony_ci if (addr < sym->start || addr >= sym->end) 102562306a36Sopenharmony_ci return -ERANGE; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (start) { 102862306a36Sopenharmony_ci if (start < sym->start || start >= sym->end) 102962306a36Sopenharmony_ci return -ERANGE; 103062306a36Sopenharmony_ci if (start >= addr) 103162306a36Sopenharmony_ci start = 0; 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci offset = addr - sym->start; 103462306a36Sopenharmony_ci return __symbol__account_cycles(cycles_hist, 103562306a36Sopenharmony_ci start ? start - sym->start : 0, 103662306a36Sopenharmony_ci offset, cycles, 103762306a36Sopenharmony_ci !!start); 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ciint addr_map_symbol__account_cycles(struct addr_map_symbol *ams, 104162306a36Sopenharmony_ci struct addr_map_symbol *start, 104262306a36Sopenharmony_ci unsigned cycles) 104362306a36Sopenharmony_ci{ 104462306a36Sopenharmony_ci u64 saddr = 0; 104562306a36Sopenharmony_ci int err; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci if (!cycles) 104862306a36Sopenharmony_ci return 0; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci /* 105162306a36Sopenharmony_ci * Only set start when IPC can be computed. We can only 105262306a36Sopenharmony_ci * compute it when the basic block is completely in a single 105362306a36Sopenharmony_ci * function. 105462306a36Sopenharmony_ci * Special case the case when the jump is elsewhere, but 105562306a36Sopenharmony_ci * it starts on the function start. 105662306a36Sopenharmony_ci */ 105762306a36Sopenharmony_ci if (start && 105862306a36Sopenharmony_ci (start->ms.sym == ams->ms.sym || 105962306a36Sopenharmony_ci (ams->ms.sym && 106062306a36Sopenharmony_ci start->addr == ams->ms.sym->start + map__start(ams->ms.map)))) 106162306a36Sopenharmony_ci saddr = start->al_addr; 106262306a36Sopenharmony_ci if (saddr == 0) 106362306a36Sopenharmony_ci pr_debug2("BB with bad start: addr %"PRIx64" start %"PRIx64" sym %"PRIx64" saddr %"PRIx64"\n", 106462306a36Sopenharmony_ci ams->addr, 106562306a36Sopenharmony_ci start ? start->addr : 0, 106662306a36Sopenharmony_ci ams->ms.sym ? ams->ms.sym->start + map__start(ams->ms.map) : 0, 106762306a36Sopenharmony_ci saddr); 106862306a36Sopenharmony_ci err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles); 106962306a36Sopenharmony_ci if (err) 107062306a36Sopenharmony_ci pr_debug2("account_cycles failed %d\n", err); 107162306a36Sopenharmony_ci return err; 107262306a36Sopenharmony_ci} 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_cistatic unsigned annotation__count_insn(struct annotation *notes, u64 start, u64 end) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci unsigned n_insn = 0; 107762306a36Sopenharmony_ci u64 offset; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci for (offset = start; offset <= end; offset++) { 108062306a36Sopenharmony_ci if (notes->offsets[offset]) 108162306a36Sopenharmony_ci n_insn++; 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci return n_insn; 108462306a36Sopenharmony_ci} 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_cistatic void annotation__count_and_fill(struct annotation *notes, u64 start, u64 end, struct cyc_hist *ch) 108762306a36Sopenharmony_ci{ 108862306a36Sopenharmony_ci unsigned n_insn; 108962306a36Sopenharmony_ci unsigned int cover_insn = 0; 109062306a36Sopenharmony_ci u64 offset; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci n_insn = annotation__count_insn(notes, start, end); 109362306a36Sopenharmony_ci if (n_insn && ch->num && ch->cycles) { 109462306a36Sopenharmony_ci float ipc = n_insn / ((double)ch->cycles / (double)ch->num); 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci /* Hide data when there are too many overlaps. */ 109762306a36Sopenharmony_ci if (ch->reset >= 0x7fff) 109862306a36Sopenharmony_ci return; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci for (offset = start; offset <= end; offset++) { 110162306a36Sopenharmony_ci struct annotation_line *al = notes->offsets[offset]; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci if (al && al->ipc == 0.0) { 110462306a36Sopenharmony_ci al->ipc = ipc; 110562306a36Sopenharmony_ci cover_insn++; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci if (cover_insn) { 111062306a36Sopenharmony_ci notes->hit_cycles += ch->cycles; 111162306a36Sopenharmony_ci notes->hit_insn += n_insn * ch->num; 111262306a36Sopenharmony_ci notes->cover_insn += cover_insn; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci} 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_civoid annotation__compute_ipc(struct annotation *notes, size_t size) 111862306a36Sopenharmony_ci{ 111962306a36Sopenharmony_ci s64 offset; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci if (!notes->src || !notes->src->cycles_hist) 112262306a36Sopenharmony_ci return; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci notes->total_insn = annotation__count_insn(notes, 0, size - 1); 112562306a36Sopenharmony_ci notes->hit_cycles = 0; 112662306a36Sopenharmony_ci notes->hit_insn = 0; 112762306a36Sopenharmony_ci notes->cover_insn = 0; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci annotation__lock(notes); 113062306a36Sopenharmony_ci for (offset = size - 1; offset >= 0; --offset) { 113162306a36Sopenharmony_ci struct cyc_hist *ch; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci ch = ¬es->src->cycles_hist[offset]; 113462306a36Sopenharmony_ci if (ch && ch->cycles) { 113562306a36Sopenharmony_ci struct annotation_line *al; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (ch->have_start) 113862306a36Sopenharmony_ci annotation__count_and_fill(notes, ch->start, offset, ch); 113962306a36Sopenharmony_ci al = notes->offsets[offset]; 114062306a36Sopenharmony_ci if (al && ch->num_aggr) { 114162306a36Sopenharmony_ci al->cycles = ch->cycles_aggr / ch->num_aggr; 114262306a36Sopenharmony_ci al->cycles_max = ch->cycles_max; 114362306a36Sopenharmony_ci al->cycles_min = ch->cycles_min; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci notes->have_cycles = true; 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci annotation__unlock(notes); 114962306a36Sopenharmony_ci} 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ciint addr_map_symbol__inc_samples(struct addr_map_symbol *ams, struct perf_sample *sample, 115262306a36Sopenharmony_ci struct evsel *evsel) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci return symbol__inc_addr_samples(&ams->ms, evsel, ams->al_addr, sample); 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ciint hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *sample, 115862306a36Sopenharmony_ci struct evsel *evsel, u64 ip) 115962306a36Sopenharmony_ci{ 116062306a36Sopenharmony_ci return symbol__inc_addr_samples(&he->ms, evsel, ip, sample); 116162306a36Sopenharmony_ci} 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_cistatic void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map_symbol *ms) 116462306a36Sopenharmony_ci{ 116562306a36Sopenharmony_ci dl->ins.ops = ins__find(arch, dl->ins.name); 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci if (!dl->ins.ops) 116862306a36Sopenharmony_ci return; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci if (dl->ins.ops->parse && dl->ins.ops->parse(arch, &dl->ops, ms) < 0) 117162306a36Sopenharmony_ci dl->ins.ops = NULL; 117262306a36Sopenharmony_ci} 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_cistatic int disasm_line__parse(char *line, const char **namep, char **rawp) 117562306a36Sopenharmony_ci{ 117662306a36Sopenharmony_ci char tmp, *name = skip_spaces(line); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci if (name[0] == '\0') 117962306a36Sopenharmony_ci return -1; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci *rawp = name + 1; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci while ((*rawp)[0] != '\0' && !isspace((*rawp)[0])) 118462306a36Sopenharmony_ci ++*rawp; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci tmp = (*rawp)[0]; 118762306a36Sopenharmony_ci (*rawp)[0] = '\0'; 118862306a36Sopenharmony_ci *namep = strdup(name); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci if (*namep == NULL) 119162306a36Sopenharmony_ci goto out; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci (*rawp)[0] = tmp; 119462306a36Sopenharmony_ci *rawp = strim(*rawp); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci return 0; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ciout: 119962306a36Sopenharmony_ci return -1; 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistruct annotate_args { 120362306a36Sopenharmony_ci struct arch *arch; 120462306a36Sopenharmony_ci struct map_symbol ms; 120562306a36Sopenharmony_ci struct evsel *evsel; 120662306a36Sopenharmony_ci struct annotation_options *options; 120762306a36Sopenharmony_ci s64 offset; 120862306a36Sopenharmony_ci char *line; 120962306a36Sopenharmony_ci int line_nr; 121062306a36Sopenharmony_ci char *fileloc; 121162306a36Sopenharmony_ci}; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_cistatic void annotation_line__init(struct annotation_line *al, 121462306a36Sopenharmony_ci struct annotate_args *args, 121562306a36Sopenharmony_ci int nr) 121662306a36Sopenharmony_ci{ 121762306a36Sopenharmony_ci al->offset = args->offset; 121862306a36Sopenharmony_ci al->line = strdup(args->line); 121962306a36Sopenharmony_ci al->line_nr = args->line_nr; 122062306a36Sopenharmony_ci al->fileloc = args->fileloc; 122162306a36Sopenharmony_ci al->data_nr = nr; 122262306a36Sopenharmony_ci} 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_cistatic void annotation_line__exit(struct annotation_line *al) 122562306a36Sopenharmony_ci{ 122662306a36Sopenharmony_ci zfree_srcline(&al->path); 122762306a36Sopenharmony_ci zfree(&al->line); 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistatic size_t disasm_line_size(int nr) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci struct annotation_line *al; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci return (sizeof(struct disasm_line) + (sizeof(al->data[0]) * nr)); 123562306a36Sopenharmony_ci} 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci/* 123862306a36Sopenharmony_ci * Allocating the disasm annotation line data with 123962306a36Sopenharmony_ci * following structure: 124062306a36Sopenharmony_ci * 124162306a36Sopenharmony_ci * ------------------------------------------- 124262306a36Sopenharmony_ci * struct disasm_line | struct annotation_line 124362306a36Sopenharmony_ci * ------------------------------------------- 124462306a36Sopenharmony_ci * 124562306a36Sopenharmony_ci * We have 'struct annotation_line' member as last member 124662306a36Sopenharmony_ci * of 'struct disasm_line' to have an easy access. 124762306a36Sopenharmony_ci */ 124862306a36Sopenharmony_cistatic struct disasm_line *disasm_line__new(struct annotate_args *args) 124962306a36Sopenharmony_ci{ 125062306a36Sopenharmony_ci struct disasm_line *dl = NULL; 125162306a36Sopenharmony_ci int nr = 1; 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci if (evsel__is_group_event(args->evsel)) 125462306a36Sopenharmony_ci nr = args->evsel->core.nr_members; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci dl = zalloc(disasm_line_size(nr)); 125762306a36Sopenharmony_ci if (!dl) 125862306a36Sopenharmony_ci return NULL; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci annotation_line__init(&dl->al, args, nr); 126162306a36Sopenharmony_ci if (dl->al.line == NULL) 126262306a36Sopenharmony_ci goto out_delete; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci if (args->offset != -1) { 126562306a36Sopenharmony_ci if (disasm_line__parse(dl->al.line, &dl->ins.name, &dl->ops.raw) < 0) 126662306a36Sopenharmony_ci goto out_free_line; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci disasm_line__init_ins(dl, args->arch, &args->ms); 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci return dl; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ciout_free_line: 127462306a36Sopenharmony_ci zfree(&dl->al.line); 127562306a36Sopenharmony_ciout_delete: 127662306a36Sopenharmony_ci free(dl); 127762306a36Sopenharmony_ci return NULL; 127862306a36Sopenharmony_ci} 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_civoid disasm_line__free(struct disasm_line *dl) 128162306a36Sopenharmony_ci{ 128262306a36Sopenharmony_ci if (dl->ins.ops && dl->ins.ops->free) 128362306a36Sopenharmony_ci dl->ins.ops->free(&dl->ops); 128462306a36Sopenharmony_ci else 128562306a36Sopenharmony_ci ins__delete(&dl->ops); 128662306a36Sopenharmony_ci zfree(&dl->ins.name); 128762306a36Sopenharmony_ci annotation_line__exit(&dl->al); 128862306a36Sopenharmony_ci free(dl); 128962306a36Sopenharmony_ci} 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ciint disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw, int max_ins_name) 129262306a36Sopenharmony_ci{ 129362306a36Sopenharmony_ci if (raw || !dl->ins.ops) 129462306a36Sopenharmony_ci return scnprintf(bf, size, "%-*s %s", max_ins_name, dl->ins.name, dl->ops.raw); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci return ins__scnprintf(&dl->ins, bf, size, &dl->ops, max_ins_name); 129762306a36Sopenharmony_ci} 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_civoid annotation__exit(struct annotation *notes) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci annotated_source__delete(notes->src); 130262306a36Sopenharmony_ci} 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_cistatic struct sharded_mutex *sharded_mutex; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_cistatic void annotation__init_sharded_mutex(void) 130762306a36Sopenharmony_ci{ 130862306a36Sopenharmony_ci /* As many mutexes as there are CPUs. */ 130962306a36Sopenharmony_ci sharded_mutex = sharded_mutex__new(cpu__max_present_cpu().cpu); 131062306a36Sopenharmony_ci} 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_cistatic size_t annotation__hash(const struct annotation *notes) 131362306a36Sopenharmony_ci{ 131462306a36Sopenharmony_ci return (size_t)notes; 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_cistatic struct mutex *annotation__get_mutex(const struct annotation *notes) 131862306a36Sopenharmony_ci{ 131962306a36Sopenharmony_ci static pthread_once_t once = PTHREAD_ONCE_INIT; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci pthread_once(&once, annotation__init_sharded_mutex); 132262306a36Sopenharmony_ci if (!sharded_mutex) 132362306a36Sopenharmony_ci return NULL; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci return sharded_mutex__get_mutex(sharded_mutex, annotation__hash(notes)); 132662306a36Sopenharmony_ci} 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_civoid annotation__lock(struct annotation *notes) 132962306a36Sopenharmony_ci NO_THREAD_SAFETY_ANALYSIS 133062306a36Sopenharmony_ci{ 133162306a36Sopenharmony_ci struct mutex *mutex = annotation__get_mutex(notes); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci if (mutex) 133462306a36Sopenharmony_ci mutex_lock(mutex); 133562306a36Sopenharmony_ci} 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_civoid annotation__unlock(struct annotation *notes) 133862306a36Sopenharmony_ci NO_THREAD_SAFETY_ANALYSIS 133962306a36Sopenharmony_ci{ 134062306a36Sopenharmony_ci struct mutex *mutex = annotation__get_mutex(notes); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci if (mutex) 134362306a36Sopenharmony_ci mutex_unlock(mutex); 134462306a36Sopenharmony_ci} 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_cibool annotation__trylock(struct annotation *notes) 134762306a36Sopenharmony_ci{ 134862306a36Sopenharmony_ci struct mutex *mutex = annotation__get_mutex(notes); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci if (!mutex) 135162306a36Sopenharmony_ci return false; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci return mutex_trylock(mutex); 135462306a36Sopenharmony_ci} 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_cistatic void annotation_line__add(struct annotation_line *al, struct list_head *head) 135862306a36Sopenharmony_ci{ 135962306a36Sopenharmony_ci list_add_tail(&al->node, head); 136062306a36Sopenharmony_ci} 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_cistruct annotation_line * 136362306a36Sopenharmony_ciannotation_line__next(struct annotation_line *pos, struct list_head *head) 136462306a36Sopenharmony_ci{ 136562306a36Sopenharmony_ci list_for_each_entry_continue(pos, head, node) 136662306a36Sopenharmony_ci if (pos->offset >= 0) 136762306a36Sopenharmony_ci return pos; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci return NULL; 137062306a36Sopenharmony_ci} 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_cistatic const char *annotate__address_color(struct block_range *br) 137362306a36Sopenharmony_ci{ 137462306a36Sopenharmony_ci double cov = block_range__coverage(br); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci if (cov >= 0) { 137762306a36Sopenharmony_ci /* mark red for >75% coverage */ 137862306a36Sopenharmony_ci if (cov > 0.75) 137962306a36Sopenharmony_ci return PERF_COLOR_RED; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci /* mark dull for <1% coverage */ 138262306a36Sopenharmony_ci if (cov < 0.01) 138362306a36Sopenharmony_ci return PERF_COLOR_NORMAL; 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci return PERF_COLOR_MAGENTA; 138762306a36Sopenharmony_ci} 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_cistatic const char *annotate__asm_color(struct block_range *br) 139062306a36Sopenharmony_ci{ 139162306a36Sopenharmony_ci double cov = block_range__coverage(br); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci if (cov >= 0) { 139462306a36Sopenharmony_ci /* mark dull for <1% coverage */ 139562306a36Sopenharmony_ci if (cov < 0.01) 139662306a36Sopenharmony_ci return PERF_COLOR_NORMAL; 139762306a36Sopenharmony_ci } 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci return PERF_COLOR_BLUE; 140062306a36Sopenharmony_ci} 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_cistatic void annotate__branch_printf(struct block_range *br, u64 addr) 140362306a36Sopenharmony_ci{ 140462306a36Sopenharmony_ci bool emit_comment = true; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci if (!br) 140762306a36Sopenharmony_ci return; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci#if 1 141062306a36Sopenharmony_ci if (br->is_target && br->start == addr) { 141162306a36Sopenharmony_ci struct block_range *branch = br; 141262306a36Sopenharmony_ci double p; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci /* 141562306a36Sopenharmony_ci * Find matching branch to our target. 141662306a36Sopenharmony_ci */ 141762306a36Sopenharmony_ci while (!branch->is_branch) 141862306a36Sopenharmony_ci branch = block_range__next(branch); 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci p = 100 *(double)br->entry / branch->coverage; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci if (p > 0.1) { 142362306a36Sopenharmony_ci if (emit_comment) { 142462306a36Sopenharmony_ci emit_comment = false; 142562306a36Sopenharmony_ci printf("\t#"); 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci /* 142962306a36Sopenharmony_ci * The percentage of coverage joined at this target in relation 143062306a36Sopenharmony_ci * to the next branch. 143162306a36Sopenharmony_ci */ 143262306a36Sopenharmony_ci printf(" +%.2f%%", p); 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci } 143562306a36Sopenharmony_ci#endif 143662306a36Sopenharmony_ci if (br->is_branch && br->end == addr) { 143762306a36Sopenharmony_ci double p = 100*(double)br->taken / br->coverage; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci if (p > 0.1) { 144062306a36Sopenharmony_ci if (emit_comment) { 144162306a36Sopenharmony_ci emit_comment = false; 144262306a36Sopenharmony_ci printf("\t#"); 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci /* 144662306a36Sopenharmony_ci * The percentage of coverage leaving at this branch, and 144762306a36Sopenharmony_ci * its prediction ratio. 144862306a36Sopenharmony_ci */ 144962306a36Sopenharmony_ci printf(" -%.2f%% (p:%.2f%%)", p, 100*(double)br->pred / br->taken); 145062306a36Sopenharmony_ci } 145162306a36Sopenharmony_ci } 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_cistatic int disasm_line__print(struct disasm_line *dl, u64 start, int addr_fmt_width) 145562306a36Sopenharmony_ci{ 145662306a36Sopenharmony_ci s64 offset = dl->al.offset; 145762306a36Sopenharmony_ci const u64 addr = start + offset; 145862306a36Sopenharmony_ci struct block_range *br; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci br = block_range__find(addr); 146162306a36Sopenharmony_ci color_fprintf(stdout, annotate__address_color(br), " %*" PRIx64 ":", addr_fmt_width, addr); 146262306a36Sopenharmony_ci color_fprintf(stdout, annotate__asm_color(br), "%s", dl->al.line); 146362306a36Sopenharmony_ci annotate__branch_printf(br, addr); 146462306a36Sopenharmony_ci return 0; 146562306a36Sopenharmony_ci} 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_cistatic int 146862306a36Sopenharmony_ciannotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start, 146962306a36Sopenharmony_ci struct evsel *evsel, u64 len, int min_pcnt, int printed, 147062306a36Sopenharmony_ci int max_lines, struct annotation_line *queue, int addr_fmt_width, 147162306a36Sopenharmony_ci int percent_type) 147262306a36Sopenharmony_ci{ 147362306a36Sopenharmony_ci struct disasm_line *dl = container_of(al, struct disasm_line, al); 147462306a36Sopenharmony_ci static const char *prev_line; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci if (al->offset != -1) { 147762306a36Sopenharmony_ci double max_percent = 0.0; 147862306a36Sopenharmony_ci int i, nr_percent = 1; 147962306a36Sopenharmony_ci const char *color; 148062306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci for (i = 0; i < al->data_nr; i++) { 148362306a36Sopenharmony_ci double percent; 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci percent = annotation_data__percent(&al->data[i], 148662306a36Sopenharmony_ci percent_type); 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci if (percent > max_percent) 148962306a36Sopenharmony_ci max_percent = percent; 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci if (al->data_nr > nr_percent) 149362306a36Sopenharmony_ci nr_percent = al->data_nr; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci if (max_percent < min_pcnt) 149662306a36Sopenharmony_ci return -1; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci if (max_lines && printed >= max_lines) 149962306a36Sopenharmony_ci return 1; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci if (queue != NULL) { 150262306a36Sopenharmony_ci list_for_each_entry_from(queue, ¬es->src->source, node) { 150362306a36Sopenharmony_ci if (queue == al) 150462306a36Sopenharmony_ci break; 150562306a36Sopenharmony_ci annotation_line__print(queue, sym, start, evsel, len, 150662306a36Sopenharmony_ci 0, 0, 1, NULL, addr_fmt_width, 150762306a36Sopenharmony_ci percent_type); 150862306a36Sopenharmony_ci } 150962306a36Sopenharmony_ci } 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci color = get_percent_color(max_percent); 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci for (i = 0; i < nr_percent; i++) { 151462306a36Sopenharmony_ci struct annotation_data *data = &al->data[i]; 151562306a36Sopenharmony_ci double percent; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci percent = annotation_data__percent(data, percent_type); 151862306a36Sopenharmony_ci color = get_percent_color(percent); 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci if (symbol_conf.show_total_period) 152162306a36Sopenharmony_ci color_fprintf(stdout, color, " %11" PRIu64, 152262306a36Sopenharmony_ci data->he.period); 152362306a36Sopenharmony_ci else if (symbol_conf.show_nr_samples) 152462306a36Sopenharmony_ci color_fprintf(stdout, color, " %7" PRIu64, 152562306a36Sopenharmony_ci data->he.nr_samples); 152662306a36Sopenharmony_ci else 152762306a36Sopenharmony_ci color_fprintf(stdout, color, " %7.2f", percent); 152862306a36Sopenharmony_ci } 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci printf(" : "); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci disasm_line__print(dl, start, addr_fmt_width); 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci /* 153562306a36Sopenharmony_ci * Also color the filename and line if needed, with 153662306a36Sopenharmony_ci * the same color than the percentage. Don't print it 153762306a36Sopenharmony_ci * twice for close colored addr with the same filename:line 153862306a36Sopenharmony_ci */ 153962306a36Sopenharmony_ci if (al->path) { 154062306a36Sopenharmony_ci if (!prev_line || strcmp(prev_line, al->path)) { 154162306a36Sopenharmony_ci color_fprintf(stdout, color, " // %s", al->path); 154262306a36Sopenharmony_ci prev_line = al->path; 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci } 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci printf("\n"); 154762306a36Sopenharmony_ci } else if (max_lines && printed >= max_lines) 154862306a36Sopenharmony_ci return 1; 154962306a36Sopenharmony_ci else { 155062306a36Sopenharmony_ci int width = symbol_conf.show_total_period ? 12 : 8; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci if (queue) 155362306a36Sopenharmony_ci return -1; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci if (evsel__is_group_event(evsel)) 155662306a36Sopenharmony_ci width *= evsel->core.nr_members; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci if (!*al->line) 155962306a36Sopenharmony_ci printf(" %*s:\n", width, " "); 156062306a36Sopenharmony_ci else 156162306a36Sopenharmony_ci printf(" %*s: %-*d %s\n", width, " ", addr_fmt_width, al->line_nr, al->line); 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci return 0; 156562306a36Sopenharmony_ci} 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci/* 156862306a36Sopenharmony_ci * symbol__parse_objdump_line() parses objdump output (with -d --no-show-raw) 156962306a36Sopenharmony_ci * which looks like following 157062306a36Sopenharmony_ci * 157162306a36Sopenharmony_ci * 0000000000415500 <_init>: 157262306a36Sopenharmony_ci * 415500: sub $0x8,%rsp 157362306a36Sopenharmony_ci * 415504: mov 0x2f5ad5(%rip),%rax # 70afe0 <_DYNAMIC+0x2f8> 157462306a36Sopenharmony_ci * 41550b: test %rax,%rax 157562306a36Sopenharmony_ci * 41550e: je 415515 <_init+0x15> 157662306a36Sopenharmony_ci * 415510: callq 416e70 <__gmon_start__@plt> 157762306a36Sopenharmony_ci * 415515: add $0x8,%rsp 157862306a36Sopenharmony_ci * 415519: retq 157962306a36Sopenharmony_ci * 158062306a36Sopenharmony_ci * it will be parsed and saved into struct disasm_line as 158162306a36Sopenharmony_ci * <offset> <name> <ops.raw> 158262306a36Sopenharmony_ci * 158362306a36Sopenharmony_ci * The offset will be a relative offset from the start of the symbol and -1 158462306a36Sopenharmony_ci * means that it's not a disassembly line so should be treated differently. 158562306a36Sopenharmony_ci * The ops.raw part will be parsed further according to type of the instruction. 158662306a36Sopenharmony_ci */ 158762306a36Sopenharmony_cistatic int symbol__parse_objdump_line(struct symbol *sym, 158862306a36Sopenharmony_ci struct annotate_args *args, 158962306a36Sopenharmony_ci char *parsed_line, int *line_nr, char **fileloc) 159062306a36Sopenharmony_ci{ 159162306a36Sopenharmony_ci struct map *map = args->ms.map; 159262306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 159362306a36Sopenharmony_ci struct disasm_line *dl; 159462306a36Sopenharmony_ci char *tmp; 159562306a36Sopenharmony_ci s64 line_ip, offset = -1; 159662306a36Sopenharmony_ci regmatch_t match[2]; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci /* /filename:linenr ? Save line number and ignore. */ 159962306a36Sopenharmony_ci if (regexec(&file_lineno, parsed_line, 2, match, 0) == 0) { 160062306a36Sopenharmony_ci *line_nr = atoi(parsed_line + match[1].rm_so); 160162306a36Sopenharmony_ci free(*fileloc); 160262306a36Sopenharmony_ci *fileloc = strdup(parsed_line); 160362306a36Sopenharmony_ci return 0; 160462306a36Sopenharmony_ci } 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci /* Process hex address followed by ':'. */ 160762306a36Sopenharmony_ci line_ip = strtoull(parsed_line, &tmp, 16); 160862306a36Sopenharmony_ci if (parsed_line != tmp && tmp[0] == ':' && tmp[1] != '\0') { 160962306a36Sopenharmony_ci u64 start = map__rip_2objdump(map, sym->start), 161062306a36Sopenharmony_ci end = map__rip_2objdump(map, sym->end); 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci offset = line_ip - start; 161362306a36Sopenharmony_ci if ((u64)line_ip < start || (u64)line_ip >= end) 161462306a36Sopenharmony_ci offset = -1; 161562306a36Sopenharmony_ci else 161662306a36Sopenharmony_ci parsed_line = tmp + 1; 161762306a36Sopenharmony_ci } 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci args->offset = offset; 162062306a36Sopenharmony_ci args->line = parsed_line; 162162306a36Sopenharmony_ci args->line_nr = *line_nr; 162262306a36Sopenharmony_ci args->fileloc = *fileloc; 162362306a36Sopenharmony_ci args->ms.sym = sym; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci dl = disasm_line__new(args); 162662306a36Sopenharmony_ci (*line_nr)++; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci if (dl == NULL) 162962306a36Sopenharmony_ci return -1; 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci if (!disasm_line__has_local_offset(dl)) { 163262306a36Sopenharmony_ci dl->ops.target.offset = dl->ops.target.addr - 163362306a36Sopenharmony_ci map__rip_2objdump(map, sym->start); 163462306a36Sopenharmony_ci dl->ops.target.offset_avail = true; 163562306a36Sopenharmony_ci } 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci /* kcore has no symbols, so add the call target symbol */ 163862306a36Sopenharmony_ci if (dl->ins.ops && ins__is_call(&dl->ins) && !dl->ops.target.sym) { 163962306a36Sopenharmony_ci struct addr_map_symbol target = { 164062306a36Sopenharmony_ci .addr = dl->ops.target.addr, 164162306a36Sopenharmony_ci .ms = { .map = map, }, 164262306a36Sopenharmony_ci }; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci if (!maps__find_ams(args->ms.maps, &target) && 164562306a36Sopenharmony_ci target.ms.sym->start == target.al_addr) 164662306a36Sopenharmony_ci dl->ops.target.sym = target.ms.sym; 164762306a36Sopenharmony_ci } 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci annotation_line__add(&dl->al, ¬es->src->source); 165062306a36Sopenharmony_ci return 0; 165162306a36Sopenharmony_ci} 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_cistatic __attribute__((constructor)) void symbol__init_regexpr(void) 165462306a36Sopenharmony_ci{ 165562306a36Sopenharmony_ci regcomp(&file_lineno, "^/[^:]+:([0-9]+)", REG_EXTENDED); 165662306a36Sopenharmony_ci} 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_cistatic void delete_last_nop(struct symbol *sym) 165962306a36Sopenharmony_ci{ 166062306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 166162306a36Sopenharmony_ci struct list_head *list = ¬es->src->source; 166262306a36Sopenharmony_ci struct disasm_line *dl; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci while (!list_empty(list)) { 166562306a36Sopenharmony_ci dl = list_entry(list->prev, struct disasm_line, al.node); 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci if (dl->ins.ops) { 166862306a36Sopenharmony_ci if (dl->ins.ops != &nop_ops) 166962306a36Sopenharmony_ci return; 167062306a36Sopenharmony_ci } else { 167162306a36Sopenharmony_ci if (!strstr(dl->al.line, " nop ") && 167262306a36Sopenharmony_ci !strstr(dl->al.line, " nopl ") && 167362306a36Sopenharmony_ci !strstr(dl->al.line, " nopw ")) 167462306a36Sopenharmony_ci return; 167562306a36Sopenharmony_ci } 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci list_del_init(&dl->al.node); 167862306a36Sopenharmony_ci disasm_line__free(dl); 167962306a36Sopenharmony_ci } 168062306a36Sopenharmony_ci} 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ciint symbol__strerror_disassemble(struct map_symbol *ms, int errnum, char *buf, size_t buflen) 168362306a36Sopenharmony_ci{ 168462306a36Sopenharmony_ci struct dso *dso = map__dso(ms->map); 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_ci BUG_ON(buflen == 0); 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci if (errnum >= 0) { 168962306a36Sopenharmony_ci str_error_r(errnum, buf, buflen); 169062306a36Sopenharmony_ci return 0; 169162306a36Sopenharmony_ci } 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci switch (errnum) { 169462306a36Sopenharmony_ci case SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX: { 169562306a36Sopenharmony_ci char bf[SBUILD_ID_SIZE + 15] = " with build id "; 169662306a36Sopenharmony_ci char *build_id_msg = NULL; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci if (dso->has_build_id) { 169962306a36Sopenharmony_ci build_id__sprintf(&dso->bid, bf + 15); 170062306a36Sopenharmony_ci build_id_msg = bf; 170162306a36Sopenharmony_ci } 170262306a36Sopenharmony_ci scnprintf(buf, buflen, 170362306a36Sopenharmony_ci "No vmlinux file%s\nwas found in the path.\n\n" 170462306a36Sopenharmony_ci "Note that annotation using /proc/kcore requires CAP_SYS_RAWIO capability.\n\n" 170562306a36Sopenharmony_ci "Please use:\n\n" 170662306a36Sopenharmony_ci " perf buildid-cache -vu vmlinux\n\n" 170762306a36Sopenharmony_ci "or:\n\n" 170862306a36Sopenharmony_ci " --vmlinux vmlinux\n", build_id_msg ?: ""); 170962306a36Sopenharmony_ci } 171062306a36Sopenharmony_ci break; 171162306a36Sopenharmony_ci case SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF: 171262306a36Sopenharmony_ci scnprintf(buf, buflen, "Please link with binutils's libopcode to enable BPF annotation"); 171362306a36Sopenharmony_ci break; 171462306a36Sopenharmony_ci case SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_REGEXP: 171562306a36Sopenharmony_ci scnprintf(buf, buflen, "Problems with arch specific instruction name regular expressions."); 171662306a36Sopenharmony_ci break; 171762306a36Sopenharmony_ci case SYMBOL_ANNOTATE_ERRNO__ARCH_INIT_CPUID_PARSING: 171862306a36Sopenharmony_ci scnprintf(buf, buflen, "Problems while parsing the CPUID in the arch specific initialization."); 171962306a36Sopenharmony_ci break; 172062306a36Sopenharmony_ci case SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE: 172162306a36Sopenharmony_ci scnprintf(buf, buflen, "Invalid BPF file: %s.", dso->long_name); 172262306a36Sopenharmony_ci break; 172362306a36Sopenharmony_ci case SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF: 172462306a36Sopenharmony_ci scnprintf(buf, buflen, "The %s BPF file has no BTF section, compile with -g or use pahole -J.", 172562306a36Sopenharmony_ci dso->long_name); 172662306a36Sopenharmony_ci break; 172762306a36Sopenharmony_ci default: 172862306a36Sopenharmony_ci scnprintf(buf, buflen, "Internal error: Invalid %d error code\n", errnum); 172962306a36Sopenharmony_ci break; 173062306a36Sopenharmony_ci } 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci return 0; 173362306a36Sopenharmony_ci} 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_cistatic int dso__disassemble_filename(struct dso *dso, char *filename, size_t filename_size) 173662306a36Sopenharmony_ci{ 173762306a36Sopenharmony_ci char linkname[PATH_MAX]; 173862306a36Sopenharmony_ci char *build_id_filename; 173962306a36Sopenharmony_ci char *build_id_path = NULL; 174062306a36Sopenharmony_ci char *pos; 174162306a36Sopenharmony_ci int len; 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && 174462306a36Sopenharmony_ci !dso__is_kcore(dso)) 174562306a36Sopenharmony_ci return SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci build_id_filename = dso__build_id_filename(dso, NULL, 0, false); 174862306a36Sopenharmony_ci if (build_id_filename) { 174962306a36Sopenharmony_ci __symbol__join_symfs(filename, filename_size, build_id_filename); 175062306a36Sopenharmony_ci free(build_id_filename); 175162306a36Sopenharmony_ci } else { 175262306a36Sopenharmony_ci if (dso->has_build_id) 175362306a36Sopenharmony_ci return ENOMEM; 175462306a36Sopenharmony_ci goto fallback; 175562306a36Sopenharmony_ci } 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci build_id_path = strdup(filename); 175862306a36Sopenharmony_ci if (!build_id_path) 175962306a36Sopenharmony_ci return ENOMEM; 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci /* 176262306a36Sopenharmony_ci * old style build-id cache has name of XX/XXXXXXX.. while 176362306a36Sopenharmony_ci * new style has XX/XXXXXXX../{elf,kallsyms,vdso}. 176462306a36Sopenharmony_ci * extract the build-id part of dirname in the new style only. 176562306a36Sopenharmony_ci */ 176662306a36Sopenharmony_ci pos = strrchr(build_id_path, '/'); 176762306a36Sopenharmony_ci if (pos && strlen(pos) < SBUILD_ID_SIZE - 2) 176862306a36Sopenharmony_ci dirname(build_id_path); 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci if (dso__is_kcore(dso)) 177162306a36Sopenharmony_ci goto fallback; 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci len = readlink(build_id_path, linkname, sizeof(linkname) - 1); 177462306a36Sopenharmony_ci if (len < 0) 177562306a36Sopenharmony_ci goto fallback; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci linkname[len] = '\0'; 177862306a36Sopenharmony_ci if (strstr(linkname, DSO__NAME_KALLSYMS) || 177962306a36Sopenharmony_ci access(filename, R_OK)) { 178062306a36Sopenharmony_cifallback: 178162306a36Sopenharmony_ci /* 178262306a36Sopenharmony_ci * If we don't have build-ids or the build-id file isn't in the 178362306a36Sopenharmony_ci * cache, or is just a kallsyms file, well, lets hope that this 178462306a36Sopenharmony_ci * DSO is the same as when 'perf record' ran. 178562306a36Sopenharmony_ci */ 178662306a36Sopenharmony_ci if (dso->kernel && dso->long_name[0] == '/') 178762306a36Sopenharmony_ci snprintf(filename, filename_size, "%s", dso->long_name); 178862306a36Sopenharmony_ci else 178962306a36Sopenharmony_ci __symbol__join_symfs(filename, filename_size, dso->long_name); 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci mutex_lock(&dso->lock); 179262306a36Sopenharmony_ci if (access(filename, R_OK) && errno == ENOENT && dso->nsinfo) { 179362306a36Sopenharmony_ci char *new_name = dso__filename_with_chroot(dso, filename); 179462306a36Sopenharmony_ci if (new_name) { 179562306a36Sopenharmony_ci strlcpy(filename, new_name, filename_size); 179662306a36Sopenharmony_ci free(new_name); 179762306a36Sopenharmony_ci } 179862306a36Sopenharmony_ci } 179962306a36Sopenharmony_ci mutex_unlock(&dso->lock); 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci free(build_id_path); 180362306a36Sopenharmony_ci return 0; 180462306a36Sopenharmony_ci} 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci#if defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) 180762306a36Sopenharmony_ci#define PACKAGE "perf" 180862306a36Sopenharmony_ci#include <bfd.h> 180962306a36Sopenharmony_ci#include <dis-asm.h> 181062306a36Sopenharmony_ci#include <bpf/bpf.h> 181162306a36Sopenharmony_ci#include <bpf/btf.h> 181262306a36Sopenharmony_ci#include <bpf/libbpf.h> 181362306a36Sopenharmony_ci#include <linux/btf.h> 181462306a36Sopenharmony_ci#include <tools/dis-asm-compat.h> 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_cistatic int symbol__disassemble_bpf(struct symbol *sym, 181762306a36Sopenharmony_ci struct annotate_args *args) 181862306a36Sopenharmony_ci{ 181962306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 182062306a36Sopenharmony_ci struct annotation_options *opts = args->options; 182162306a36Sopenharmony_ci struct bpf_prog_linfo *prog_linfo = NULL; 182262306a36Sopenharmony_ci struct bpf_prog_info_node *info_node; 182362306a36Sopenharmony_ci int len = sym->end - sym->start; 182462306a36Sopenharmony_ci disassembler_ftype disassemble; 182562306a36Sopenharmony_ci struct map *map = args->ms.map; 182662306a36Sopenharmony_ci struct perf_bpil *info_linear; 182762306a36Sopenharmony_ci struct disassemble_info info; 182862306a36Sopenharmony_ci struct dso *dso = map__dso(map); 182962306a36Sopenharmony_ci int pc = 0, count, sub_id; 183062306a36Sopenharmony_ci struct btf *btf = NULL; 183162306a36Sopenharmony_ci char tpath[PATH_MAX]; 183262306a36Sopenharmony_ci size_t buf_size; 183362306a36Sopenharmony_ci int nr_skip = 0; 183462306a36Sopenharmony_ci char *buf; 183562306a36Sopenharmony_ci bfd *bfdf; 183662306a36Sopenharmony_ci int ret; 183762306a36Sopenharmony_ci FILE *s; 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci if (dso->binary_type != DSO_BINARY_TYPE__BPF_PROG_INFO) 184062306a36Sopenharmony_ci return SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE; 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci pr_debug("%s: handling sym %s addr %" PRIx64 " len %" PRIx64 "\n", __func__, 184362306a36Sopenharmony_ci sym->name, sym->start, sym->end - sym->start); 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci memset(tpath, 0, sizeof(tpath)); 184662306a36Sopenharmony_ci perf_exe(tpath, sizeof(tpath)); 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci bfdf = bfd_openr(tpath, NULL); 184962306a36Sopenharmony_ci if (bfdf == NULL) 185062306a36Sopenharmony_ci abort(); 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci if (!bfd_check_format(bfdf, bfd_object)) 185362306a36Sopenharmony_ci abort(); 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci s = open_memstream(&buf, &buf_size); 185662306a36Sopenharmony_ci if (!s) { 185762306a36Sopenharmony_ci ret = errno; 185862306a36Sopenharmony_ci goto out; 185962306a36Sopenharmony_ci } 186062306a36Sopenharmony_ci init_disassemble_info_compat(&info, s, 186162306a36Sopenharmony_ci (fprintf_ftype) fprintf, 186262306a36Sopenharmony_ci fprintf_styled); 186362306a36Sopenharmony_ci info.arch = bfd_get_arch(bfdf); 186462306a36Sopenharmony_ci info.mach = bfd_get_mach(bfdf); 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci info_node = perf_env__find_bpf_prog_info(dso->bpf_prog.env, 186762306a36Sopenharmony_ci dso->bpf_prog.id); 186862306a36Sopenharmony_ci if (!info_node) { 186962306a36Sopenharmony_ci ret = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF; 187062306a36Sopenharmony_ci goto out; 187162306a36Sopenharmony_ci } 187262306a36Sopenharmony_ci info_linear = info_node->info_linear; 187362306a36Sopenharmony_ci sub_id = dso->bpf_prog.sub_id; 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci info.buffer = (void *)(uintptr_t)(info_linear->info.jited_prog_insns); 187662306a36Sopenharmony_ci info.buffer_length = info_linear->info.jited_prog_len; 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci if (info_linear->info.nr_line_info) 187962306a36Sopenharmony_ci prog_linfo = bpf_prog_linfo__new(&info_linear->info); 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci if (info_linear->info.btf_id) { 188262306a36Sopenharmony_ci struct btf_node *node; 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci node = perf_env__find_btf(dso->bpf_prog.env, 188562306a36Sopenharmony_ci info_linear->info.btf_id); 188662306a36Sopenharmony_ci if (node) 188762306a36Sopenharmony_ci btf = btf__new((__u8 *)(node->data), 188862306a36Sopenharmony_ci node->data_size); 188962306a36Sopenharmony_ci } 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci disassemble_init_for_target(&info); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci#ifdef DISASM_FOUR_ARGS_SIGNATURE 189462306a36Sopenharmony_ci disassemble = disassembler(info.arch, 189562306a36Sopenharmony_ci bfd_big_endian(bfdf), 189662306a36Sopenharmony_ci info.mach, 189762306a36Sopenharmony_ci bfdf); 189862306a36Sopenharmony_ci#else 189962306a36Sopenharmony_ci disassemble = disassembler(bfdf); 190062306a36Sopenharmony_ci#endif 190162306a36Sopenharmony_ci if (disassemble == NULL) 190262306a36Sopenharmony_ci abort(); 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci fflush(s); 190562306a36Sopenharmony_ci do { 190662306a36Sopenharmony_ci const struct bpf_line_info *linfo = NULL; 190762306a36Sopenharmony_ci struct disasm_line *dl; 190862306a36Sopenharmony_ci size_t prev_buf_size; 190962306a36Sopenharmony_ci const char *srcline; 191062306a36Sopenharmony_ci u64 addr; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci addr = pc + ((u64 *)(uintptr_t)(info_linear->info.jited_ksyms))[sub_id]; 191362306a36Sopenharmony_ci count = disassemble(pc, &info); 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci if (prog_linfo) 191662306a36Sopenharmony_ci linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo, 191762306a36Sopenharmony_ci addr, sub_id, 191862306a36Sopenharmony_ci nr_skip); 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci if (linfo && btf) { 192162306a36Sopenharmony_ci srcline = btf__name_by_offset(btf, linfo->line_off); 192262306a36Sopenharmony_ci nr_skip++; 192362306a36Sopenharmony_ci } else 192462306a36Sopenharmony_ci srcline = NULL; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci fprintf(s, "\n"); 192762306a36Sopenharmony_ci prev_buf_size = buf_size; 192862306a36Sopenharmony_ci fflush(s); 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci if (!opts->hide_src_code && srcline) { 193162306a36Sopenharmony_ci args->offset = -1; 193262306a36Sopenharmony_ci args->line = strdup(srcline); 193362306a36Sopenharmony_ci args->line_nr = 0; 193462306a36Sopenharmony_ci args->fileloc = NULL; 193562306a36Sopenharmony_ci args->ms.sym = sym; 193662306a36Sopenharmony_ci dl = disasm_line__new(args); 193762306a36Sopenharmony_ci if (dl) { 193862306a36Sopenharmony_ci annotation_line__add(&dl->al, 193962306a36Sopenharmony_ci ¬es->src->source); 194062306a36Sopenharmony_ci } 194162306a36Sopenharmony_ci } 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci args->offset = pc; 194462306a36Sopenharmony_ci args->line = buf + prev_buf_size; 194562306a36Sopenharmony_ci args->line_nr = 0; 194662306a36Sopenharmony_ci args->fileloc = NULL; 194762306a36Sopenharmony_ci args->ms.sym = sym; 194862306a36Sopenharmony_ci dl = disasm_line__new(args); 194962306a36Sopenharmony_ci if (dl) 195062306a36Sopenharmony_ci annotation_line__add(&dl->al, ¬es->src->source); 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci pc += count; 195362306a36Sopenharmony_ci } while (count > 0 && pc < len); 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci ret = 0; 195662306a36Sopenharmony_ciout: 195762306a36Sopenharmony_ci free(prog_linfo); 195862306a36Sopenharmony_ci btf__free(btf); 195962306a36Sopenharmony_ci fclose(s); 196062306a36Sopenharmony_ci bfd_close(bfdf); 196162306a36Sopenharmony_ci return ret; 196262306a36Sopenharmony_ci} 196362306a36Sopenharmony_ci#else // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) 196462306a36Sopenharmony_cistatic int symbol__disassemble_bpf(struct symbol *sym __maybe_unused, 196562306a36Sopenharmony_ci struct annotate_args *args __maybe_unused) 196662306a36Sopenharmony_ci{ 196762306a36Sopenharmony_ci return SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF; 196862306a36Sopenharmony_ci} 196962306a36Sopenharmony_ci#endif // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_cistatic int 197262306a36Sopenharmony_cisymbol__disassemble_bpf_image(struct symbol *sym, 197362306a36Sopenharmony_ci struct annotate_args *args) 197462306a36Sopenharmony_ci{ 197562306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 197662306a36Sopenharmony_ci struct disasm_line *dl; 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci args->offset = -1; 197962306a36Sopenharmony_ci args->line = strdup("to be implemented"); 198062306a36Sopenharmony_ci args->line_nr = 0; 198162306a36Sopenharmony_ci args->fileloc = NULL; 198262306a36Sopenharmony_ci dl = disasm_line__new(args); 198362306a36Sopenharmony_ci if (dl) 198462306a36Sopenharmony_ci annotation_line__add(&dl->al, ¬es->src->source); 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci zfree(&args->line); 198762306a36Sopenharmony_ci return 0; 198862306a36Sopenharmony_ci} 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci/* 199162306a36Sopenharmony_ci * Possibly create a new version of line with tabs expanded. Returns the 199262306a36Sopenharmony_ci * existing or new line, storage is updated if a new line is allocated. If 199362306a36Sopenharmony_ci * allocation fails then NULL is returned. 199462306a36Sopenharmony_ci */ 199562306a36Sopenharmony_cistatic char *expand_tabs(char *line, char **storage, size_t *storage_len) 199662306a36Sopenharmony_ci{ 199762306a36Sopenharmony_ci size_t i, src, dst, len, new_storage_len, num_tabs; 199862306a36Sopenharmony_ci char *new_line; 199962306a36Sopenharmony_ci size_t line_len = strlen(line); 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci for (num_tabs = 0, i = 0; i < line_len; i++) 200262306a36Sopenharmony_ci if (line[i] == '\t') 200362306a36Sopenharmony_ci num_tabs++; 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci if (num_tabs == 0) 200662306a36Sopenharmony_ci return line; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci /* 200962306a36Sopenharmony_ci * Space for the line and '\0', less the leading and trailing 201062306a36Sopenharmony_ci * spaces. Each tab may introduce 7 additional spaces. 201162306a36Sopenharmony_ci */ 201262306a36Sopenharmony_ci new_storage_len = line_len + 1 + (num_tabs * 7); 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci new_line = malloc(new_storage_len); 201562306a36Sopenharmony_ci if (new_line == NULL) { 201662306a36Sopenharmony_ci pr_err("Failure allocating memory for tab expansion\n"); 201762306a36Sopenharmony_ci return NULL; 201862306a36Sopenharmony_ci } 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci /* 202162306a36Sopenharmony_ci * Copy regions starting at src and expand tabs. If there are two 202262306a36Sopenharmony_ci * adjacent tabs then 'src == i', the memcpy is of size 0 and the spaces 202362306a36Sopenharmony_ci * are inserted. 202462306a36Sopenharmony_ci */ 202562306a36Sopenharmony_ci for (i = 0, src = 0, dst = 0; i < line_len && num_tabs; i++) { 202662306a36Sopenharmony_ci if (line[i] == '\t') { 202762306a36Sopenharmony_ci len = i - src; 202862306a36Sopenharmony_ci memcpy(&new_line[dst], &line[src], len); 202962306a36Sopenharmony_ci dst += len; 203062306a36Sopenharmony_ci new_line[dst++] = ' '; 203162306a36Sopenharmony_ci while (dst % 8 != 0) 203262306a36Sopenharmony_ci new_line[dst++] = ' '; 203362306a36Sopenharmony_ci src = i + 1; 203462306a36Sopenharmony_ci num_tabs--; 203562306a36Sopenharmony_ci } 203662306a36Sopenharmony_ci } 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci /* Expand the last region. */ 203962306a36Sopenharmony_ci len = line_len - src; 204062306a36Sopenharmony_ci memcpy(&new_line[dst], &line[src], len); 204162306a36Sopenharmony_ci dst += len; 204262306a36Sopenharmony_ci new_line[dst] = '\0'; 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci free(*storage); 204562306a36Sopenharmony_ci *storage = new_line; 204662306a36Sopenharmony_ci *storage_len = new_storage_len; 204762306a36Sopenharmony_ci return new_line; 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci} 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_cistatic int symbol__disassemble(struct symbol *sym, struct annotate_args *args) 205262306a36Sopenharmony_ci{ 205362306a36Sopenharmony_ci struct annotation_options *opts = args->options; 205462306a36Sopenharmony_ci struct map *map = args->ms.map; 205562306a36Sopenharmony_ci struct dso *dso = map__dso(map); 205662306a36Sopenharmony_ci char *command; 205762306a36Sopenharmony_ci FILE *file; 205862306a36Sopenharmony_ci char symfs_filename[PATH_MAX]; 205962306a36Sopenharmony_ci struct kcore_extract kce; 206062306a36Sopenharmony_ci bool delete_extract = false; 206162306a36Sopenharmony_ci bool decomp = false; 206262306a36Sopenharmony_ci int lineno = 0; 206362306a36Sopenharmony_ci char *fileloc = NULL; 206462306a36Sopenharmony_ci int nline; 206562306a36Sopenharmony_ci char *line; 206662306a36Sopenharmony_ci size_t line_len; 206762306a36Sopenharmony_ci const char *objdump_argv[] = { 206862306a36Sopenharmony_ci "/bin/sh", 206962306a36Sopenharmony_ci "-c", 207062306a36Sopenharmony_ci NULL, /* Will be the objdump command to run. */ 207162306a36Sopenharmony_ci "--", 207262306a36Sopenharmony_ci NULL, /* Will be the symfs path. */ 207362306a36Sopenharmony_ci NULL, 207462306a36Sopenharmony_ci }; 207562306a36Sopenharmony_ci struct child_process objdump_process; 207662306a36Sopenharmony_ci int err = dso__disassemble_filename(dso, symfs_filename, sizeof(symfs_filename)); 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ci if (err) 207962306a36Sopenharmony_ci return err; 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, 208262306a36Sopenharmony_ci symfs_filename, sym->name, map__unmap_ip(map, sym->start), 208362306a36Sopenharmony_ci map__unmap_ip(map, sym->end)); 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci pr_debug("annotating [%p] %30s : [%p] %30s\n", 208662306a36Sopenharmony_ci dso, dso->long_name, sym, sym->name); 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci if (dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO) { 208962306a36Sopenharmony_ci return symbol__disassemble_bpf(sym, args); 209062306a36Sopenharmony_ci } else if (dso->binary_type == DSO_BINARY_TYPE__BPF_IMAGE) { 209162306a36Sopenharmony_ci return symbol__disassemble_bpf_image(sym, args); 209262306a36Sopenharmony_ci } else if (dso__is_kcore(dso)) { 209362306a36Sopenharmony_ci kce.kcore_filename = symfs_filename; 209462306a36Sopenharmony_ci kce.addr = map__rip_2objdump(map, sym->start); 209562306a36Sopenharmony_ci kce.offs = sym->start; 209662306a36Sopenharmony_ci kce.len = sym->end - sym->start; 209762306a36Sopenharmony_ci if (!kcore_extract__create(&kce)) { 209862306a36Sopenharmony_ci delete_extract = true; 209962306a36Sopenharmony_ci strlcpy(symfs_filename, kce.extract_filename, 210062306a36Sopenharmony_ci sizeof(symfs_filename)); 210162306a36Sopenharmony_ci } 210262306a36Sopenharmony_ci } else if (dso__needs_decompress(dso)) { 210362306a36Sopenharmony_ci char tmp[KMOD_DECOMP_LEN]; 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci if (dso__decompress_kmodule_path(dso, symfs_filename, 210662306a36Sopenharmony_ci tmp, sizeof(tmp)) < 0) 210762306a36Sopenharmony_ci return -1; 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci decomp = true; 211062306a36Sopenharmony_ci strcpy(symfs_filename, tmp); 211162306a36Sopenharmony_ci } 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci err = asprintf(&command, 211462306a36Sopenharmony_ci "%s %s%s --start-address=0x%016" PRIx64 211562306a36Sopenharmony_ci " --stop-address=0x%016" PRIx64 211662306a36Sopenharmony_ci " -l -d %s %s %s %c%s%c %s%s -C \"$1\"", 211762306a36Sopenharmony_ci opts->objdump_path ?: "objdump", 211862306a36Sopenharmony_ci opts->disassembler_style ? "-M " : "", 211962306a36Sopenharmony_ci opts->disassembler_style ?: "", 212062306a36Sopenharmony_ci map__rip_2objdump(map, sym->start), 212162306a36Sopenharmony_ci map__rip_2objdump(map, sym->end), 212262306a36Sopenharmony_ci opts->show_asm_raw ? "" : "--no-show-raw-insn", 212362306a36Sopenharmony_ci opts->annotate_src ? "-S" : "", 212462306a36Sopenharmony_ci opts->prefix ? "--prefix " : "", 212562306a36Sopenharmony_ci opts->prefix ? '"' : ' ', 212662306a36Sopenharmony_ci opts->prefix ?: "", 212762306a36Sopenharmony_ci opts->prefix ? '"' : ' ', 212862306a36Sopenharmony_ci opts->prefix_strip ? "--prefix-strip=" : "", 212962306a36Sopenharmony_ci opts->prefix_strip ?: ""); 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci if (err < 0) { 213262306a36Sopenharmony_ci pr_err("Failure allocating memory for the command to run\n"); 213362306a36Sopenharmony_ci goto out_remove_tmp; 213462306a36Sopenharmony_ci } 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci pr_debug("Executing: %s\n", command); 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci objdump_argv[2] = command; 213962306a36Sopenharmony_ci objdump_argv[4] = symfs_filename; 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci /* Create a pipe to read from for stdout */ 214262306a36Sopenharmony_ci memset(&objdump_process, 0, sizeof(objdump_process)); 214362306a36Sopenharmony_ci objdump_process.argv = objdump_argv; 214462306a36Sopenharmony_ci objdump_process.out = -1; 214562306a36Sopenharmony_ci objdump_process.err = -1; 214662306a36Sopenharmony_ci objdump_process.no_stderr = 1; 214762306a36Sopenharmony_ci if (start_command(&objdump_process)) { 214862306a36Sopenharmony_ci pr_err("Failure starting to run %s\n", command); 214962306a36Sopenharmony_ci err = -1; 215062306a36Sopenharmony_ci goto out_free_command; 215162306a36Sopenharmony_ci } 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci file = fdopen(objdump_process.out, "r"); 215462306a36Sopenharmony_ci if (!file) { 215562306a36Sopenharmony_ci pr_err("Failure creating FILE stream for %s\n", command); 215662306a36Sopenharmony_ci /* 215762306a36Sopenharmony_ci * If we were using debug info should retry with 215862306a36Sopenharmony_ci * original binary. 215962306a36Sopenharmony_ci */ 216062306a36Sopenharmony_ci err = -1; 216162306a36Sopenharmony_ci goto out_close_stdout; 216262306a36Sopenharmony_ci } 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci /* Storage for getline. */ 216562306a36Sopenharmony_ci line = NULL; 216662306a36Sopenharmony_ci line_len = 0; 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci nline = 0; 216962306a36Sopenharmony_ci while (!feof(file)) { 217062306a36Sopenharmony_ci const char *match; 217162306a36Sopenharmony_ci char *expanded_line; 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci if (getline(&line, &line_len, file) < 0 || !line) 217462306a36Sopenharmony_ci break; 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci /* Skip lines containing "filename:" */ 217762306a36Sopenharmony_ci match = strstr(line, symfs_filename); 217862306a36Sopenharmony_ci if (match && match[strlen(symfs_filename)] == ':') 217962306a36Sopenharmony_ci continue; 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci expanded_line = strim(line); 218262306a36Sopenharmony_ci expanded_line = expand_tabs(expanded_line, &line, &line_len); 218362306a36Sopenharmony_ci if (!expanded_line) 218462306a36Sopenharmony_ci break; 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci /* 218762306a36Sopenharmony_ci * The source code line number (lineno) needs to be kept in 218862306a36Sopenharmony_ci * across calls to symbol__parse_objdump_line(), so that it 218962306a36Sopenharmony_ci * can associate it with the instructions till the next one. 219062306a36Sopenharmony_ci * See disasm_line__new() and struct disasm_line::line_nr. 219162306a36Sopenharmony_ci */ 219262306a36Sopenharmony_ci if (symbol__parse_objdump_line(sym, args, expanded_line, 219362306a36Sopenharmony_ci &lineno, &fileloc) < 0) 219462306a36Sopenharmony_ci break; 219562306a36Sopenharmony_ci nline++; 219662306a36Sopenharmony_ci } 219762306a36Sopenharmony_ci free(line); 219862306a36Sopenharmony_ci free(fileloc); 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci err = finish_command(&objdump_process); 220162306a36Sopenharmony_ci if (err) 220262306a36Sopenharmony_ci pr_err("Error running %s\n", command); 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci if (nline == 0) { 220562306a36Sopenharmony_ci err = -1; 220662306a36Sopenharmony_ci pr_err("No output from %s\n", command); 220762306a36Sopenharmony_ci } 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci /* 221062306a36Sopenharmony_ci * kallsyms does not have symbol sizes so there may a nop at the end. 221162306a36Sopenharmony_ci * Remove it. 221262306a36Sopenharmony_ci */ 221362306a36Sopenharmony_ci if (dso__is_kcore(dso)) 221462306a36Sopenharmony_ci delete_last_nop(sym); 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci fclose(file); 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ciout_close_stdout: 221962306a36Sopenharmony_ci close(objdump_process.out); 222062306a36Sopenharmony_ci 222162306a36Sopenharmony_ciout_free_command: 222262306a36Sopenharmony_ci free(command); 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ciout_remove_tmp: 222562306a36Sopenharmony_ci if (decomp) 222662306a36Sopenharmony_ci unlink(symfs_filename); 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci if (delete_extract) 222962306a36Sopenharmony_ci kcore_extract__delete(&kce); 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci return err; 223262306a36Sopenharmony_ci} 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_cistatic void calc_percent(struct sym_hist *sym_hist, 223562306a36Sopenharmony_ci struct hists *hists, 223662306a36Sopenharmony_ci struct annotation_data *data, 223762306a36Sopenharmony_ci s64 offset, s64 end) 223862306a36Sopenharmony_ci{ 223962306a36Sopenharmony_ci unsigned int hits = 0; 224062306a36Sopenharmony_ci u64 period = 0; 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci while (offset < end) { 224362306a36Sopenharmony_ci hits += sym_hist->addr[offset].nr_samples; 224462306a36Sopenharmony_ci period += sym_hist->addr[offset].period; 224562306a36Sopenharmony_ci ++offset; 224662306a36Sopenharmony_ci } 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci if (sym_hist->nr_samples) { 224962306a36Sopenharmony_ci data->he.period = period; 225062306a36Sopenharmony_ci data->he.nr_samples = hits; 225162306a36Sopenharmony_ci data->percent[PERCENT_HITS_LOCAL] = 100.0 * hits / sym_hist->nr_samples; 225262306a36Sopenharmony_ci } 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci if (hists->stats.nr_non_filtered_samples) 225562306a36Sopenharmony_ci data->percent[PERCENT_HITS_GLOBAL] = 100.0 * hits / hists->stats.nr_non_filtered_samples; 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci if (sym_hist->period) 225862306a36Sopenharmony_ci data->percent[PERCENT_PERIOD_LOCAL] = 100.0 * period / sym_hist->period; 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci if (hists->stats.total_period) 226162306a36Sopenharmony_ci data->percent[PERCENT_PERIOD_GLOBAL] = 100.0 * period / hists->stats.total_period; 226262306a36Sopenharmony_ci} 226362306a36Sopenharmony_ci 226462306a36Sopenharmony_cistatic void annotation__calc_percent(struct annotation *notes, 226562306a36Sopenharmony_ci struct evsel *leader, s64 len) 226662306a36Sopenharmony_ci{ 226762306a36Sopenharmony_ci struct annotation_line *al, *next; 226862306a36Sopenharmony_ci struct evsel *evsel; 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci list_for_each_entry(al, ¬es->src->source, node) { 227162306a36Sopenharmony_ci s64 end; 227262306a36Sopenharmony_ci int i = 0; 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci if (al->offset == -1) 227562306a36Sopenharmony_ci continue; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci next = annotation_line__next(al, ¬es->src->source); 227862306a36Sopenharmony_ci end = next ? next->offset : len; 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci for_each_group_evsel(evsel, leader) { 228162306a36Sopenharmony_ci struct hists *hists = evsel__hists(evsel); 228262306a36Sopenharmony_ci struct annotation_data *data; 228362306a36Sopenharmony_ci struct sym_hist *sym_hist; 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ci BUG_ON(i >= al->data_nr); 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci sym_hist = annotation__histogram(notes, evsel->core.idx); 228862306a36Sopenharmony_ci data = &al->data[i++]; 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci calc_percent(sym_hist, hists, data, al->offset, end); 229162306a36Sopenharmony_ci } 229262306a36Sopenharmony_ci } 229362306a36Sopenharmony_ci} 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_civoid symbol__calc_percent(struct symbol *sym, struct evsel *evsel) 229662306a36Sopenharmony_ci{ 229762306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci annotation__calc_percent(notes, evsel, symbol__size(sym)); 230062306a36Sopenharmony_ci} 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_ciint symbol__annotate(struct map_symbol *ms, struct evsel *evsel, 230362306a36Sopenharmony_ci struct annotation_options *options, struct arch **parch) 230462306a36Sopenharmony_ci{ 230562306a36Sopenharmony_ci struct symbol *sym = ms->sym; 230662306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 230762306a36Sopenharmony_ci struct annotate_args args = { 230862306a36Sopenharmony_ci .evsel = evsel, 230962306a36Sopenharmony_ci .options = options, 231062306a36Sopenharmony_ci }; 231162306a36Sopenharmony_ci struct perf_env *env = evsel__env(evsel); 231262306a36Sopenharmony_ci const char *arch_name = perf_env__arch(env); 231362306a36Sopenharmony_ci struct arch *arch; 231462306a36Sopenharmony_ci int err; 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci if (!arch_name) 231762306a36Sopenharmony_ci return errno; 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci args.arch = arch = arch__find(arch_name); 232062306a36Sopenharmony_ci if (arch == NULL) { 232162306a36Sopenharmony_ci pr_err("%s: unsupported arch %s\n", __func__, arch_name); 232262306a36Sopenharmony_ci return ENOTSUP; 232362306a36Sopenharmony_ci } 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci if (parch) 232662306a36Sopenharmony_ci *parch = arch; 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_ci if (arch->init) { 232962306a36Sopenharmony_ci err = arch->init(arch, env ? env->cpuid : NULL); 233062306a36Sopenharmony_ci if (err) { 233162306a36Sopenharmony_ci pr_err("%s: failed to initialize %s arch priv area\n", __func__, arch->name); 233262306a36Sopenharmony_ci return err; 233362306a36Sopenharmony_ci } 233462306a36Sopenharmony_ci } 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci args.ms = *ms; 233762306a36Sopenharmony_ci if (notes->options && notes->options->full_addr) 233862306a36Sopenharmony_ci notes->start = map__objdump_2mem(ms->map, ms->sym->start); 233962306a36Sopenharmony_ci else 234062306a36Sopenharmony_ci notes->start = map__rip_2objdump(ms->map, ms->sym->start); 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci return symbol__disassemble(sym, &args); 234362306a36Sopenharmony_ci} 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_cistatic void insert_source_line(struct rb_root *root, struct annotation_line *al, 234662306a36Sopenharmony_ci struct annotation_options *opts) 234762306a36Sopenharmony_ci{ 234862306a36Sopenharmony_ci struct annotation_line *iter; 234962306a36Sopenharmony_ci struct rb_node **p = &root->rb_node; 235062306a36Sopenharmony_ci struct rb_node *parent = NULL; 235162306a36Sopenharmony_ci int i, ret; 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci while (*p != NULL) { 235462306a36Sopenharmony_ci parent = *p; 235562306a36Sopenharmony_ci iter = rb_entry(parent, struct annotation_line, rb_node); 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci ret = strcmp(iter->path, al->path); 235862306a36Sopenharmony_ci if (ret == 0) { 235962306a36Sopenharmony_ci for (i = 0; i < al->data_nr; i++) { 236062306a36Sopenharmony_ci iter->data[i].percent_sum += annotation_data__percent(&al->data[i], 236162306a36Sopenharmony_ci opts->percent_type); 236262306a36Sopenharmony_ci } 236362306a36Sopenharmony_ci return; 236462306a36Sopenharmony_ci } 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci if (ret < 0) 236762306a36Sopenharmony_ci p = &(*p)->rb_left; 236862306a36Sopenharmony_ci else 236962306a36Sopenharmony_ci p = &(*p)->rb_right; 237062306a36Sopenharmony_ci } 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_ci for (i = 0; i < al->data_nr; i++) { 237362306a36Sopenharmony_ci al->data[i].percent_sum = annotation_data__percent(&al->data[i], 237462306a36Sopenharmony_ci opts->percent_type); 237562306a36Sopenharmony_ci } 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci rb_link_node(&al->rb_node, parent, p); 237862306a36Sopenharmony_ci rb_insert_color(&al->rb_node, root); 237962306a36Sopenharmony_ci} 238062306a36Sopenharmony_ci 238162306a36Sopenharmony_cistatic int cmp_source_line(struct annotation_line *a, struct annotation_line *b) 238262306a36Sopenharmony_ci{ 238362306a36Sopenharmony_ci int i; 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci for (i = 0; i < a->data_nr; i++) { 238662306a36Sopenharmony_ci if (a->data[i].percent_sum == b->data[i].percent_sum) 238762306a36Sopenharmony_ci continue; 238862306a36Sopenharmony_ci return a->data[i].percent_sum > b->data[i].percent_sum; 238962306a36Sopenharmony_ci } 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ci return 0; 239262306a36Sopenharmony_ci} 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_cistatic void __resort_source_line(struct rb_root *root, struct annotation_line *al) 239562306a36Sopenharmony_ci{ 239662306a36Sopenharmony_ci struct annotation_line *iter; 239762306a36Sopenharmony_ci struct rb_node **p = &root->rb_node; 239862306a36Sopenharmony_ci struct rb_node *parent = NULL; 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_ci while (*p != NULL) { 240162306a36Sopenharmony_ci parent = *p; 240262306a36Sopenharmony_ci iter = rb_entry(parent, struct annotation_line, rb_node); 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci if (cmp_source_line(al, iter)) 240562306a36Sopenharmony_ci p = &(*p)->rb_left; 240662306a36Sopenharmony_ci else 240762306a36Sopenharmony_ci p = &(*p)->rb_right; 240862306a36Sopenharmony_ci } 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ci rb_link_node(&al->rb_node, parent, p); 241162306a36Sopenharmony_ci rb_insert_color(&al->rb_node, root); 241262306a36Sopenharmony_ci} 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_cistatic void resort_source_line(struct rb_root *dest_root, struct rb_root *src_root) 241562306a36Sopenharmony_ci{ 241662306a36Sopenharmony_ci struct annotation_line *al; 241762306a36Sopenharmony_ci struct rb_node *node; 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci node = rb_first(src_root); 242062306a36Sopenharmony_ci while (node) { 242162306a36Sopenharmony_ci struct rb_node *next; 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci al = rb_entry(node, struct annotation_line, rb_node); 242462306a36Sopenharmony_ci next = rb_next(node); 242562306a36Sopenharmony_ci rb_erase(node, src_root); 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci __resort_source_line(dest_root, al); 242862306a36Sopenharmony_ci node = next; 242962306a36Sopenharmony_ci } 243062306a36Sopenharmony_ci} 243162306a36Sopenharmony_ci 243262306a36Sopenharmony_cistatic void print_summary(struct rb_root *root, const char *filename) 243362306a36Sopenharmony_ci{ 243462306a36Sopenharmony_ci struct annotation_line *al; 243562306a36Sopenharmony_ci struct rb_node *node; 243662306a36Sopenharmony_ci 243762306a36Sopenharmony_ci printf("\nSorted summary for file %s\n", filename); 243862306a36Sopenharmony_ci printf("----------------------------------------------\n\n"); 243962306a36Sopenharmony_ci 244062306a36Sopenharmony_ci if (RB_EMPTY_ROOT(root)) { 244162306a36Sopenharmony_ci printf(" Nothing higher than %1.1f%%\n", MIN_GREEN); 244262306a36Sopenharmony_ci return; 244362306a36Sopenharmony_ci } 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci node = rb_first(root); 244662306a36Sopenharmony_ci while (node) { 244762306a36Sopenharmony_ci double percent, percent_max = 0.0; 244862306a36Sopenharmony_ci const char *color; 244962306a36Sopenharmony_ci char *path; 245062306a36Sopenharmony_ci int i; 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci al = rb_entry(node, struct annotation_line, rb_node); 245362306a36Sopenharmony_ci for (i = 0; i < al->data_nr; i++) { 245462306a36Sopenharmony_ci percent = al->data[i].percent_sum; 245562306a36Sopenharmony_ci color = get_percent_color(percent); 245662306a36Sopenharmony_ci color_fprintf(stdout, color, " %7.2f", percent); 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci if (percent > percent_max) 245962306a36Sopenharmony_ci percent_max = percent; 246062306a36Sopenharmony_ci } 246162306a36Sopenharmony_ci 246262306a36Sopenharmony_ci path = al->path; 246362306a36Sopenharmony_ci color = get_percent_color(percent_max); 246462306a36Sopenharmony_ci color_fprintf(stdout, color, " %s\n", path); 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci node = rb_next(node); 246762306a36Sopenharmony_ci } 246862306a36Sopenharmony_ci} 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_cistatic void symbol__annotate_hits(struct symbol *sym, struct evsel *evsel) 247162306a36Sopenharmony_ci{ 247262306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 247362306a36Sopenharmony_ci struct sym_hist *h = annotation__histogram(notes, evsel->core.idx); 247462306a36Sopenharmony_ci u64 len = symbol__size(sym), offset; 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci for (offset = 0; offset < len; ++offset) 247762306a36Sopenharmony_ci if (h->addr[offset].nr_samples != 0) 247862306a36Sopenharmony_ci printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2, 247962306a36Sopenharmony_ci sym->start + offset, h->addr[offset].nr_samples); 248062306a36Sopenharmony_ci printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->nr_samples", h->nr_samples); 248162306a36Sopenharmony_ci} 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_cistatic int annotated_source__addr_fmt_width(struct list_head *lines, u64 start) 248462306a36Sopenharmony_ci{ 248562306a36Sopenharmony_ci char bf[32]; 248662306a36Sopenharmony_ci struct annotation_line *line; 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_ci list_for_each_entry_reverse(line, lines, node) { 248962306a36Sopenharmony_ci if (line->offset != -1) 249062306a36Sopenharmony_ci return scnprintf(bf, sizeof(bf), "%" PRIx64, start + line->offset); 249162306a36Sopenharmony_ci } 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci return 0; 249462306a36Sopenharmony_ci} 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ciint symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel, 249762306a36Sopenharmony_ci struct annotation_options *opts) 249862306a36Sopenharmony_ci{ 249962306a36Sopenharmony_ci struct map *map = ms->map; 250062306a36Sopenharmony_ci struct symbol *sym = ms->sym; 250162306a36Sopenharmony_ci struct dso *dso = map__dso(map); 250262306a36Sopenharmony_ci char *filename; 250362306a36Sopenharmony_ci const char *d_filename; 250462306a36Sopenharmony_ci const char *evsel_name = evsel__name(evsel); 250562306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 250662306a36Sopenharmony_ci struct sym_hist *h = annotation__histogram(notes, evsel->core.idx); 250762306a36Sopenharmony_ci struct annotation_line *pos, *queue = NULL; 250862306a36Sopenharmony_ci u64 start = map__rip_2objdump(map, sym->start); 250962306a36Sopenharmony_ci int printed = 2, queue_len = 0, addr_fmt_width; 251062306a36Sopenharmony_ci int more = 0; 251162306a36Sopenharmony_ci bool context = opts->context; 251262306a36Sopenharmony_ci u64 len; 251362306a36Sopenharmony_ci int width = symbol_conf.show_total_period ? 12 : 8; 251462306a36Sopenharmony_ci int graph_dotted_len; 251562306a36Sopenharmony_ci char buf[512]; 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_ci filename = strdup(dso->long_name); 251862306a36Sopenharmony_ci if (!filename) 251962306a36Sopenharmony_ci return -ENOMEM; 252062306a36Sopenharmony_ci 252162306a36Sopenharmony_ci if (opts->full_path) 252262306a36Sopenharmony_ci d_filename = filename; 252362306a36Sopenharmony_ci else 252462306a36Sopenharmony_ci d_filename = basename(filename); 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci len = symbol__size(sym); 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci if (evsel__is_group_event(evsel)) { 252962306a36Sopenharmony_ci width *= evsel->core.nr_members; 253062306a36Sopenharmony_ci evsel__group_desc(evsel, buf, sizeof(buf)); 253162306a36Sopenharmony_ci evsel_name = buf; 253262306a36Sopenharmony_ci } 253362306a36Sopenharmony_ci 253462306a36Sopenharmony_ci graph_dotted_len = printf(" %-*.*s| Source code & Disassembly of %s for %s (%" PRIu64 " samples, " 253562306a36Sopenharmony_ci "percent: %s)\n", 253662306a36Sopenharmony_ci width, width, symbol_conf.show_total_period ? "Period" : 253762306a36Sopenharmony_ci symbol_conf.show_nr_samples ? "Samples" : "Percent", 253862306a36Sopenharmony_ci d_filename, evsel_name, h->nr_samples, 253962306a36Sopenharmony_ci percent_type_str(opts->percent_type)); 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci printf("%-*.*s----\n", 254262306a36Sopenharmony_ci graph_dotted_len, graph_dotted_len, graph_dotted_line); 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci if (verbose > 0) 254562306a36Sopenharmony_ci symbol__annotate_hits(sym, evsel); 254662306a36Sopenharmony_ci 254762306a36Sopenharmony_ci addr_fmt_width = annotated_source__addr_fmt_width(¬es->src->source, start); 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_ci list_for_each_entry(pos, ¬es->src->source, node) { 255062306a36Sopenharmony_ci int err; 255162306a36Sopenharmony_ci 255262306a36Sopenharmony_ci if (context && queue == NULL) { 255362306a36Sopenharmony_ci queue = pos; 255462306a36Sopenharmony_ci queue_len = 0; 255562306a36Sopenharmony_ci } 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci err = annotation_line__print(pos, sym, start, evsel, len, 255862306a36Sopenharmony_ci opts->min_pcnt, printed, opts->max_lines, 255962306a36Sopenharmony_ci queue, addr_fmt_width, opts->percent_type); 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci switch (err) { 256262306a36Sopenharmony_ci case 0: 256362306a36Sopenharmony_ci ++printed; 256462306a36Sopenharmony_ci if (context) { 256562306a36Sopenharmony_ci printed += queue_len; 256662306a36Sopenharmony_ci queue = NULL; 256762306a36Sopenharmony_ci queue_len = 0; 256862306a36Sopenharmony_ci } 256962306a36Sopenharmony_ci break; 257062306a36Sopenharmony_ci case 1: 257162306a36Sopenharmony_ci /* filtered by max_lines */ 257262306a36Sopenharmony_ci ++more; 257362306a36Sopenharmony_ci break; 257462306a36Sopenharmony_ci case -1: 257562306a36Sopenharmony_ci default: 257662306a36Sopenharmony_ci /* 257762306a36Sopenharmony_ci * Filtered by min_pcnt or non IP lines when 257862306a36Sopenharmony_ci * context != 0 257962306a36Sopenharmony_ci */ 258062306a36Sopenharmony_ci if (!context) 258162306a36Sopenharmony_ci break; 258262306a36Sopenharmony_ci if (queue_len == context) 258362306a36Sopenharmony_ci queue = list_entry(queue->node.next, typeof(*queue), node); 258462306a36Sopenharmony_ci else 258562306a36Sopenharmony_ci ++queue_len; 258662306a36Sopenharmony_ci break; 258762306a36Sopenharmony_ci } 258862306a36Sopenharmony_ci } 258962306a36Sopenharmony_ci 259062306a36Sopenharmony_ci free(filename); 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ci return more; 259362306a36Sopenharmony_ci} 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_cistatic void FILE__set_percent_color(void *fp __maybe_unused, 259662306a36Sopenharmony_ci double percent __maybe_unused, 259762306a36Sopenharmony_ci bool current __maybe_unused) 259862306a36Sopenharmony_ci{ 259962306a36Sopenharmony_ci} 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_cistatic int FILE__set_jumps_percent_color(void *fp __maybe_unused, 260262306a36Sopenharmony_ci int nr __maybe_unused, bool current __maybe_unused) 260362306a36Sopenharmony_ci{ 260462306a36Sopenharmony_ci return 0; 260562306a36Sopenharmony_ci} 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_cistatic int FILE__set_color(void *fp __maybe_unused, int color __maybe_unused) 260862306a36Sopenharmony_ci{ 260962306a36Sopenharmony_ci return 0; 261062306a36Sopenharmony_ci} 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_cistatic void FILE__printf(void *fp, const char *fmt, ...) 261362306a36Sopenharmony_ci{ 261462306a36Sopenharmony_ci va_list args; 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci va_start(args, fmt); 261762306a36Sopenharmony_ci vfprintf(fp, fmt, args); 261862306a36Sopenharmony_ci va_end(args); 261962306a36Sopenharmony_ci} 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_cistatic void FILE__write_graph(void *fp, int graph) 262262306a36Sopenharmony_ci{ 262362306a36Sopenharmony_ci const char *s; 262462306a36Sopenharmony_ci switch (graph) { 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_ci case DARROW_CHAR: s = "↓"; break; 262762306a36Sopenharmony_ci case UARROW_CHAR: s = "↑"; break; 262862306a36Sopenharmony_ci case LARROW_CHAR: s = "←"; break; 262962306a36Sopenharmony_ci case RARROW_CHAR: s = "→"; break; 263062306a36Sopenharmony_ci default: s = "?"; break; 263162306a36Sopenharmony_ci } 263262306a36Sopenharmony_ci 263362306a36Sopenharmony_ci fputs(s, fp); 263462306a36Sopenharmony_ci} 263562306a36Sopenharmony_ci 263662306a36Sopenharmony_cistatic int symbol__annotate_fprintf2(struct symbol *sym, FILE *fp, 263762306a36Sopenharmony_ci struct annotation_options *opts) 263862306a36Sopenharmony_ci{ 263962306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 264062306a36Sopenharmony_ci struct annotation_write_ops wops = { 264162306a36Sopenharmony_ci .first_line = true, 264262306a36Sopenharmony_ci .obj = fp, 264362306a36Sopenharmony_ci .set_color = FILE__set_color, 264462306a36Sopenharmony_ci .set_percent_color = FILE__set_percent_color, 264562306a36Sopenharmony_ci .set_jumps_percent_color = FILE__set_jumps_percent_color, 264662306a36Sopenharmony_ci .printf = FILE__printf, 264762306a36Sopenharmony_ci .write_graph = FILE__write_graph, 264862306a36Sopenharmony_ci }; 264962306a36Sopenharmony_ci struct annotation_line *al; 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_ci list_for_each_entry(al, ¬es->src->source, node) { 265262306a36Sopenharmony_ci if (annotation_line__filter(al, notes)) 265362306a36Sopenharmony_ci continue; 265462306a36Sopenharmony_ci annotation_line__write(al, notes, &wops, opts); 265562306a36Sopenharmony_ci fputc('\n', fp); 265662306a36Sopenharmony_ci wops.first_line = false; 265762306a36Sopenharmony_ci } 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_ci return 0; 266062306a36Sopenharmony_ci} 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_ciint map_symbol__annotation_dump(struct map_symbol *ms, struct evsel *evsel, 266362306a36Sopenharmony_ci struct annotation_options *opts) 266462306a36Sopenharmony_ci{ 266562306a36Sopenharmony_ci const char *ev_name = evsel__name(evsel); 266662306a36Sopenharmony_ci char buf[1024]; 266762306a36Sopenharmony_ci char *filename; 266862306a36Sopenharmony_ci int err = -1; 266962306a36Sopenharmony_ci FILE *fp; 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_ci if (asprintf(&filename, "%s.annotation", ms->sym->name) < 0) 267262306a36Sopenharmony_ci return -1; 267362306a36Sopenharmony_ci 267462306a36Sopenharmony_ci fp = fopen(filename, "w"); 267562306a36Sopenharmony_ci if (fp == NULL) 267662306a36Sopenharmony_ci goto out_free_filename; 267762306a36Sopenharmony_ci 267862306a36Sopenharmony_ci if (evsel__is_group_event(evsel)) { 267962306a36Sopenharmony_ci evsel__group_desc(evsel, buf, sizeof(buf)); 268062306a36Sopenharmony_ci ev_name = buf; 268162306a36Sopenharmony_ci } 268262306a36Sopenharmony_ci 268362306a36Sopenharmony_ci fprintf(fp, "%s() %s\nEvent: %s\n\n", 268462306a36Sopenharmony_ci ms->sym->name, map__dso(ms->map)->long_name, ev_name); 268562306a36Sopenharmony_ci symbol__annotate_fprintf2(ms->sym, fp, opts); 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci fclose(fp); 268862306a36Sopenharmony_ci err = 0; 268962306a36Sopenharmony_ciout_free_filename: 269062306a36Sopenharmony_ci free(filename); 269162306a36Sopenharmony_ci return err; 269262306a36Sopenharmony_ci} 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_civoid symbol__annotate_zero_histogram(struct symbol *sym, int evidx) 269562306a36Sopenharmony_ci{ 269662306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 269762306a36Sopenharmony_ci struct sym_hist *h = annotation__histogram(notes, evidx); 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci memset(h, 0, notes->src->sizeof_sym_hist); 270062306a36Sopenharmony_ci} 270162306a36Sopenharmony_ci 270262306a36Sopenharmony_civoid symbol__annotate_decay_histogram(struct symbol *sym, int evidx) 270362306a36Sopenharmony_ci{ 270462306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 270562306a36Sopenharmony_ci struct sym_hist *h = annotation__histogram(notes, evidx); 270662306a36Sopenharmony_ci int len = symbol__size(sym), offset; 270762306a36Sopenharmony_ci 270862306a36Sopenharmony_ci h->nr_samples = 0; 270962306a36Sopenharmony_ci for (offset = 0; offset < len; ++offset) { 271062306a36Sopenharmony_ci h->addr[offset].nr_samples = h->addr[offset].nr_samples * 7 / 8; 271162306a36Sopenharmony_ci h->nr_samples += h->addr[offset].nr_samples; 271262306a36Sopenharmony_ci } 271362306a36Sopenharmony_ci} 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_civoid annotated_source__purge(struct annotated_source *as) 271662306a36Sopenharmony_ci{ 271762306a36Sopenharmony_ci struct annotation_line *al, *n; 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_ci list_for_each_entry_safe(al, n, &as->source, node) { 272062306a36Sopenharmony_ci list_del_init(&al->node); 272162306a36Sopenharmony_ci disasm_line__free(disasm_line(al)); 272262306a36Sopenharmony_ci } 272362306a36Sopenharmony_ci} 272462306a36Sopenharmony_ci 272562306a36Sopenharmony_cistatic size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp) 272662306a36Sopenharmony_ci{ 272762306a36Sopenharmony_ci size_t printed; 272862306a36Sopenharmony_ci 272962306a36Sopenharmony_ci if (dl->al.offset == -1) 273062306a36Sopenharmony_ci return fprintf(fp, "%s\n", dl->al.line); 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci printed = fprintf(fp, "%#" PRIx64 " %s", dl->al.offset, dl->ins.name); 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_ci if (dl->ops.raw[0] != '\0') { 273562306a36Sopenharmony_ci printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ", 273662306a36Sopenharmony_ci dl->ops.raw); 273762306a36Sopenharmony_ci } 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ci return printed + fprintf(fp, "\n"); 274062306a36Sopenharmony_ci} 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_cisize_t disasm__fprintf(struct list_head *head, FILE *fp) 274362306a36Sopenharmony_ci{ 274462306a36Sopenharmony_ci struct disasm_line *pos; 274562306a36Sopenharmony_ci size_t printed = 0; 274662306a36Sopenharmony_ci 274762306a36Sopenharmony_ci list_for_each_entry(pos, head, al.node) 274862306a36Sopenharmony_ci printed += disasm_line__fprintf(pos, fp); 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci return printed; 275162306a36Sopenharmony_ci} 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_cibool disasm_line__is_valid_local_jump(struct disasm_line *dl, struct symbol *sym) 275462306a36Sopenharmony_ci{ 275562306a36Sopenharmony_ci if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins) || 275662306a36Sopenharmony_ci !disasm_line__has_local_offset(dl) || dl->ops.target.offset < 0 || 275762306a36Sopenharmony_ci dl->ops.target.offset >= (s64)symbol__size(sym)) 275862306a36Sopenharmony_ci return false; 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci return true; 276162306a36Sopenharmony_ci} 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_civoid annotation__mark_jump_targets(struct annotation *notes, struct symbol *sym) 276462306a36Sopenharmony_ci{ 276562306a36Sopenharmony_ci u64 offset, size = symbol__size(sym); 276662306a36Sopenharmony_ci 276762306a36Sopenharmony_ci /* PLT symbols contain external offsets */ 276862306a36Sopenharmony_ci if (strstr(sym->name, "@plt")) 276962306a36Sopenharmony_ci return; 277062306a36Sopenharmony_ci 277162306a36Sopenharmony_ci for (offset = 0; offset < size; ++offset) { 277262306a36Sopenharmony_ci struct annotation_line *al = notes->offsets[offset]; 277362306a36Sopenharmony_ci struct disasm_line *dl; 277462306a36Sopenharmony_ci 277562306a36Sopenharmony_ci dl = disasm_line(al); 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_ci if (!disasm_line__is_valid_local_jump(dl, sym)) 277862306a36Sopenharmony_ci continue; 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_ci al = notes->offsets[dl->ops.target.offset]; 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_ci /* 278362306a36Sopenharmony_ci * FIXME: Oops, no jump target? Buggy disassembler? Or do we 278462306a36Sopenharmony_ci * have to adjust to the previous offset? 278562306a36Sopenharmony_ci */ 278662306a36Sopenharmony_ci if (al == NULL) 278762306a36Sopenharmony_ci continue; 278862306a36Sopenharmony_ci 278962306a36Sopenharmony_ci if (++al->jump_sources > notes->max_jump_sources) 279062306a36Sopenharmony_ci notes->max_jump_sources = al->jump_sources; 279162306a36Sopenharmony_ci } 279262306a36Sopenharmony_ci} 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_civoid annotation__set_offsets(struct annotation *notes, s64 size) 279562306a36Sopenharmony_ci{ 279662306a36Sopenharmony_ci struct annotation_line *al; 279762306a36Sopenharmony_ci 279862306a36Sopenharmony_ci notes->max_line_len = 0; 279962306a36Sopenharmony_ci notes->nr_entries = 0; 280062306a36Sopenharmony_ci notes->nr_asm_entries = 0; 280162306a36Sopenharmony_ci 280262306a36Sopenharmony_ci list_for_each_entry(al, ¬es->src->source, node) { 280362306a36Sopenharmony_ci size_t line_len = strlen(al->line); 280462306a36Sopenharmony_ci 280562306a36Sopenharmony_ci if (notes->max_line_len < line_len) 280662306a36Sopenharmony_ci notes->max_line_len = line_len; 280762306a36Sopenharmony_ci al->idx = notes->nr_entries++; 280862306a36Sopenharmony_ci if (al->offset != -1) { 280962306a36Sopenharmony_ci al->idx_asm = notes->nr_asm_entries++; 281062306a36Sopenharmony_ci /* 281162306a36Sopenharmony_ci * FIXME: short term bandaid to cope with assembly 281262306a36Sopenharmony_ci * routines that comes with labels in the same column 281362306a36Sopenharmony_ci * as the address in objdump, sigh. 281462306a36Sopenharmony_ci * 281562306a36Sopenharmony_ci * E.g. copy_user_generic_unrolled 281662306a36Sopenharmony_ci */ 281762306a36Sopenharmony_ci if (al->offset < size) 281862306a36Sopenharmony_ci notes->offsets[al->offset] = al; 281962306a36Sopenharmony_ci } else 282062306a36Sopenharmony_ci al->idx_asm = -1; 282162306a36Sopenharmony_ci } 282262306a36Sopenharmony_ci} 282362306a36Sopenharmony_ci 282462306a36Sopenharmony_cistatic inline int width_jumps(int n) 282562306a36Sopenharmony_ci{ 282662306a36Sopenharmony_ci if (n >= 100) 282762306a36Sopenharmony_ci return 5; 282862306a36Sopenharmony_ci if (n / 10) 282962306a36Sopenharmony_ci return 2; 283062306a36Sopenharmony_ci return 1; 283162306a36Sopenharmony_ci} 283262306a36Sopenharmony_ci 283362306a36Sopenharmony_cistatic int annotation__max_ins_name(struct annotation *notes) 283462306a36Sopenharmony_ci{ 283562306a36Sopenharmony_ci int max_name = 0, len; 283662306a36Sopenharmony_ci struct annotation_line *al; 283762306a36Sopenharmony_ci 283862306a36Sopenharmony_ci list_for_each_entry(al, ¬es->src->source, node) { 283962306a36Sopenharmony_ci if (al->offset == -1) 284062306a36Sopenharmony_ci continue; 284162306a36Sopenharmony_ci 284262306a36Sopenharmony_ci len = strlen(disasm_line(al)->ins.name); 284362306a36Sopenharmony_ci if (max_name < len) 284462306a36Sopenharmony_ci max_name = len; 284562306a36Sopenharmony_ci } 284662306a36Sopenharmony_ci 284762306a36Sopenharmony_ci return max_name; 284862306a36Sopenharmony_ci} 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_civoid annotation__init_column_widths(struct annotation *notes, struct symbol *sym) 285162306a36Sopenharmony_ci{ 285262306a36Sopenharmony_ci notes->widths.addr = notes->widths.target = 285362306a36Sopenharmony_ci notes->widths.min_addr = hex_width(symbol__size(sym)); 285462306a36Sopenharmony_ci notes->widths.max_addr = hex_width(sym->end); 285562306a36Sopenharmony_ci notes->widths.jumps = width_jumps(notes->max_jump_sources); 285662306a36Sopenharmony_ci notes->widths.max_ins_name = annotation__max_ins_name(notes); 285762306a36Sopenharmony_ci} 285862306a36Sopenharmony_ci 285962306a36Sopenharmony_civoid annotation__update_column_widths(struct annotation *notes) 286062306a36Sopenharmony_ci{ 286162306a36Sopenharmony_ci if (notes->options->use_offset) 286262306a36Sopenharmony_ci notes->widths.target = notes->widths.min_addr; 286362306a36Sopenharmony_ci else if (notes->options->full_addr) 286462306a36Sopenharmony_ci notes->widths.target = BITS_PER_LONG / 4; 286562306a36Sopenharmony_ci else 286662306a36Sopenharmony_ci notes->widths.target = notes->widths.max_addr; 286762306a36Sopenharmony_ci 286862306a36Sopenharmony_ci notes->widths.addr = notes->widths.target; 286962306a36Sopenharmony_ci 287062306a36Sopenharmony_ci if (notes->options->show_nr_jumps) 287162306a36Sopenharmony_ci notes->widths.addr += notes->widths.jumps + 1; 287262306a36Sopenharmony_ci} 287362306a36Sopenharmony_ci 287462306a36Sopenharmony_civoid annotation__toggle_full_addr(struct annotation *notes, struct map_symbol *ms) 287562306a36Sopenharmony_ci{ 287662306a36Sopenharmony_ci notes->options->full_addr = !notes->options->full_addr; 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci if (notes->options->full_addr) 287962306a36Sopenharmony_ci notes->start = map__objdump_2mem(ms->map, ms->sym->start); 288062306a36Sopenharmony_ci else 288162306a36Sopenharmony_ci notes->start = map__rip_2objdump(ms->map, ms->sym->start); 288262306a36Sopenharmony_ci 288362306a36Sopenharmony_ci annotation__update_column_widths(notes); 288462306a36Sopenharmony_ci} 288562306a36Sopenharmony_ci 288662306a36Sopenharmony_cistatic void annotation__calc_lines(struct annotation *notes, struct map *map, 288762306a36Sopenharmony_ci struct rb_root *root, 288862306a36Sopenharmony_ci struct annotation_options *opts) 288962306a36Sopenharmony_ci{ 289062306a36Sopenharmony_ci struct annotation_line *al; 289162306a36Sopenharmony_ci struct rb_root tmp_root = RB_ROOT; 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci list_for_each_entry(al, ¬es->src->source, node) { 289462306a36Sopenharmony_ci double percent_max = 0.0; 289562306a36Sopenharmony_ci int i; 289662306a36Sopenharmony_ci 289762306a36Sopenharmony_ci for (i = 0; i < al->data_nr; i++) { 289862306a36Sopenharmony_ci double percent; 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_ci percent = annotation_data__percent(&al->data[i], 290162306a36Sopenharmony_ci opts->percent_type); 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_ci if (percent > percent_max) 290462306a36Sopenharmony_ci percent_max = percent; 290562306a36Sopenharmony_ci } 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ci if (percent_max <= 0.5) 290862306a36Sopenharmony_ci continue; 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci al->path = get_srcline(map__dso(map), notes->start + al->offset, NULL, 291162306a36Sopenharmony_ci false, true, notes->start + al->offset); 291262306a36Sopenharmony_ci insert_source_line(&tmp_root, al, opts); 291362306a36Sopenharmony_ci } 291462306a36Sopenharmony_ci 291562306a36Sopenharmony_ci resort_source_line(root, &tmp_root); 291662306a36Sopenharmony_ci} 291762306a36Sopenharmony_ci 291862306a36Sopenharmony_cistatic void symbol__calc_lines(struct map_symbol *ms, struct rb_root *root, 291962306a36Sopenharmony_ci struct annotation_options *opts) 292062306a36Sopenharmony_ci{ 292162306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(ms->sym); 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci annotation__calc_lines(notes, ms->map, root, opts); 292462306a36Sopenharmony_ci} 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_ciint symbol__tty_annotate2(struct map_symbol *ms, struct evsel *evsel, 292762306a36Sopenharmony_ci struct annotation_options *opts) 292862306a36Sopenharmony_ci{ 292962306a36Sopenharmony_ci struct dso *dso = map__dso(ms->map); 293062306a36Sopenharmony_ci struct symbol *sym = ms->sym; 293162306a36Sopenharmony_ci struct rb_root source_line = RB_ROOT; 293262306a36Sopenharmony_ci struct hists *hists = evsel__hists(evsel); 293362306a36Sopenharmony_ci char buf[1024]; 293462306a36Sopenharmony_ci int err; 293562306a36Sopenharmony_ci 293662306a36Sopenharmony_ci err = symbol__annotate2(ms, evsel, opts, NULL); 293762306a36Sopenharmony_ci if (err) { 293862306a36Sopenharmony_ci char msg[BUFSIZ]; 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci dso->annotate_warned = true; 294162306a36Sopenharmony_ci symbol__strerror_disassemble(ms, err, msg, sizeof(msg)); 294262306a36Sopenharmony_ci ui__error("Couldn't annotate %s:\n%s", sym->name, msg); 294362306a36Sopenharmony_ci return -1; 294462306a36Sopenharmony_ci } 294562306a36Sopenharmony_ci 294662306a36Sopenharmony_ci if (opts->print_lines) { 294762306a36Sopenharmony_ci srcline_full_filename = opts->full_path; 294862306a36Sopenharmony_ci symbol__calc_lines(ms, &source_line, opts); 294962306a36Sopenharmony_ci print_summary(&source_line, dso->long_name); 295062306a36Sopenharmony_ci } 295162306a36Sopenharmony_ci 295262306a36Sopenharmony_ci hists__scnprintf_title(hists, buf, sizeof(buf)); 295362306a36Sopenharmony_ci fprintf(stdout, "%s, [percent: %s]\n%s() %s\n", 295462306a36Sopenharmony_ci buf, percent_type_str(opts->percent_type), sym->name, dso->long_name); 295562306a36Sopenharmony_ci symbol__annotate_fprintf2(sym, stdout, opts); 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_ci annotated_source__purge(symbol__annotation(sym)->src); 295862306a36Sopenharmony_ci 295962306a36Sopenharmony_ci return 0; 296062306a36Sopenharmony_ci} 296162306a36Sopenharmony_ci 296262306a36Sopenharmony_ciint symbol__tty_annotate(struct map_symbol *ms, struct evsel *evsel, 296362306a36Sopenharmony_ci struct annotation_options *opts) 296462306a36Sopenharmony_ci{ 296562306a36Sopenharmony_ci struct dso *dso = map__dso(ms->map); 296662306a36Sopenharmony_ci struct symbol *sym = ms->sym; 296762306a36Sopenharmony_ci struct rb_root source_line = RB_ROOT; 296862306a36Sopenharmony_ci int err; 296962306a36Sopenharmony_ci 297062306a36Sopenharmony_ci err = symbol__annotate(ms, evsel, opts, NULL); 297162306a36Sopenharmony_ci if (err) { 297262306a36Sopenharmony_ci char msg[BUFSIZ]; 297362306a36Sopenharmony_ci 297462306a36Sopenharmony_ci dso->annotate_warned = true; 297562306a36Sopenharmony_ci symbol__strerror_disassemble(ms, err, msg, sizeof(msg)); 297662306a36Sopenharmony_ci ui__error("Couldn't annotate %s:\n%s", sym->name, msg); 297762306a36Sopenharmony_ci return -1; 297862306a36Sopenharmony_ci } 297962306a36Sopenharmony_ci 298062306a36Sopenharmony_ci symbol__calc_percent(sym, evsel); 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_ci if (opts->print_lines) { 298362306a36Sopenharmony_ci srcline_full_filename = opts->full_path; 298462306a36Sopenharmony_ci symbol__calc_lines(ms, &source_line, opts); 298562306a36Sopenharmony_ci print_summary(&source_line, dso->long_name); 298662306a36Sopenharmony_ci } 298762306a36Sopenharmony_ci 298862306a36Sopenharmony_ci symbol__annotate_printf(ms, evsel, opts); 298962306a36Sopenharmony_ci 299062306a36Sopenharmony_ci annotated_source__purge(symbol__annotation(sym)->src); 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_ci return 0; 299362306a36Sopenharmony_ci} 299462306a36Sopenharmony_ci 299562306a36Sopenharmony_cibool ui__has_annotation(void) 299662306a36Sopenharmony_ci{ 299762306a36Sopenharmony_ci return use_browser == 1 && perf_hpp_list.sym; 299862306a36Sopenharmony_ci} 299962306a36Sopenharmony_ci 300062306a36Sopenharmony_ci 300162306a36Sopenharmony_cistatic double annotation_line__max_percent(struct annotation_line *al, 300262306a36Sopenharmony_ci struct annotation *notes, 300362306a36Sopenharmony_ci unsigned int percent_type) 300462306a36Sopenharmony_ci{ 300562306a36Sopenharmony_ci double percent_max = 0.0; 300662306a36Sopenharmony_ci int i; 300762306a36Sopenharmony_ci 300862306a36Sopenharmony_ci for (i = 0; i < notes->nr_events; i++) { 300962306a36Sopenharmony_ci double percent; 301062306a36Sopenharmony_ci 301162306a36Sopenharmony_ci percent = annotation_data__percent(&al->data[i], 301262306a36Sopenharmony_ci percent_type); 301362306a36Sopenharmony_ci 301462306a36Sopenharmony_ci if (percent > percent_max) 301562306a36Sopenharmony_ci percent_max = percent; 301662306a36Sopenharmony_ci } 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_ci return percent_max; 301962306a36Sopenharmony_ci} 302062306a36Sopenharmony_ci 302162306a36Sopenharmony_cistatic void disasm_line__write(struct disasm_line *dl, struct annotation *notes, 302262306a36Sopenharmony_ci void *obj, char *bf, size_t size, 302362306a36Sopenharmony_ci void (*obj__printf)(void *obj, const char *fmt, ...), 302462306a36Sopenharmony_ci void (*obj__write_graph)(void *obj, int graph)) 302562306a36Sopenharmony_ci{ 302662306a36Sopenharmony_ci if (dl->ins.ops && dl->ins.ops->scnprintf) { 302762306a36Sopenharmony_ci if (ins__is_jump(&dl->ins)) { 302862306a36Sopenharmony_ci bool fwd; 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_ci if (dl->ops.target.outside) 303162306a36Sopenharmony_ci goto call_like; 303262306a36Sopenharmony_ci fwd = dl->ops.target.offset > dl->al.offset; 303362306a36Sopenharmony_ci obj__write_graph(obj, fwd ? DARROW_CHAR : UARROW_CHAR); 303462306a36Sopenharmony_ci obj__printf(obj, " "); 303562306a36Sopenharmony_ci } else if (ins__is_call(&dl->ins)) { 303662306a36Sopenharmony_cicall_like: 303762306a36Sopenharmony_ci obj__write_graph(obj, RARROW_CHAR); 303862306a36Sopenharmony_ci obj__printf(obj, " "); 303962306a36Sopenharmony_ci } else if (ins__is_ret(&dl->ins)) { 304062306a36Sopenharmony_ci obj__write_graph(obj, LARROW_CHAR); 304162306a36Sopenharmony_ci obj__printf(obj, " "); 304262306a36Sopenharmony_ci } else { 304362306a36Sopenharmony_ci obj__printf(obj, " "); 304462306a36Sopenharmony_ci } 304562306a36Sopenharmony_ci } else { 304662306a36Sopenharmony_ci obj__printf(obj, " "); 304762306a36Sopenharmony_ci } 304862306a36Sopenharmony_ci 304962306a36Sopenharmony_ci disasm_line__scnprintf(dl, bf, size, !notes->options->use_offset, notes->widths.max_ins_name); 305062306a36Sopenharmony_ci} 305162306a36Sopenharmony_ci 305262306a36Sopenharmony_cistatic void ipc_coverage_string(char *bf, int size, struct annotation *notes) 305362306a36Sopenharmony_ci{ 305462306a36Sopenharmony_ci double ipc = 0.0, coverage = 0.0; 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci if (notes->hit_cycles) 305762306a36Sopenharmony_ci ipc = notes->hit_insn / ((double)notes->hit_cycles); 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_ci if (notes->total_insn) { 306062306a36Sopenharmony_ci coverage = notes->cover_insn * 100.0 / 306162306a36Sopenharmony_ci ((double)notes->total_insn); 306262306a36Sopenharmony_ci } 306362306a36Sopenharmony_ci 306462306a36Sopenharmony_ci scnprintf(bf, size, "(Average IPC: %.2f, IPC Coverage: %.1f%%)", 306562306a36Sopenharmony_ci ipc, coverage); 306662306a36Sopenharmony_ci} 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_cistatic void __annotation_line__write(struct annotation_line *al, struct annotation *notes, 306962306a36Sopenharmony_ci bool first_line, bool current_entry, bool change_color, int width, 307062306a36Sopenharmony_ci void *obj, unsigned int percent_type, 307162306a36Sopenharmony_ci int (*obj__set_color)(void *obj, int color), 307262306a36Sopenharmony_ci void (*obj__set_percent_color)(void *obj, double percent, bool current), 307362306a36Sopenharmony_ci int (*obj__set_jumps_percent_color)(void *obj, int nr, bool current), 307462306a36Sopenharmony_ci void (*obj__printf)(void *obj, const char *fmt, ...), 307562306a36Sopenharmony_ci void (*obj__write_graph)(void *obj, int graph)) 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci{ 307862306a36Sopenharmony_ci double percent_max = annotation_line__max_percent(al, notes, percent_type); 307962306a36Sopenharmony_ci int pcnt_width = annotation__pcnt_width(notes), 308062306a36Sopenharmony_ci cycles_width = annotation__cycles_width(notes); 308162306a36Sopenharmony_ci bool show_title = false; 308262306a36Sopenharmony_ci char bf[256]; 308362306a36Sopenharmony_ci int printed; 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_ci if (first_line && (al->offset == -1 || percent_max == 0.0)) { 308662306a36Sopenharmony_ci if (notes->have_cycles) { 308762306a36Sopenharmony_ci if (al->ipc == 0.0 && al->cycles == 0) 308862306a36Sopenharmony_ci show_title = true; 308962306a36Sopenharmony_ci } else 309062306a36Sopenharmony_ci show_title = true; 309162306a36Sopenharmony_ci } 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci if (al->offset != -1 && percent_max != 0.0) { 309462306a36Sopenharmony_ci int i; 309562306a36Sopenharmony_ci 309662306a36Sopenharmony_ci for (i = 0; i < notes->nr_events; i++) { 309762306a36Sopenharmony_ci double percent; 309862306a36Sopenharmony_ci 309962306a36Sopenharmony_ci percent = annotation_data__percent(&al->data[i], percent_type); 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_ci obj__set_percent_color(obj, percent, current_entry); 310262306a36Sopenharmony_ci if (symbol_conf.show_total_period) { 310362306a36Sopenharmony_ci obj__printf(obj, "%11" PRIu64 " ", al->data[i].he.period); 310462306a36Sopenharmony_ci } else if (symbol_conf.show_nr_samples) { 310562306a36Sopenharmony_ci obj__printf(obj, "%6" PRIu64 " ", 310662306a36Sopenharmony_ci al->data[i].he.nr_samples); 310762306a36Sopenharmony_ci } else { 310862306a36Sopenharmony_ci obj__printf(obj, "%6.2f ", percent); 310962306a36Sopenharmony_ci } 311062306a36Sopenharmony_ci } 311162306a36Sopenharmony_ci } else { 311262306a36Sopenharmony_ci obj__set_percent_color(obj, 0, current_entry); 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_ci if (!show_title) 311562306a36Sopenharmony_ci obj__printf(obj, "%-*s", pcnt_width, " "); 311662306a36Sopenharmony_ci else { 311762306a36Sopenharmony_ci obj__printf(obj, "%-*s", pcnt_width, 311862306a36Sopenharmony_ci symbol_conf.show_total_period ? "Period" : 311962306a36Sopenharmony_ci symbol_conf.show_nr_samples ? "Samples" : "Percent"); 312062306a36Sopenharmony_ci } 312162306a36Sopenharmony_ci } 312262306a36Sopenharmony_ci 312362306a36Sopenharmony_ci if (notes->have_cycles) { 312462306a36Sopenharmony_ci if (al->ipc) 312562306a36Sopenharmony_ci obj__printf(obj, "%*.2f ", ANNOTATION__IPC_WIDTH - 1, al->ipc); 312662306a36Sopenharmony_ci else if (!show_title) 312762306a36Sopenharmony_ci obj__printf(obj, "%*s", ANNOTATION__IPC_WIDTH, " "); 312862306a36Sopenharmony_ci else 312962306a36Sopenharmony_ci obj__printf(obj, "%*s ", ANNOTATION__IPC_WIDTH - 1, "IPC"); 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_ci if (!notes->options->show_minmax_cycle) { 313262306a36Sopenharmony_ci if (al->cycles) 313362306a36Sopenharmony_ci obj__printf(obj, "%*" PRIu64 " ", 313462306a36Sopenharmony_ci ANNOTATION__CYCLES_WIDTH - 1, al->cycles); 313562306a36Sopenharmony_ci else if (!show_title) 313662306a36Sopenharmony_ci obj__printf(obj, "%*s", 313762306a36Sopenharmony_ci ANNOTATION__CYCLES_WIDTH, " "); 313862306a36Sopenharmony_ci else 313962306a36Sopenharmony_ci obj__printf(obj, "%*s ", 314062306a36Sopenharmony_ci ANNOTATION__CYCLES_WIDTH - 1, 314162306a36Sopenharmony_ci "Cycle"); 314262306a36Sopenharmony_ci } else { 314362306a36Sopenharmony_ci if (al->cycles) { 314462306a36Sopenharmony_ci char str[32]; 314562306a36Sopenharmony_ci 314662306a36Sopenharmony_ci scnprintf(str, sizeof(str), 314762306a36Sopenharmony_ci "%" PRIu64 "(%" PRIu64 "/%" PRIu64 ")", 314862306a36Sopenharmony_ci al->cycles, al->cycles_min, 314962306a36Sopenharmony_ci al->cycles_max); 315062306a36Sopenharmony_ci 315162306a36Sopenharmony_ci obj__printf(obj, "%*s ", 315262306a36Sopenharmony_ci ANNOTATION__MINMAX_CYCLES_WIDTH - 1, 315362306a36Sopenharmony_ci str); 315462306a36Sopenharmony_ci } else if (!show_title) 315562306a36Sopenharmony_ci obj__printf(obj, "%*s", 315662306a36Sopenharmony_ci ANNOTATION__MINMAX_CYCLES_WIDTH, 315762306a36Sopenharmony_ci " "); 315862306a36Sopenharmony_ci else 315962306a36Sopenharmony_ci obj__printf(obj, "%*s ", 316062306a36Sopenharmony_ci ANNOTATION__MINMAX_CYCLES_WIDTH - 1, 316162306a36Sopenharmony_ci "Cycle(min/max)"); 316262306a36Sopenharmony_ci } 316362306a36Sopenharmony_ci 316462306a36Sopenharmony_ci if (show_title && !*al->line) { 316562306a36Sopenharmony_ci ipc_coverage_string(bf, sizeof(bf), notes); 316662306a36Sopenharmony_ci obj__printf(obj, "%*s", ANNOTATION__AVG_IPC_WIDTH, bf); 316762306a36Sopenharmony_ci } 316862306a36Sopenharmony_ci } 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_ci obj__printf(obj, " "); 317162306a36Sopenharmony_ci 317262306a36Sopenharmony_ci if (!*al->line) 317362306a36Sopenharmony_ci obj__printf(obj, "%-*s", width - pcnt_width - cycles_width, " "); 317462306a36Sopenharmony_ci else if (al->offset == -1) { 317562306a36Sopenharmony_ci if (al->line_nr && notes->options->show_linenr) 317662306a36Sopenharmony_ci printed = scnprintf(bf, sizeof(bf), "%-*d ", notes->widths.addr + 1, al->line_nr); 317762306a36Sopenharmony_ci else 317862306a36Sopenharmony_ci printed = scnprintf(bf, sizeof(bf), "%-*s ", notes->widths.addr, " "); 317962306a36Sopenharmony_ci obj__printf(obj, bf); 318062306a36Sopenharmony_ci obj__printf(obj, "%-*s", width - printed - pcnt_width - cycles_width + 1, al->line); 318162306a36Sopenharmony_ci } else { 318262306a36Sopenharmony_ci u64 addr = al->offset; 318362306a36Sopenharmony_ci int color = -1; 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_ci if (!notes->options->use_offset) 318662306a36Sopenharmony_ci addr += notes->start; 318762306a36Sopenharmony_ci 318862306a36Sopenharmony_ci if (!notes->options->use_offset) { 318962306a36Sopenharmony_ci printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr); 319062306a36Sopenharmony_ci } else { 319162306a36Sopenharmony_ci if (al->jump_sources && 319262306a36Sopenharmony_ci notes->options->offset_level >= ANNOTATION__OFFSET_JUMP_TARGETS) { 319362306a36Sopenharmony_ci if (notes->options->show_nr_jumps) { 319462306a36Sopenharmony_ci int prev; 319562306a36Sopenharmony_ci printed = scnprintf(bf, sizeof(bf), "%*d ", 319662306a36Sopenharmony_ci notes->widths.jumps, 319762306a36Sopenharmony_ci al->jump_sources); 319862306a36Sopenharmony_ci prev = obj__set_jumps_percent_color(obj, al->jump_sources, 319962306a36Sopenharmony_ci current_entry); 320062306a36Sopenharmony_ci obj__printf(obj, bf); 320162306a36Sopenharmony_ci obj__set_color(obj, prev); 320262306a36Sopenharmony_ci } 320362306a36Sopenharmony_ciprint_addr: 320462306a36Sopenharmony_ci printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ", 320562306a36Sopenharmony_ci notes->widths.target, addr); 320662306a36Sopenharmony_ci } else if (ins__is_call(&disasm_line(al)->ins) && 320762306a36Sopenharmony_ci notes->options->offset_level >= ANNOTATION__OFFSET_CALL) { 320862306a36Sopenharmony_ci goto print_addr; 320962306a36Sopenharmony_ci } else if (notes->options->offset_level == ANNOTATION__MAX_OFFSET_LEVEL) { 321062306a36Sopenharmony_ci goto print_addr; 321162306a36Sopenharmony_ci } else { 321262306a36Sopenharmony_ci printed = scnprintf(bf, sizeof(bf), "%-*s ", 321362306a36Sopenharmony_ci notes->widths.addr, " "); 321462306a36Sopenharmony_ci } 321562306a36Sopenharmony_ci } 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_ci if (change_color) 321862306a36Sopenharmony_ci color = obj__set_color(obj, HE_COLORSET_ADDR); 321962306a36Sopenharmony_ci obj__printf(obj, bf); 322062306a36Sopenharmony_ci if (change_color) 322162306a36Sopenharmony_ci obj__set_color(obj, color); 322262306a36Sopenharmony_ci 322362306a36Sopenharmony_ci disasm_line__write(disasm_line(al), notes, obj, bf, sizeof(bf), obj__printf, obj__write_graph); 322462306a36Sopenharmony_ci 322562306a36Sopenharmony_ci obj__printf(obj, "%-*s", width - pcnt_width - cycles_width - 3 - printed, bf); 322662306a36Sopenharmony_ci } 322762306a36Sopenharmony_ci 322862306a36Sopenharmony_ci} 322962306a36Sopenharmony_ci 323062306a36Sopenharmony_civoid annotation_line__write(struct annotation_line *al, struct annotation *notes, 323162306a36Sopenharmony_ci struct annotation_write_ops *wops, 323262306a36Sopenharmony_ci struct annotation_options *opts) 323362306a36Sopenharmony_ci{ 323462306a36Sopenharmony_ci __annotation_line__write(al, notes, wops->first_line, wops->current_entry, 323562306a36Sopenharmony_ci wops->change_color, wops->width, wops->obj, 323662306a36Sopenharmony_ci opts->percent_type, 323762306a36Sopenharmony_ci wops->set_color, wops->set_percent_color, 323862306a36Sopenharmony_ci wops->set_jumps_percent_color, wops->printf, 323962306a36Sopenharmony_ci wops->write_graph); 324062306a36Sopenharmony_ci} 324162306a36Sopenharmony_ci 324262306a36Sopenharmony_ciint symbol__annotate2(struct map_symbol *ms, struct evsel *evsel, 324362306a36Sopenharmony_ci struct annotation_options *options, struct arch **parch) 324462306a36Sopenharmony_ci{ 324562306a36Sopenharmony_ci struct symbol *sym = ms->sym; 324662306a36Sopenharmony_ci struct annotation *notes = symbol__annotation(sym); 324762306a36Sopenharmony_ci size_t size = symbol__size(sym); 324862306a36Sopenharmony_ci int nr_pcnt = 1, err; 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_ci notes->offsets = zalloc(size * sizeof(struct annotation_line *)); 325162306a36Sopenharmony_ci if (notes->offsets == NULL) 325262306a36Sopenharmony_ci return ENOMEM; 325362306a36Sopenharmony_ci 325462306a36Sopenharmony_ci if (evsel__is_group_event(evsel)) 325562306a36Sopenharmony_ci nr_pcnt = evsel->core.nr_members; 325662306a36Sopenharmony_ci 325762306a36Sopenharmony_ci err = symbol__annotate(ms, evsel, options, parch); 325862306a36Sopenharmony_ci if (err) 325962306a36Sopenharmony_ci goto out_free_offsets; 326062306a36Sopenharmony_ci 326162306a36Sopenharmony_ci notes->options = options; 326262306a36Sopenharmony_ci 326362306a36Sopenharmony_ci symbol__calc_percent(sym, evsel); 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_ci annotation__set_offsets(notes, size); 326662306a36Sopenharmony_ci annotation__mark_jump_targets(notes, sym); 326762306a36Sopenharmony_ci annotation__compute_ipc(notes, size); 326862306a36Sopenharmony_ci annotation__init_column_widths(notes, sym); 326962306a36Sopenharmony_ci notes->nr_events = nr_pcnt; 327062306a36Sopenharmony_ci 327162306a36Sopenharmony_ci annotation__update_column_widths(notes); 327262306a36Sopenharmony_ci sym->annotate2 = 1; 327362306a36Sopenharmony_ci 327462306a36Sopenharmony_ci return 0; 327562306a36Sopenharmony_ci 327662306a36Sopenharmony_ciout_free_offsets: 327762306a36Sopenharmony_ci zfree(¬es->offsets); 327862306a36Sopenharmony_ci return err; 327962306a36Sopenharmony_ci} 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_cistatic int annotation__config(const char *var, const char *value, void *data) 328262306a36Sopenharmony_ci{ 328362306a36Sopenharmony_ci struct annotation_options *opt = data; 328462306a36Sopenharmony_ci 328562306a36Sopenharmony_ci if (!strstarts(var, "annotate.")) 328662306a36Sopenharmony_ci return 0; 328762306a36Sopenharmony_ci 328862306a36Sopenharmony_ci if (!strcmp(var, "annotate.offset_level")) { 328962306a36Sopenharmony_ci perf_config_u8(&opt->offset_level, "offset_level", value); 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_ci if (opt->offset_level > ANNOTATION__MAX_OFFSET_LEVEL) 329262306a36Sopenharmony_ci opt->offset_level = ANNOTATION__MAX_OFFSET_LEVEL; 329362306a36Sopenharmony_ci else if (opt->offset_level < ANNOTATION__MIN_OFFSET_LEVEL) 329462306a36Sopenharmony_ci opt->offset_level = ANNOTATION__MIN_OFFSET_LEVEL; 329562306a36Sopenharmony_ci } else if (!strcmp(var, "annotate.hide_src_code")) { 329662306a36Sopenharmony_ci opt->hide_src_code = perf_config_bool("hide_src_code", value); 329762306a36Sopenharmony_ci } else if (!strcmp(var, "annotate.jump_arrows")) { 329862306a36Sopenharmony_ci opt->jump_arrows = perf_config_bool("jump_arrows", value); 329962306a36Sopenharmony_ci } else if (!strcmp(var, "annotate.show_linenr")) { 330062306a36Sopenharmony_ci opt->show_linenr = perf_config_bool("show_linenr", value); 330162306a36Sopenharmony_ci } else if (!strcmp(var, "annotate.show_nr_jumps")) { 330262306a36Sopenharmony_ci opt->show_nr_jumps = perf_config_bool("show_nr_jumps", value); 330362306a36Sopenharmony_ci } else if (!strcmp(var, "annotate.show_nr_samples")) { 330462306a36Sopenharmony_ci symbol_conf.show_nr_samples = perf_config_bool("show_nr_samples", 330562306a36Sopenharmony_ci value); 330662306a36Sopenharmony_ci } else if (!strcmp(var, "annotate.show_total_period")) { 330762306a36Sopenharmony_ci symbol_conf.show_total_period = perf_config_bool("show_total_period", 330862306a36Sopenharmony_ci value); 330962306a36Sopenharmony_ci } else if (!strcmp(var, "annotate.use_offset")) { 331062306a36Sopenharmony_ci opt->use_offset = perf_config_bool("use_offset", value); 331162306a36Sopenharmony_ci } else if (!strcmp(var, "annotate.disassembler_style")) { 331262306a36Sopenharmony_ci opt->disassembler_style = strdup(value); 331362306a36Sopenharmony_ci if (!opt->disassembler_style) { 331462306a36Sopenharmony_ci pr_err("Not enough memory for annotate.disassembler_style\n"); 331562306a36Sopenharmony_ci return -1; 331662306a36Sopenharmony_ci } 331762306a36Sopenharmony_ci } else if (!strcmp(var, "annotate.objdump")) { 331862306a36Sopenharmony_ci opt->objdump_path = strdup(value); 331962306a36Sopenharmony_ci if (!opt->objdump_path) { 332062306a36Sopenharmony_ci pr_err("Not enough memory for annotate.objdump\n"); 332162306a36Sopenharmony_ci return -1; 332262306a36Sopenharmony_ci } 332362306a36Sopenharmony_ci } else if (!strcmp(var, "annotate.addr2line")) { 332462306a36Sopenharmony_ci symbol_conf.addr2line_path = strdup(value); 332562306a36Sopenharmony_ci if (!symbol_conf.addr2line_path) { 332662306a36Sopenharmony_ci pr_err("Not enough memory for annotate.addr2line\n"); 332762306a36Sopenharmony_ci return -1; 332862306a36Sopenharmony_ci } 332962306a36Sopenharmony_ci } else if (!strcmp(var, "annotate.demangle")) { 333062306a36Sopenharmony_ci symbol_conf.demangle = perf_config_bool("demangle", value); 333162306a36Sopenharmony_ci } else if (!strcmp(var, "annotate.demangle_kernel")) { 333262306a36Sopenharmony_ci symbol_conf.demangle_kernel = perf_config_bool("demangle_kernel", value); 333362306a36Sopenharmony_ci } else { 333462306a36Sopenharmony_ci pr_debug("%s variable unknown, ignoring...", var); 333562306a36Sopenharmony_ci } 333662306a36Sopenharmony_ci 333762306a36Sopenharmony_ci return 0; 333862306a36Sopenharmony_ci} 333962306a36Sopenharmony_ci 334062306a36Sopenharmony_civoid annotation_options__init(struct annotation_options *opt) 334162306a36Sopenharmony_ci{ 334262306a36Sopenharmony_ci memset(opt, 0, sizeof(*opt)); 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_ci /* Default values. */ 334562306a36Sopenharmony_ci opt->use_offset = true; 334662306a36Sopenharmony_ci opt->jump_arrows = true; 334762306a36Sopenharmony_ci opt->annotate_src = true; 334862306a36Sopenharmony_ci opt->offset_level = ANNOTATION__OFFSET_JUMP_TARGETS; 334962306a36Sopenharmony_ci opt->percent_type = PERCENT_PERIOD_LOCAL; 335062306a36Sopenharmony_ci} 335162306a36Sopenharmony_ci 335262306a36Sopenharmony_ci 335362306a36Sopenharmony_civoid annotation_options__exit(struct annotation_options *opt) 335462306a36Sopenharmony_ci{ 335562306a36Sopenharmony_ci zfree(&opt->disassembler_style); 335662306a36Sopenharmony_ci zfree(&opt->objdump_path); 335762306a36Sopenharmony_ci} 335862306a36Sopenharmony_ci 335962306a36Sopenharmony_civoid annotation_config__init(struct annotation_options *opt) 336062306a36Sopenharmony_ci{ 336162306a36Sopenharmony_ci perf_config(annotation__config, opt); 336262306a36Sopenharmony_ci} 336362306a36Sopenharmony_ci 336462306a36Sopenharmony_cistatic unsigned int parse_percent_type(char *str1, char *str2) 336562306a36Sopenharmony_ci{ 336662306a36Sopenharmony_ci unsigned int type = (unsigned int) -1; 336762306a36Sopenharmony_ci 336862306a36Sopenharmony_ci if (!strcmp("period", str1)) { 336962306a36Sopenharmony_ci if (!strcmp("local", str2)) 337062306a36Sopenharmony_ci type = PERCENT_PERIOD_LOCAL; 337162306a36Sopenharmony_ci else if (!strcmp("global", str2)) 337262306a36Sopenharmony_ci type = PERCENT_PERIOD_GLOBAL; 337362306a36Sopenharmony_ci } 337462306a36Sopenharmony_ci 337562306a36Sopenharmony_ci if (!strcmp("hits", str1)) { 337662306a36Sopenharmony_ci if (!strcmp("local", str2)) 337762306a36Sopenharmony_ci type = PERCENT_HITS_LOCAL; 337862306a36Sopenharmony_ci else if (!strcmp("global", str2)) 337962306a36Sopenharmony_ci type = PERCENT_HITS_GLOBAL; 338062306a36Sopenharmony_ci } 338162306a36Sopenharmony_ci 338262306a36Sopenharmony_ci return type; 338362306a36Sopenharmony_ci} 338462306a36Sopenharmony_ci 338562306a36Sopenharmony_ciint annotate_parse_percent_type(const struct option *opt, const char *_str, 338662306a36Sopenharmony_ci int unset __maybe_unused) 338762306a36Sopenharmony_ci{ 338862306a36Sopenharmony_ci struct annotation_options *opts = opt->value; 338962306a36Sopenharmony_ci unsigned int type; 339062306a36Sopenharmony_ci char *str1, *str2; 339162306a36Sopenharmony_ci int err = -1; 339262306a36Sopenharmony_ci 339362306a36Sopenharmony_ci str1 = strdup(_str); 339462306a36Sopenharmony_ci if (!str1) 339562306a36Sopenharmony_ci return -ENOMEM; 339662306a36Sopenharmony_ci 339762306a36Sopenharmony_ci str2 = strchr(str1, '-'); 339862306a36Sopenharmony_ci if (!str2) 339962306a36Sopenharmony_ci goto out; 340062306a36Sopenharmony_ci 340162306a36Sopenharmony_ci *str2++ = 0; 340262306a36Sopenharmony_ci 340362306a36Sopenharmony_ci type = parse_percent_type(str1, str2); 340462306a36Sopenharmony_ci if (type == (unsigned int) -1) 340562306a36Sopenharmony_ci type = parse_percent_type(str2, str1); 340662306a36Sopenharmony_ci if (type != (unsigned int) -1) { 340762306a36Sopenharmony_ci opts->percent_type = type; 340862306a36Sopenharmony_ci err = 0; 340962306a36Sopenharmony_ci } 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_ciout: 341262306a36Sopenharmony_ci free(str1); 341362306a36Sopenharmony_ci return err; 341462306a36Sopenharmony_ci} 341562306a36Sopenharmony_ci 341662306a36Sopenharmony_ciint annotate_check_args(struct annotation_options *args) 341762306a36Sopenharmony_ci{ 341862306a36Sopenharmony_ci if (args->prefix_strip && !args->prefix) { 341962306a36Sopenharmony_ci pr_err("--prefix-strip requires --prefix\n"); 342062306a36Sopenharmony_ci return -1; 342162306a36Sopenharmony_ci } 342262306a36Sopenharmony_ci return 0; 342362306a36Sopenharmony_ci} 3424