Skip to content
Snippets Groups Projects
trace-event-parse.c 61.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * 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.
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    
    
    #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_overwrite_offset;
    int header_page_overwrite_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 int is_flag_field;
    static int is_symbolic_field;
    
    static struct format_field *
    find_any_field(struct event *event, const char *name);
    
    
    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 *)(void *)&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 __used;
    
    	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 *)(void *)&addr_str, /* workaround gcc warning */
    
    			     (float *)(void *)&item->func,
    			     (float *)(void *)&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 i;
    
    	line = strtok_r(file, "\n", &next);
    	while (line) {
    
    		addr_str = strsep(&line, ":");
    		if (!line) {
    			warning("error parsing print strings");
    			break;
    		}
    
    		item = malloc_or_die(sizeof(*item));
    		item->addr = strtoull(addr_str, NULL, 16);
    
    		/* fmt still has a space, skip it */
    
    		item->printk = strdup(line+1);
    
    		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;
    
    			/* the '\' '\' will cancel itself */
    			if (ch == '\\' && last_ch == '\\')
    				last_ch = 0;
    		} 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) {
    
    		warning("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, const char *expect_tok,
    			     bool warn)
    
    {
    	if (type != expect) {
    
    		if (warn)
    			warning("Error: expected type %d but read %d",
    				expect, type);
    
    		return -1;
    	}
    
    	if (strcmp(token, expect_tok) != 0) {
    
    		if (warn)
    			warning("Error: expected '%s' but read '%s'",
    				expect_tok, token);
    
    static int test_type_token(enum event_type type, char *token,
    			   enum event_type expect, const char *expect_tok)
    {
    	return __test_type_token(type, token, expect, expect_tok, true);
    }
    
    
    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, const char *str,
    			   int newline_ok, bool warn)
    
    {
    	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, warn);
    
    static int read_expected(enum event_type expect, const char *str)
    
    	return __read_expected(expect, str, 1, true);
    
    static int read_expected_item(enum event_type expect, const char *str)
    
    	return __read_expected(expect, str, 0, true);
    
    }
    
    static char *event_read_name(void)
    {
    	char *token;
    
    
    	if (read_expected(EVENT_ITEM, "name") < 0)
    
    	if (read_expected(EVENT_OP, ":") < 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, "ID") < 0)
    
    	if (read_expected(EVENT_OP, ":") < 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 field_is_string(struct format_field *field)
    {
    	if ((field->flags & FIELD_IS_ARRAY) &&
    	    (!strstr(field->type, "char") || !strstr(field->type, "u8") ||
    	     !strstr(field->type, "s8")))
    		return 1;
    
    	return 0;
    }
    
    static int field_is_dynamic(struct format_field *field)
    {
    
    	if (!strncmp(field->type, "__data_loc", 10))
    
    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, "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, ":") < 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 (field_is_string(field)) {
    			field->flags |= FIELD_IS_STRING;
    			if (field_is_dynamic(field))
    				field->flags |= FIELD_IS_DYNAMIC;
    		}
    
    
    		if (test_type_token(type, token,  EVENT_OP, ";"))
    
    			goto fail;
    		free_token(token);
    
    
    		if (read_expected(EVENT_ITEM, "offset") < 0)
    
    		if (read_expected(EVENT_OP, ":") < 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, ";") < 0)
    
    		if (read_expected(EVENT_ITEM, "size") < 0)
    
    		if (read_expected(EVENT_OP, ":") < 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, ";") < 0)
    
    		type = read_token(&token);
    		if (type != EVENT_NEWLINE) {
    			/* newer versions of the kernel have a "signed" type */
    
    			if (test_type_token(type, token, EVENT_ITEM, "signed"))
    
    			if (read_expected(EVENT_OP, ":") < 0)
    
    			if (read_expect_type(EVENT_ITEM, &token))
    				goto fail;
    
    
    			if (strtoul(token, NULL, 0))
    				field->flags |= FIELD_IS_SIGNED;
    
    			if (read_expected(EVENT_OP, ";") < 0)
    
    				goto fail_expect;
    
    			if (read_expect_type(EVENT_NEWLINE, &token))
    				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, "format") < 0)
    
    	if (read_expected(EVENT_OP, ":") < 0)
    
    		return -1;
    
    	if (read_expect_type(EVENT_NEWLINE, &token))
    		goto fail;
    	free_token(token);