| /* SPDX-License-Identifier: GPL-2.0 */ |
| #include "sample.h" |
| #include "debug.h" |
| #include "thread.h" |
| #include <elf.h> |
| #ifndef EM_CSKY |
| #define EM_CSKY 252 |
| #endif |
| #ifndef EM_LOONGARCH |
| #define EM_LOONGARCH 258 |
| #endif |
| #include <linux/zalloc.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "../../arch/x86/include/asm/insn.h" |
| |
| void perf_sample__init(struct perf_sample *sample, bool all) |
| { |
| if (all) { |
| memset(sample, 0, sizeof(*sample)); |
| } else { |
| sample->user_regs = NULL; |
| sample->intr_regs = NULL; |
| } |
| } |
| |
| void perf_sample__exit(struct perf_sample *sample) |
| { |
| free(sample->user_regs); |
| free(sample->intr_regs); |
| } |
| |
| struct regs_dump *perf_sample__user_regs(struct perf_sample *sample) |
| { |
| if (!sample->user_regs) { |
| sample->user_regs = zalloc(sizeof(*sample->user_regs)); |
| if (!sample->user_regs) |
| pr_err("Failure to allocate sample user_regs"); |
| } |
| return sample->user_regs; |
| } |
| |
| |
| struct regs_dump *perf_sample__intr_regs(struct perf_sample *sample) |
| { |
| if (!sample->intr_regs) { |
| sample->intr_regs = zalloc(sizeof(*sample->intr_regs)); |
| if (!sample->intr_regs) |
| pr_err("Failure to allocate sample intr_regs"); |
| } |
| return sample->intr_regs; |
| } |
| |
| static int elf_machine_max_instruction_length(uint16_t e_machine) |
| { |
| switch (e_machine) { |
| /* Fixed 4-byte (32-bit) architectures */ |
| case EM_AARCH64: |
| case EM_PPC: |
| case EM_PPC64: |
| case EM_MIPS: |
| case EM_SPARC: |
| case EM_SPARCV9: |
| case EM_ALPHA: |
| case EM_LOONGARCH: |
| case EM_PARISC: |
| case EM_SH: |
| return 4; |
| |
| /* Variable length or mixed-mode architectures */ |
| case EM_ARM: /* Variable due to Thumb/Thumb-2 */ |
| case EM_RISCV: /* Variable due to Compressed (C) extension */ |
| case EM_CSKY: /* Variable (16 or 32 bit) */ |
| case EM_ARC: /* Variable (ARCompact) */ |
| return 4; |
| case EM_S390: /* Variable (2, 4, or 6 bytes) */ |
| return 6; |
| case EM_68K: |
| return 10; |
| case EM_386: |
| case EM_X86_64: |
| return 15; |
| case EM_XTENSA: /* Variable (FLIX) */ |
| return 16; |
| default: |
| return MAX_INSN; |
| } |
| } |
| |
| void perf_sample__fetch_insn(struct perf_sample *sample, |
| struct thread *thread, |
| struct machine *machine) |
| { |
| int ret, len; |
| bool is64bit = false; |
| uint16_t e_machine; |
| |
| if (!sample->ip || sample->insn_len != 0) |
| return; |
| |
| e_machine = thread__e_machine(thread, machine, /*e_flags=*/NULL); |
| len = elf_machine_max_instruction_length(e_machine); |
| len = thread__memcpy(thread, machine, sample->insn, |
| sample->ip, len, |
| &is64bit); |
| if (len <= 0) |
| return; |
| |
| sample->insn_len = len; |
| |
| if (e_machine == EM_386 || e_machine == EM_X86_64) { |
| /* Refine the x86 instruction length with the decoder. */ |
| struct insn insn; |
| |
| ret = insn_decode(&insn, sample->insn, len, |
| is64bit ? INSN_MODE_64 : INSN_MODE_32); |
| if (ret >= 0 && insn.length <= len) |
| sample->insn_len = insn.length; |
| } |
| } |