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