summaryrefslogtreecommitdiff
path: root/tools/perf/util/sample.c
blob: 8f82aaf1aab6e1c2cf258418048e807eef2f903e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/* 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;
	}
}