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