perf tools: Add trace event information parser
authorSteven Rostedt <rostedt@goodmis.org>
Mon, 17 Aug 2009 14:18:07 +0000 (16:18 +0200)
committerIngo Molnar <mingo@elte.hu>
Mon, 17 Aug 2009 14:32:39 +0000 (16:32 +0200)
Add util/trace-event-parse.c which provides the handlers to
parse the ftrace events info from the stream and handles the
ftrace perf samples event printing.

This file is a rename of the parse-events.c file from the
trace-cmd tools, written by Steven Rostedt and Josh Triplett,
originated from the git tree:

  git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git

This is a perf tools integration.

[ fweisbec@gmail.com: various changes for perf tools
                      integration. ]

Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: "Luis Claudio R. Goncalves" <lclaudio@uudg.org>
Cc: Clark Williams <williams@redhat.com>
Cc: Jon Masters <jonathan@jonmasters.org>
Cc: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Xiao Guangrong <xiaoguangrong@cn.fujitsu.com>
Cc: Zhaolei <zhaolei@cn.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Lai Jiangshan <laijs@cn.fujitsu.com>
Cc: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Tom Zanussi <tzanussi@gmail.com>
Cc: "Frank Ch. Eigler" <fche@redhat.com>
Cc: Roland McGrath <roland@redhat.com>
Cc: Jason Baron <jbaron@redhat.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Jiaying Zhang <jiayingz@google.com>
Cc: Anton Blanchard <anton@samba.org>
LKML-Reference: <1250518688-7207-3-git-send-email-fweisbec@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
tools/perf/util/trace-event-parse.c [new file with mode: 0644]

diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
new file mode 100644 (file)
index 0000000..ead6a9a
--- /dev/null
@@ -0,0 +1,2905 @@
+/*
+ * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License (not later!)
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  The parts for function graph printing was taken and modified from the
+ *  Linux Kernel that were written by Frederic Weisbecker.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#undef _GNU_SOURCE
+#include "util.h"
+#include "trace-event.h"
+
+int header_page_ts_offset;
+int header_page_ts_size;
+int header_page_size_offset;
+int header_page_size_size;
+int header_page_data_offset;
+int header_page_data_size;
+
+static char *input_buf;
+static unsigned long long input_buf_ptr;
+static unsigned long long input_buf_siz;
+
+static int cpus;
+static int long_size;
+
+static void init_input_buf(char *buf, unsigned long long size)
+{
+       input_buf = buf;
+       input_buf_siz = size;
+       input_buf_ptr = 0;
+}
+
+struct cmdline {
+       char *comm;
+       int pid;
+};
+
+static struct cmdline *cmdlines;
+static int cmdline_count;
+
+static int cmdline_cmp(const void *a, const void *b)
+{
+       const struct cmdline *ca = a;
+       const struct cmdline *cb = b;
+
+       if (ca->pid < cb->pid)
+               return -1;
+       if (ca->pid > cb->pid)
+               return 1;
+
+       return 0;
+}
+
+void parse_cmdlines(char *file, int size __unused)
+{
+       struct cmdline_list {
+               struct cmdline_list     *next;
+               char                    *comm;
+               int                     pid;
+       } *list = NULL, *item;
+       char *line;
+       char *next = NULL;
+       int i;
+
+       line = strtok_r(file, "\n", &next);
+       while (line) {
+               item = malloc_or_die(sizeof(*item));
+               sscanf(line, "%d %as", &item->pid,
+                      (float *)&item->comm); /* workaround gcc warning */
+               item->next = list;
+               list = item;
+               line = strtok_r(NULL, "\n", &next);
+               cmdline_count++;
+       }
+
+       cmdlines = malloc_or_die(sizeof(*cmdlines) * cmdline_count);
+
+       i = 0;
+       while (list) {
+               cmdlines[i].pid = list->pid;
+               cmdlines[i].comm = list->comm;
+               i++;
+               item = list;
+               list = list->next;
+               free(item);
+       }
+
+       qsort(cmdlines, cmdline_count, sizeof(*cmdlines), cmdline_cmp);
+}
+
+static struct func_map {
+       unsigned long long              addr;
+       char                            *func;
+       char                            *mod;
+} *func_list;
+static unsigned int func_count;
+
+static int func_cmp(const void *a, const void *b)
+{
+       const struct func_map *fa = a;
+       const struct func_map *fb = b;
+
+       if (fa->addr < fb->addr)
+               return -1;
+       if (fa->addr > fb->addr)
+               return 1;
+
+       return 0;
+}
+
+void parse_proc_kallsyms(char *file, unsigned int size __unused)
+{
+       struct func_list {
+               struct func_list        *next;
+               unsigned long long      addr;
+               char                    *func;
+               char                    *mod;
+       } *list = NULL, *item;
+       char *line;
+       char *next = NULL;
+       char *addr_str;
+       char ch;
+       int ret;
+       int i;
+
+       line = strtok_r(file, "\n", &next);
+       while (line) {
+               item = malloc_or_die(sizeof(*item));
+               item->mod = NULL;
+               ret = sscanf(line, "%as %c %as\t[%as",
+                            (float *)&addr_str, /* workaround gcc warning */
+                            &ch,
+                            (float *)&item->func,
+                            (float *)&item->mod);
+               item->addr = strtoull(addr_str, NULL, 16);
+               free(addr_str);
+
+               /* truncate the extra ']' */
+               if (item->mod)
+                       item->mod[strlen(item->mod) - 1] = 0;
+
+
+               item->next = list;
+               list = item;
+               line = strtok_r(NULL, "\n", &next);
+               func_count++;
+       }
+
+       func_list = malloc_or_die(sizeof(*func_list) * func_count + 1);
+
+       i = 0;
+       while (list) {
+               func_list[i].func = list->func;
+               func_list[i].addr = list->addr;
+               func_list[i].mod = list->mod;
+               i++;
+               item = list;
+               list = list->next;
+               free(item);
+       }
+
+       qsort(func_list, func_count, sizeof(*func_list), func_cmp);
+
+       /*
+        * Add a special record at the end.
+        */
+       func_list[func_count].func = NULL;
+       func_list[func_count].addr = 0;
+       func_list[func_count].mod = NULL;
+}
+
+/*
+ * We are searching for a record in between, not an exact
+ * match.
+ */
+static int func_bcmp(const void *a, const void *b)
+{
+       const struct func_map *fa = a;
+       const struct func_map *fb = b;
+
+       if ((fa->addr == fb->addr) ||
+
+           (fa->addr > fb->addr &&
+            fa->addr < (fb+1)->addr))
+               return 0;
+
+       if (fa->addr < fb->addr)
+               return -1;
+
+       return 1;
+}
+
+static struct func_map *find_func(unsigned long long addr)
+{
+       struct func_map *func;
+       struct func_map key;
+
+       key.addr = addr;
+
+       func = bsearch(&key, func_list, func_count, sizeof(*func_list),
+                      func_bcmp);
+
+       return func;
+}
+
+void print_funcs(void)
+{
+       int i;
+
+       for (i = 0; i < (int)func_count; i++) {
+               printf("%016llx %s",
+                      func_list[i].addr,
+                      func_list[i].func);
+               if (func_list[i].mod)
+                       printf(" [%s]\n", func_list[i].mod);
+               else
+                       printf("\n");
+       }
+}
+
+static struct printk_map {
+       unsigned long long              addr;
+       char                            *printk;
+} *printk_list;
+static unsigned int printk_count;
+
+static int printk_cmp(const void *a, const void *b)
+{
+       const struct func_map *fa = a;
+       const struct func_map *fb = b;
+
+       if (fa->addr < fb->addr)
+               return -1;
+       if (fa->addr > fb->addr)
+               return 1;
+
+       return 0;
+}
+
+static struct printk_map *find_printk(unsigned long long addr)
+{
+       struct printk_map *printk;
+       struct printk_map key;
+
+       key.addr = addr;
+
+       printk = bsearch(&key, printk_list, printk_count, sizeof(*printk_list),
+                        printk_cmp);
+
+       return printk;
+}
+
+void parse_ftrace_printk(char *file, unsigned int size __unused)
+{
+       struct printk_list {
+               struct printk_list      *next;
+               unsigned long long      addr;
+               char                    *printk;
+       } *list = NULL, *item;
+       char *line;
+       char *next = NULL;
+       char *addr_str;
+       int ret;
+       int i;
+
+       line = strtok_r(file, "\n", &next);
+       while (line) {
+               item = malloc_or_die(sizeof(*item));
+               ret = sscanf(line, "%as : %as",
+                            (float *)&addr_str, /* workaround gcc warning */
+                            (float *)&item->printk);
+               item->addr = strtoull(addr_str, NULL, 16);
+               free(addr_str);
+
+               item->next = list;
+               list = item;
+               line = strtok_r(NULL, "\n", &next);
+               printk_count++;
+       }
+
+       printk_list = malloc_or_die(sizeof(*printk_list) * printk_count + 1);
+
+       i = 0;
+       while (list) {
+               printk_list[i].printk = list->printk;
+               printk_list[i].addr = list->addr;
+               i++;
+               item = list;
+               list = list->next;
+               free(item);
+       }
+
+       qsort(printk_list, printk_count, sizeof(*printk_list), printk_cmp);
+}
+
+void print_printk(void)
+{
+       int i;
+
+       for (i = 0; i < (int)printk_count; i++) {
+               printf("%016llx %s\n",
+                      printk_list[i].addr,
+                      printk_list[i].printk);
+       }
+}
+
+static struct event *alloc_event(void)
+{
+       struct event *event;
+
+       event = malloc_or_die(sizeof(*event));
+       memset(event, 0, sizeof(*event));
+
+       return event;
+}
+
+enum event_type {
+       EVENT_ERROR,
+       EVENT_NONE,
+       EVENT_SPACE,
+       EVENT_NEWLINE,
+       EVENT_OP,
+       EVENT_DELIM,
+       EVENT_ITEM,
+       EVENT_DQUOTE,
+       EVENT_SQUOTE,
+};
+
+static struct event *event_list;
+
+static void add_event(struct event *event)
+{
+       event->next = event_list;
+       event_list = event;
+}
+
+static int event_item_type(enum event_type type)
+{
+       switch (type) {
+       case EVENT_ITEM ... EVENT_SQUOTE:
+               return 1;
+       case EVENT_ERROR ... EVENT_DELIM:
+       default:
+               return 0;
+       }
+}
+
+static void free_arg(struct print_arg *arg)
+{
+       if (!arg)
+               return;
+
+       switch (arg->type) {
+       case PRINT_ATOM:
+               if (arg->atom.atom)
+                       free(arg->atom.atom);
+               break;
+       case PRINT_NULL:
+       case PRINT_FIELD ... PRINT_OP:
+       default:
+               /* todo */
+               break;
+       }
+
+       free(arg);
+}
+
+static enum event_type get_type(int ch)
+{
+       if (ch == '\n')
+               return EVENT_NEWLINE;
+       if (isspace(ch))
+               return EVENT_SPACE;
+       if (isalnum(ch) || ch == '_')
+               return EVENT_ITEM;
+       if (ch == '\'')
+               return EVENT_SQUOTE;
+       if (ch == '"')
+               return EVENT_DQUOTE;
+       if (!isprint(ch))
+               return EVENT_NONE;
+       if (ch == '(' || ch == ')' || ch == ',')
+               return EVENT_DELIM;
+
+       return EVENT_OP;
+}
+
+static int __read_char(void)
+{
+       if (input_buf_ptr >= input_buf_siz)
+               return -1;
+
+       return input_buf[input_buf_ptr++];
+}
+
+static int __peek_char(void)
+{
+       if (input_buf_ptr >= input_buf_siz)
+               return -1;
+
+       return input_buf[input_buf_ptr];
+}
+
+static enum event_type __read_token(char **tok)
+{
+       char buf[BUFSIZ];
+       int ch, last_ch, quote_ch, next_ch;
+       int i = 0;
+       int tok_size = 0;
+       enum event_type type;
+
+       *tok = NULL;
+
+
+       ch = __read_char();
+       if (ch < 0)
+               return EVENT_NONE;
+
+       type = get_type(ch);
+       if (type == EVENT_NONE)
+               return type;
+
+       buf[i++] = ch;
+
+       switch (type) {
+       case EVENT_NEWLINE:
+       case EVENT_DELIM:
+               *tok = malloc_or_die(2);
+               (*tok)[0] = ch;
+               (*tok)[1] = 0;
+               return type;
+
+       case EVENT_OP:
+               switch (ch) {
+               case '-':
+                       next_ch = __peek_char();
+                       if (next_ch == '>') {
+                               buf[i++] = __read_char();
+                               break;
+                       }
+                       /* fall through */
+               case '+':
+               case '|':
+               case '&':
+               case '>':
+               case '<':
+                       last_ch = ch;
+                       ch = __peek_char();
+                       if (ch != last_ch)
+                               goto test_equal;
+                       buf[i++] = __read_char();
+                       switch (last_ch) {
+                       case '>':
+                       case '<':
+                               goto test_equal;
+                       default:
+                               break;
+                       }
+                       break;
+               case '!':
+               case '=':
+                       goto test_equal;
+               default: /* what should we do instead? */
+                       break;
+               }
+               buf[i] = 0;
+               *tok = strdup(buf);
+               return type;
+
+ test_equal:
+               ch = __peek_char();
+               if (ch == '=')
+                       buf[i++] = __read_char();
+               break;
+
+       case EVENT_DQUOTE:
+       case EVENT_SQUOTE:
+               /* don't keep quotes */
+               i--;
+               quote_ch = ch;
+               last_ch = 0;
+               do {
+                       if (i == (BUFSIZ - 1)) {
+                               buf[i] = 0;
+                               if (*tok) {
+                                       *tok = realloc(*tok, tok_size + BUFSIZ);
+                                       if (!*tok)
+                                               return EVENT_NONE;
+                                       strcat(*tok, buf);
+                               } else
+                                       *tok = strdup(buf);
+
+                               if (!*tok)
+                                       return EVENT_NONE;
+                               tok_size += BUFSIZ;
+                               i = 0;
+                       }
+                       last_ch = ch;
+                       ch = __read_char();
+                       buf[i++] = ch;
+               } while (ch != quote_ch && last_ch != '\\');
+               /* remove the last quote */
+               i--;
+               goto out;
+
+       case EVENT_ERROR ... EVENT_SPACE:
+       case EVENT_ITEM:
+       default:
+               break;
+       }
+
+       while (get_type(__peek_char()) == type) {
+               if (i == (BUFSIZ - 1)) {
+                       buf[i] = 0;
+                       if (*tok) {
+                               *tok = realloc(*tok, tok_size + BUFSIZ);
+                               if (!*tok)
+                                       return EVENT_NONE;
+                               strcat(*tok, buf);
+                       } else
+                               *tok = strdup(buf);
+
+                       if (!*tok)
+                               return EVENT_NONE;
+                       tok_size += BUFSIZ;
+                       i = 0;
+               }
+               ch = __read_char();
+               buf[i++] = ch;
+       }
+
+ out:
+       buf[i] = 0;
+       if (*tok) {
+               *tok = realloc(*tok, tok_size + i);
+               if (!*tok)
+                       return EVENT_NONE;
+               strcat(*tok, buf);
+       } else
+               *tok = strdup(buf);
+       if (!*tok)
+               return EVENT_NONE;
+
+       return type;
+}
+
+static void free_token(char *tok)
+{
+       if (tok)
+               free(tok);
+}
+
+static enum event_type read_token(char **tok)
+{
+       enum event_type type;
+
+       for (;;) {
+               type = __read_token(tok);
+               if (type != EVENT_SPACE)
+                       return type;
+
+               free_token(*tok);
+       }
+
+       /* not reached */
+       return EVENT_NONE;
+}
+
+/* no newline */
+static enum event_type read_token_item(char **tok)
+{
+       enum event_type type;
+
+       for (;;) {
+               type = __read_token(tok);
+               if (type != EVENT_SPACE && type != EVENT_NEWLINE)
+                       return type;
+
+               free_token(*tok);
+       }
+
+       /* not reached */
+       return EVENT_NONE;
+}
+
+static int test_type(enum event_type type, enum event_type expect)
+{
+       if (type != expect) {
+               die("Error: expected type %d but read %d",
+                   expect, type);
+               return -1;
+       }
+       return 0;
+}
+
+static int test_type_token(enum event_type type, char *token,
+                   enum event_type expect, char *expect_tok)
+{
+       if (type != expect) {
+               die("Error: expected type %d but read %d",
+                   expect, type);
+               return -1;
+       }
+
+       if (strcmp(token, expect_tok) != 0) {
+               die("Error: expected '%s' but read '%s'",
+                   expect_tok, token);
+               return -1;
+       }
+       return 0;
+}
+
+static int __read_expect_type(enum event_type expect, char **tok, int newline_ok)
+{
+       enum event_type type;
+
+       if (newline_ok)
+               type = read_token(tok);
+       else
+               type = read_token_item(tok);
+       return test_type(type, expect);
+}
+
+static int read_expect_type(enum event_type expect, char **tok)
+{
+       return __read_expect_type(expect, tok, 1);
+}
+
+static int __read_expected(enum event_type expect, char *str, int newline_ok)
+{
+       enum event_type type;
+       char *token;
+       int ret;
+
+       if (newline_ok)
+               type = read_token(&token);
+       else
+               type = read_token_item(&token);
+
+       ret = test_type_token(type, token, expect, str);
+
+       free_token(token);
+
+       return 0;
+}
+
+static int read_expected(enum event_type expect, char *str)
+{
+       return __read_expected(expect, str, 1);
+}
+
+static int read_expected_item(enum event_type expect, char *str)
+{
+       return __read_expected(expect, str, 0);
+}
+
+static char *event_read_name(void)
+{
+       char *token;
+
+       if (read_expected(EVENT_ITEM, (char *)"name") < 0)
+               return NULL;
+
+       if (read_expected(EVENT_OP, (char *)":") < 0)
+               return NULL;
+
+       if (read_expect_type(EVENT_ITEM, &token) < 0)
+               goto fail;
+
+       return token;
+
+ fail:
+       free_token(token);
+       return NULL;
+}
+
+static int event_read_id(void)
+{
+       char *token;
+       int id;
+
+       if (read_expected_item(EVENT_ITEM, (char *)"ID") < 0)
+               return -1;
+
+       if (read_expected(EVENT_OP, (char *)":") < 0)
+               return -1;
+
+       if (read_expect_type(EVENT_ITEM, &token) < 0)
+               goto fail;
+
+       id = strtoul(token, NULL, 0);
+       free_token(token);
+       return id;
+
+ fail:
+       free_token(token);
+       return -1;
+}
+
+static int event_read_fields(struct event *event, struct format_field **fields)
+{
+       struct format_field *field = NULL;
+       enum event_type type;
+       char *token;
+       char *last_token;
+       int count = 0;
+
+       do {
+               type = read_token(&token);
+               if (type == EVENT_NEWLINE) {
+                       free_token(token);
+                       return count;
+               }
+
+               count++;
+
+               if (test_type_token(type, token, EVENT_ITEM, (char *)"field"))
+                       goto fail;
+               free_token(token);
+
+               type = read_token(&token);
+               /*
+                * The ftrace fields may still use the "special" name.
+                * Just ignore it.
+                */
+               if (event->flags & EVENT_FL_ISFTRACE &&
+                   type == EVENT_ITEM && strcmp(token, "special") == 0) {
+                       free_token(token);
+                       type = read_token(&token);
+               }
+
+               if (test_type_token(type, token, EVENT_OP, (char *)":") < 0)
+                       return -1;
+
+               if (read_expect_type(EVENT_ITEM, &token) < 0)
+                       goto fail;
+
+               last_token = token;
+
+               field = malloc_or_die(sizeof(*field));
+               memset(field, 0, sizeof(*field));
+
+               /* read the rest of the type */
+               for (;;) {
+                       type = read_token(&token);
+                       if (type == EVENT_ITEM ||
+                           (type == EVENT_OP && strcmp(token, "*") == 0) ||
+                           /*
+                            * Some of the ftrace fields are broken and have
+                            * an illegal "." in them.
+                            */
+                           (event->flags & EVENT_FL_ISFTRACE &&
+                            type == EVENT_OP && strcmp(token, ".") == 0)) {
+
+                               if (strcmp(token, "*") == 0)
+                                       field->flags |= FIELD_IS_POINTER;
+
+                               if (field->type) {
+                                       field->type = realloc(field->type,
+                                                             strlen(field->type) +
+                                                             strlen(last_token) + 2);
+                                       strcat(field->type, " ");
+                                       strcat(field->type, last_token);
+                               } else
+                                       field->type = last_token;
+                               last_token = token;
+                               continue;
+                       }
+
+                       break;
+               }
+
+               if (!field->type) {
+                       die("no type found");
+                       goto fail;
+               }
+               field->name = last_token;
+
+               if (test_type(type, EVENT_OP))
+                       goto fail;
+
+               if (strcmp(token, "[") == 0) {
+                       enum event_type last_type = type;
+                       char *brackets = token;
+                       int len;
+
+                       field->flags |= FIELD_IS_ARRAY;
+
+                       type = read_token(&token);
+                       while (strcmp(token, "]") != 0) {
+                               if (last_type == EVENT_ITEM &&
+                                   type == EVENT_ITEM)
+                                       len = 2;
+                               else
+                                       len = 1;
+                               last_type = type;
+
+                               brackets = realloc(brackets,
+                                                  strlen(brackets) +
+                                                  strlen(token) + len);
+                               if (len == 2)
+                                       strcat(brackets, " ");
+                               strcat(brackets, token);
+                               free_token(token);
+                               type = read_token(&token);
+                               if (type == EVENT_NONE) {
+                                       die("failed to find token");
+                                       goto fail;
+                               }
+                       }
+
+                       free_token(token);
+
+                       brackets = realloc(brackets, strlen(brackets) + 2);
+                       strcat(brackets, "]");
+
+                       /* add brackets to type */
+
+                       type = read_token(&token);
+                       /*
+                        * If the next token is not an OP, then it is of
+                        * the format: type [] item;
+                        */
+                       if (type == EVENT_ITEM) {
+                               field->type = realloc(field->type,
+                                                     strlen(field->type) +
+                                                     strlen(field->name) +
+                                                     strlen(brackets) + 2);
+                               strcat(field->type, " ");
+                               strcat(field->type, field->name);
+                               free_token(field->name);
+                               strcat(field->type, brackets);
+                               field->name = token;
+                               type = read_token(&token);
+                       } else {
+                               field->type = realloc(field->type,
+                                                     strlen(field->type) +
+                                                     strlen(brackets) + 1);
+                               strcat(field->type, brackets);
+                       }
+                       free(brackets);
+               }
+
+               if (test_type_token(type, token,  EVENT_OP, (char *)";"))
+                       goto fail;
+               free_token(token);
+
+               if (read_expected(EVENT_ITEM, (char *)"offset") < 0)
+                       goto fail_expect;
+
+               if (read_expected(EVENT_OP, (char *)":") < 0)
+                       goto fail_expect;
+
+               if (read_expect_type(EVENT_ITEM, &token))
+                       goto fail;
+               field->offset = strtoul(token, NULL, 0);
+               free_token(token);
+
+               if (read_expected(EVENT_OP, (char *)";") < 0)
+                       goto fail_expect;
+
+               if (read_expected(EVENT_ITEM, (char *)"size") < 0)
+                       goto fail_expect;
+
+               if (read_expected(EVENT_OP, (char *)":") < 0)
+                       goto fail_expect;
+
+               if (read_expect_type(EVENT_ITEM, &token))
+                       goto fail;
+               field->size = strtoul(token, NULL, 0);
+               free_token(token);
+
+               if (read_expected(EVENT_OP, (char *)";") < 0)
+                       goto fail_expect;
+
+               if (read_expect_type(EVENT_NEWLINE, &token) < 0)
+                       goto fail;
+               free_token(token);
+
+               *fields = field;
+               fields = &field->next;
+
+       } while (1);
+
+       return 0;
+
+fail:
+       free_token(token);
+fail_expect:
+       if (field)
+               free(field);
+       return -1;
+}
+
+static int event_read_format(struct event *event)
+{
+       char *token;
+       int ret;
+
+       if (read_expected_item(EVENT_ITEM, (char *)"format") < 0)
+               return -1;
+
+       if (read_expected(EVENT_OP, (char *)":") < 0)
+               return -1;
+
+       if (read_expect_type(EVENT_NEWLINE, &token))
+               goto fail;
+       free_token(token);
+
+       ret = event_read_fields(event, &event->format.common_fields);
+       if (ret < 0)
+               return ret;
+       event->format.nr_common = ret;
+
+       ret = event_read_fields(event, &event->format.fields);
+       if (ret < 0)
+               return ret;
+       event->format.nr_fields = ret;
+
+       return 0;
+
+ fail:
+       free_token(token);
+       return -1;
+}
+
+enum event_type
+process_arg_token(struct event *event, struct print_arg *arg,
+                 char **tok, enum event_type type);
+
+static enum event_type
+process_arg(struct event *event, struct print_arg *arg, char **tok)
+{
+       enum event_type type;
+       char *token;
+
+       type = read_token(&token);
+       *tok = token;
+
+       return process_arg_token(event, arg, tok, type);
+}
+
+static enum event_type
+process_cond(struct event *event, struct print_arg *top, char **tok)
+{
+       struct print_arg *arg, *left, *right;
+       enum event_type type;
+       char *token = NULL;
+
+       arg = malloc_or_die(sizeof(*arg));
+       memset(arg, 0, sizeof(*arg));
+
+       left = malloc_or_die(sizeof(*left));
+
+       right = malloc_or_die(sizeof(*right));
+
+       arg->type = PRINT_OP;
+       arg->op.left = left;
+       arg->op.right = right;
+
+       *tok = NULL;
+       type = process_arg(event, left, &token);
+       if (test_type_token(type, token, EVENT_OP, (char *)":"))
+               goto out_free;
+
+       arg->op.op = token;
+
+       type = process_arg(event, right, &token);
+
+       top->op.right = arg;
+
+       *tok = token;
+       return type;
+
+out_free:
+       free_token(*tok);
+       free(right);
+       free(left);
+       free_arg(arg);
+       return EVENT_ERROR;
+}
+
+static int get_op_prio(char *op)
+{
+       if (!op[1]) {
+               switch (op[0]) {
+               case '*':
+               case '/':
+               case '%':
+                       return 6;
+               case '+':
+               case '-':
+                       return 7;
+                       /* '>>' and '<<' are 8 */
+               case '<':
+               case '>':
+                       return 9;
+                       /* '==' and '!=' are 10 */
+               case '&':
+                       return 11;
+               case '^':
+                       return 12;
+               case '|':
+                       return 13;
+               case '?':
+                       return 16;
+               default:
+                       die("unknown op '%c'", op[0]);
+                       return -1;
+               }
+       } else {
+               if (strcmp(op, "++") == 0 ||
+                   strcmp(op, "--") == 0) {
+                       return 3;
+               } else if (strcmp(op, ">>") == 0 ||
+                          strcmp(op, "<<") == 0) {
+                       return 8;
+               } else if (strcmp(op, ">=") == 0 ||
+                          strcmp(op, "<=") == 0) {
+                       return 9;
+               } else if (strcmp(op, "==") == 0 ||
+                          strcmp(op, "!=") == 0) {
+                       return 10;
+               } else if (strcmp(op, "&&") == 0) {
+                       return 14;
+               } else if (strcmp(op, "||") == 0) {
+                       return 15;
+               } else {
+                       die("unknown op '%s'", op);
+                       return -1;
+               }
+       }
+}
+
+static void set_op_prio(struct print_arg *arg)
+{
+
+       /* single ops are the greatest */
+       if (!arg->op.left || arg->op.left->type == PRINT_NULL) {
+               arg->op.prio = 0;
+               return;
+       }
+
+       arg->op.prio = get_op_prio(arg->op.op);
+}
+
+static enum event_type
+process_op(struct event *event, struct print_arg *arg, char **tok)
+{
+       struct print_arg *left, *right = NULL;
+       enum event_type type;
+       char *token;
+
+       /* the op is passed in via tok */
+       token = *tok;
+
+       if (arg->type == PRINT_OP && !arg->op.left) {
+               /* handle single op */
+               if (token[1]) {
+                       die("bad op token %s", token);
+                       return EVENT_ERROR;
+               }
+               switch (token[0]) {
+               case '!':
+               case '+':
+               case '-':
+                       break;
+               default:
+                       die("bad op token %s", token);
+                       return EVENT_ERROR;
+               }
+
+               /* make an empty left */
+               left = malloc_or_die(sizeof(*left));
+               left->type = PRINT_NULL;
+               arg->op.left = left;
+
+               right = malloc_or_die(sizeof(*right));
+               arg->op.right = right;
+
+               type = process_arg(event, right, tok);
+
+       } else if (strcmp(token, "?") == 0) {
+
+               left = malloc_or_die(sizeof(*left));
+               /* copy the top arg to the left */
+               *left = *arg;
+
+               arg->type = PRINT_OP;
+               arg->op.op = token;
+               arg->op.left = left;
+               arg->op.prio = 0;
+
+               type = process_cond(event, arg, tok);
+
+       } else if (strcmp(token, ">>") == 0 ||
+                  strcmp(token, "<<") == 0 ||
+                  strcmp(token, "&") == 0 ||
+                  strcmp(token, "|") == 0 ||
+                  strcmp(token, "&&") == 0 ||
+                  strcmp(token, "||") == 0 ||
+                  strcmp(token, "-") == 0 ||
+                  strcmp(token, "+") == 0 ||
+                  strcmp(token, "*") == 0 ||
+                  strcmp(token, "^") == 0 ||
+                  strcmp(token, "/") == 0 ||
+                  strcmp(token, "==") == 0 ||
+                  strcmp(token, "!=") == 0) {
+
+               left = malloc_or_die(sizeof(*left));
+
+               /* copy the top arg to the left */
+               *left = *arg;
+
+               arg->type = PRINT_OP;
+               arg->op.op = token;
+               arg->op.left = left;
+
+               set_op_prio(arg);
+
+               right = malloc_or_die(sizeof(*right));
+
+               type = process_arg(event, right, tok);
+
+               arg->op.right = right;
+
+       } else {
+               die("unknown op '%s'", token);
+               /* the arg is now the left side */
+               return EVENT_NONE;
+       }
+
+
+       if (type == EVENT_OP) {
+               int prio;
+
+               /* higher prios need to be closer to the root */
+               prio = get_op_prio(*tok);
+
+               if (prio > arg->op.prio)
+                       return process_op(event, arg, tok);
+
+               return process_op(event, right, tok);
+       }
+
+       return type;
+}
+
+static enum event_type
+process_entry(struct event *event __unused, struct print_arg *arg,
+             char **tok)
+{
+       enum event_type type;
+       char *field;
+       char *token;
+
+       if (read_expected(EVENT_OP, (char *)"->") < 0)
+               return EVENT_ERROR;
+
+       if (read_expect_type(EVENT_ITEM, &token) < 0)
+               goto fail;
+       field = token;
+
+       arg->type = PRINT_FIELD;
+       arg->field.name = field;
+
+       type = read_token(&token);
+       *tok = token;
+
+       return type;
+
+fail:
+       free_token(token);
+       return EVENT_ERROR;
+}
+
+static char *arg_eval (struct print_arg *arg);
+
+static long long arg_num_eval(struct print_arg *arg)
+{
+       long long left, right;
+       long long val = 0;
+
+       switch (arg->type) {
+       case PRINT_ATOM:
+               val = strtoll(arg->atom.atom, NULL, 0);
+               break;
+       case PRINT_TYPE:
+               val = arg_num_eval(arg->typecast.item);
+               break;
+       case PRINT_OP:
+               switch (arg->op.op[0]) {
+               case '|':
+                       left = arg_num_eval(arg->op.left);
+                       right = arg_num_eval(arg->op.right);
+                       if (arg->op.op[1])
+                               val = left || right;
+                       else
+                               val = left | right;
+                       break;
+               case '&':
+                       left = arg_num_eval(arg->op.left);
+                       right = arg_num_eval(arg->op.right);
+                       if (arg->op.op[1])
+                               val = left && right;
+                       else
+                               val = left & right;
+                       break;
+               case '<':
+                       left = arg_num_eval(arg->op.left);
+                       right = arg_num_eval(arg->op.right);
+                       switch (arg->op.op[1]) {
+                       case 0:
+                               val = left < right;
+                               break;
+                       case '<':
+                               val = left << right;
+                               break;
+                       case '=':
+                               val = left <= right;
+                               break;
+                       default:
+                               die("unknown op '%s'", arg->op.op);
+                       }
+                       break;
+               case '>':
+                       left = arg_num_eval(arg->op.left);
+                       right = arg_num_eval(arg->op.right);
+                       switch (arg->op.op[1]) {
+                       case 0:
+                               val = left > right;
+                               break;
+                       case '>':
+                               val = left >> right;
+                               break;
+                       case '=':
+                               val = left >= right;
+                               break;
+                       default:
+                               die("unknown op '%s'", arg->op.op);
+                       }
+                       break;
+               case '=':
+                       left = arg_num_eval(arg->op.left);
+                       right = arg_num_eval(arg->op.right);
+
+                       if (arg->op.op[1] != '=')
+                               die("unknown op '%s'", arg->op.op);
+
+                       val = left == right;
+                       break;
+               case '!':
+                       left = arg_num_eval(arg->op.left);
+                       right = arg_num_eval(arg->op.right);
+
+                       switch (arg->op.op[1]) {
+                       case '=':
+                               val = left != right;
+                               break;
+                       default:
+                               die("unknown op '%s'", arg->op.op);
+                       }
+                       break;
+               default:
+                       die("unknown op '%s'", arg->op.op);
+               }
+               break;
+
+       case PRINT_NULL:
+       case PRINT_FIELD ... PRINT_SYMBOL:
+       case PRINT_STRING:
+       default:
+               die("invalid eval type %d", arg->type);
+
+       }
+       return val;
+}
+
+static char *arg_eval (struct print_arg *arg)
+{
+       long long val;
+       static char buf[20];
+
+       switch (arg->type) {
+       case PRINT_ATOM:
+               return arg->atom.atom;
+       case PRINT_TYPE:
+               return arg_eval(arg->typecast.item);
+       case PRINT_OP:
+               val = arg_num_eval(arg);
+               sprintf(buf, "%lld", val);
+               return buf;
+
+       case PRINT_NULL:
+       case PRINT_FIELD ... PRINT_SYMBOL:
+       case PRINT_STRING:
+       default:
+               die("invalid eval type %d", arg->type);
+               break;
+       }
+
+       return NULL;
+}
+
+static enum event_type
+process_fields(struct event *event, struct print_flag_sym **list, char **tok)
+{
+       enum event_type type;
+       struct print_arg *arg = NULL;
+       struct print_flag_sym *field;
+       char *token = NULL;
+       char *value;
+
+       do {
+               free_token(token);
+               type = read_token_item(&token);
+               if (test_type_token(type, token, EVENT_OP, (char *)"{"))
+                       break;
+
+               arg = malloc_or_die(sizeof(*arg));
+
+               free_token(token);
+               type = process_arg(event, arg, &token);
+               if (test_type_token(type, token, EVENT_DELIM, (char *)","))
+                       goto out_free;
+
+               field = malloc_or_die(sizeof(*field));
+               memset(field, 0, sizeof(field));
+
+               value = arg_eval(arg);
+               field->value = strdup(value);
+
+               free_token(token);
+               type = process_arg(event, arg, &token);
+               if (test_type_token(type, token, EVENT_OP, (char *)"}"))
+                       goto out_free;
+
+               value = arg_eval(arg);
+               field->str = strdup(value);
+               free_arg(arg);
+               arg = NULL;
+
+               *list = field;
+               list = &field->next;
+
+               free_token(token);
+               type = read_token_item(&token);
+       } while (type == EVENT_DELIM && strcmp(token, ",") == 0);
+
+       *tok = token;
+       return type;
+
+out_free:
+       free_arg(arg);
+       free_token(token);
+
+       return EVENT_ERROR;
+}
+
+static enum event_type
+process_flags(struct event *event, struct print_arg *arg, char **tok)
+{
+       struct print_arg *field;
+       enum event_type type;
+       char *token;
+
+       memset(arg, 0, sizeof(*arg));
+       arg->type = PRINT_FLAGS;
+
+       if (read_expected_item(EVENT_DELIM, (char *)"(") < 0)
+               return EVENT_ERROR;
+
+       field = malloc_or_die(sizeof(*field));
+
+       type = process_arg(event, field, &token);
+       if (test_type_token(type, token, EVENT_DELIM, (char *)","))
+               goto out_free;
+
+       arg->flags.field = field;
+
+       type = read_token_item(&token);
+       if (event_item_type(type)) {
+               arg->flags.delim = token;
+               type = read_token_item(&token);
+       }
+
+       if (test_type_token(type, token, EVENT_DELIM, (char *)","))
+               goto out_free;
+
+       type = process_fields(event, &arg->flags.flags, &token);
+       if (test_type_token(type, token, EVENT_DELIM, (char *)")"))
+               goto out_free;
+
+       free_token(token);
+       type = read_token_item(tok);
+       return type;
+
+out_free:
+       free_token(token);
+       return EVENT_ERROR;
+}
+
+static enum event_type
+process_symbols(struct event *event, struct print_arg *arg, char **tok)
+{
+       struct print_arg *field;
+       enum event_type type;
+       char *token;
+
+       memset(arg, 0, sizeof(*arg));
+       arg->type = PRINT_SYMBOL;
+
+       if (read_expected_item(EVENT_DELIM, (char *)"(") < 0)
+               return EVENT_ERROR;
+
+       field = malloc_or_die(sizeof(*field));
+
+       type = process_arg(event, field, &token);
+       if (test_type_token(type, token, EVENT_DELIM, (char *)","))
+               goto out_free;
+
+       arg->symbol.field = field;
+
+       type = process_fields(event, &arg->symbol.symbols, &token);
+       if (test_type_token(type, token, EVENT_DELIM, (char *)")"))
+               goto out_free;
+
+       free_token(token);
+       type = read_token_item(tok);
+       return type;
+
+out_free:
+       free_token(token);
+       return EVENT_ERROR;
+}
+
+static enum event_type
+process_paren(struct event *event, struct print_arg *arg, char **tok)
+{
+       struct print_arg *item_arg;
+       enum event_type type;
+       char *token;
+
+       type = process_arg(event, arg, &token);
+
+       if (type == EVENT_ERROR)
+               return EVENT_ERROR;
+
+       if (type == EVENT_OP)
+               type = process_op(event, arg, &token);
+
+       if (type == EVENT_ERROR)
+               return EVENT_ERROR;
+
+       if (test_type_token(type, token, EVENT_DELIM, (char *)")")) {
+               free_token(token);
+               return EVENT_ERROR;
+       }
+
+       free_token(token);
+       type = read_token_item(&token);
+
+       /*
+        * If the next token is an item or another open paren, then
+        * this was a typecast.
+        */
+       if (event_item_type(type) ||
+           (type == EVENT_DELIM && strcmp(token, "(") == 0)) {
+
+               /* make this a typecast and contine */
+
+               /* prevous must be an atom */
+               if (arg->type != PRINT_ATOM)
+                       die("previous needed to be PRINT_ATOM");
+
+               item_arg = malloc_or_die(sizeof(*item_arg));
+
+               arg->type = PRINT_TYPE;
+               arg->typecast.type = arg->atom.atom;
+               arg->typecast.item = item_arg;
+               type = process_arg_token(event, item_arg, &token, type);
+
+       }
+
+       *tok = token;
+       return type;
+}
+
+
+static enum event_type
+process_str(struct event *event __unused, struct print_arg *arg, char **tok)
+{
+       enum event_type type;
+       char *token;
+
+       if (read_expected(EVENT_DELIM, (char *)"(") < 0)
+               return EVENT_ERROR;
+
+       if (read_expect_type(EVENT_ITEM, &token) < 0)
+               goto fail;
+
+       arg->type = PRINT_STRING;
+       arg->string.string = token;
+
+       if (read_expected(EVENT_DELIM, (char *)")") < 0)
+               return EVENT_ERROR;
+
+       type = read_token(&token);
+       *tok = token;
+
+       return type;
+fail:
+       free_token(token);
+       return EVENT_ERROR;
+}
+
+enum event_type
+process_arg_token(struct event *event, struct print_arg *arg,
+                 char **tok, enum event_type type)
+{
+       char *token;
+       char *atom;
+
+       token = *tok;
+
+       switch (type) {
+       case EVENT_ITEM:
+               if (strcmp(token, "REC") == 0) {
+                       free_token(token);
+                       type = process_entry(event, arg, &token);
+               } else if (strcmp(token, "__print_flags") == 0) {
+                       free_token(token);
+                       type = process_flags(event, arg, &token);
+               } else if (strcmp(token, "__print_symbolic") == 0) {
+                       free_token(token);
+                       type = process_symbols(event, arg, &token);
+               } else if (strcmp(token, "__get_str") == 0) {
+                       free_token(token);
+                       type = process_str(event, arg, &token);
+               } else {
+                       atom = token;
+                       /* test the next token */
+                       type = read_token_item(&token);
+
+                       /* atoms can be more than one token long */
+                       while (type == EVENT_ITEM) {
+                               atom = realloc(atom, strlen(atom) + strlen(token) + 2);
+                               strcat(atom, " ");
+                               strcat(atom, token);
+                               free_token(token);
+                               type = read_token_item(&token);
+                       }
+
+                       /* todo, test for function */
+
+                       arg->type = PRINT_ATOM;
+                       arg->atom.atom = atom;
+               }
+               break;
+       case EVENT_DQUOTE:
+       case EVENT_SQUOTE:
+               arg->type = PRINT_ATOM;
+               arg->atom.atom = token;
+               type = read_token_item(&token);
+               break;
+       case EVENT_DELIM:
+               if (strcmp(token, "(") == 0) {
+                       free_token(token);
+                       type = process_paren(event, arg, &token);
+                       break;
+               }
+       case EVENT_OP:
+               /* handle single ops */
+               arg->type = PRINT_OP;
+               arg->op.op = token;
+               arg->op.left = NULL;
+               type = process_op(event, arg, &token);
+
+               break;
+
+       case EVENT_ERROR ... EVENT_NEWLINE:
+       default:
+               die("unexpected type %d", type);
+       }
+       *tok = token;
+
+       return type;
+}
+
+static int event_read_print_args(struct event *event, struct print_arg **list)
+{
+       enum event_type type;
+       struct print_arg *arg;
+       char *token;
+       int args = 0;
+
+       do {
+               arg = malloc_or_die(sizeof(*arg));
+               memset(arg, 0, sizeof(*arg));
+
+               type = process_arg(event, arg, &token);
+
+               if (type == EVENT_ERROR) {
+                       free_arg(arg);
+                       return -1;
+               }
+
+               *list = arg;
+               args++;
+
+               if (type == EVENT_OP) {
+                       type = process_op(event, arg, &token);
+                       list = &arg->next;
+                       continue;
+               }
+
+               if (type == EVENT_DELIM && strcmp(token, ",") == 0) {
+                       free_token(token);
+                       *list = arg;
+                       list = &arg->next;
+                       continue;
+               }
+               break;
+       } while (type != EVENT_NONE);
+
+       if (type != EVENT_NONE)
+               free_token(token);
+
+       return args;
+}
+
+static int event_read_print(struct event *event)
+{
+       enum event_type type;
+       char *token;
+       int ret;
+
+       if (read_expected_item(EVENT_ITEM, (char *)"print") < 0)
+               return -1;
+
+       if (read_expected(EVENT_ITEM, (char *)"fmt") < 0)
+               return -1;
+
+       if (read_expected(EVENT_OP, (char *)":") < 0)
+               return -1;
+
+       if (read_expect_type(EVENT_DQUOTE, &token) < 0)
+               goto fail;
+
+       event->print_fmt.format = token;
+       event->print_fmt.args = NULL;
+
+       /* ok to have no arg */
+       type = read_token_item(&token);
+
+       if (type == EVENT_NONE)
+               return 0;
+
+       if (test_type_token(type, token, EVENT_DELIM, (char *)","))
+               goto fail;
+
+       free_token(token);
+
+       ret = event_read_print_args(event, &event->print_fmt.args);
+       if (ret < 0)
+               return -1;
+
+       return 0;
+
+ fail:
+       free_token(token);
+       return -1;
+}
+
+static struct format_field *
+find_common_field(struct event *event, const char *name)
+{
+       struct format_field *format;
+
+       for (format = event->format.common_fields;
+            format; format = format->next) {
+               if (strcmp(format->name, name) == 0)
+                       break;
+       }
+
+       return format;
+}
+
+static struct format_field *
+find_field(struct event *event, const char *name)
+{
+       struct format_field *format;
+
+       for (format = event->format.fields;
+            format; format = format->next) {
+               if (strcmp(format->name, name) == 0)
+                       break;
+       }
+
+       return format;
+}
+
+static struct format_field *
+find_any_field(struct event *event, const char *name)
+{
+       struct format_field *format;
+
+       format = find_common_field(event, name);
+       if (format)
+               return format;
+       return find_field(event, name);
+}
+
+static unsigned long long read_size(void *ptr, int size)
+{
+       switch (size) {
+       case 1:
+               return *(unsigned char *)ptr;
+       case 2:
+               return data2host2(ptr);
+       case 4:
+               return data2host4(ptr);
+       case 8:
+               return data2host8(ptr);
+       default:
+               /* BUG! */
+               return 0;
+       }
+}
+
+static int get_common_info(const char *type, int *offset, int *size)
+{
+       struct event *event;
+       struct format_field *field;
+
+       /*
+        * All events should have the same common elements.
+        * Pick any event to find where the type is;
+        */
+       if (!event_list)
+               die("no event_list!");
+
+       event = event_list;
+       field = find_common_field(event, type);
+       if (!field)
+               die("field '%s' not found", type);
+
+       *offset = field->offset;
+       *size = field->size;
+
+       return 0;
+}
+
+static int parse_common_type(void *data)
+{
+       static int type_offset;
+       static int type_size;
+       int ret;
+
+       if (!type_size) {
+               ret = get_common_info("common_type",
+                                     &type_offset,
+                                     &type_size);
+               if (ret < 0)
+                       return ret;
+       }
+       return read_size(data + type_offset, type_size);
+}
+
+static int parse_common_pid(void *data)
+{
+       static int pid_offset;
+       static int pid_size;
+       int ret;
+
+       if (!pid_size) {
+               ret = get_common_info("common_pid",
+                                     &pid_offset,
+                                     &pid_size);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return read_size(data + pid_offset, pid_size);
+}
+
+static struct event *find_event(int id)
+{
+       struct event *event;
+
+       for (event = event_list; event; event = event->next) {
+               if (event->id == id)
+                       break;
+       }
+       return event;
+}
+
+static unsigned long long eval_num_arg(void *data, int size,
+                                  struct event *event, struct print_arg *arg)
+{
+       unsigned long long val = 0;
+       unsigned long long left, right;
+
+       switch (arg->type) {
+       case PRINT_NULL:
+               /* ?? */
+               return 0;
+       case PRINT_ATOM:
+               return strtoull(arg->atom.atom, NULL, 0);
+       case PRINT_FIELD:
+               if (!arg->field.field) {
+                       arg->field.field = find_any_field(event, arg->field.name);
+                       if (!arg->field.field)
+                               die("field %s not found", arg->field.name);
+               }
+               /* must be a number */
+               val = read_size(data + arg->field.field->offset,
+                               arg->field.field->size);
+               break;
+       case PRINT_FLAGS:
+       case PRINT_SYMBOL:
+               break;
+       case PRINT_TYPE:
+               return eval_num_arg(data, size, event, arg->typecast.item);
+       case PRINT_STRING:
+               return 0;
+               break;
+       case PRINT_OP:
+               left = eval_num_arg(data, size, event, arg->op.left);
+               right = eval_num_arg(data, size, event, arg->op.right);
+               switch (arg->op.op[0]) {
+               case '|':
+                       if (arg->op.op[1])
+                               val = left || right;
+                       else
+                               val = left | right;
+                       break;
+               case '&':
+                       if (arg->op.op[1])
+                               val = left && right;
+                       else
+                               val = left & right;
+                       break;
+               case '<':
+                       switch (arg->op.op[1]) {
+                       case 0:
+                               val = left < right;
+                               break;
+                       case '<':
+                               val = left << right;
+                               break;
+                       case '=':
+                               val = left <= right;
+                               break;
+                       default:
+                               die("unknown op '%s'", arg->op.op);
+                       }
+                       break;
+               case '>':
+                       switch (arg->op.op[1]) {
+                       case 0:
+                               val = left > right;
+                               break;
+                       case '>':
+                               val = left >> right;
+                               break;
+                       case '=':
+                               val = left >= right;
+                               break;
+                       default:
+                               die("unknown op '%s'", arg->op.op);
+                       }
+                       break;
+               case '=':
+                       if (arg->op.op[1] != '=')
+                               die("unknown op '%s'", arg->op.op);
+                       val = left == right;
+                       break;
+               default:
+                       die("unknown op '%s'", arg->op.op);
+               }
+               break;
+       default: /* not sure what to do there */
+               return 0;
+       }
+       return val;
+}
+
+struct flag {
+       const char *name;
+       unsigned long long value;
+};
+
+static const struct flag flags[] = {
+       { "HI_SOFTIRQ", 0 },
+       { "TIMER_SOFTIRQ", 1 },
+       { "NET_TX_SOFTIRQ", 2 },
+       { "NET_RX_SOFTIRQ", 3 },
+       { "BLOCK_SOFTIRQ", 4 },
+       { "TASKLET_SOFTIRQ", 5 },
+       { "SCHED_SOFTIRQ", 6 },
+       { "HRTIMER_SOFTIRQ", 7 },
+       { "RCU_SOFTIRQ", 8 },
+
+       { "HRTIMER_NORESTART", 0 },
+       { "HRTIMER_RESTART", 1 },
+};
+
+static unsigned long long eval_flag(const char *flag)
+{
+       int i;
+
+       /*
+        * Some flags in the format files do not get converted.
+        * If the flag is not numeric, see if it is something that
+        * we already know about.
+        */
+       if (isdigit(flag[0]))
+               return strtoull(flag, NULL, 0);
+
+       for (i = 0; i < (int)(sizeof(flags)/sizeof(flags[0])); i++)
+               if (strcmp(flags[i].name, flag) == 0)
+                       return flags[i].value;
+
+       return 0;
+}
+
+static void print_str_arg(void *data, int size,
+                         struct event *event, struct print_arg *arg)
+{
+       struct print_flag_sym *flag;
+       unsigned long long val, fval;
+       char *str;
+       int print;
+
+       switch (arg->type) {
+       case PRINT_NULL:
+               /* ?? */
+               return;
+       case PRINT_ATOM:
+               printf("%s", arg->atom.atom);
+               return;
+       case PRINT_FIELD:
+               if (!arg->field.field) {
+                       arg->field.field = find_any_field(event, arg->field.name);
+                       if (!arg->field.field)
+                               die("field %s not found", arg->field.name);
+               }
+               str = malloc_or_die(arg->field.field->size + 1);
+               memcpy(str, data + arg->field.field->offset,
+                      arg->field.field->size);
+               str[arg->field.field->size] = 0;
+               free(str);
+               break;
+       case PRINT_FLAGS:
+               val = eval_num_arg(data, size, event, arg->flags.field);
+               print = 0;
+               for (flag = arg->flags.flags; flag; flag = flag->next) {
+                       fval = eval_flag(flag->value);
+                       if (!val && !fval) {
+                               printf("%s", flag->str);
+                               break;
+                       }
+                       if (fval && (val & fval) == fval) {
+                               if (print && arg->flags.delim)
+                                       printf("%s", arg->flags.delim);
+                               printf("%s", flag->str);
+                               print = 1;
+                               val &= ~fval;
+                       }
+               }
+               break;
+       case PRINT_SYMBOL:
+               val = eval_num_arg(data, size, event, arg->symbol.field);
+               for (flag = arg->symbol.symbols; flag; flag = flag->next) {
+                       fval = eval_flag(flag->value);
+                       if (val == fval) {
+                               printf("%s", flag->str);
+                               break;
+                       }
+               }
+               break;
+
+       case PRINT_TYPE:
+               break;
+       case PRINT_STRING:
+               printf("%s", arg->string.string);
+               break;
+       case PRINT_OP:
+               /*
+                * The only op for string should be ? :
+                */
+               if (arg->op.op[0] != '?')
+                       return;
+               val = eval_num_arg(data, size, event, arg->op.left);
+               if (val)
+                       print_str_arg(data, size, event, arg->op.right->op.left);
+               else
+                       print_str_arg(data, size, event, arg->op.right->op.right);
+               break;
+       default:
+               /* well... */
+               break;
+       }
+}
+
+static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struct event *event)
+{
+       static struct format_field *field, *ip_field;
+       struct print_arg *args, *arg, **next;
+       unsigned long long ip, val;
+       char *ptr;
+       void *bptr;
+
+       if (!field) {
+               field = find_field(event, "buf");
+               if (!field)
+                       die("can't find buffer field for binary printk");
+               ip_field = find_field(event, "ip");
+               if (!ip_field)
+                       die("can't find ip field for binary printk");
+       }
+
+       ip = read_size(data + ip_field->offset, ip_field->size);
+
+       /*
+        * The first arg is the IP pointer.
+        */
+       args = malloc_or_die(sizeof(*args));
+       arg = args;
+       arg->next = NULL;
+       next = &arg->next;
+
+       arg->type = PRINT_ATOM;
+       arg->atom.atom = malloc_or_die(32);
+       sprintf(arg->atom.atom, "%lld", ip);
+
+       /* skip the first "%pf : " */
+       for (ptr = fmt + 6, bptr = data + field->offset;
+            bptr < data + size && *ptr; ptr++) {
+               int ls = 0;
+
+               if (*ptr == '%') {
+ process_again:
+                       ptr++;
+                       switch (*ptr) {
+                       case '%':
+                               break;
+                       case 'l':
+                               ls++;
+                               goto process_again;
+                       case 'L':
+                               ls = 2;
+                               goto process_again;
+                       case '0' ... '9':
+                               goto process_again;
+                       case 'p':
+                               ls = 1;
+                               /* fall through */
+                       case 'd':
+                       case 'u':
+                       case 'x':
+                       case 'i':
+                               bptr = (void *)(((unsigned long)bptr + (long_size - 1)) &
+                                               ~(long_size - 1));
+                               switch (ls) {
+                               case 0:
+                               case 1:
+                                       ls = long_size;
+                                       break;
+                               case 2:
+                                       ls = 8;
+                               default:
+                                       break;
+                               }
+                               val = read_size(bptr, ls);
+                               bptr += ls;
+                               arg = malloc_or_die(sizeof(*arg));
+                               arg->next = NULL;
+                               arg->type = PRINT_ATOM;
+                               arg->atom.atom = malloc_or_die(32);
+                               sprintf(arg->atom.atom, "%lld", val);
+                               *next = arg;
+                               next = &arg->next;
+                               break;
+                       case 's':
+                               arg = malloc_or_die(sizeof(*arg));
+                               arg->next = NULL;
+                               arg->type = PRINT_STRING;
+                               arg->string.string = strdup(bptr);
+                               bptr += strlen(bptr) + 1;
+                               *next = arg;
+                               next = &arg->next;
+                       default:
+                               break;
+                       }
+               }
+       }
+
+       return args;
+}
+
+static void free_args(struct print_arg *args)
+{
+       struct print_arg *next;
+
+       while (args) {
+               next = args->next;
+
+               if (args->type == PRINT_ATOM)
+                       free(args->atom.atom);
+               else
+                       free(args->string.string);
+               free(args);
+               args = next;
+       }
+}
+
+static char *get_bprint_format(void *data, int size __unused, struct event *event)
+{
+       unsigned long long addr;
+       static struct format_field *field;
+       struct printk_map *printk;
+       char *format;
+       char *p;
+
+       if (!field) {
+               field = find_field(event, "fmt");
+               if (!field)
+                       die("can't find format field for binary printk");
+               printf("field->offset = %d size=%d\n", field->offset, field->size);
+       }
+
+       addr = read_size(data + field->offset, field->size);
+
+       printk = find_printk(addr);
+       if (!printk) {
+               format = malloc_or_die(45);
+               sprintf(format, "%%pf : (NO FORMAT FOUND at %llx)\n",
+                       addr);
+               return format;
+       }
+
+       p = printk->printk;
+       /* Remove any quotes. */
+       if (*p == '"')
+               p++;
+       format = malloc_or_die(strlen(p) + 10);
+       sprintf(format, "%s : %s", "%pf", p);
+       /* remove ending quotes and new line since we will add one too */
+       p = format + strlen(format) - 1;
+       if (*p == '"')
+               *p = 0;
+
+       p -= 2;
+       if (strcmp(p, "\\n") == 0)
+               *p = 0;
+
+       return format;
+}
+
+static void pretty_print(void *data, int size, struct event *event)
+{
+       struct print_fmt *print_fmt = &event->print_fmt;
+       struct print_arg *arg = print_fmt->args;
+       struct print_arg *args = NULL;
+       const char *ptr = print_fmt->format;
+       unsigned long long val;
+       struct func_map *func;
+       const char *saveptr;
+       char *bprint_fmt = NULL;
+       char format[32];
+       int show_func;
+       int len;
+       int ls;
+
+       if (event->flags & EVENT_FL_ISFUNC)
+               ptr = " %pF <-- %pF";
+
+       if (event->flags & EVENT_FL_ISBPRINT) {
+               bprint_fmt = get_bprint_format(data, size, event);
+               args = make_bprint_args(bprint_fmt, data, size, event);
+               arg = args;
+               ptr = bprint_fmt;
+       }
+
+       for (; *ptr; ptr++) {
+               ls = 0;
+               if (*ptr == '%') {
+                       saveptr = ptr;
+                       show_func = 0;
+ cont_process:
+                       ptr++;
+                       switch (*ptr) {
+                       case '%':
+                               printf("%%");
+                               break;
+                       case 'l':
+                               ls++;
+                               goto cont_process;
+                       case 'L':
+                               ls = 2;
+                               goto cont_process;
+                       case 'z':
+                       case 'Z':
+                       case '0' ... '9':
+                               goto cont_process;
+                       case 'p':
+                               if (long_size == 4)
+                                       ls = 1;
+                               else
+                                       ls = 2;
+
+                               if (*(ptr+1) == 'F' ||
+                                   *(ptr+1) == 'f') {
+                                       ptr++;
+                                       show_func = *ptr;
+                               }
+
+                               /* fall through */
+                       case 'd':
+                       case 'i':
+                       case 'x':
+                       case 'X':
+                       case 'u':
+                               if (!arg)
+                                       die("no argument match");
+
+                               len = ((unsigned long)ptr + 1) -
+                                       (unsigned long)saveptr;
+
+                               /* should never happen */
+                               if (len > 32)
+                                       die("bad format!");
+
+                               memcpy(format, saveptr, len);
+                               format[len] = 0;
+
+                               val = eval_num_arg(data, size, event, arg);
+                               arg = arg->next;
+
+                               if (show_func) {
+                                       func = find_func(val);
+                                       if (func) {
+                                               printf("%s", func->func);
+                                               if (show_func == 'F')
+                                                       printf("+0x%llx",
+                                                              val - func->addr);
+                                               break;
+                                       }
+                               }
+                               switch (ls) {
+                               case 0:
+                                       printf(format, (int)val);
+                                       break;
+                               case 1:
+                                       printf(format, (long)val);
+                                       break;
+                               case 2:
+                                       printf(format, (long long)val);
+                                       break;
+                               default:
+                                       die("bad count (%d)", ls);
+                               }
+                               break;
+                       case 's':
+                               if (!arg)
+                                       die("no matching argument");
+
+                               print_str_arg(data, size, event, arg);
+                               arg = arg->next;
+                               break;
+                       default:
+                               printf(">%c<", *ptr);
+
+                       }
+               } else
+                       printf("%c", *ptr);
+       }
+
+       if (args) {
+               free_args(args);
+               free(bprint_fmt);
+       }
+}
+
+static inline int log10_cpu(int nb)
+{
+       if (nb / 100)
+               return 3;
+       if (nb / 10)
+               return 2;
+       return 1;
+}
+
+/* taken from Linux, written by Frederic Weisbecker */
+static void print_graph_cpu(int cpu)
+{
+       int i;
+       int log10_this = log10_cpu(cpu);
+       int log10_all = log10_cpu(cpus);
+
+
+       /*
+        * Start with a space character - to make it stand out
+        * to the right a bit when trace output is pasted into
+        * email:
+        */
+       printf(" ");
+
+       /*
+        * Tricky - we space the CPU field according to the max
+        * number of online CPUs. On a 2-cpu system it would take
+        * a maximum of 1 digit - on a 128 cpu system it would
+        * take up to 3 digits:
+        */
+       for (i = 0; i < log10_all - log10_this; i++)
+               printf(" ");
+
+       printf("%d) ", cpu);
+}
+
+#define TRACE_GRAPH_PROCINFO_LENGTH    14
+#define TRACE_GRAPH_INDENT     2
+
+static void print_graph_proc(int pid, const char *comm)
+{
+       /* sign + log10(MAX_INT) + '\0' */
+       char pid_str[11];
+       int spaces = 0;
+       int len;
+       int i;
+
+       sprintf(pid_str, "%d", pid);
+
+       /* 1 stands for the "-" character */
+       len = strlen(comm) + strlen(pid_str) + 1;
+
+       if (len < TRACE_GRAPH_PROCINFO_LENGTH)
+               spaces = TRACE_GRAPH_PROCINFO_LENGTH - len;
+
+       /* First spaces to align center */
+       for (i = 0; i < spaces / 2; i++)
+               printf(" ");
+
+       printf("%s-%s", comm, pid_str);
+
+       /* Last spaces to align center */
+       for (i = 0; i < spaces - (spaces / 2); i++)
+               printf(" ");
+}
+
+static struct record *
+get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func,
+                   struct record *next)
+{
+       struct format_field *field;
+       struct event *event;
+       unsigned long val;
+       int type;
+       int pid;
+
+       type = parse_common_type(next->data);
+       event = find_event(type);
+       if (!event)
+               return NULL;
+
+       if (!(event->flags & EVENT_FL_ISFUNCRET))
+               return NULL;
+
+       pid = parse_common_pid(next->data);
+       field = find_field(event, "func");
+       if (!field)
+               die("function return does not have field func");
+
+       val = read_size(next->data + field->offset, field->size);
+
+       if (cur_pid != pid || cur_func != val)
+               return NULL;
+
+       /* this is a leaf, now advance the iterator */
+       return trace_read_data(cpu);
+}
+
+/* Signal a overhead of time execution to the output */
+static void print_graph_overhead(unsigned long long duration)
+{
+       /* Non nested entry or return */
+       if (duration == ~0ULL)
+               return (void)printf("  ");
+
+       /* Duration exceeded 100 msecs */
+       if (duration > 100000ULL)
+               return (void)printf("! ");
+
+       /* Duration exceeded 10 msecs */
+       if (duration > 10000ULL)
+               return (void)printf("+ ");
+
+       printf("  ");
+}
+
+static void print_graph_duration(unsigned long long duration)
+{
+       unsigned long usecs = duration / 1000;
+       unsigned long nsecs_rem = duration % 1000;
+       /* log10(ULONG_MAX) + '\0' */
+       char msecs_str[21];
+       char nsecs_str[5];
+       int len;
+       int i;
+
+       sprintf(msecs_str, "%lu", usecs);
+
+       /* Print msecs */
+       len = printf("%lu", usecs);
+
+       /* Print nsecs (we don't want to exceed 7 numbers) */
+       if (len < 7) {
+               snprintf(nsecs_str, 8 - len, "%03lu", nsecs_rem);
+               len += printf(".%s", nsecs_str);
+       }
+
+       printf(" us ");
+
+       /* Print remaining spaces to fit the row's width */
+       for (i = len; i < 7; i++)
+               printf(" ");
+
+       printf("|  ");
+}
+
+static void
+print_graph_entry_leaf(struct event *event, void *data, struct record *ret_rec)
+{
+       unsigned long long rettime, calltime;
+       unsigned long long duration, depth;
+       unsigned long long val;
+       struct format_field *field;
+       struct func_map *func;
+       struct event *ret_event;
+       int type;
+       int i;
+
+       type = parse_common_type(ret_rec->data);
+       ret_event = find_event(type);
+
+       field = find_field(ret_event, "rettime");
+       if (!field)
+               die("can't find rettime in return graph");
+       rettime = read_size(ret_rec->data + field->offset, field->size);
+
+       field = find_field(ret_event, "calltime");
+       if (!field)
+               die("can't find rettime in return graph");
+       calltime = read_size(ret_rec->data + field->offset, field->size);
+
+       duration = rettime - calltime;
+
+       /* Overhead */
+       print_graph_overhead(duration);
+
+       /* Duration */
+       print_graph_duration(duration);
+
+       field = find_field(event, "depth");
+       if (!field)
+               die("can't find depth in entry graph");
+       depth = read_size(data + field->offset, field->size);
+
+       /* Function */
+       for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
+               printf(" ");
+
+       field = find_field(event, "func");
+       if (!field)
+               die("can't find func in entry graph");
+       val = read_size(data + field->offset, field->size);
+       func = find_func(val);
+
+       if (func)
+               printf("%s();", func->func);
+       else
+               printf("%llx();", val);
+}
+
+static void print_graph_nested(struct event *event, void *data)
+{
+       struct format_field *field;
+       unsigned long long depth;
+       unsigned long long val;
+       struct func_map *func;
+       int i;
+
+       /* No overhead */
+       print_graph_overhead(-1);
+
+       /* No time */
+       printf("           |  ");
+
+       field = find_field(event, "depth");
+       if (!field)
+               die("can't find depth in entry graph");
+       depth = read_size(data + field->offset, field->size);
+
+       /* Function */
+       for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
+               printf(" ");
+
+       field = find_field(event, "func");
+       if (!field)
+               die("can't find func in entry graph");
+       val = read_size(data + field->offset, field->size);
+       func = find_func(val);
+
+       if (func)
+               printf("%s() {", func->func);
+       else
+               printf("%llx() {", val);
+}
+
+static void
+pretty_print_func_ent(void *data, int size, struct event *event,
+                     int cpu, int pid, const char *comm,
+                     unsigned long secs, unsigned long usecs)
+{
+       struct format_field *field;
+       struct record *rec;
+       void *copy_data;
+       unsigned long val;
+
+       printf("%5lu.%06lu |  ", secs, usecs);
+
+       print_graph_cpu(cpu);
+       print_graph_proc(pid, comm);
+
+       printf(" | ");
+
+       field = find_field(event, "func");
+       if (!field)
+               die("function entry does not have func field");
+
+       val = read_size(data + field->offset, field->size);
+
+       /*
+        * peek_data may unmap the data pointer. Copy it first.
+        */
+       copy_data = malloc_or_die(size);
+       memcpy(copy_data, data, size);
+       data = copy_data;
+
+       rec = trace_peek_data(cpu);
+       if (rec) {
+               rec = get_return_for_leaf(cpu, pid, val, rec);
+               if (rec) {
+                       print_graph_entry_leaf(event, data, rec);
+                       goto out_free;
+               }
+       }
+       print_graph_nested(event, data);
+out_free:
+       free(data);
+}
+
+static void
+pretty_print_func_ret(void *data, int size __unused, struct event *event,
+                     int cpu, int pid, const char *comm,
+                     unsigned long secs, unsigned long usecs)
+{
+       unsigned long long rettime, calltime;
+       unsigned long long duration, depth;
+       struct format_field *field;
+       int i;
+
+       printf("%5lu.%06lu |  ", secs, usecs);
+
+       print_graph_cpu(cpu);
+       print_graph_proc(pid, comm);
+
+       printf(" | ");
+
+       field = find_field(event, "rettime");
+       if (!field)
+               die("can't find rettime in return graph");
+       rettime = read_size(data + field->offset, field->size);
+
+       field = find_field(event, "calltime");
+       if (!field)
+               die("can't find calltime in return graph");
+       calltime = read_size(data + field->offset, field->size);
+
+       duration = rettime - calltime;
+
+       /* Overhead */
+       print_graph_overhead(duration);
+
+       /* Duration */
+       print_graph_duration(duration);
+
+       field = find_field(event, "depth");
+       if (!field)
+               die("can't find depth in entry graph");
+       depth = read_size(data + field->offset, field->size);
+
+       /* Function */
+       for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
+               printf(" ");
+
+       printf("}");
+}
+
+static void
+pretty_print_func_graph(void *data, int size, struct event *event,
+                       int cpu, int pid, const char *comm,
+                       unsigned long secs, unsigned long usecs)
+{
+       if (event->flags & EVENT_FL_ISFUNCENT)
+               pretty_print_func_ent(data, size, event,
+                                     cpu, pid, comm, secs, usecs);
+       else if (event->flags & EVENT_FL_ISFUNCRET)
+               pretty_print_func_ret(data, size, event,
+                                     cpu, pid, comm, secs, usecs);
+       printf("\n");
+}
+
+void print_event(int cpu, void *data, int size, unsigned long long nsecs,
+                 char *comm)
+{
+       struct event *event;
+       unsigned long secs;
+       unsigned long usecs;
+       int type;
+       int pid;
+
+       secs = nsecs / NSECS_PER_SEC;
+       nsecs -= secs * NSECS_PER_SEC;
+       usecs = nsecs / NSECS_PER_USEC;
+
+       type = parse_common_type(data);
+
+       event = find_event(type);
+       if (!event)
+               die("ug! no event found for type %d", type);
+
+       pid = parse_common_pid(data);
+
+       if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET))
+               return pretty_print_func_graph(data, size, event, cpu,
+                                              pid, comm, secs, usecs);
+
+       printf("%16s-%-5d [%03d] %5lu.%06lu: %s: ",
+              comm, pid,  cpu,
+              secs, usecs, event->name);
+
+       pretty_print(data, size, event);
+       printf("\n");
+}
+
+static void print_fields(struct print_flag_sym *field)
+{
+       printf("{ %s, %s }", field->value, field->str);
+       if (field->next) {
+               printf(", ");
+               print_fields(field->next);
+       }
+}
+
+static void print_args(struct print_arg *args)
+{
+       int print_paren = 1;
+
+       switch (args->type) {
+       case PRINT_NULL:
+               printf("null");
+               break;
+       case PRINT_ATOM:
+               printf("%s", args->atom.atom);
+               break;
+       case PRINT_FIELD:
+               printf("REC->%s", args->field.name);
+               break;
+       case PRINT_FLAGS:
+               printf("__print_flags(");
+               print_args(args->flags.field);
+               printf(", %s, ", args->flags.delim);
+               print_fields(args->flags.flags);
+               printf(")");
+               break;
+       case PRINT_SYMBOL:
+               printf("__print_symbolic(");
+               print_args(args->symbol.field);
+               printf(", ");
+               print_fields(args->symbol.symbols);
+               printf(")");
+               break;
+       case PRINT_STRING:
+               printf("__get_str(%s)", args->string.string);
+               break;
+       case PRINT_TYPE:
+               printf("(%s)", args->typecast.type);
+               print_args(args->typecast.item);
+               break;
+       case PRINT_OP:
+               if (strcmp(args->op.op, ":") == 0)
+                       print_paren = 0;
+               if (print_paren)
+                       printf("(");
+               print_args(args->op.left);
+               printf(" %s ", args->op.op);
+               print_args(args->op.right);
+               if (print_paren)
+                       printf(")");
+               break;
+       default:
+               /* we should warn... */
+               return;
+       }
+       if (args->next) {
+               printf("\n");
+               print_args(args->next);
+       }
+}
+
+static void parse_header_field(char *type,
+                              int *offset, int *size)
+{
+       char *token;
+
+       if (read_expected(EVENT_ITEM, (char *)"field") < 0)
+               return;
+       if (read_expected(EVENT_OP, (char *)":") < 0)
+               return;
+       /* type */
+       if (read_expect_type(EVENT_ITEM, &token) < 0)
+               return;
+       free_token(token);
+
+       if (read_expected(EVENT_ITEM, type) < 0)
+               return;
+       if (read_expected(EVENT_OP, (char *)";") < 0)
+               return;
+       if (read_expected(EVENT_ITEM, (char *)"offset") < 0)
+               return;
+       if (read_expected(EVENT_OP, (char *)":") < 0)
+               return;
+       if (read_expect_type(EVENT_ITEM, &token) < 0)
+               return;
+       *offset = atoi(token);
+       free_token(token);
+       if (read_expected(EVENT_OP, (char *)";") < 0)
+               return;
+       if (read_expected(EVENT_ITEM, (char *)"size") < 0)
+               return;
+       if (read_expected(EVENT_OP, (char *)":") < 0)
+               return;
+       if (read_expect_type(EVENT_ITEM, &token) < 0)
+               return;
+       *size = atoi(token);
+       free_token(token);
+       if (read_expected(EVENT_OP, (char *)";") < 0)
+               return;
+       if (read_expect_type(EVENT_NEWLINE, &token) < 0)
+               return;
+       free_token(token);
+}
+
+int parse_header_page(char *buf, unsigned long size)
+{
+       init_input_buf(buf, size);
+
+       parse_header_field((char *)"timestamp", &header_page_ts_offset,
+                          &header_page_ts_size);
+       parse_header_field((char *)"commit", &header_page_size_offset,
+                          &header_page_size_size);
+       parse_header_field((char *)"data", &header_page_data_offset,
+                          &header_page_data_size);
+
+       return 0;
+}
+
+int parse_ftrace_file(char *buf, unsigned long size)
+{
+       struct format_field *field;
+       struct print_arg *arg, **list;
+       struct event *event;
+       int ret;
+
+       init_input_buf(buf, size);
+
+       event = alloc_event();
+       if (!event)
+               return -ENOMEM;
+
+       event->flags |= EVENT_FL_ISFTRACE;
+
+       event->name = event_read_name();
+       if (!event->name)
+               die("failed to read ftrace event name");
+
+       if (strcmp(event->name, "function") == 0)
+               event->flags |= EVENT_FL_ISFUNC;
+
+       else if (strcmp(event->name, "funcgraph_entry") == 0)
+               event->flags |= EVENT_FL_ISFUNCENT;
+
+       else if (strcmp(event->name, "funcgraph_exit") == 0)
+               event->flags |= EVENT_FL_ISFUNCRET;
+
+       else if (strcmp(event->name, "bprint") == 0)
+               event->flags |= EVENT_FL_ISBPRINT;
+
+       event->id = event_read_id();
+       if (event->id < 0)
+               die("failed to read ftrace event id");
+
+       add_event(event);
+
+       ret = event_read_format(event);
+       if (ret < 0)
+               die("failed to read ftrace event format");
+
+       ret = event_read_print(event);
+       if (ret < 0)
+               die("failed to read ftrace event print fmt");
+
+       /*
+        * The arguments for ftrace files are parsed by the fields.
+        * Set up the fields as their arguments.
+        */
+       list = &event->print_fmt.args;
+       for (field = event->format.fields; field; field = field->next) {
+               arg = malloc_or_die(sizeof(*arg));
+               memset(arg, 0, sizeof(*arg));
+               *list = arg;
+               list = &arg->next;
+               arg->type = PRINT_FIELD;
+               arg->field.name = field->name;
+               arg->field.field = field;
+       }
+       return 0;
+}
+
+int parse_event_file(char *buf, unsigned long size, char *system__unused __unused)
+{
+       struct event *event;
+       int ret;
+
+       init_input_buf(buf, size);
+
+       event = alloc_event();
+       if (!event)
+               return -ENOMEM;
+
+       event->name = event_read_name();
+       if (!event->name)
+               die("failed to read event name");
+
+       event->id = event_read_id();
+       if (event->id < 0)
+               die("failed to read event id");
+
+       ret = event_read_format(event);
+       if (ret < 0)
+               die("failed to read event format");
+
+       ret = event_read_print(event);
+       if (ret < 0)
+               die("failed to read event print fmt");
+
+#define PRINT_ARGS 0
+       if (PRINT_ARGS && event->print_fmt.args)
+               print_args(event->print_fmt.args);
+
+       add_event(event);
+       return 0;
+}
+
+void parse_set_info(int nr_cpus, int long_sz)
+{
+       cpus = nr_cpus;
+       long_size = long_sz;
+}