Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf annotate: Fix instruction association and parsing for LoongArch

In the perf annotate view for LoongArch, there is no arrowed line
pointing to the target from the branch instruction. This issue is
caused by incorrect instruction association and parsing.

$ perf record alloc-6276705c94ad1398 # rust benchmark
$ perf report

0.28 │ ori $a1, $zero, 0x63
│ move $a2, $zero
10.55 │ addi.d $a3, $a2, 1(0x1)
│ sltu $a4, $a3, $s7
9.53 │ masknez $a4, $s7, $a4
│ sub.d $a3, $a3, $a4
12.12 │ st.d $a1, $fp, 24(0x18)
│ st.d $a3, $fp, 16(0x10)
16.29 │ slli.d $a2, $a2, 0x2
│ ldx.w $a2, $s8, $a2
12.77 │ st.w $a2, $sp, 724(0x2d4)
│ st.w $s0, $sp, 720(0x2d0)
7.03 │ addi.d $a2, $sp, 720(0x2d0)
│ addi.d $a1, $a1, -1(0xfff)
12.03 │ move $a2, $a3
│ → bne $a1, $s3, -52(0x3ffcc) # 82ce8 <test::bench::Bencher::iter+0x3f4>
2.50 │ addi.d $a0, $a0, 1(0x1)

This patch fixes instruction association issues, such as associating
branch instructions with jump_ops instead of call_ops, and corrects
false instruction matches. It also implements branch instruction parsing
specifically for LoongArch. With this patch, we will be able to see the
arrowed line.

0.79 │3ec: ori $a1, $zero, 0x63
│ move $a2, $zero
10.32 │3f4:┌─→addi.d $a3, $a2, 1(0x1)
│ │ sltu $a4, $a3, $s7
10.44 │ │ masknez $a4, $s7, $a4
│ │ sub.d $a3, $a3, $a4
14.17 │ │ st.d $a1, $fp, 24(0x18)
│ │ st.d $a3, $fp, 16(0x10)
13.15 │ │ slli.d $a2, $a2, 0x2
│ │ ldx.w $a2, $s8, $a2
11.00 │ │ st.w $a2, $sp, 724(0x2d4)
│ │ st.w $s0, $sp, 720(0x2d0)
8.00 │ │ addi.d $a2, $sp, 720(0x2d0)
│ │ addi.d $a1, $a1, -1(0xfff)
11.99 │ │ move $a2, $a3
│ └──bne $a1, $s3, 3f4
3.17 │ addi.d $a0, $a0, 1(0x1)

Signed-off-by: WANG Rui <wangrui@loongson.cn>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: loongarch@lists.linux.dev
Cc: loongson-kernel@lists.loongnix.cn
Cc: Huacai Chen <chenhuacai@loongson.cn>
Cc: Tiezhu Yang <yangtiezhu@loongson.cn>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: WANG Xuerui <kernel@xen0n.name>
Link: https://lore.kernel.org/r/20230620132025.105563-1-wangrui@loongson.cn
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

authored by

WANG Rui and committed by
Namhyung Kim
4ca0d340 2e9f9d4a

