From nobody Thu Dec 18 04:42:42 2025 Received: from szxga01-in.huawei.com (szxga01-in.huawei.com [45.249.212.187]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D526C1B94D; Tue, 16 Jan 2024 11:35:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Received: from mail.maildlp.com (unknown [172.19.163.48]) by szxga01-in.huawei.com (SkyGuard) with ESMTP id 4TDn2V4TgszvV1n; Tue, 16 Jan 2024 19:33:38 +0800 (CST) Received: from kwepemd100002.china.huawei.com (unknown [7.221.188.184]) by mail.maildlp.com (Postfix) with ESMTPS id 4177418006E; Tue, 16 Jan 2024 19:35:02 +0800 (CST) Received: from M910t.huawei.com (10.110.54.157) by kwepemd100002.china.huawei.com (7.221.188.184) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.2.1258.28; Tue, 16 Jan 2024 19:35:01 +0800 From: Changbin Du To: Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo CC: Mark Rutland , Alexander Shishkin , Jiri Olsa , Namhyung Kim , Ian Rogers , Adrian Hunter , , , Changbin Du Subject: [PATCH 2/3] perf: script: use capstone disasm engine to show assembly instructions Date: Tue, 16 Jan 2024 19:34:36 +0800 Message-ID: <20240116113437.1507537-3-changbin.du@huawei.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20240116113437.1507537-1-changbin.du@huawei.com> References: <20240116113437.1507537-1-changbin.du@huawei.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ClientProxiedBy: dggems702-chm.china.huawei.com (10.3.19.179) To kwepemd100002.china.huawei.com (7.221.188.184) Content-Type: text/plain; charset="utf-8" Currently, the instructions of samples are shown as raw hex strings which are hard to read. x86 has a special option '--xed' to disassemble the hex string via intel XED tool. Here we use capstone as our disassembler engine to give more friendly instructions. We select libcapstone because capstone can provide more insn details. Perf will fallback to raw instructions if libcapstone is not available. The advantages compared to XED tool: * Support arm, arm64, x86-32, x86_64 (more could be supported), xed only for x86_64. * Immediate address operands are shown as symbol+offs. Before: $ sudo perf record --event intel_pt//u -- ls $ sudo perf script --insn-trace perf 17423 [000] 423271.557970005: 7f2d95f16217 __GI___ioc= tl+0x7 (/lib/x86_64-linux-gnu/libc-2.27.so) insn: 48 3d 01 f0 ff ff perf 17423 [000] 423271.557970005: 7f2d95f1621d __GI___ioc= tl+0xd (/lib/x86_64-linux-gnu/libc-2.27.so) insn: 73 01 perf 17423 [000] 423271.557970338: 7f2d95f1621f __GI___ioc= tl+0xf (/lib/x86_64-linux-gnu/libc-2.27.so) insn: c3 perf 17423 [000] 423271.557970338: 5593ad3346d7 perf_evsel= __enable_cpu+0x97 (/work/linux/tools/perf/perf) insn: 85 c0 perf 17423 [000] 423271.557970338: 5593ad3346d9 perf_evsel= __enable_cpu+0x99 (/work/linux/tools/perf/perf) insn: 75 12 perf 17423 [000] 423271.557970338: 5593ad3346db perf_evsel= __enable_cpu+0x9b (/work/linux/tools/perf/perf) insn: 49 8b 84 24 a8 00 00 = 00 perf 17423 [000] 423271.557970338: 5593ad3346e3 perf_evsel= __enable_cpu+0xa3 (/work/linux/tools/perf/perf) insn: 48 8b 50 20 After: $ sudo perf script --insn-trace perf 17423 [000] 423271.557970005: 7f2d95f16217 __GI___ioc= tl+0x7 (/lib/x86_64-linux-gnu/libc-2.27.so) insn: cmpq $-0xfff, %rax perf 17423 [000] 423271.557970005: 7f2d95f1621d __GI___ioc= tl+0xd (/lib/x86_64-linux-gnu/libc-2.27.so) insn: jae __GI___ioctl+0x10 perf 17423 [000] 423271.557970338: 7f2d95f1621f __GI___ioc= tl+0xf (/lib/x86_64-linux-gnu/libc-2.27.so) insn: retq perf 17423 [000] 423271.557970338: 5593ad3346d7 perf_evsel= __enable_cpu+0x97 (/work/linux/tools/perf/perf) insn: testl %eax, %eax perf 17423 [000] 423271.557970338: 5593ad3346d9 perf_evsel= __enable_cpu+0x99 (/work/linux/tools/perf/perf) insn: jne perf_evsel__enabl= e_cpu+0xad perf 17423 [000] 423271.557970338: 5593ad3346db perf_evsel= __enable_cpu+0x9b (/work/linux/tools/perf/perf) insn: movq 0xa8(%r12), %rax perf 17423 [000] 423271.557970338: 5593ad3346e3 perf_evsel= __enable_cpu+0xa3 (/work/linux/tools/perf/perf) insn: movq 0x20(%rax), %rdx perf 17423 [000] 423271.557970338: 5593ad3346e7 perf_evsel= __enable_cpu+0xa7 (/work/linux/tools/perf/perf) insn: cmpl %edx, %ebx perf 17423 [000] 423271.557970338: 5593ad3346e9 perf_evsel= __enable_cpu+0xa9 (/work/linux/tools/perf/perf) insn: jl perf_evsel__enable= _cpu+0x60 perf 17423 [000] 423271.557970338: 5593ad3346eb perf_evsel= __enable_cpu+0xab (/work/linux/tools/perf/perf) insn: xorl %eax, %eax Signed-off-by: Changbin Du --- tools/perf/builtin-script.c | 8 +-- tools/perf/util/Build | 1 + tools/perf/util/print_insn.c | 118 +++++++++++++++++++++++++++++++++++ tools/perf/util/print_insn.h | 14 +++++ 4 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 tools/perf/util/print_insn.c create mode 100644 tools/perf/util/print_insn.h diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index b1f57401ff23..c32a326e660f 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -34,6 +34,7 @@ #include "util/event.h" #include "ui/ui.h" #include "print_binary.h" +#include "print_insn.h" #include "archinsn.h" #include #include @@ -1511,11 +1512,8 @@ static int perf_sample__fprintf_insn(struct perf_sam= ple *sample, if (PRINT_FIELD(INSNLEN)) printed +=3D fprintf(fp, " ilen: %d", sample->insn_len); if (PRINT_FIELD(INSN) && sample->insn_len) { - int i; - - printed +=3D fprintf(fp, " insn:"); - for (i =3D 0; i < sample->insn_len; i++) - printed +=3D fprintf(fp, " %02x", (unsigned char)sample->insn[i]); + printed +=3D fprintf(fp, " insn: "); + printed +=3D sample__fprintf_insn(sample, thread, machine, fp); } if (PRINT_FIELD(BRSTACKINSN) || PRINT_FIELD(BRSTACKINSNLEN)) printed +=3D perf_sample__fprintf_brstackinsn(sample, thread, attr, mach= ine, fp); diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 988473bf907a..c33aab53d8dd 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -32,6 +32,7 @@ perf-y +=3D perf_regs.o perf-y +=3D perf-regs-arch/ perf-y +=3D path.o perf-y +=3D print_binary.o +perf-y +=3D print_insn.o perf-y +=3D rlimit.o perf-y +=3D argv_split.o perf-y +=3D rbtree.o diff --git a/tools/perf/util/print_insn.c b/tools/perf/util/print_insn.c new file mode 100644 index 000000000000..c8d9741748cd --- /dev/null +++ b/tools/perf/util/print_insn.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Instruction binary disassembler based on capstone. + * + * Author(s): Changbin Du + */ +#include "print_insn.h" +#include +#include +#include +#include "util/debug.h" +#include "util/symbol.h" +#include "machine.h" + +size_t sample__fprintf_insn_raw(struct perf_sample *sample, FILE *fp) +{ + int printed =3D 0; + + for (int i =3D 0; i < sample->insn_len; i++) + printed +=3D fprintf(fp, "%02x ", (unsigned char)sample->insn[i]); + return printed; +} + +#ifdef HAVE_LIBCAPSTONE_SUPPORT +#include + +static int capstone_init(struct machine *machine, csh *cs_handle) +{ + cs_arch arch; + cs_mode mode; + + if (machine__is(machine, "x86_64")) { + arch =3D CS_ARCH_X86; + mode =3D CS_MODE_64; + } else if (machine__normalized_is(machine, "x86")) { + arch =3D CS_ARCH_X86; + mode =3D CS_MODE_32; + } else if (machine__normalized_is(machine, "arm64")) { + arch =3D CS_ARCH_ARM64; + mode =3D CS_MODE_ARM; + } else if (machine__normalized_is(machine, "arm")) { + arch =3D CS_ARCH_ARM; + mode =3D CS_MODE_ARM + CS_MODE_V8; + } else { + return -1; + } + + if (cs_open(arch, mode, cs_handle) !=3D CS_ERR_OK) { + pr_warning_once("cs_open failed\n"); + return -1; + } + + cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT); + if (machine__normalized_is(machine, "x86")) + cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON); + + return 0; +} + +static size_t print_insn_x86(struct perf_sample *sample, struct thread *th= read, + cs_insn *insn, FILE *fp) +{ + struct addr_location al; + size_t printed =3D 0; + + if (insn->detail && insn->detail->x86.op_count =3D=3D 1) { + cs_x86_op *op =3D &insn->detail->x86.operands[0]; + + addr_location__init(&al); + if (op->type =3D=3D X86_OP_IMM && + thread__find_symbol(thread, sample->cpumode, op->imm, &al)) { + printed +=3D fprintf(fp, "%s ", insn[0].mnemonic); + printed +=3D symbol__fprintf_symname_offs(al.sym, &al, fp); + return printed; + } + } + + printed +=3D fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str); + return printed; +} + +size_t sample__fprintf_insn(struct perf_sample *sample, struct thread *thr= ead, + struct machine *machine, FILE *fp) +{ + static csh cs_handle; + cs_insn *insn; + size_t count; + size_t printed =3D 0; + int ret; + + ret =3D capstone_init(machine, &cs_handle); + if (ret < 0) { + /* fallback */ + return sample__fprintf_insn_raw(sample, fp); + } + + count =3D cs_disasm(cs_handle, (uint8_t *)sample->insn, sample->insn_len, + sample->ip, 1, &insn); + if (count > 0) { + if (machine__normalized_is(machine, "x86")) + printed +=3D print_insn_x86(sample, thread, &insn[0], fp); + else + printed +=3D fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str); + cs_free(insn, count); + } else { + printed +=3D fprintf(fp, "illegal instruction"); + } + + cs_close(&cs_handle); + return printed; +} +#else +size_t sample__fprintf_insn(struct perf_sample *sample, struct thread *thr= ead __maybe_unused, + struct machine *machine __maybe_unused, FILE *fp) +{ + return sample__fprintf_insn_raw(sample, fp); +} +#endif diff --git a/tools/perf/util/print_insn.h b/tools/perf/util/print_insn.h new file mode 100644 index 000000000000..af8fa5d01fb7 --- /dev/null +++ b/tools/perf/util/print_insn.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef PERF_PRINT_ISNS_H +#define PERF_PRINT_ISNS_H + +#include +#include +#include "event.h" +#include "util/thread.h" + +size_t sample__fprintf_insn(struct perf_sample *sample, struct thread *thr= ead, + struct machine *machine, FILE *fp); +size_t sample__fprintf_insn_raw(struct perf_sample *sample, FILE *fp); + +#endif /* PERF_PRINT_ISNS_H */ --=20 2.25.1