1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * intel_pt_log.c: Intel Processor Trace support 4 * Copyright (c) 2013-2014, Intel Corporation. 5 */ 6 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <stdint.h> 10 #include <inttypes.h> 11 #include <stdarg.h> 12 #include <stdbool.h> 13 #include <string.h> 14 15 #include <linux/zalloc.h> 16 #include <linux/kernel.h> 17 18 #include "intel-pt-log.h" 19 #include "intel-pt-insn-decoder.h" 20 21 #include "intel-pt-pkt-decoder.h" 22 23 #define MAX_LOG_NAME 256 24 25 #define DFLT_BUF_SZ (16 * 1024) 26 27 struct log_buf { 28 char *buf; 29 size_t buf_sz; 30 size_t head; 31 bool wrapped; 32 FILE *backend; 33 }; 34 35 static FILE *f; 36 static char log_name[MAX_LOG_NAME]; 37 bool intel_pt_enable_logging; 38 static bool intel_pt_dump_log_on_error; 39 static unsigned int intel_pt_log_on_error_size; 40 static struct log_buf log_buf; 41 42 void *intel_pt_log_fp(void) 43 { 44 return f; 45 } 46 47 void intel_pt_log_enable(bool dump_log_on_error, unsigned int log_on_error_size) 48 { 49 intel_pt_enable_logging = true; 50 intel_pt_dump_log_on_error = dump_log_on_error; 51 intel_pt_log_on_error_size = log_on_error_size; 52 } 53 54 void intel_pt_log_disable(void) 55 { 56 if (f) 57 fflush(f); 58 intel_pt_enable_logging = false; 59 } 60 61 void intel_pt_log_set_name(const char *name) 62 { 63 strncpy(log_name, name, MAX_LOG_NAME - 5); 64 strcat(log_name, ".log"); 65 } 66 67 static void intel_pt_print_data(const unsigned char *buf, int len, uint64_t pos, 68 int indent) 69 { 70 int i; 71 72 for (i = 0; i < indent; i++) 73 fprintf(f, " "); 74 75 fprintf(f, " %08" PRIx64 ": ", pos); 76 for (i = 0; i < len; i++) 77 fprintf(f, " %02x", buf[i]); 78 for (; i < 16; i++) 79 fprintf(f, " "); 80 fprintf(f, " "); 81 } 82 83 static void intel_pt_print_no_data(uint64_t pos, int indent) 84 { 85 int i; 86 87 for (i = 0; i < indent; i++) 88 fprintf(f, " "); 89 90 fprintf(f, " %08" PRIx64 ": ", pos); 91 for (i = 0; i < 16; i++) 92 fprintf(f, " "); 93 fprintf(f, " "); 94 } 95 96 static ssize_t log_buf__write(void *cookie, const char *buf, size_t size) 97 { 98 struct log_buf *b = cookie; 99 size_t sz = size; 100 101 if (!b->buf) 102 return size; 103 104 while (sz) { 105 size_t space = b->buf_sz - b->head; 106 size_t n = min(space, sz); 107 108 memcpy(b->buf + b->head, buf, n); 109 sz -= n; 110 buf += n; 111 b->head += n; 112 if (sz && b->head >= b->buf_sz) { 113 b->head = 0; 114 b->wrapped = true; 115 } 116 } 117 return size; 118 } 119 120 static int log_buf__close(void *cookie) 121 { 122 struct log_buf *b = cookie; 123 124 zfree(&b->buf); 125 return 0; 126 } 127 128 static FILE *log_buf__open(struct log_buf *b, FILE *backend, unsigned int sz) 129 { 130 cookie_io_functions_t fns = { 131 .write = log_buf__write, 132 .close = log_buf__close, 133 }; 134 FILE *file; 135 136 memset(b, 0, sizeof(*b)); 137 b->buf_sz = sz; 138 b->buf = malloc(b->buf_sz); 139 b->backend = backend; 140 file = fopencookie(b, "a", fns); 141 if (!file) 142 zfree(&b->buf); 143 return file; 144 } 145 146 static bool remove_first_line(const char **p, size_t *n) 147 { 148 for (; *n && **p != '\n'; ++*p, --*n) 149 ; 150 if (*n) { 151 *p += 1; 152 *n -= 1; 153 return true; 154 } 155 return false; 156 } 157 158 static void write_lines(const char *p, size_t n, FILE *fp, bool *remove_first) 159 { 160 if (*remove_first) 161 *remove_first = !remove_first_line(&p, &n); 162 fwrite(p, n, 1, fp); 163 } 164 165 static void log_buf__dump(struct log_buf *b) 166 { 167 bool remove_first = false; 168 169 if (!b->buf) 170 return; 171 172 fflush(f); /* Could update b->head and b->wrapped */ 173 fprintf(b->backend, "Dumping debug log buffer\n"); 174 if (b->wrapped) { 175 remove_first = true; 176 write_lines(b->buf + b->head, b->buf_sz - b->head, b->backend, &remove_first); 177 } 178 write_lines(b->buf, b->head, b->backend, &remove_first); 179 fprintf(b->backend, "End of debug log buffer dump\n"); 180 181 b->head = 0; 182 b->wrapped = false; 183 } 184 185 void intel_pt_log_dump_buf(void) 186 { 187 log_buf__dump(&log_buf); 188 } 189 190 static int intel_pt_log_open(void) 191 { 192 if (!intel_pt_enable_logging) 193 return -1; 194 195 if (f) 196 return 0; 197 198 if (log_name[0]) 199 f = fopen(log_name, "w+"); 200 else 201 f = stdout; 202 if (f && intel_pt_dump_log_on_error) 203 f = log_buf__open(&log_buf, f, intel_pt_log_on_error_size); 204 if (!f) { 205 intel_pt_enable_logging = false; 206 return -1; 207 } 208 209 return 0; 210 } 211 212 void __intel_pt_log_packet(const struct intel_pt_pkt *packet, int pkt_len, 213 uint64_t pos, const unsigned char *buf) 214 { 215 char desc[INTEL_PT_PKT_DESC_MAX]; 216 217 if (intel_pt_log_open()) 218 return; 219 220 intel_pt_print_data(buf, pkt_len, pos, 0); 221 intel_pt_pkt_desc(packet, desc, INTEL_PT_PKT_DESC_MAX); 222 fprintf(f, "%s\n", desc); 223 } 224 225 void __intel_pt_log_insn(struct intel_pt_insn *intel_pt_insn, uint64_t ip) 226 { 227 char desc[INTEL_PT_INSN_DESC_MAX]; 228 size_t len = intel_pt_insn->length; 229 230 if (intel_pt_log_open()) 231 return; 232 233 if (len > INTEL_PT_INSN_BUF_SZ) 234 len = INTEL_PT_INSN_BUF_SZ; 235 intel_pt_print_data(intel_pt_insn->buf, len, ip, 8); 236 if (intel_pt_insn_desc(intel_pt_insn, desc, INTEL_PT_INSN_DESC_MAX) > 0) 237 fprintf(f, "%s\n", desc); 238 else 239 fprintf(f, "Bad instruction!\n"); 240 } 241 242 void __intel_pt_log_insn_no_data(struct intel_pt_insn *intel_pt_insn, 243 uint64_t ip) 244 { 245 char desc[INTEL_PT_INSN_DESC_MAX]; 246 247 if (intel_pt_log_open()) 248 return; 249 250 intel_pt_print_no_data(ip, 8); 251 if (intel_pt_insn_desc(intel_pt_insn, desc, INTEL_PT_INSN_DESC_MAX) > 0) 252 fprintf(f, "%s\n", desc); 253 else 254 fprintf(f, "Bad instruction!\n"); 255 } 256 257 void __intel_pt_log(const char *fmt, ...) 258 { 259 va_list args; 260 261 if (intel_pt_log_open()) 262 return; 263 264 va_start(args, fmt); 265 vfprintf(f, fmt, args); 266 va_end(args); 267 } 268