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 15 struct 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 38 static bool trace_check_bounds(struct trace_buffer *tb, void *p) 39 { 40 return p < ((void *)tb + tb->size); 41 } 42 43 static 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 62 static 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 76 static 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 87 int 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 104 int 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 120 int 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 142 int 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 155 int 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 168 static void trace_print_header(int seq, int prefix) 169 { 170 printf("%*s[%d]: ", prefix, "", seq); 171 } 172 173 static 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 200 static 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 216 static 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 224 static 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 235 static 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 269 void 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 297 void trace_print_location(struct trace_buffer *tb) 298 { 299 printf("Trace buffer 0x%llx bytes @ %p\n", tb->size, tb); 300 } 301
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.