blob: dac291f8c90f8ae3a9bbfb8a2f1bf992ee952f2b [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <lib/zircon-internal/ktrace.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fbl/unique_fd.h>
constexpr uint32_t kChunkSize = 65536;
typedef enum { Tag16B, Tag32B, TagNAME } TagType;
typedef struct {
uint32_t num;
uint32_t group;
TagType type;
const char* name;
} TagInfo;
static const TagInfo g_tags[] = {
#define KTRACE_DEF(num, type, name, group) [num] = {num, KTRACE_GRP_##group, Tag##type, #name},
#include <lib/zircon-internal/ktrace-def.h>
};
static const char kUsage[] =
"\
Usage: ktrace-pretty-print <path>\n\
ktrace-pretty-print --help\n\
";
static uint8_t buffer[kChunkSize];
// Where to read from next.
static uint8_t* current = buffer;
// Limit of data read into |buffer|.
static uint8_t* marker = buffer;
static uint8_t* const end = buffer + kChunkSize;
static size_t number_records_read = 0;
static size_t number_bytes_read = 0;
static inline size_t AvailableBytes() {
assert(marker >= current);
return marker - current;
}
static void PrintUsage(FILE* f) { fputs(kUsage, f); }
static void ReadMoreData(int fd) {
memcpy(buffer, current, AvailableBytes());
marker = buffer + AvailableBytes();
while (marker < end) {
ssize_t bytes_read = read(fd, marker, end - marker);
if (bytes_read <= 0)
break;
marker += bytes_read;
}
current = buffer;
}
// no-inline for easier debugging in gdb
__NO_INLINE static ktrace_header_t* ReadNextRecord(int fd) {
if (AvailableBytes() < sizeof(ktrace_header_t))
ReadMoreData(fd);
if (AvailableBytes() < sizeof(ktrace_header_t))
return nullptr;
ktrace_header_t* record = (ktrace_header_t*)current;
if (AvailableBytes() < KTRACE_LEN(record->tag))
ReadMoreData(fd);
if (AvailableBytes() < KTRACE_LEN(record->tag))
return nullptr;
record = (ktrace_header_t*)current;
// If the record has zero length we're hosed.
if (KTRACE_LEN(record->tag) == 0) {
printf("Zero length tag, done.\n");
return nullptr;
}
current += KTRACE_LEN(record->tag);
number_bytes_read += KTRACE_LEN(record->tag);
number_records_read += 1;
return record;
}
static void PrintTag(uint32_t tag) {
uint32_t event = KTRACE_EVENT(tag);
uint32_t flags = KTRACE_FLAGS(tag);
const TagInfo* info = &g_tags[event];
assert(info->name != nullptr);
printf("%s(0x%x)", info->name, event);
if (flags != 0) {
printf(", flags 0x%x", flags);
}
}
static void Dump16B(const TagInfo* info, ktrace_header_t* r) {
printf("%" PRIu64 ": ", r->ts);
PrintTag(r->tag);
// TODO(dje): Further decode args.
printf(", arg 0x%x\n", r->tid);
}
static void Dump32B(const TagInfo* info, ktrace_rec_32b_t* r) {
printf("%" PRIu64 ": ", r->ts);
PrintTag(r->tag);
// TODO(dje): Further decode args.
printf(", tid 0x%x, a 0x%x, b 0x%x, c 0x%x, d 0x%x\n", r->tid, r->a, r->b, r->c, r->d);
}
static void DumpName(const TagInfo* info, ktrace_rec_name_t* r) {
PrintTag(r->tag);
printf(", id 0x%x, arg 0x%x, %s\n", r->id, r->arg, r->name);
}
static int DoDump(const fbl::unique_fd& fd) {
ktrace_header_t* record;
while ((record = ReadNextRecord(fd.get())) != nullptr) {
uint32_t event = KTRACE_EVENT(record->tag);
if (event >= std::size(g_tags)) {
printf("Unexpected event: 0x%x\n", event);
continue;
}
const TagInfo* info = &g_tags[event];
if (info->name == nullptr) {
printf("Unexpected event: 0x%x\n", event);
continue;
}
switch (info->type) {
case Tag16B:
Dump16B(info, record);
break;
case Tag32B:
Dump32B(info, (ktrace_rec_32b_t*)record);
break;
case TagNAME:
DumpName(info, (ktrace_rec_name_t*)record);
break;
default:
printf("Unexpected tag type: 0x%x\n", info->type);
break;
}
}
printf("%zu records, %zu bytes\n", number_records_read, number_bytes_read);
return EXIT_SUCCESS;
}
int main(int argc, char* argv[]) {
if (argc >= 2 && strcmp(argv[1], "--help") == 0) {
PrintUsage(stdout);
return EXIT_SUCCESS;
}
if (argc != 2) {
PrintUsage(stderr);
return EXIT_FAILURE;
}
const char* path = argv[1];
fbl::unique_fd fd(open(path, O_RDONLY));
if (!fd.is_valid()) {
fprintf(stderr, "Unable to open file for reading: %s\n", path);
return EXIT_FAILURE;
}
return DoDump(fd);
}