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