xref: /openbmc/linux/tools/perf/util/arm-spe.c (revision a20eefae)
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 #include <linux/zalloc.h>
16 
17 #include "cpumap.h"
18 #include "color.h"
19 #include "evsel.h"
20 #include "evlist.h"
21 #include "machine.h"
22 #include "session.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 }
232