323 lines
7.1 KiB
C
323 lines
7.1 KiB
C
/* Copyright 2013-2014 IBM Corp.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
* implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#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;
|
|
}
|
|
|
|
static void display_header(const struct trace_hdr *h)
|
|
{
|
|
static u64 prev_ts;
|
|
u64 ts = be64_to_cpu(h->timestamp);
|
|
|
|
printf("%16lx (+%8lx) [%03x] : ",
|
|
ts, prev_ts ? (ts - prev_ts) : 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 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;
|
|
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;
|
|
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)
|
|
err(1, "Mmaping %s", argv[i]);
|
|
|
|
trs[i].tb = &ti->tb;
|
|
list_head_init(&trs[i].traces);
|
|
}
|
|
|
|
do {
|
|
load_traces(trs, argc);
|
|
display_traces(trs, argc);
|
|
if (follow)
|
|
usleep(poll_msecs * 1000);
|
|
} while (follow);
|
|
|
|
return 0;
|
|
}
|