diff options
Diffstat (limited to 'roms/skiboot/external/trace')
-rw-r--r-- | roms/skiboot/external/trace/Makefile | 8 | ||||
-rw-r--r-- | roms/skiboot/external/trace/dump_trace.c | 366 | ||||
-rw-r--r-- | roms/skiboot/external/trace/trace.c | 110 | ||||
-rw-r--r-- | roms/skiboot/external/trace/trace.h | 28 |
4 files changed, 512 insertions, 0 deletions
diff --git a/roms/skiboot/external/trace/Makefile b/roms/skiboot/external/trace/Makefile new file mode 100644 index 000000000..737397bca --- /dev/null +++ b/roms/skiboot/external/trace/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +HOSTEND=$(shell uname -m | sed -e 's/^i.*86$$/LITTLE/' -e 's/^x86.*/LITTLE/' -e 's/^ppc64le/LITTLE/' -e 's/^ppc.*/BIG/') +CFLAGS=-g -Wall -DHAVE_$(HOSTEND)_ENDIAN -I../../include -I../../ + +dump_trace: dump_trace.c trace.c ../../ccan/heap/heap.c + +clean: + rm -f dump_trace *.o 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; +} diff --git a/roms/skiboot/external/trace/trace.c b/roms/skiboot/external/trace/trace.c new file mode 100644 index 000000000..97cec7d9d --- /dev/null +++ b/roms/skiboot/external/trace/trace.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* + * This example code shows how to read from the trace buffer. + * + * Copyright 2013-2019 IBM Corp. + */ + +#include <external/trace/trace.h> +#include "../ccan/endian/endian.h" +#include "../ccan/short_types/short_types.h" +#include "trace.h" +#include <trace_types.h> +#include <errno.h> + +#if defined(__powerpc__) || defined(__powerpc64__) +#define rmb() lwsync() +#else +#define rmb() +#endif + +bool trace_empty(const struct trace_reader *tr) +{ + const struct trace_repeat *rep; + + if (tr->rpos == be64_to_cpu(tr->tb->end)) + return true; + + /* + * If we have a single element only, and it's a repeat buffer + * we've already seen every repeat for (yet which may be + * incremented in future), we're also empty. + */ + rep = (void *)tr->tb->buf + tr->rpos % be64_to_cpu(tr->tb->buf_size); + if (be64_to_cpu(tr->tb->end) != tr->rpos + sizeof(*rep)) + return false; + + if (rep->type != TRACE_REPEAT) + return false; + + if (be16_to_cpu(rep->num) != tr->last_repeat) + return false; + + return true; +} + +/* You can't read in parallel, so some locking required in caller. */ +bool trace_get(union trace *t, struct trace_reader *tr) +{ + u64 start, rpos; + size_t len; + + len = sizeof(*t) < be32_to_cpu(tr->tb->max_size) ? sizeof(*t) : + be32_to_cpu(tr->tb->max_size); + + if (trace_empty(tr)) + return false; + +again: + /* + * The actual buffer is slightly larger than tbsize, so this + * memcpy is always valid. + */ + memcpy(t, tr->tb->buf + tr->rpos % be64_to_cpu(tr->tb->buf_size), len); + + rmb(); /* read barrier, so we read tr->tb->start after copying record. */ + + start = be64_to_cpu(tr->tb->start); + rpos = tr->rpos; + + /* Now, was that overwritten? */ + if (rpos < start) { + /* Create overflow record. */ + t->overflow.unused64 = 0; + t->overflow.type = TRACE_OVERFLOW; + t->overflow.len_div_8 = sizeof(t->overflow) / 8; + t->overflow.bytes_missed = cpu_to_be64(start - rpos); + tr->rpos = start; + return true; + } + + /* Repeat entries need special handling */ + if (t->hdr.type == TRACE_REPEAT) { + u32 num = be16_to_cpu(t->repeat.num); + + /* In case we've read some already... */ + t->repeat.num = cpu_to_be16(num - tr->last_repeat); + + /* Record how many repeats we saw this time. */ + tr->last_repeat = num; + + /* Don't report an empty repeat buffer. */ + if (t->repeat.num == 0) { + /* + * This can't be the last buffer, otherwise + * trace_empty would have returned true. + */ + assert(be64_to_cpu(tr->tb->end) > + rpos + t->hdr.len_div_8 * 8); + /* Skip to next entry. */ + tr->rpos = rpos + t->hdr.len_div_8 * 8; + tr->last_repeat = 0; + goto again; + } + } else { + tr->last_repeat = 0; + tr->rpos = rpos + t->hdr.len_div_8 * 8; + } + + return true; +} diff --git a/roms/skiboot/external/trace/trace.h b/roms/skiboot/external/trace/trace.h new file mode 100644 index 000000000..1a5a138ae --- /dev/null +++ b/roms/skiboot/external/trace/trace.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +/* + * Copyright 2013-2019 IBM Corp. + */ +#ifndef E_TRACE_H +#define E_TRACE_H + +#include <stdbool.h> +#include <types.h> +#include <trace.h> +#include <trace_types.h> + +struct trace_reader { + /* This is where the reader is up to. */ + u64 rpos; + /* If the last one we read was a repeat, this shows how many. */ + u32 last_repeat; + struct list_head traces; + struct tracebuf *tb; +}; + +/* Is this tracebuf empty? */ +bool trace_empty(const struct trace_reader *tr); + +/* Get the next trace from this buffer (false if empty). */ +bool trace_get(union trace *t, struct trace_reader *tr); + +#endif |