From 54867b0f9cce8174fcd403a6dd8383c3f1849e8b Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Thu, 26 May 2016 21:58:14 -0700 Subject: [PATCH] Add support for printing kernel traces Add a 'trace' command which uses the debugfs file created by the scoutfs kernel module to read and print trace messages. Signed-off-by: Zach Brown --- utils/src/ioctl.h | 30 ++++++++ utils/src/sparse.h | 2 + utils/src/trace.c | 169 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 utils/src/ioctl.h create mode 100644 utils/src/trace.c diff --git a/utils/src/ioctl.h b/utils/src/ioctl.h new file mode 100644 index 00000000..1accfdae --- /dev/null +++ b/utils/src/ioctl.h @@ -0,0 +1,30 @@ +#ifndef _SCOUTFS_IOCTL_H_ +#define _SCOUTFS_IOCTL_H_ + +/* XXX I have no idea how these are chosen. */ +#define SCOUTFS_IOCTL_MAGIC 's' + +struct scoutfs_ioctl_buf { + __u64 ptr; + __s32 len; +} __packed; + +/* + * Fills the buffer with a packed array of format strings. Trace records + * refer to the format strings in the buffer by their byte offset. + */ +#define SCOUTFS_IOC_GET_TRACE_FORMATS _IOW(SCOUTFS_IOCTL_MAGIC, 1, \ + struct scoutfs_ioctl_buf) + +struct scoutfs_trace_record { + __u16 format_off; + __u8 nr; + __u8 data[0]; +} __packed; +/* + * Fills the buffer with trace records. + */ +#define SCOUTFS_IOC_GET_TRACE_RECORDS _IOW(SCOUTFS_IOCTL_MAGIC, 2, \ + struct scoutfs_ioctl_buf) + +#endif diff --git a/utils/src/sparse.h b/utils/src/sparse.h index 63b66ca9..7842aca2 100644 --- a/utils/src/sparse.h +++ b/utils/src/sparse.h @@ -24,12 +24,14 @@ extern unsigned int __builtin_ia32_crc32qi(unsigned int, unsigned char); typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; +typedef int s32; typedef unsigned long long u64; typedef signed long long s64; typedef u8 __u8; typedef u16 __u16; typedef u32 __u32; +typedef s32 __s32; typedef u64 __u64; typedef u16 __bitwise __le16; diff --git a/utils/src/trace.c b/utils/src/trace.c new file mode 100644 index 00000000..ebff1df0 --- /dev/null +++ b/utils/src/trace.c @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sparse.h" +#include "util.h" +#include "ioctl.h" +#include "cmd.h" + +static int get_ibuf(int fd, int cmd, struct scoutfs_ioctl_buf *ibuf) +{ + int ret; + + ibuf->ptr = 0; + ret = 1024 * 1024; + do { + ibuf->len = ret; + if (ibuf->ptr) + free((void *)(intptr_t)ibuf->ptr); + ibuf->ptr = (intptr_t)malloc(ibuf->len); + if (!ibuf->ptr) { + ret = -errno; + fprintf(stderr, "allocate %d bytes failed: %s (%d)\n", + ibuf->len, strerror(errno), errno); + break; + } + + ret = ioctl(fd, cmd, ibuf); + if (ret < 0) { + ret = -errno; + fprintf(stderr, "ioctl cmd %u failed: %s (%d)\n", + cmd, strerror(errno), errno); + break; + } + } while (ret > ibuf->len); + + if (ret >= 0) { + ibuf->len = ret; + ret = 0; + } + + return ret; +} + +static int decode_u64_bytes(struct scoutfs_trace_record *rec, u64 *args) +{ + u8 *data; + int shift; + u64 val; + int i; + + data = rec->data; + for (i = 0; i < rec->nr; i++) { + val = 0; + shift = 0; + for (;;) { + val |= (u64)(*data & 127) << shift; + + if (!((*(data++)) & 128)) + break; + + shift += 7; + } + + args[i] = val; + } + + return data - rec->data; +} + +/* MY EYES */ +static void printf_nr_args(char *fmt, int nr, u64 *args) +{ + switch(nr) { + case 0: printf(fmt); break; + case 1: printf(fmt, args[0]); break; + case 2: printf(fmt, args[0], args[1]); break; + case 3: printf(fmt, args[0], args[1], args[2]); break; + case 4: printf(fmt, args[0], args[1], args[2], args[3]); break; + case 5: printf(fmt, args[0], args[1], args[2], args[3], args[4]); break; + case 6: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5]); break; + case 7: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6]); break; + case 8: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7]); break; + case 9: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8]); break; + case 10: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9]); break; + case 11: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9], + args[10]); break; + case 12: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9], + args[10], args[11]); break; + case 13: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9], + args[10], args[11], args[12]); break; + case 14: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9], + args[10], args[11], args[12], args[13]); break; + case 15: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9], + args[10], args[11], args[12], args[13], args[14]); break; + case 16: printf(fmt, args[0], args[1], args[2], args[3], args[4], + args[5], args[6], args[7], args[8], args[9], + args[10], args[11], args[12], args[13], args[14], + args[15]); break; + default: + printf("(too many args: fmt '%s' nr %d)\n", fmt, nr); + break; + } +} + +static int trace_cmd(int argc, char **argv) +{ + char *path = "/sys/kernel/debug/scoutfs/trace"; + struct scoutfs_ioctl_buf fmts = {0,}; + struct scoutfs_ioctl_buf recs = {0,}; + struct scoutfs_trace_record *rec; + u64 args[32]; /* absurdly huge */ + int off; + int ret; + int fd; + + fd = open(path, O_RDONLY); + if (fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + path, strerror(errno), errno); + return ret; + } + + /* + * Read the formats and records. Our fd on the debugfs file + * should prevent the module from unloading which is the only + * way the per-module formats could change. + */ + ret = get_ibuf(fd, SCOUTFS_IOC_GET_TRACE_FORMATS, &fmts) ?: + get_ibuf(fd, SCOUTFS_IOC_GET_TRACE_RECORDS, &recs); + if (ret) + goto out; + + for (off = 0; off < recs.len; ) { + rec = (void *)(intptr_t)(recs.ptr + off); + off += sizeof(*rec) + decode_u64_bytes(rec, args); + + printf_nr_args((char *)fmts.ptr + rec->format_off, + rec->nr, args); + printf("\n"); + } + +out: + close(fd); + return ret; +}; + +static void __attribute__((constructor)) trace_ctor(void) +{ + cmd_register("trace", "", "print scoutfs kernel traces", + trace_cmd); +}