1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Perf annotate functions. 4 * 5 * Copyright (C) 2020-2023 Loongson Technology Corporation Limited 6 */ 7 #include <stdlib.h> 8 #include <string.h> 9 #include <linux/compiler.h> 10 #include "../disasm.h" 11 #include "../map.h" 12 #include "../maps.h" 13 #include "../symbol.h" 14 #include "../thread.h" 15 16 static int loongarch_call__parse(const struct arch *arch, struct ins_operands *ops, 17 struct map_symbol *ms, 18 struct disasm_line *dl __maybe_unused) 19 { 20 char *c, *endptr, *tok, *name; 21 struct map *map = ms->map; 22 struct addr_map_symbol target; 23 24 c = strchr(ops->raw, '#'); 25 if (c++ == NULL) 26 return -1; 27 28 ops->target.addr = strtoull(c, &endptr, 16); 29 30 name = strchr(endptr, '<'); 31 name++; 32 33 if (arch->objdump.skip_functions_char && 34 strchr(name, arch->objdump.skip_functions_char)) 35 return -1; 36 37 tok = strchr(name, '>'); 38 if (tok == NULL) 39 return -1; 40 41 *tok = '\0'; 42 ops->target.name = strdup(name); 43 *tok = '>'; 44 45 if (ops->target.name == NULL) 46 return -1; 47 48 target = (struct addr_map_symbol) { 49 .ms = { .map = map__get(map), }, 50 .addr = map__objdump_2mem(map, ops->target.addr), 51 }; 52 53 if (maps__find_ams(thread__maps(ms->thread), &target) == 0 && 54 map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr) 55 ops->target.sym = target.ms.sym; 56 57 addr_map_symbol__exit(&target); 58 return 0; 59 } 60 61 static const struct ins_ops loongarch_call_ops = { 62 .parse = loongarch_call__parse, 63 .scnprintf = call__scnprintf, 64 .is_call = true, 65 }; 66 67 static int loongarch_jump__parse(const struct arch *arch, struct ins_operands *ops, 68 struct map_symbol *ms, 69 struct disasm_line *dl __maybe_unused) 70 71 { 72 struct map *map = ms->map; 73 struct symbol *sym = ms->sym; 74 struct addr_map_symbol target = { 75 .ms = { .map = map__get(map), }, 76 }; 77 const char *c = strchr(ops->raw, '#'); 78 u64 start, end; 79 80 ops->jump.raw_comment = strchr(ops->raw, arch->objdump.comment_char); 81 ops->jump.raw_func_start = strchr(ops->raw, '<'); 82 83 if (ops->jump.raw_func_start && c > ops->jump.raw_func_start) 84 c = NULL; 85 86 if (c++ != NULL) 87 ops->target.addr = strtoull(c, NULL, 16); 88 else 89 ops->target.addr = strtoull(ops->raw, NULL, 16); 90 91 target.addr = map__objdump_2mem(map, ops->target.addr); 92 start = map__unmap_ip(map, sym->start); 93 end = map__unmap_ip(map, sym->end); 94 95 ops->target.outside = target.addr < start || target.addr > end; 96 97 if (maps__find_ams(thread__maps(ms->thread), &target) == 0 && 98 map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr) 99 ops->target.sym = target.ms.sym; 100 101 if (!ops->target.outside) { 102 ops->target.offset = target.addr - start; 103 ops->target.offset_avail = true; 104 } else { 105 ops->target.offset_avail = false; 106 } 107 addr_map_symbol__exit(&target); 108 return 0; 109 } 110 111 static const struct ins_ops loongarch_jump_ops = { 112 .parse = loongarch_jump__parse, 113 .scnprintf = jump__scnprintf, 114 .is_jump = true, 115 }; 116 117 static 118 const struct ins_ops *loongarch__associate_ins_ops(struct arch *arch, const char *name) 119 { 120 const struct ins_ops *ops = NULL; 121 122 if (!strcmp(name, "bl")) 123 ops = &loongarch_call_ops; 124 else if (!strcmp(name, "jirl")) 125 ops = &ret_ops; 126 else if (!strcmp(name, "b") || 127 !strncmp(name, "beq", 3) || 128 !strncmp(name, "bne", 3) || 129 !strncmp(name, "blt", 3) || 130 !strncmp(name, "bge", 3) || 131 !strncmp(name, "bltu", 4) || 132 !strncmp(name, "bgeu", 4)) 133 ops = &loongarch_jump_ops; 134 else 135 return NULL; 136 137 arch__associate_ins_ops(arch, name, ops); 138 139 return ops; 140 } 141 142 int loongarch__annotate_init(struct arch *arch, char *cpuid __maybe_unused) 143 { 144 if (!arch->initialized) { 145 arch->associate_instruction_ops = loongarch__associate_ins_ops; 146 arch->initialized = true; 147 arch->objdump.comment_char = '#'; 148 } 149 150 return 0; 151 } 152