| /* SPDX-License-Identifier: GPL-2.0 */ | |
| #include "sample.h" | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <elf.h> | |
| #include <linux/zalloc.h> | |
| #include "../../arch/x86/include/asm/insn.h" | |
| #include "debug.h" | |
| #include "evsel.h" | |
| #include "thread.h" | |
| #ifndef EM_CSKY | |
| #define EM_CSKY 252 | |
| #endif | |
| #ifndef EM_LOONGARCH | |
| #define EM_LOONGARCH 258 | |
| #endif | |
| void perf_sample__init(struct perf_sample *sample, bool all) | |
| { | |
| if (all) { | |
| memset(sample, 0, sizeof(*sample)); | |
| } else { | |
| sample->evsel = NULL; | |
| sample->user_regs = NULL; | |
| sample->intr_regs = NULL; | |
| sample->merged_callchain = false; | |
| sample->callchain = NULL; | |
| } | |
| } | |
| void perf_sample__exit(struct perf_sample *sample) | |
| { | |
| evsel__put(sample->evsel); | |
| sample->evsel = NULL; | |
| zfree(&sample->user_regs); | |
| zfree(&sample->intr_regs); | |
| if (sample->merged_callchain) { | |
| zfree(&sample->callchain); | |
| sample->merged_callchain = false; | |
| } | |
| } | |
| 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; | |
| } | |
| } |