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 <zab@versity.com>
This commit is contained in:
Zach Brown
2016-05-26 21:58:14 -07:00
parent 29c1f529f1
commit 54867b0f9c
3 changed files with 201 additions and 0 deletions

30
utils/src/ioctl.h Normal file
View File

@@ -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

View File

@@ -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;

169
utils/src/trace.c Normal file
View File

@@ -0,0 +1,169 @@
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#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", "<device>", "print scoutfs kernel traces",
trace_cmd);
}