mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
perf: util: use capstone disasm engine to show assembly instructions
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. Signed-off-by: Changbin Du <changbin.du@huawei.com> Reviewed-by: Adrian Hunter <adrian.hunter@intel.com> Cc: changbin.du@gmail.com Cc: Thomas Richter <tmricht@linux.ibm.com> Cc: Andi Kleen <ak@linux.intel.com> Signed-off-by: Namhyung Kim <namhyung@kernel.org> Link: https://lore.kernel.org/r/20240217074046.4100789-3-changbin.du@huawei.com
This commit is contained in:
parent
8b767db330
commit
8f0ec15ff6
@ -34,6 +34,7 @@
|
||||
#include "util/event.h"
|
||||
#include "ui/ui.h"
|
||||
#include "print_binary.h"
|
||||
#include "print_insn.h"
|
||||
#include "archinsn.h"
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -1523,11 +1524,8 @@ static int perf_sample__fprintf_insn(struct perf_sample *sample,
|
||||
if (PRINT_FIELD(INSNLEN))
|
||||
printed += fprintf(fp, " ilen: %d", sample->insn_len);
|
||||
if (PRINT_FIELD(INSN) && sample->insn_len) {
|
||||
int i;
|
||||
|
||||
printed += fprintf(fp, " insn:");
|
||||
for (i = 0; i < sample->insn_len; i++)
|
||||
printed += fprintf(fp, " %02x", (unsigned char)sample->insn[i]);
|
||||
printed += fprintf(fp, " insn: ");
|
||||
printed += sample__fprintf_insn_raw(sample, fp);
|
||||
}
|
||||
if (PRINT_FIELD(BRSTACKINSN) || PRINT_FIELD(BRSTACKINSNLEN))
|
||||
printed += perf_sample__fprintf_brstackinsn(sample, thread, attr, machine, fp);
|
||||
|
@ -32,6 +32,7 @@ perf-y += perf_regs.o
|
||||
perf-y += perf-regs-arch/
|
||||
perf-y += path.o
|
||||
perf-y += print_binary.o
|
||||
perf-y += print_insn.o
|
||||
perf-y += rlimit.o
|
||||
perf-y += argv_split.o
|
||||
perf-y += rbtree.o
|
||||
|
135
tools/perf/util/print_insn.c
Normal file
135
tools/perf/util/print_insn.c
Normal file
@ -0,0 +1,135 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Instruction binary disassembler based on capstone.
|
||||
*
|
||||
* Author(s): Changbin Du <changbin.du@huawei.com>
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "debug.h"
|
||||
#include "sample.h"
|
||||
#include "symbol.h"
|
||||
#include "machine.h"
|
||||
#include "thread.h"
|
||||
#include "print_insn.h"
|
||||
|
||||
size_t sample__fprintf_insn_raw(struct perf_sample *sample, FILE *fp)
|
||||
{
|
||||
int printed = 0;
|
||||
|
||||
for (int i = 0; i < sample->insn_len; i++) {
|
||||
printed += fprintf(fp, "%02x", (unsigned char)sample->insn[i]);
|
||||
if (sample->insn_len - i > 1)
|
||||
printed += fprintf(fp, " ");
|
||||
}
|
||||
return printed;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBCAPSTONE_SUPPORT
|
||||
#include <capstone/capstone.h>
|
||||
|
||||
static int capstone_init(struct machine *machine, csh *cs_handle)
|
||||
{
|
||||
cs_arch arch;
|
||||
cs_mode mode;
|
||||
|
||||
if (machine__is(machine, "x86_64")) {
|
||||
arch = CS_ARCH_X86;
|
||||
mode = CS_MODE_64;
|
||||
} else if (machine__normalized_is(machine, "x86")) {
|
||||
arch = CS_ARCH_X86;
|
||||
mode = CS_MODE_32;
|
||||
} else if (machine__normalized_is(machine, "arm64")) {
|
||||
arch = CS_ARCH_ARM64;
|
||||
mode = CS_MODE_ARM;
|
||||
} else if (machine__normalized_is(machine, "arm")) {
|
||||
arch = CS_ARCH_ARM;
|
||||
mode = CS_MODE_ARM + CS_MODE_V8;
|
||||
} else if (machine__normalized_is(machine, "s390")) {
|
||||
arch = CS_ARCH_SYSZ;
|
||||
mode = CS_MODE_BIG_ENDIAN;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cs_open(arch, mode, cs_handle) != CS_ERR_OK) {
|
||||
pr_warning_once("cs_open failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (machine__normalized_is(machine, "x86")) {
|
||||
cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);
|
||||
/*
|
||||
* Resolving address operands to symbols is implemented
|
||||
* on x86 by investigating instruction details.
|
||||
*/
|
||||
cs_option(*cs_handle, CS_OPT_DETAIL, CS_OPT_ON);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t print_insn_x86(struct perf_sample *sample, struct thread *thread,
|
||||
cs_insn *insn, FILE *fp)
|
||||
{
|
||||
struct addr_location al;
|
||||
size_t printed = 0;
|
||||
|
||||
if (insn->detail && insn->detail->x86.op_count == 1) {
|
||||
cs_x86_op *op = &insn->detail->x86.operands[0];
|
||||
|
||||
addr_location__init(&al);
|
||||
if (op->type == X86_OP_IMM &&
|
||||
thread__find_symbol(thread, sample->cpumode, op->imm, &al)) {
|
||||
printed += fprintf(fp, "%s ", insn[0].mnemonic);
|
||||
printed += symbol__fprintf_symname_offs(al.sym, &al, fp);
|
||||
addr_location__exit(&al);
|
||||
return printed;
|
||||
}
|
||||
addr_location__exit(&al);
|
||||
}
|
||||
|
||||
printed += fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);
|
||||
return printed;
|
||||
}
|
||||
|
||||
size_t sample__fprintf_insn_asm(struct perf_sample *sample, struct thread *thread,
|
||||
struct machine *machine, FILE *fp)
|
||||
{
|
||||
csh cs_handle;
|
||||
cs_insn *insn;
|
||||
size_t count;
|
||||
size_t printed = 0;
|
||||
int ret;
|
||||
|
||||
/* TODO: Try to initiate capstone only once but need a proper place. */
|
||||
ret = capstone_init(machine, &cs_handle);
|
||||
if (ret < 0) {
|
||||
/* fallback */
|
||||
return sample__fprintf_insn_raw(sample, fp);
|
||||
}
|
||||
|
||||
count = 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 += print_insn_x86(sample, thread, &insn[0], fp);
|
||||
else
|
||||
printed += fprintf(fp, "%s %s", insn[0].mnemonic, insn[0].op_str);
|
||||
cs_free(insn, count);
|
||||
} else {
|
||||
printed += fprintf(fp, "illegal instruction");
|
||||
}
|
||||
|
||||
cs_close(&cs_handle);
|
||||
return printed;
|
||||
}
|
||||
#else
|
||||
size_t sample__fprintf_insn_asm(struct perf_sample *sample __maybe_unused,
|
||||
struct thread *thread __maybe_unused,
|
||||
struct machine *machine __maybe_unused,
|
||||
FILE *fp __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
16
tools/perf/util/print_insn.h
Normal file
16
tools/perf/util/print_insn.h
Normal file
@ -0,0 +1,16 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef PERF_PRINT_INSN_H
|
||||
#define PERF_PRINT_INSN_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct perf_sample;
|
||||
struct thread;
|
||||
struct machine;
|
||||
|
||||
size_t sample__fprintf_insn_asm(struct perf_sample *sample, struct thread *thread,
|
||||
struct machine *machine, FILE *fp);
|
||||
size_t sample__fprintf_insn_raw(struct perf_sample *sample, FILE *fp);
|
||||
|
||||
#endif /* PERF_PRINT_INSN_H */
|
@ -13,7 +13,6 @@
|
||||
#include <strlist.h>
|
||||
#include <intlist.h>
|
||||
#include "rwsem.h"
|
||||
#include "event.h"
|
||||
#include "callchain.h"
|
||||
#include <internal/rc_check.h>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user