1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * HiSilicon PCIe Trace and Tuning (PTT) support 4 * Copyright (c) 2022 HiSilicon Technologies Co., Ltd. 5 */ 6 7 #include <byteswap.h> 8 #include <endian.h> 9 #include <errno.h> 10 #include <inttypes.h> 11 #include <linux/bitops.h> 12 #include <linux/kernel.h> 13 #include <linux/log2.h> 14 #include <linux/types.h> 15 #include <linux/zalloc.h> 16 #include <stdlib.h> 17 #include <unistd.h> 18 19 #include "auxtrace.h" 20 #include "color.h" 21 #include "debug.h" 22 #include "evsel.h" 23 #include "hisi-ptt.h" 24 #include "hisi-ptt-decoder/hisi-ptt-pkt-decoder.h" 25 #include "machine.h" 26 #include "session.h" 27 #include "tool.h" 28 #include <internal/lib.h> 29 30 struct hisi_ptt { 31 struct auxtrace auxtrace; 32 u32 auxtrace_type; 33 struct perf_session *session; 34 struct machine *machine; 35 u32 pmu_type; 36 }; 37 38 struct hisi_ptt_queue { 39 struct hisi_ptt *ptt; 40 struct auxtrace_buffer *buffer; 41 }; 42 43 static enum hisi_ptt_pkt_type hisi_ptt_check_packet_type(unsigned char *buf) 44 { 45 uint32_t head = *(uint32_t *)buf; 46 47 if ((HISI_PTT_8DW_CHECK_MASK & head) == HISI_PTT_IS_8DW_PKT) 48 return HISI_PTT_8DW_PKT; 49 50 return HISI_PTT_4DW_PKT; 51 } 52 53 static void hisi_ptt_dump(struct hisi_ptt *ptt __maybe_unused, 54 unsigned char *buf, size_t len) 55 { 56 const char *color = PERF_COLOR_BLUE; 57 enum hisi_ptt_pkt_type type; 58 size_t pos = 0; 59 int pkt_len; 60 61 type = hisi_ptt_check_packet_type(buf); 62 len = round_down(len, hisi_ptt_pkt_size[type]); 63 color_fprintf(stdout, color, ". ... HISI PTT data: size %zu bytes\n", 64 len); 65 66 while (len > 0) { 67 pkt_len = hisi_ptt_pkt_desc(buf, pos, type); 68 if (!pkt_len) 69 color_fprintf(stdout, color, " Bad packet!\n"); 70 71 pos += pkt_len; 72 len -= pkt_len; 73 } 74 } 75 76 static void hisi_ptt_dump_event(struct hisi_ptt *ptt, unsigned char *buf, 77 size_t len) 78 { 79 printf(".\n"); 80 81 hisi_ptt_dump(ptt, buf, len); 82 } 83 84 static int hisi_ptt_process_event(struct perf_session *session __maybe_unused, 85 union perf_event *event __maybe_unused, 86 struct perf_sample *sample __maybe_unused, 87 struct perf_tool *tool __maybe_unused) 88 { 89 return 0; 90 } 91 92 static int hisi_ptt_process_auxtrace_event(struct perf_session *session, 93 union perf_event *event, 94 struct perf_tool *tool __maybe_unused) 95 { 96 struct hisi_ptt *ptt = container_of(session->auxtrace, struct hisi_ptt, 97 auxtrace); 98 int fd = perf_data__fd(session->data); 99 int size = event->auxtrace.size; 100 void *data = malloc(size); 101 off_t data_offset; 102 int err; 103 104 if (!data) 105 return -errno; 106 107 if (perf_data__is_pipe(session->data)) { 108 data_offset = 0; 109 } else { 110 data_offset = lseek(fd, 0, SEEK_CUR); 111 if (data_offset == -1) { 112 free(data); 113 return -errno; 114 } 115 } 116 117 err = readn(fd, data, size); 118 if (err != (ssize_t)size) { 119 free(data); 120 return -errno; 121 } 122 123 if (dump_trace) 124 hisi_ptt_dump_event(ptt, data, size); 125 126 free(data); 127 return 0; 128 } 129 130 static int hisi_ptt_flush(struct perf_session *session __maybe_unused, 131 struct perf_tool *tool __maybe_unused) 132 { 133 return 0; 134 } 135 136 static void hisi_ptt_free_events(struct perf_session *session __maybe_unused) 137 { 138 } 139 140 static void hisi_ptt_free(struct perf_session *session) 141 { 142 struct hisi_ptt *ptt = container_of(session->auxtrace, struct hisi_ptt, 143 auxtrace); 144 145 session->auxtrace = NULL; 146 free(ptt); 147 } 148 149 static bool hisi_ptt_evsel_is_auxtrace(struct perf_session *session, 150 struct evsel *evsel) 151 { 152 struct hisi_ptt *ptt = container_of(session->auxtrace, struct hisi_ptt, auxtrace); 153 154 return evsel->core.attr.type == ptt->pmu_type; 155 } 156 157 static void hisi_ptt_print_info(__u64 type) 158 { 159 if (!dump_trace) 160 return; 161 162 fprintf(stdout, " PMU Type %" PRId64 "\n", (s64) type); 163 } 164 165 int hisi_ptt_process_auxtrace_info(union perf_event *event, 166 struct perf_session *session) 167 { 168 struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info; 169 struct hisi_ptt *ptt; 170 171 if (auxtrace_info->header.size < HISI_PTT_AUXTRACE_PRIV_SIZE + 172 sizeof(struct perf_record_auxtrace_info)) 173 return -EINVAL; 174 175 ptt = zalloc(sizeof(*ptt)); 176 if (!ptt) 177 return -ENOMEM; 178 179 ptt->session = session; 180 ptt->machine = &session->machines.host; /* No kvm support */ 181 ptt->auxtrace_type = auxtrace_info->type; 182 ptt->pmu_type = auxtrace_info->priv[0]; 183 184 ptt->auxtrace.process_event = hisi_ptt_process_event; 185 ptt->auxtrace.process_auxtrace_event = hisi_ptt_process_auxtrace_event; 186 ptt->auxtrace.flush_events = hisi_ptt_flush; 187 ptt->auxtrace.free_events = hisi_ptt_free_events; 188 ptt->auxtrace.free = hisi_ptt_free; 189 ptt->auxtrace.evsel_is_auxtrace = hisi_ptt_evsel_is_auxtrace; 190 session->auxtrace = &ptt->auxtrace; 191 192 hisi_ptt_print_info(auxtrace_info->priv[0]); 193 194 return 0; 195 } 196