trace.c (5796B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright 2014, Michael Ellerman, IBM Corp. 4 */ 5 6#include <errno.h> 7#include <stdio.h> 8#include <stdlib.h> 9#include <string.h> 10#include <sys/mman.h> 11 12#include "trace.h" 13 14 15struct trace_buffer *trace_buffer_allocate(u64 size) 16{ 17 struct trace_buffer *tb; 18 19 if (size < sizeof(*tb)) { 20 fprintf(stderr, "Error: trace buffer too small\n"); 21 return NULL; 22 } 23 24 tb = mmap(NULL, size, PROT_READ | PROT_WRITE, 25 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 26 if (tb == MAP_FAILED) { 27 perror("mmap"); 28 return NULL; 29 } 30 31 tb->size = size; 32 tb->tail = tb->data; 33 tb->overflow = false; 34 35 return tb; 36} 37 38static bool trace_check_bounds(struct trace_buffer *tb, void *p) 39{ 40 return p < ((void *)tb + tb->size); 41} 42 43static bool trace_check_alloc(struct trace_buffer *tb, void *p) 44{ 45 /* 46 * If we ever overflowed don't allow any more input. This prevents us 47 * from dropping a large item and then later logging a small one. The 48 * buffer should just stop when overflow happened, not be patchy. If 49 * you're overflowing, make your buffer bigger. 50 */ 51 if (tb->overflow) 52 return false; 53 54 if (!trace_check_bounds(tb, p)) { 55 tb->overflow = true; 56 return false; 57 } 58 59 return true; 60} 61 62static void *trace_alloc(struct trace_buffer *tb, int bytes) 63{ 64 void *p, *newtail; 65 66 p = tb->tail; 67 newtail = tb->tail + bytes; 68 if (!trace_check_alloc(tb, newtail)) 69 return NULL; 70 71 tb->tail = newtail; 72 73 return p; 74} 75 76static struct trace_entry *trace_alloc_entry(struct trace_buffer *tb, int payload_size) 77{ 78 struct trace_entry *e; 79 80 e = trace_alloc(tb, sizeof(*e) + payload_size); 81 if (e) 82 e->length = payload_size; 83 84 return e; 85} 86 87int trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value) 88{ 89 struct trace_entry *e; 90 u64 *p; 91 92 e = trace_alloc_entry(tb, sizeof(reg) + sizeof(value)); 93 if (!e) 94 return -ENOSPC; 95 96 e->type = TRACE_TYPE_REG; 97 p = (u64 *)e->data; 98 *p++ = reg; 99 *p++ = value; 100 101 return 0; 102} 103 104int trace_log_counter(struct trace_buffer *tb, u64 value) 105{ 106 struct trace_entry *e; 107 u64 *p; 108 109 e = trace_alloc_entry(tb, sizeof(value)); 110 if (!e) 111 return -ENOSPC; 112 113 e->type = TRACE_TYPE_COUNTER; 114 p = (u64 *)e->data; 115 *p++ = value; 116 117 return 0; 118} 119 120int trace_log_string(struct trace_buffer *tb, char *str) 121{ 122 struct trace_entry *e; 123 char *p; 124 int len; 125 126 len = strlen(str); 127 128 /* We NULL terminate to make printing easier */ 129 e = trace_alloc_entry(tb, len + 1); 130 if (!e) 131 return -ENOSPC; 132 133 e->type = TRACE_TYPE_STRING; 134 p = (char *)e->data; 135 memcpy(p, str, len); 136 p += len; 137 *p = '\0'; 138 139 return 0; 140} 141 142int trace_log_indent(struct trace_buffer *tb) 143{ 144 struct trace_entry *e; 145 146 e = trace_alloc_entry(tb, 0); 147 if (!e) 148 return -ENOSPC; 149 150 e->type = TRACE_TYPE_INDENT; 151 152 return 0; 153} 154 155int trace_log_outdent(struct trace_buffer *tb) 156{ 157 struct trace_entry *e; 158 159 e = trace_alloc_entry(tb, 0); 160 if (!e) 161 return -ENOSPC; 162 163 e->type = TRACE_TYPE_OUTDENT; 164 165 return 0; 166} 167 168static void trace_print_header(int seq, int prefix) 169{ 170 printf("%*s[%d]: ", prefix, "", seq); 171} 172 173static char *trace_decode_reg(int reg) 174{ 175 switch (reg) { 176 case 769: return "SPRN_MMCR2"; break; 177 case 770: return "SPRN_MMCRA"; break; 178 case 779: return "SPRN_MMCR0"; break; 179 case 804: return "SPRN_EBBHR"; break; 180 case 805: return "SPRN_EBBRR"; break; 181 case 806: return "SPRN_BESCR"; break; 182 case 800: return "SPRN_BESCRS"; break; 183 case 801: return "SPRN_BESCRSU"; break; 184 case 802: return "SPRN_BESCRR"; break; 185 case 803: return "SPRN_BESCRRU"; break; 186 case 771: return "SPRN_PMC1"; break; 187 case 772: return "SPRN_PMC2"; break; 188 case 773: return "SPRN_PMC3"; break; 189 case 774: return "SPRN_PMC4"; break; 190 case 775: return "SPRN_PMC5"; break; 191 case 776: return "SPRN_PMC6"; break; 192 case 780: return "SPRN_SIAR"; break; 193 case 781: return "SPRN_SDAR"; break; 194 case 768: return "SPRN_SIER"; break; 195 } 196 197 return NULL; 198} 199 200static void trace_print_reg(struct trace_entry *e) 201{ 202 u64 *p, *reg, *value; 203 char *name; 204 205 p = (u64 *)e->data; 206 reg = p++; 207 value = p; 208 209 name = trace_decode_reg(*reg); 210 if (name) 211 printf("register %-10s = 0x%016llx\n", name, *value); 212 else 213 printf("register %lld = 0x%016llx\n", *reg, *value); 214} 215 216static void trace_print_counter(struct trace_entry *e) 217{ 218 u64 *value; 219 220 value = (u64 *)e->data; 221 printf("counter = %lld\n", *value); 222} 223 224static void trace_print_string(struct trace_entry *e) 225{ 226 char *str; 227 228 str = (char *)e->data; 229 puts(str); 230} 231 232#define BASE_PREFIX 2 233#define PREFIX_DELTA 8 234 235static void trace_print_entry(struct trace_entry *e, int seq, int *prefix) 236{ 237 switch (e->type) { 238 case TRACE_TYPE_REG: 239 trace_print_header(seq, *prefix); 240 trace_print_reg(e); 241 break; 242 case TRACE_TYPE_COUNTER: 243 trace_print_header(seq, *prefix); 244 trace_print_counter(e); 245 break; 246 case TRACE_TYPE_STRING: 247 trace_print_header(seq, *prefix); 248 trace_print_string(e); 249 break; 250 case TRACE_TYPE_INDENT: 251 trace_print_header(seq, *prefix); 252 puts("{"); 253 *prefix += PREFIX_DELTA; 254 break; 255 case TRACE_TYPE_OUTDENT: 256 *prefix -= PREFIX_DELTA; 257 if (*prefix < BASE_PREFIX) 258 *prefix = BASE_PREFIX; 259 trace_print_header(seq, *prefix); 260 puts("}"); 261 break; 262 default: 263 trace_print_header(seq, *prefix); 264 printf("entry @ %p type %d\n", e, e->type); 265 break; 266 } 267} 268 269void trace_buffer_print(struct trace_buffer *tb) 270{ 271 struct trace_entry *e; 272 int i, prefix; 273 void *p; 274 275 printf("Trace buffer dump:\n"); 276 printf(" address %p \n", tb); 277 printf(" tail %p\n", tb->tail); 278 printf(" size %llu\n", tb->size); 279 printf(" overflow %s\n", tb->overflow ? "TRUE" : "false"); 280 printf(" Content:\n"); 281 282 p = tb->data; 283 284 i = 0; 285 prefix = BASE_PREFIX; 286 287 while (trace_check_bounds(tb, p) && p < tb->tail) { 288 e = p; 289 290 trace_print_entry(e, i, &prefix); 291 292 i++; 293 p = (void *)e + sizeof(*e) + e->length; 294 } 295} 296 297void trace_print_location(struct trace_buffer *tb) 298{ 299 printf("Trace buffer 0x%llx bytes @ %p\n", tb->size, tb); 300}