162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2014, Michael Ellerman, IBM Corp. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <errno.h> 762306a36Sopenharmony_ci#include <stdio.h> 862306a36Sopenharmony_ci#include <stdlib.h> 962306a36Sopenharmony_ci#include <string.h> 1062306a36Sopenharmony_ci#include <sys/mman.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "trace.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistruct trace_buffer *trace_buffer_allocate(u64 size) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci struct trace_buffer *tb; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci if (size < sizeof(*tb)) { 2062306a36Sopenharmony_ci fprintf(stderr, "Error: trace buffer too small\n"); 2162306a36Sopenharmony_ci return NULL; 2262306a36Sopenharmony_ci } 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci tb = mmap(NULL, size, PROT_READ | PROT_WRITE, 2562306a36Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 2662306a36Sopenharmony_ci if (tb == MAP_FAILED) { 2762306a36Sopenharmony_ci perror("mmap"); 2862306a36Sopenharmony_ci return NULL; 2962306a36Sopenharmony_ci } 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci tb->size = size; 3262306a36Sopenharmony_ci tb->tail = tb->data; 3362306a36Sopenharmony_ci tb->overflow = false; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci return tb; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic bool trace_check_bounds(struct trace_buffer *tb, void *p) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci return p < ((void *)tb + tb->size); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic bool trace_check_alloc(struct trace_buffer *tb, void *p) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci /* 4662306a36Sopenharmony_ci * If we ever overflowed don't allow any more input. This prevents us 4762306a36Sopenharmony_ci * from dropping a large item and then later logging a small one. The 4862306a36Sopenharmony_ci * buffer should just stop when overflow happened, not be patchy. If 4962306a36Sopenharmony_ci * you're overflowing, make your buffer bigger. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci if (tb->overflow) 5262306a36Sopenharmony_ci return false; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (!trace_check_bounds(tb, p)) { 5562306a36Sopenharmony_ci tb->overflow = true; 5662306a36Sopenharmony_ci return false; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci return true; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic void *trace_alloc(struct trace_buffer *tb, int bytes) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci void *p, *newtail; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci p = tb->tail; 6762306a36Sopenharmony_ci newtail = tb->tail + bytes; 6862306a36Sopenharmony_ci if (!trace_check_alloc(tb, newtail)) 6962306a36Sopenharmony_ci return NULL; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci tb->tail = newtail; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return p; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic struct trace_entry *trace_alloc_entry(struct trace_buffer *tb, int payload_size) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct trace_entry *e; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci e = trace_alloc(tb, sizeof(*e) + payload_size); 8162306a36Sopenharmony_ci if (e) 8262306a36Sopenharmony_ci e->length = payload_size; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return e; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ciint trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct trace_entry *e; 9062306a36Sopenharmony_ci u64 *p; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci e = trace_alloc_entry(tb, sizeof(reg) + sizeof(value)); 9362306a36Sopenharmony_ci if (!e) 9462306a36Sopenharmony_ci return -ENOSPC; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci e->type = TRACE_TYPE_REG; 9762306a36Sopenharmony_ci p = (u64 *)e->data; 9862306a36Sopenharmony_ci *p++ = reg; 9962306a36Sopenharmony_ci *p++ = value; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ciint trace_log_counter(struct trace_buffer *tb, u64 value) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct trace_entry *e; 10762306a36Sopenharmony_ci u64 *p; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci e = trace_alloc_entry(tb, sizeof(value)); 11062306a36Sopenharmony_ci if (!e) 11162306a36Sopenharmony_ci return -ENOSPC; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci e->type = TRACE_TYPE_COUNTER; 11462306a36Sopenharmony_ci p = (u64 *)e->data; 11562306a36Sopenharmony_ci *p++ = value; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ciint trace_log_string(struct trace_buffer *tb, char *str) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct trace_entry *e; 12362306a36Sopenharmony_ci char *p; 12462306a36Sopenharmony_ci int len; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci len = strlen(str); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* We NULL terminate to make printing easier */ 12962306a36Sopenharmony_ci e = trace_alloc_entry(tb, len + 1); 13062306a36Sopenharmony_ci if (!e) 13162306a36Sopenharmony_ci return -ENOSPC; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci e->type = TRACE_TYPE_STRING; 13462306a36Sopenharmony_ci p = (char *)e->data; 13562306a36Sopenharmony_ci memcpy(p, str, len); 13662306a36Sopenharmony_ci p += len; 13762306a36Sopenharmony_ci *p = '\0'; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ciint trace_log_indent(struct trace_buffer *tb) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct trace_entry *e; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci e = trace_alloc_entry(tb, 0); 14762306a36Sopenharmony_ci if (!e) 14862306a36Sopenharmony_ci return -ENOSPC; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci e->type = TRACE_TYPE_INDENT; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ciint trace_log_outdent(struct trace_buffer *tb) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct trace_entry *e; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci e = trace_alloc_entry(tb, 0); 16062306a36Sopenharmony_ci if (!e) 16162306a36Sopenharmony_ci return -ENOSPC; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci e->type = TRACE_TYPE_OUTDENT; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void trace_print_header(int seq, int prefix) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci printf("%*s[%d]: ", prefix, "", seq); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic char *trace_decode_reg(int reg) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci switch (reg) { 17662306a36Sopenharmony_ci case 769: return "SPRN_MMCR2"; break; 17762306a36Sopenharmony_ci case 770: return "SPRN_MMCRA"; break; 17862306a36Sopenharmony_ci case 779: return "SPRN_MMCR0"; break; 17962306a36Sopenharmony_ci case 804: return "SPRN_EBBHR"; break; 18062306a36Sopenharmony_ci case 805: return "SPRN_EBBRR"; break; 18162306a36Sopenharmony_ci case 806: return "SPRN_BESCR"; break; 18262306a36Sopenharmony_ci case 800: return "SPRN_BESCRS"; break; 18362306a36Sopenharmony_ci case 801: return "SPRN_BESCRSU"; break; 18462306a36Sopenharmony_ci case 802: return "SPRN_BESCRR"; break; 18562306a36Sopenharmony_ci case 803: return "SPRN_BESCRRU"; break; 18662306a36Sopenharmony_ci case 771: return "SPRN_PMC1"; break; 18762306a36Sopenharmony_ci case 772: return "SPRN_PMC2"; break; 18862306a36Sopenharmony_ci case 773: return "SPRN_PMC3"; break; 18962306a36Sopenharmony_ci case 774: return "SPRN_PMC4"; break; 19062306a36Sopenharmony_ci case 775: return "SPRN_PMC5"; break; 19162306a36Sopenharmony_ci case 776: return "SPRN_PMC6"; break; 19262306a36Sopenharmony_ci case 780: return "SPRN_SIAR"; break; 19362306a36Sopenharmony_ci case 781: return "SPRN_SDAR"; break; 19462306a36Sopenharmony_ci case 768: return "SPRN_SIER"; break; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return NULL; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void trace_print_reg(struct trace_entry *e) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci u64 *p, *reg, *value; 20362306a36Sopenharmony_ci char *name; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci p = (u64 *)e->data; 20662306a36Sopenharmony_ci reg = p++; 20762306a36Sopenharmony_ci value = p; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci name = trace_decode_reg(*reg); 21062306a36Sopenharmony_ci if (name) 21162306a36Sopenharmony_ci printf("register %-10s = 0x%016llx\n", name, *value); 21262306a36Sopenharmony_ci else 21362306a36Sopenharmony_ci printf("register %lld = 0x%016llx\n", *reg, *value); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic void trace_print_counter(struct trace_entry *e) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci u64 *value; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci value = (u64 *)e->data; 22162306a36Sopenharmony_ci printf("counter = %lld\n", *value); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic void trace_print_string(struct trace_entry *e) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci char *str; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci str = (char *)e->data; 22962306a36Sopenharmony_ci puts(str); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci#define BASE_PREFIX 2 23362306a36Sopenharmony_ci#define PREFIX_DELTA 8 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void trace_print_entry(struct trace_entry *e, int seq, int *prefix) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci switch (e->type) { 23862306a36Sopenharmony_ci case TRACE_TYPE_REG: 23962306a36Sopenharmony_ci trace_print_header(seq, *prefix); 24062306a36Sopenharmony_ci trace_print_reg(e); 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci case TRACE_TYPE_COUNTER: 24362306a36Sopenharmony_ci trace_print_header(seq, *prefix); 24462306a36Sopenharmony_ci trace_print_counter(e); 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci case TRACE_TYPE_STRING: 24762306a36Sopenharmony_ci trace_print_header(seq, *prefix); 24862306a36Sopenharmony_ci trace_print_string(e); 24962306a36Sopenharmony_ci break; 25062306a36Sopenharmony_ci case TRACE_TYPE_INDENT: 25162306a36Sopenharmony_ci trace_print_header(seq, *prefix); 25262306a36Sopenharmony_ci puts("{"); 25362306a36Sopenharmony_ci *prefix += PREFIX_DELTA; 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci case TRACE_TYPE_OUTDENT: 25662306a36Sopenharmony_ci *prefix -= PREFIX_DELTA; 25762306a36Sopenharmony_ci if (*prefix < BASE_PREFIX) 25862306a36Sopenharmony_ci *prefix = BASE_PREFIX; 25962306a36Sopenharmony_ci trace_print_header(seq, *prefix); 26062306a36Sopenharmony_ci puts("}"); 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci default: 26362306a36Sopenharmony_ci trace_print_header(seq, *prefix); 26462306a36Sopenharmony_ci printf("entry @ %p type %d\n", e, e->type); 26562306a36Sopenharmony_ci break; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_civoid trace_buffer_print(struct trace_buffer *tb) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct trace_entry *e; 27262306a36Sopenharmony_ci int i, prefix; 27362306a36Sopenharmony_ci void *p; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci printf("Trace buffer dump:\n"); 27662306a36Sopenharmony_ci printf(" address %p \n", tb); 27762306a36Sopenharmony_ci printf(" tail %p\n", tb->tail); 27862306a36Sopenharmony_ci printf(" size %llu\n", tb->size); 27962306a36Sopenharmony_ci printf(" overflow %s\n", tb->overflow ? "TRUE" : "false"); 28062306a36Sopenharmony_ci printf(" Content:\n"); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci p = tb->data; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci i = 0; 28562306a36Sopenharmony_ci prefix = BASE_PREFIX; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci while (trace_check_bounds(tb, p) && p < tb->tail) { 28862306a36Sopenharmony_ci e = p; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci trace_print_entry(e, i, &prefix); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci i++; 29362306a36Sopenharmony_ci p = (void *)e + sizeof(*e) + e->length; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_civoid trace_print_location(struct trace_buffer *tb) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci printf("Trace buffer 0x%llx bytes @ %p\n", tb->size, tb); 30062306a36Sopenharmony_ci} 301