Skip to content

Commit

Permalink
external/trace: Add support for dumping multiple buffers
Browse files Browse the repository at this point in the history
dump_trace only can dump one trace buffer at a time. It would be handy
to be able to dump multiple buffers and to see the entries from these
buffers displayed in correct timestamp order. Each trace buffer is
already sorted by timestamp so use a heap to implement an efficient
k-way merge. Use the CCAN heap to implement this sort. However the CCAN
heap does not have a 'heap_replace' operation. We need to 'heap_pop'
then 'heap_push' to replace the root which means rebalancing twice
instead of once.

Signed-off-by: Jordan Niethe <jniethe5@gmail.com>
Signed-off-by: Stewart Smith <stewart@linux.ibm.com>
  • Loading branch information
iamjpn authored and stewartsmith committed May 20, 2019
1 parent 3d6aca2 commit a5038b4
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 44 deletions.
2 changes: 1 addition & 1 deletion external/trace/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
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
dump_trace: dump_trace.c trace.c ../../ccan/heap/heap.c

clean:
rm -f dump_trace *.o
176 changes: 133 additions & 43 deletions external/trace/dump_trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,26 @@

#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 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;
Expand Down Expand Up @@ -132,60 +149,133 @@ static void dump_uart(struct trace_uart *t)
}
}

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(&current->t);

next = list_pop(&trs[current->index].traces, struct trace_entry,
link);
heap_push(h, next);
free(current);
}
heap_free(h);
}

int main(int argc, char *argv[])
{
struct trace_reader tr;
struct trace_reader *trs;
struct trace_info *ti;
struct stat sb;
union trace t;
int fd;
int fd, i;

if (argc != 2)
errx(1, "Usage: dump_trace file");

fd = open(argv[1], O_RDONLY);
if (fd < 0)
err(1, "Opening %s", argv[1]);
if (argc < 2)
errx(1, "Usage: dump_trace file...");

if (fstat(fd, &sb) < 0)
err(1, "Stating %s", argv[1]);
argc--;
argv++;
trs = ezalloc(sizeof(struct trace_reader) * argc);

ti = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (ti == MAP_FAILED)
err(1, "Mmaping %s", argv[1]);
for (i = 0; i < argc; i++) {
fd = open(argv[i], O_RDONLY);
if (fd < 0)
err(1, "Opening %s", argv[i]);

memset(&tr, 0, sizeof(struct trace_reader));
tr.tb = &ti->tb;
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]);

while (trace_get(&t, &tr)) {
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);
}
trs[i].tb = &ti->tb;
list_head_init(&trs[i].traces);
}

load_traces(trs, argc);
display_traces(trs, argc);

return 0;
}
1 change: 1 addition & 0 deletions external/trace/trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ struct trace_reader {
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;
};

Expand Down

0 comments on commit a5038b4

Please sign in to comment.