diff options
Diffstat (limited to 'tools/perf/util/trace-event-parse.c')
-rw-r--r-- | tools/perf/util/trace-event-parse.c | 2905 |
1 files changed, 2905 insertions, 0 deletions
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c new file mode 100644 index 00000000000..ead6a9ad359 --- /dev/null +++ b/tools/perf/util/trace-event-parse.c @@ -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; +} |