diff options
Diffstat (limited to 'tools/perf/util/arm-spe.c')
-rw-r--r-- | tools/perf/util/arm-spe.c | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c new file mode 100644 index 000000000000..6067267cc76c --- /dev/null +++ b/tools/perf/util/arm-spe.c | |||
@@ -0,0 +1,231 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Arm Statistical Profiling Extensions (SPE) support | ||
4 | * Copyright (c) 2017-2018, Arm Ltd. | ||
5 | */ | ||
6 | |||
7 | #include <endian.h> | ||
8 | #include <errno.h> | ||
9 | #include <byteswap.h> | ||
10 | #include <inttypes.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/bitops.h> | ||
14 | #include <linux/log2.h> | ||
15 | |||
16 | #include "cpumap.h" | ||
17 | #include "color.h" | ||
18 | #include "evsel.h" | ||
19 | #include "evlist.h" | ||
20 | #include "machine.h" | ||
21 | #include "session.h" | ||
22 | #include "util.h" | ||
23 | #include "thread.h" | ||
24 | #include "debug.h" | ||
25 | #include "auxtrace.h" | ||
26 | #include "arm-spe.h" | ||
27 | #include "arm-spe-pkt-decoder.h" | ||
28 | |||
29 | struct arm_spe { | ||
30 | struct auxtrace auxtrace; | ||
31 | struct auxtrace_queues queues; | ||
32 | struct auxtrace_heap heap; | ||
33 | u32 auxtrace_type; | ||
34 | struct perf_session *session; | ||
35 | struct machine *machine; | ||
36 | u32 pmu_type; | ||
37 | }; | ||
38 | |||
39 | struct arm_spe_queue { | ||
40 | struct arm_spe *spe; | ||
41 | unsigned int queue_nr; | ||
42 | struct auxtrace_buffer *buffer; | ||
43 | bool on_heap; | ||
44 | bool done; | ||
45 | pid_t pid; | ||
46 | pid_t tid; | ||
47 | int cpu; | ||
48 | }; | ||
49 | |||
50 | static void arm_spe_dump(struct arm_spe *spe __maybe_unused, | ||
51 | unsigned char *buf, size_t len) | ||
52 | { | ||
53 | struct arm_spe_pkt packet; | ||
54 | size_t pos = 0; | ||
55 | int ret, pkt_len, i; | ||
56 | char desc[ARM_SPE_PKT_DESC_MAX]; | ||
57 | const char *color = PERF_COLOR_BLUE; | ||
58 | |||
59 | color_fprintf(stdout, color, | ||
60 | ". ... ARM SPE data: size %zu bytes\n", | ||
61 | len); | ||
62 | |||
63 | while (len) { | ||
64 | ret = arm_spe_get_packet(buf, len, &packet); | ||
65 | if (ret > 0) | ||
66 | pkt_len = ret; | ||
67 | else | ||
68 | pkt_len = 1; | ||
69 | printf("."); | ||
70 | color_fprintf(stdout, color, " %08x: ", pos); | ||
71 | for (i = 0; i < pkt_len; i++) | ||
72 | color_fprintf(stdout, color, " %02x", buf[i]); | ||
73 | for (; i < 16; i++) | ||
74 | color_fprintf(stdout, color, " "); | ||
75 | if (ret > 0) { | ||
76 | ret = arm_spe_pkt_desc(&packet, desc, | ||
77 | ARM_SPE_PKT_DESC_MAX); | ||
78 | if (ret > 0) | ||
79 | color_fprintf(stdout, color, " %s\n", desc); | ||
80 | } else { | ||
81 | color_fprintf(stdout, color, " Bad packet!\n"); | ||
82 | } | ||
83 | pos += pkt_len; | ||
84 | buf += pkt_len; | ||
85 | len -= pkt_len; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | static void arm_spe_dump_event(struct arm_spe *spe, unsigned char *buf, | ||
90 | size_t len) | ||
91 | { | ||
92 | printf(".\n"); | ||
93 | arm_spe_dump(spe, buf, len); | ||
94 | } | ||
95 | |||
96 | static int arm_spe_process_event(struct perf_session *session __maybe_unused, | ||
97 | union perf_event *event __maybe_unused, | ||
98 | struct perf_sample *sample __maybe_unused, | ||
99 | struct perf_tool *tool __maybe_unused) | ||
100 | { | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int arm_spe_process_auxtrace_event(struct perf_session *session, | ||
105 | union perf_event *event, | ||
106 | struct perf_tool *tool __maybe_unused) | ||
107 | { | ||
108 | struct arm_spe *spe = container_of(session->auxtrace, struct arm_spe, | ||
109 | auxtrace); | ||
110 | struct auxtrace_buffer *buffer; | ||
111 | off_t data_offset; | ||
112 | int fd = perf_data__fd(session->data); | ||
113 | int err; | ||
114 | |||
115 | if (perf_data__is_pipe(session->data)) { | ||
116 | data_offset = 0; | ||
117 | } else { | ||
118 | data_offset = lseek(fd, 0, SEEK_CUR); | ||
119 | if (data_offset == -1) | ||
120 | return -errno; | ||
121 | } | ||
122 | |||
123 | err = auxtrace_queues__add_event(&spe->queues, session, event, | ||
124 | data_offset, &buffer); | ||
125 | if (err) | ||
126 | return err; | ||
127 | |||
128 | /* Dump here now we have copied a piped trace out of the pipe */ | ||
129 | if (dump_trace) { | ||
130 | if (auxtrace_buffer__get_data(buffer, fd)) { | ||
131 | arm_spe_dump_event(spe, buffer->data, | ||
132 | buffer->size); | ||
133 | auxtrace_buffer__put_data(buffer); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static int arm_spe_flush(struct perf_session *session __maybe_unused, | ||
141 | struct perf_tool *tool __maybe_unused) | ||
142 | { | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static void arm_spe_free_queue(void *priv) | ||
147 | { | ||
148 | struct arm_spe_queue *speq = priv; | ||
149 | |||
150 | if (!speq) | ||
151 | return; | ||
152 | free(speq); | ||
153 | } | ||
154 | |||
155 | static void arm_spe_free_events(struct perf_session *session) | ||
156 | { | ||
157 | struct arm_spe *spe = container_of(session->auxtrace, struct arm_spe, | ||
158 | auxtrace); | ||
159 | struct auxtrace_queues *queues = &spe->queues; | ||
160 | unsigned int i; | ||
161 | |||
162 | for (i = 0; i < queues->nr_queues; i++) { | ||
163 | arm_spe_free_queue(queues->queue_array[i].priv); | ||
164 | queues->queue_array[i].priv = NULL; | ||
165 | } | ||
166 | auxtrace_queues__free(queues); | ||
167 | } | ||
168 | |||
169 | static void arm_spe_free(struct perf_session *session) | ||
170 | { | ||
171 | struct arm_spe *spe = container_of(session->auxtrace, struct arm_spe, | ||
172 | auxtrace); | ||
173 | |||
174 | auxtrace_heap__free(&spe->heap); | ||
175 | arm_spe_free_events(session); | ||
176 | session->auxtrace = NULL; | ||
177 | free(spe); | ||
178 | } | ||
179 | |||
180 | static const char * const arm_spe_info_fmts[] = { | ||
181 | [ARM_SPE_PMU_TYPE] = " PMU Type %"PRId64"\n", | ||
182 | }; | ||
183 | |||
184 | static void arm_spe_print_info(u64 *arr) | ||
185 | { | ||
186 | if (!dump_trace) | ||
187 | return; | ||
188 | |||
189 | fprintf(stdout, arm_spe_info_fmts[ARM_SPE_PMU_TYPE], arr[ARM_SPE_PMU_TYPE]); | ||
190 | } | ||
191 | |||
192 | int arm_spe_process_auxtrace_info(union perf_event *event, | ||
193 | struct perf_session *session) | ||
194 | { | ||
195 | struct auxtrace_info_event *auxtrace_info = &event->auxtrace_info; | ||
196 | size_t min_sz = sizeof(u64) * ARM_SPE_PMU_TYPE; | ||
197 | struct arm_spe *spe; | ||
198 | int err; | ||
199 | |||
200 | if (auxtrace_info->header.size < sizeof(struct auxtrace_info_event) + | ||
201 | min_sz) | ||
202 | return -EINVAL; | ||
203 | |||
204 | spe = zalloc(sizeof(struct arm_spe)); | ||
205 | if (!spe) | ||
206 | return -ENOMEM; | ||
207 | |||
208 | err = auxtrace_queues__init(&spe->queues); | ||
209 | if (err) | ||
210 | goto err_free; | ||
211 | |||
212 | spe->session = session; | ||
213 | spe->machine = &session->machines.host; /* No kvm support */ | ||
214 | spe->auxtrace_type = auxtrace_info->type; | ||
215 | spe->pmu_type = auxtrace_info->priv[ARM_SPE_PMU_TYPE]; | ||
216 | |||
217 | spe->auxtrace.process_event = arm_spe_process_event; | ||
218 | spe->auxtrace.process_auxtrace_event = arm_spe_process_auxtrace_event; | ||
219 | spe->auxtrace.flush_events = arm_spe_flush; | ||
220 | spe->auxtrace.free_events = arm_spe_free_events; | ||
221 | spe->auxtrace.free = arm_spe_free; | ||
222 | session->auxtrace = &spe->auxtrace; | ||
223 | |||
224 | arm_spe_print_info(&auxtrace_info->priv[0]); | ||
225 | |||
226 | return 0; | ||
227 | |||
228 | err_free: | ||
229 | free(spe); | ||
230 | return err; | ||
231 | } | ||