Merge branch 'sched-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.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 <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 }