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 return -errno; 113 } 114 115 err = readn(fd, data, size); 116 if (err != (ssize_t)size) { 117 free(data); 118 return -errno; 119 } 120 121 if (dump_trace) 122 hisi_ptt_dump_event(ptt, data, size); 123 124 free(data); 125 return 0; 126 } 127 128 static int hisi_ptt_flush(struct perf_session *session __maybe_unused, 129 struct perf_tool *tool __maybe_unused) 130 { 131 return 0; 132 } 133 134 static void hisi_ptt_free_events(struct perf_session *session __maybe_unused) 135 { 136 } 137 138 static void hisi_ptt_free(struct perf_session *session) 139 { 140 struct hisi_ptt *ptt = container_of(session->auxtrace, struct hisi_ptt, 141 auxtrace); 142 143 session->auxtrace = NULL; 144 free(ptt); 145 } 146 147 static bool hisi_ptt_evsel_is_auxtrace(struct perf_session *session, 148 struct evsel *evsel) 149 { 150 struct hisi_ptt *ptt = container_of(session->auxtrace, struct hisi_ptt, auxtrace); 151 152 return evsel->core.attr.type == ptt->pmu_type; 153 } 154 155 static void hisi_ptt_print_info(__u64 type) 156 { 157 if (!dump_trace) 158 return; 159 160 fprintf(stdout, " PMU Type %" PRId64 "\n", (s64) type); 161 } 162 163 int hisi_ptt_process_auxtrace_info(union perf_event *event, 164 struct perf_session *session) 165 { 166 struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info; 167 struct hisi_ptt *ptt; 168 169 if (auxtrace_info->header.size < HISI_PTT_AUXTRACE_PRIV_SIZE + 170 sizeof(struct perf_record_auxtrace_info)) 171 return -EINVAL; 172 173 ptt = zalloc(sizeof(*ptt)); 174 if (!ptt) 175 return -ENOMEM; 176 177 ptt->session = session; 178 ptt->machine = &session->machines.host; /* No kvm support */ 179 ptt->auxtrace_type = auxtrace_info->type; 180 ptt->pmu_type = auxtrace_info->priv[0]; 181 182 ptt->auxtrace.process_event = hisi_ptt_process_event; 183 ptt->auxtrace.process_auxtrace_event = hisi_ptt_process_auxtrace_event; 184 ptt->auxtrace.flush_events = hisi_ptt_flush; 185 ptt->auxtrace.free_events = hisi_ptt_free_events; 186 ptt->auxtrace.free = hisi_ptt_free; 187 ptt->auxtrace.evsel_is_auxtrace = hisi_ptt_evsel_is_auxtrace; 188 session->auxtrace = &ptt->auxtrace; 189 190 hisi_ptt_print_info(auxtrace_info->priv[0]); 191 192 return 0; 193 } 194