18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2014, Michael Ellerman, IBM Corp. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <errno.h> 78c2ecf20Sopenharmony_ci#include <stdio.h> 88c2ecf20Sopenharmony_ci#include <stdlib.h> 98c2ecf20Sopenharmony_ci#include <string.h> 108c2ecf20Sopenharmony_ci#include <sys/mman.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "trace.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistruct trace_buffer *trace_buffer_allocate(u64 size) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci struct trace_buffer *tb; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci if (size < sizeof(*tb)) { 208c2ecf20Sopenharmony_ci fprintf(stderr, "Error: trace buffer too small\n"); 218c2ecf20Sopenharmony_ci return NULL; 228c2ecf20Sopenharmony_ci } 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci tb = mmap(NULL, size, PROT_READ | PROT_WRITE, 258c2ecf20Sopenharmony_ci MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 268c2ecf20Sopenharmony_ci if (tb == MAP_FAILED) { 278c2ecf20Sopenharmony_ci perror("mmap"); 288c2ecf20Sopenharmony_ci return NULL; 298c2ecf20Sopenharmony_ci } 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci tb->size = size; 328c2ecf20Sopenharmony_ci tb->tail = tb->data; 338c2ecf20Sopenharmony_ci tb->overflow = false; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci return tb; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic bool trace_check_bounds(struct trace_buffer *tb, void *p) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return p < ((void *)tb + tb->size); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic bool trace_check_alloc(struct trace_buffer *tb, void *p) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci /* 468c2ecf20Sopenharmony_ci * If we ever overflowed don't allow any more input. This prevents us 478c2ecf20Sopenharmony_ci * from dropping a large item and then later logging a small one. The 488c2ecf20Sopenharmony_ci * buffer should just stop when overflow happened, not be patchy. If 498c2ecf20Sopenharmony_ci * you're overflowing, make your buffer bigger. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci if (tb->overflow) 528c2ecf20Sopenharmony_ci return false; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (!trace_check_bounds(tb, p)) { 558c2ecf20Sopenharmony_ci tb->overflow = true; 568c2ecf20Sopenharmony_ci return false; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return true; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic void *trace_alloc(struct trace_buffer *tb, int bytes) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci void *p, *newtail; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci p = tb->tail; 678c2ecf20Sopenharmony_ci newtail = tb->tail + bytes; 688c2ecf20Sopenharmony_ci if (!trace_check_alloc(tb, newtail)) 698c2ecf20Sopenharmony_ci return NULL; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci tb->tail = newtail; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return p; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic struct trace_entry *trace_alloc_entry(struct trace_buffer *tb, int payload_size) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct trace_entry *e; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci e = trace_alloc(tb, sizeof(*e) + payload_size); 818c2ecf20Sopenharmony_ci if (e) 828c2ecf20Sopenharmony_ci e->length = payload_size; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return e; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ciint trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct trace_entry *e; 908c2ecf20Sopenharmony_ci u64 *p; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci e = trace_alloc_entry(tb, sizeof(reg) + sizeof(value)); 938c2ecf20Sopenharmony_ci if (!e) 948c2ecf20Sopenharmony_ci return -ENOSPC; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci e->type = TRACE_TYPE_REG; 978c2ecf20Sopenharmony_ci p = (u64 *)e->data; 988c2ecf20Sopenharmony_ci *p++ = reg; 998c2ecf20Sopenharmony_ci *p++ = value; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ciint trace_log_counter(struct trace_buffer *tb, u64 value) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct trace_entry *e; 1078c2ecf20Sopenharmony_ci u64 *p; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci e = trace_alloc_entry(tb, sizeof(value)); 1108c2ecf20Sopenharmony_ci if (!e) 1118c2ecf20Sopenharmony_ci return -ENOSPC; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci e->type = TRACE_TYPE_COUNTER; 1148c2ecf20Sopenharmony_ci p = (u64 *)e->data; 1158c2ecf20Sopenharmony_ci *p++ = value; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ciint trace_log_string(struct trace_buffer *tb, char *str) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct trace_entry *e; 1238c2ecf20Sopenharmony_ci char *p; 1248c2ecf20Sopenharmony_ci int len; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci len = strlen(str); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* We NULL terminate to make printing easier */ 1298c2ecf20Sopenharmony_ci e = trace_alloc_entry(tb, len + 1); 1308c2ecf20Sopenharmony_ci if (!e) 1318c2ecf20Sopenharmony_ci return -ENOSPC; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci e->type = TRACE_TYPE_STRING; 1348c2ecf20Sopenharmony_ci p = (char *)e->data; 1358c2ecf20Sopenharmony_ci memcpy(p, str, len); 1368c2ecf20Sopenharmony_ci p += len; 1378c2ecf20Sopenharmony_ci *p = '\0'; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciint trace_log_indent(struct trace_buffer *tb) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct trace_entry *e; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci e = trace_alloc_entry(tb, 0); 1478c2ecf20Sopenharmony_ci if (!e) 1488c2ecf20Sopenharmony_ci return -ENOSPC; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci e->type = TRACE_TYPE_INDENT; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciint trace_log_outdent(struct trace_buffer *tb) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct trace_entry *e; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci e = trace_alloc_entry(tb, 0); 1608c2ecf20Sopenharmony_ci if (!e) 1618c2ecf20Sopenharmony_ci return -ENOSPC; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci e->type = TRACE_TYPE_OUTDENT; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic void trace_print_header(int seq, int prefix) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci printf("%*s[%d]: ", prefix, "", seq); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic char *trace_decode_reg(int reg) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci switch (reg) { 1768c2ecf20Sopenharmony_ci case 769: return "SPRN_MMCR2"; break; 1778c2ecf20Sopenharmony_ci case 770: return "SPRN_MMCRA"; break; 1788c2ecf20Sopenharmony_ci case 779: return "SPRN_MMCR0"; break; 1798c2ecf20Sopenharmony_ci case 804: return "SPRN_EBBHR"; break; 1808c2ecf20Sopenharmony_ci case 805: return "SPRN_EBBRR"; break; 1818c2ecf20Sopenharmony_ci case 806: return "SPRN_BESCR"; break; 1828c2ecf20Sopenharmony_ci case 800: return "SPRN_BESCRS"; break; 1838c2ecf20Sopenharmony_ci case 801: return "SPRN_BESCRSU"; break; 1848c2ecf20Sopenharmony_ci case 802: return "SPRN_BESCRR"; break; 1858c2ecf20Sopenharmony_ci case 803: return "SPRN_BESCRRU"; break; 1868c2ecf20Sopenharmony_ci case 771: return "SPRN_PMC1"; break; 1878c2ecf20Sopenharmony_ci case 772: return "SPRN_PMC2"; break; 1888c2ecf20Sopenharmony_ci case 773: return "SPRN_PMC3"; break; 1898c2ecf20Sopenharmony_ci case 774: return "SPRN_PMC4"; break; 1908c2ecf20Sopenharmony_ci case 775: return "SPRN_PMC5"; break; 1918c2ecf20Sopenharmony_ci case 776: return "SPRN_PMC6"; break; 1928c2ecf20Sopenharmony_ci case 780: return "SPRN_SIAR"; break; 1938c2ecf20Sopenharmony_ci case 781: return "SPRN_SDAR"; break; 1948c2ecf20Sopenharmony_ci case 768: return "SPRN_SIER"; break; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci return NULL; 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic void trace_print_reg(struct trace_entry *e) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci u64 *p, *reg, *value; 2038c2ecf20Sopenharmony_ci char *name; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci p = (u64 *)e->data; 2068c2ecf20Sopenharmony_ci reg = p++; 2078c2ecf20Sopenharmony_ci value = p; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci name = trace_decode_reg(*reg); 2108c2ecf20Sopenharmony_ci if (name) 2118c2ecf20Sopenharmony_ci printf("register %-10s = 0x%016llx\n", name, *value); 2128c2ecf20Sopenharmony_ci else 2138c2ecf20Sopenharmony_ci printf("register %lld = 0x%016llx\n", *reg, *value); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic void trace_print_counter(struct trace_entry *e) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci u64 *value; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci value = (u64 *)e->data; 2218c2ecf20Sopenharmony_ci printf("counter = %lld\n", *value); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void trace_print_string(struct trace_entry *e) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci char *str; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci str = (char *)e->data; 2298c2ecf20Sopenharmony_ci puts(str); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci#define BASE_PREFIX 2 2338c2ecf20Sopenharmony_ci#define PREFIX_DELTA 8 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic void trace_print_entry(struct trace_entry *e, int seq, int *prefix) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci switch (e->type) { 2388c2ecf20Sopenharmony_ci case TRACE_TYPE_REG: 2398c2ecf20Sopenharmony_ci trace_print_header(seq, *prefix); 2408c2ecf20Sopenharmony_ci trace_print_reg(e); 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci case TRACE_TYPE_COUNTER: 2438c2ecf20Sopenharmony_ci trace_print_header(seq, *prefix); 2448c2ecf20Sopenharmony_ci trace_print_counter(e); 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci case TRACE_TYPE_STRING: 2478c2ecf20Sopenharmony_ci trace_print_header(seq, *prefix); 2488c2ecf20Sopenharmony_ci trace_print_string(e); 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci case TRACE_TYPE_INDENT: 2518c2ecf20Sopenharmony_ci trace_print_header(seq, *prefix); 2528c2ecf20Sopenharmony_ci puts("{"); 2538c2ecf20Sopenharmony_ci *prefix += PREFIX_DELTA; 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci case TRACE_TYPE_OUTDENT: 2568c2ecf20Sopenharmony_ci *prefix -= PREFIX_DELTA; 2578c2ecf20Sopenharmony_ci if (*prefix < BASE_PREFIX) 2588c2ecf20Sopenharmony_ci *prefix = BASE_PREFIX; 2598c2ecf20Sopenharmony_ci trace_print_header(seq, *prefix); 2608c2ecf20Sopenharmony_ci puts("}"); 2618c2ecf20Sopenharmony_ci break; 2628c2ecf20Sopenharmony_ci default: 2638c2ecf20Sopenharmony_ci trace_print_header(seq, *prefix); 2648c2ecf20Sopenharmony_ci printf("entry @ %p type %d\n", e, e->type); 2658c2ecf20Sopenharmony_ci break; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_civoid trace_buffer_print(struct trace_buffer *tb) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct trace_entry *e; 2728c2ecf20Sopenharmony_ci int i, prefix; 2738c2ecf20Sopenharmony_ci void *p; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci printf("Trace buffer dump:\n"); 2768c2ecf20Sopenharmony_ci printf(" address %p \n", tb); 2778c2ecf20Sopenharmony_ci printf(" tail %p\n", tb->tail); 2788c2ecf20Sopenharmony_ci printf(" size %llu\n", tb->size); 2798c2ecf20Sopenharmony_ci printf(" overflow %s\n", tb->overflow ? "TRUE" : "false"); 2808c2ecf20Sopenharmony_ci printf(" Content:\n"); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci p = tb->data; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci i = 0; 2858c2ecf20Sopenharmony_ci prefix = BASE_PREFIX; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci while (trace_check_bounds(tb, p) && p < tb->tail) { 2888c2ecf20Sopenharmony_ci e = p; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci trace_print_entry(e, i, &prefix); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci i++; 2938c2ecf20Sopenharmony_ci p = (void *)e + sizeof(*e) + e->length; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_civoid trace_print_location(struct trace_buffer *tb) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci printf("Trace buffer 0x%llx bytes @ %p\n", tb->size, tb); 3008c2ecf20Sopenharmony_ci} 301