GNU Linux-libre 4.19.211-gnu1
[releases.git] / tools / perf / util / arm-spe.c
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 }