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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
/* 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;
}
}
|