+109 -18
+103 -13
tools/perf/arch/loongarch/annotate/instructions.c
··· 5 5 * Copyright (C) 2020-2023 Loongson Technology Corporation Limited 6 6 */ 7 7 8 + static int loongarch_call__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms) 9 + { 10 + char *c, *endptr, *tok, *name; 11 + struct map *map = ms->map; 12 + struct addr_map_symbol target = { 13 + .ms = { .map = map, }, 14 + }; 15 + 16 + c = strchr(ops->raw, '#'); 17 + if (c++ == NULL) 18 + return -1; 19 + 20 + ops->target.addr = strtoull(c, &endptr, 16); 21 + 22 + name = strchr(endptr, '<'); 23 + name++; 24 + 25 + if (arch->objdump.skip_functions_char && 26 + strchr(name, arch->objdump.skip_functions_char)) 27 + return -1; 28 + 29 + tok = strchr(name, '>'); 30 + if (tok == NULL) 31 + return -1; 32 + 33 + *tok = '\0'; 34 + ops->target.name = strdup(name); 35 + *tok = '>'; 36 + 37 + if (ops->target.name == NULL) 38 + return -1; 39 + 40 + target.addr = map__objdump_2mem(map, ops->target.addr); 41 + 42 + if (maps__find_ams(ms->maps, &target) == 0 && 43 + map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr) 44 + ops->target.sym = target.ms.sym; 45 + 46 + return 0; 47 + } 48 + 49 + static struct ins_ops loongarch_call_ops = { 50 + .parse = loongarch_call__parse, 51 + .scnprintf = call__scnprintf, 52 + }; 53 + 54 + static int loongarch_jump__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms) 55 + { 56 + struct map *map = ms->map; 57 + struct symbol *sym = ms->sym; 58 + struct addr_map_symbol target = { 59 + .ms = { .map = map, }, 60 + }; 61 + const char *c = strchr(ops->raw, '#'); 62 + u64 start, end; 63 + 64 + ops->raw_comment = strchr(ops->raw, arch->objdump.comment_char); 65 + ops->raw_func_start = strchr(ops->raw, '<'); 66 + 67 + if (ops->raw_func_start && c > ops->raw_func_start) 68 + c = NULL; 69 + 70 + if (c++ != NULL) 71 + ops->target.addr = strtoull(c, NULL, 16); 72 + else 73 + ops->target.addr = strtoull(ops->raw, NULL, 16); 74 + 75 + target.addr = map__objdump_2mem(map, ops->target.addr); 76 + start = map__unmap_ip(map, sym->start); 77 + end = map__unmap_ip(map, sym->end); 78 + 79 + ops->target.outside = target.addr < start || target.addr > end; 80 + 81 + if (maps__find_ams(ms->maps, &target) == 0 && 82 + map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr) 83 + ops->target.sym = target.ms.sym; 84 + 85 + if (!ops->target.outside) { 86 + ops->target.offset = target.addr - start; 87 + ops->target.offset_avail = true; 88 + } else { 89 + ops->target.offset_avail = false; 90 + } 91 + 92 + return 0; 93 + } 94 + 95 + static struct ins_ops loongarch_jump_ops = { 96 + .parse = loongarch_jump__parse, 97 + .scnprintf = jump__scnprintf, 98 + }; 99 + 8 100 static 9 101 struct ins_ops *loongarch__associate_ins_ops(struct arch *arch, const char *name) 10 102 { 11 103 struct ins_ops *ops = NULL; 12 104 13 - if (!strncmp(name, "beqz", 4) || 14 - !strncmp(name, "bnez", 4) || 15 - !strncmp(name, "beq", 3) || 16 - !strncmp(name, "bne", 3) || 17 - !strncmp(name, "blt", 3) || 18 - !strncmp(name, "bge", 3) || 19 - !strncmp(name, "bltu", 4) || 20 - !strncmp(name, "bgeu", 4) || 21 - !strncmp(name, "bl", 2)) 22 - ops = &call_ops; 23 - else if (!strncmp(name, "jirl", 4)) 105 + if (!strcmp(name, "bl")) 106 + ops = &loongarch_call_ops; 107 + else if (!strcmp(name, "jirl")) 24 108 ops = &ret_ops; 25 - else if (name[0] == 'b') 26 - ops = &jump_ops; 109 + else if (!strcmp(name, "b") || 110 + !strncmp(name, "beq", 3) || 111 + !strncmp(name, "bne", 3) || 112 + !strncmp(name, "blt", 3) || 113 + !strncmp(name, "bge", 3) || 114 + !strncmp(name, "bltu", 4) || 115 + !strncmp(name, "bgeu", 4)) 116 + ops = &loongarch_jump_ops; 27 117 else 28 118 return NULL; 29 119
-3
tools/perf/arch/s390/annotate/instructions.c
··· 45 45 return 0; 46 46 } 47 47 48 - static int call__scnprintf(struct ins *ins, char *bf, size_t size, 49 - struct ins_operands *ops, int max_ins_name); 50 - 51 48 static struct ins_ops s390_call_ops = { 52 49 .parse = s390_call__parse, 53 50 .scnprintf = call__scnprintf,
+6 -2
tools/perf/util/annotate.c
··· 62 62 static struct ins_ops *ins__find(struct arch *arch, const char *name); 63 63 static void ins__sort(struct arch *arch); 64 64 static int disasm_line__parse(char *line, const char **namep, char **rawp); 65 + static int call__scnprintf(struct ins *ins, char *bf, size_t size, 66 + struct ins_operands *ops, int max_ins_name); 67 + static int jump__scnprintf(struct ins *ins, char *bf, size_t size, 68 + struct ins_operands *ops, int max_ins_name); 65 69 66 70 struct arch { 67 71 const char *name; ··· 328 324 329 325 bool ins__is_call(const struct ins *ins) 330 326 { 331 - return ins->ops == &call_ops || ins->ops == &s390_call_ops; 327 + return ins->ops == &call_ops || ins->ops == &s390_call_ops || ins->ops == &loongarch_call_ops; 332 328 } 333 329 334 330 /* ··· 469 465 470 466 bool ins__is_jump(const struct ins *ins) 471 467 { 472 - return ins->ops == &jump_ops; 468 + return ins->ops == &jump_ops || ins->ops == &loongarch_jump_ops; 473 469 } 474 470 475 471 static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep)