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 static enum hisi_ptt_pkt_type hisi_ptt_check_packet_type(unsigned char *buf) 39 { 40 uint32_t head = *(uint32_t *)buf; 41 42 if ((HISI_PTT_8DW_CHECK_MASK & head) == HISI_PTT_IS_8DW_PKT) 43 return HISI_PTT_8DW_PKT; 44 45 return HISI_PTT_4DW_PKT; 46 } 47 48 static void hisi_ptt_dump(struct hisi_ptt *ptt __maybe_unused, 49 unsigned char *buf, size_t len) 50 { 51 const char *color = PERF_COLOR_BLUE; 52 enum hisi_ptt_pkt_type type; 53 size_t pos = 0; 54 int pkt_len; 55 56 type = hisi_ptt_check_packet_type(buf); 57 len = round_down(len, hisi_ptt_pkt_size[type]); 58 color_fprintf(stdout, color, ". ... HISI PTT data: size %zu bytes\n", 59 len); 60 61 while (len > 0) { 62 pkt_len = hisi_ptt_pkt_desc(buf, pos, type); 63 if (!pkt_len) 64 color_fprintf(stdout, color, " Bad packet!\n"); 65 66 pos += pkt_len; 67 len -= pkt_len; 68 } 69 } 70 71 static void hisi_ptt_dump_event(struct hisi_ptt *ptt, unsigned char *buf, 72 size_t len) 73 { 74 printf(".\n"); 75 76 hisi_ptt_dump(ptt, buf, len); 77 } 78 79 static int hisi_ptt_process_event(struct perf_session *session __maybe_unused, 80 union perf_event *event __maybe_unused, 81 struct perf_sample *sample __maybe_unused, 82 const struct perf_tool *tool __maybe_unused) 83 { 84 return 0; 85 } 86 87 static int hisi_ptt_process_auxtrace_event(struct perf_session *session, 88 union perf_event *event, 89 const struct perf_tool *tool __maybe_unused) 90 { 91 struct hisi_ptt *ptt = container_of(session->auxtrace, struct hisi_ptt, 92 auxtrace); 93 int fd = perf_data__fd(session->data); 94 int size = event->auxtrace.size; 95 void *data = malloc(size); 96 off_t data_offset; 97 int err; 98 99 if (!data) 100 return -errno; 101 102 if (perf_data__is_pipe(session->data)) { 103 data_offset = 0; 104 } else { 105 data_offset = lseek(fd, 0, SEEK_CUR); 106 if (data_offset == -1) { 107 free(data); 108 return -errno; 109 } 110 } 111 112 err = readn(fd, data, size); 113 if (err != (ssize_t)size) { 114 free(data); 115 return -errno; 116 } 117 118 if (dump_trace) 119 hisi_ptt_dump_event(ptt, data, size); 120 121 free(data); 122 return 0; 123 } 124 125 static int hisi_ptt_flush(struct perf_session *session __maybe_unused, 126 const struct perf_tool *tool __maybe_unused) 127 { 128 return 0; 129 } 130 131 static void hisi_ptt_free_events(struct perf_session *session __maybe_unused) 132 { 133 } 134 135 static void hisi_ptt_free(struct perf_session *session) 136 { 137 struct hisi_ptt *ptt = container_of(session->auxtrace, struct hisi_ptt, 138 auxtrace); 139 140 session->auxtrace = NULL; 141 free(ptt); 142 } 143 144 static bool hisi_ptt_evsel_is_auxtrace(struct perf_session *session, 145 struct evsel *evsel) 146 { 147 struct hisi_ptt *ptt = container_of(session->auxtrace, struct hisi_ptt, auxtrace); 148 149 return evsel->core.attr.type == ptt->pmu_type; 150 } 151 152 static void hisi_ptt_print_info(__u64 type) 153 { 154 if (!dump_trace) 155 return; 156 157 fprintf(stdout, " PMU Type %" PRId64 "\n", (s64) type); 158 } 159 160 int hisi_ptt_process_auxtrace_info(union perf_event *event, 161 struct perf_session *session) 162 { 163 struct perf_record_auxtrace_info *auxtrace_info = &event->auxtrace_info; 164 struct hisi_ptt *ptt; 165 166 if (auxtrace_info->header.size < HISI_PTT_AUXTRACE_PRIV_SIZE + 167 sizeof(struct perf_record_auxtrace_info)) 168 return -EINVAL; 169 170 ptt = zalloc(sizeof(*ptt)); 171 if (!ptt) 172 return -ENOMEM; 173 174 ptt->session = session; 175 ptt->machine = &session->machines.host; /* No kvm support */ 176 ptt->auxtrace_type = auxtrace_info->type; 177 ptt->pmu_type = auxtrace_info->priv[0]; 178 179 ptt->auxtrace.process_event = hisi_ptt_process_event; 180 ptt->auxtrace.process_auxtrace_event = hisi_ptt_process_auxtrace_event; 181 ptt->auxtrace.flush_events = hisi_ptt_flush; 182 ptt->auxtrace.free_events = hisi_ptt_free_events; 183 ptt->auxtrace.free = hisi_ptt_free; 184 ptt->auxtrace.evsel_is_auxtrace = hisi_ptt_evsel_is_auxtrace; 185 session->auxtrace = &ptt->auxtrace; 186 187 hisi_ptt_print_info(auxtrace_info->priv[0]); 188 189 return 0; 190 } 191