diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/skiboot/external/trace/dump_trace.c | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/skiboot/external/trace/dump_trace.c')
-rw-r--r-- | roms/skiboot/external/trace/dump_trace.c | 366 |
1 files changed, 366 insertions, 0 deletions
diff --git a/roms/skiboot/external/trace/dump_trace.c b/roms/skiboot/external/trace/dump_trace.c new file mode 100644 index 000000000..f54dfa081 --- /dev/null +++ b/roms/skiboot/external/trace/dump_trace.c @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* + * Dump the content of an OPAL trace + * + * Copyright 2013-2019 IBM Corp. + */ + +#include <trace.h> +#include <err.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <string.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stddef.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> + +#include "../../ccan/endian/endian.h" +#include "../../ccan/short_types/short_types.h" +#include "../../ccan/heap/heap.h" +#include "trace.h" + + +struct trace_entry { + int index; + union trace t; + struct list_node link; +}; + +static int follow; +static long poll_msecs; + +static void *ezalloc(size_t size) +{ + void *p; + + p = calloc(size, 1); + if (!p) + err(1, "Allocating memory"); + return p; +} + +#define TB_HZ 512000000ul + +static void display_header(const struct trace_hdr *h) +{ + static u64 prev_ts; + u64 ts = be64_to_cpu(h->timestamp); + + printf("[%5lu.%09lu,%d] (+%8lx) [%03x] : ", + ts / TB_HZ, /* match the usual skiboot log header */ + ts % TB_HZ, + h->type, /* hey why not */ + prev_ts ? (ts - prev_ts) % TB_HZ : 0, be16_to_cpu(h->cpu)); + prev_ts = ts; +} + +static void dump_fsp_event(struct trace_fsp_event *t) +{ + printf("FSP_EVT [st=%d] ", be16_to_cpu(t->fsp_state)); + + switch(be16_to_cpu(t->event)) { + case TRACE_FSP_EVT_LINK_DOWN: + printf("LINK DOWN"); + break; + case TRACE_FSP_EVT_DISR_CHG: + printf("DISR CHANGE (0x%08x)", be32_to_cpu(t->data[0])); + break; + case TRACE_FSP_EVT_SOFT_RR: + printf("SOFT R&R (DISR=0x%08x)", be32_to_cpu(t->data[0])); + break; + case TRACE_FSP_EVT_RR_COMPL: + printf("R&R COMPLETE"); + break; + case TRACE_FSP_EVT_HDES_CHG: + printf("HDES CHANGE (0x%08x)", be32_to_cpu(t->data[0])); + break; + case TRACE_FSP_EVT_POLL_IRQ: + printf("%s HDIR=%08x CTL=%08x PSI_IRQ=%d", + t->data[0] ? "IRQ " : "POLL", be32_to_cpu(t->data[1]), + be32_to_cpu(t->data[2]), be32_to_cpu(t->data[3])); + break; + default: + printf("Unknown %d (d: %08x %08x %08x %08x)", + be16_to_cpu(t->event), be32_to_cpu(t->data[0]), + be32_to_cpu(t->data[1]), be32_to_cpu(t->data[2]), + be32_to_cpu(t->data[3])); + } + printf("\n"); +} + +static void dump_opal_call(struct trace_opal *t) +{ + unsigned int i, n; + + printf("OPAL CALL %"PRIu64, be64_to_cpu(t->token)); + printf(" LR=0x%016"PRIx64" SP=0x%016"PRIx64, + be64_to_cpu(t->lr), be64_to_cpu(t->sp)); + n = (t->hdr.len_div_8 * 8 - offsetof(union trace, opal.r3_to_11)) + / sizeof(u64); + for (i = 0; i < n; i++) + printf(" R%u=0x%016"PRIx64, + i+3, be64_to_cpu(t->r3_to_11[i])); + printf("\n"); +} + +static void dump_fsp_msg(struct trace_fsp_msg *t) +{ + unsigned int i; + + printf("FSP_MSG: CMD %u SEQ %u MOD %u SUB %u DLEN %u %s [", + be32_to_cpu(t->word0) & 0xFFFF, + be32_to_cpu(t->word0) >> 16, + be32_to_cpu(t->word1) >> 8, + be32_to_cpu(t->word1) & 0xFF, + t->dlen, + t->dir == TRACE_FSP_MSG_IN ? "IN" : + (t->dir == TRACE_FSP_MSG_OUT ? "OUT" : "UNKNOWN")); + + for (i = 0; i < t->dlen; i++) + printf("%s%02x", i ? " " : "", t->data[i]); + printf("]\n"); +} + +static void dump_uart(struct trace_uart *t) +{ + switch(t->ctx) { + case TRACE_UART_CTX_IRQ: + printf(": IRQ IRQEN=%d IN_CNT=%d\n", + !t->irq_state, be16_to_cpu(t->in_count)); + break; + case TRACE_UART_CTX_POLL: + printf(": POLL IRQEN=%d IN_CNT=%d\n", + !t->irq_state, be16_to_cpu(t->in_count)); + break; + case TRACE_UART_CTX_READ: + printf(": READ IRQEN=%d IN_CNT=%d READ=%d\n", + !t->irq_state, be16_to_cpu(t->in_count), t->cnt); + break; + default: + printf(": ???? IRQEN=%d IN_CNT=%d\n", + !t->irq_state, be16_to_cpu(t->in_count)); + break; + } +} + +static void dump_i2c(struct trace_i2c *t) +{ + uint16_t type = be16_to_cpu(t->type); + + printf("I2C: bus: %d dev: %02x len: %x ", + be16_to_cpu(t->bus), + be16_to_cpu(t->i2c_addr), + be16_to_cpu(t->size) + ); + + switch (type & 0x3) { + case 0: + printf("read"); + break; + case 1: + printf("write"); + break; + case 2: + printf("smbus read from %x", be16_to_cpu(t->smbus_reg)); + break; + case 3: + printf("smbus write to %x", be16_to_cpu(t->smbus_reg)); + break; + default: + printf("u wot?"); + } + + printf(", rc = %hd\n", (int16_t) be16_to_cpu(t->rc)); +} + +static void load_traces(struct trace_reader *trs, int count) +{ + struct trace_entry *te; + union trace t; + int i; + + for (i = 0; i < count; i++) { + while (trace_get(&t, &trs[i])) { + te = ezalloc(sizeof(struct trace_entry)); + memcpy(&te->t, &t, sizeof(union trace)); + te->index = i; + list_add_tail(&trs[i].traces, &te->link); + } + } +} + +static void print_trace(union trace *t) +{ + display_header(&t->hdr); + switch (t->hdr.type) { + case TRACE_REPEAT: + printf("REPEATS: %u times\n", + be16_to_cpu(t->repeat.num)); + break; + case TRACE_OVERFLOW: + printf("**OVERFLOW**: %"PRIu64" bytes missed\n", + be64_to_cpu(t->overflow.bytes_missed)); + break; + case TRACE_OPAL: + dump_opal_call(&t->opal); + break; + case TRACE_FSP_MSG: + dump_fsp_msg(&t->fsp_msg); + break; + case TRACE_FSP_EVENT: + dump_fsp_event(&t->fsp_evt); + break; + case TRACE_UART: + dump_uart(&t->uart); + break; + case TRACE_I2C: + dump_i2c(&t->i2c); + break; + default: + printf("UNKNOWN(%u) CPU %u length %u\n", + t->hdr.type, be16_to_cpu(t->hdr.cpu), + t->hdr.len_div_8 * 8); + } +} + +/* Gives a min heap */ +bool earlier_entry(const void *va, const void *vb) +{ + struct trace_entry *a, *b; + + a = (struct trace_entry *) va; + b = (struct trace_entry *) vb; + + if (!a) + return false; + if (!b) + return true; + return be64_to_cpu(a->t.hdr.timestamp) < be64_to_cpu(b->t.hdr.timestamp); +} + +static void display_traces(struct trace_reader *trs, int count) +{ + struct trace_entry *current, *next; + struct heap *h; + int i; + + h = heap_init(earlier_entry); + if (!h) + err(1, "Allocating memory"); + + for (i = 0; i < count; i++) { + current = list_pop(&trs[i].traces, struct trace_entry, link); + /* no need to add empty ones */ + if (current) + heap_push(h, current); + } + + while (h->len) { + current = heap_pop(h); + if (!current) + break; + + print_trace(¤t->t); + + next = list_pop(&trs[current->index].traces, struct trace_entry, + link); + heap_push(h, next); + free(current); + } + heap_free(h); +} + + +/* Can't poll for 0 msec, so use 0 to signify failure */ +static long get_mseconds(char *s) +{ + char *end; + long ms; + + errno = 0; + ms = strtol(s, &end, 10); + if (errno || *end || ms < 0) + return 0; + return ms; +} + +static void usage(void) +{ + errx(1, "Usage: dump_trace [-f [-s msecs]] file..."); +} + +int main(int argc, char *argv[]) +{ + struct trace_reader *trs; + struct trace_info *ti; + bool no_mmap = false; + struct stat sb; + int fd, opt, i; + + poll_msecs = 1000; + while ((opt = getopt(argc, argv, "fs:")) != -1) { + switch (opt) { + case 'f': + follow++; + break; + case 's': + poll_msecs = get_mseconds(optarg); + if (follow && poll_msecs) + break; + /* fallthru */ + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + trs = ezalloc(sizeof(struct trace_reader) * argc); + + for (i = 0; i < argc; i++) { + fd = open(argv[i], O_RDONLY); + if (fd < 0) + err(1, "Opening %s", argv[i]); + + if (fstat(fd, &sb) < 0) + err(1, "Stating %s", argv[1]); + + ti = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (ti == MAP_FAILED) { + no_mmap = true; + + ti = ezalloc(sb.st_size); + if (!ti) + err(1, "allocating memory for %s", argv[i]); + + if (read(fd, ti, sb.st_size) == -1) + err(1, "reading from %s", argv[i]); + } + + trs[i].tb = &ti->tb; + list_head_init(&trs[i].traces); + } + + if (no_mmap) { + fprintf(stderr, "disabling follow mode: can't mmap() OPAL export files\n"); + follow = 0; + } + + do { + load_traces(trs, argc); + display_traces(trs, argc); + if (follow) + usleep(poll_msecs * 1000); + } while (follow); + + return 0; +} |