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