From 6582b0aea1cca82d070961141e47ad4abca22621 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 6 Apr 2012 00:47:53 +0200 Subject: tools/events: Add files to create libtraceevent.a Copy over the files from trace-cmd to the Linux tools directory such that applications like perf and latencytrace can use the more advanced parsing code. Because some of the file names of perf conflict with trace-cmd file names, the trace-cmd files have been renamed as follows: parse-events.c ==> event-parse.c parse-events.h ==> event-parse.h utils.h ==> event-utils.h The files have been updated to handle the changes to the header files but other than that, they are identical to what was in the trace-cmd repository. The history of these files, including authorship is available at the git repo: git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git The Makefile was also copied over, but most of it was removed to focus on the parse-events code first. The parts of the Makefile for the plugins have also been removed, but will be added back when the plugin code is copied over as well. But that may be in its own separate directory. Signed-off-by: Steven Rostedt Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Borislav Petkov Cc: Jiri Olsa Cc: Arun Sharma Cc: Namhyung Kim Signed-off-by: Frederic Weisbecker --- Makefile | 303 ++++ event-parse.c | 4971 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ event-parse.h | 806 +++++++++ event-utils.h | 64 + parse-filter.c | 2262 ++++++++++++++++++++++++++ parse-utils.c | 110 ++ trace-seq.c | 199 +++ 7 files changed, 8715 insertions(+) create mode 100644 Makefile create mode 100644 event-parse.c create mode 100644 event-parse.h create mode 100644 event-utils.h create mode 100644 parse-filter.c create mode 100644 parse-utils.c create mode 100644 trace-seq.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3d69aa9 --- /dev/null +++ b/Makefile @@ -0,0 +1,303 @@ +# trace-cmd version +EP_VERSION = 1 +EP_PATCHLEVEL = 1 +EP_EXTRAVERSION = 0 + +# file format version +FILE_VERSION = 6 + +MAKEFLAGS += --no-print-directory + + +# Makefiles suck: This macro sets a default value of $(2) for the +# variable named by $(1), unless the variable has been set by +# environment or command line. This is necessary for CC and AR +# because make sets default values, so the simpler ?= approach +# won't work as expected. +define allow-override + $(if $(or $(findstring environment,$(origin $(1))),\ + $(findstring command line,$(origin $(1)))),,\ + $(eval $(1) = $(2))) +endef + +# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix. +$(call allow-override,CC,$(CROSS_COMPILE)gcc) +$(call allow-override,AR,$(CROSS_COMPILE)ar) + +EXT = -std=gnu99 +INSTALL = install + +# Use DESTDIR for installing into a different root directory. +# This is useful for building a package. The program will be +# installed in this directory as if it was the root directory. +# Then the build tool can move it later. +DESTDIR ?= +DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))' + +prefix ?= /usr/local +bindir_relative = bin +bindir = $(prefix)/$(bindir_relative) +man_dir = $(prefix)/share/man +man_dir_SQ = '$(subst ','\'',$(man_dir))' +html_install = $(prefix)/share/kernelshark/html +html_install_SQ = '$(subst ','\'',$(html_install))' +img_install = $(prefix)/share/kernelshark/html/images +img_install_SQ = '$(subst ','\'',$(img_install))' + +export man_dir man_dir_SQ html_install html_install_SQ INSTALL +export img_install img_install_SQ +export DESTDIR DESTDIR_SQ + +# copy a bit from Linux kbuild + +ifeq ("$(origin V)", "command line") + VERBOSE = $(V) +endif +ifndef VERBOSE + VERBOSE = 0 +endif + +ifeq ("$(origin O)", "command line") + BUILD_OUTPUT := $(O) +endif + +ifeq ($(BUILD_SRC),) +ifneq ($(BUILD_OUTPUT),) + +define build_output + $(if $(VERBOSE:1=),@)$(MAKE) -C $(BUILD_OUTPUT) \ + BUILD_SRC=$(CURDIR) -f $(CURDIR)/Makefile $1 +endef + +saved-output := $(BUILD_OUTPUT) +BUILD_OUTPUT := $(shell cd $(BUILD_OUTPUT) && /bin/pwd) +$(if $(BUILD_OUTPUT),, \ + $(error output directory "$(saved-output)" does not exist)) + +all: sub-make + +gui: force + $(call build_output, all_cmd) + +$(filter-out gui,$(MAKECMDGOALS)): sub-make + +sub-make: force + $(call build_output, $(MAKECMDGOALS)) + + +# Leave processing to above invocation of make +skip-makefile := 1 + +endif # BUILD_OUTPUT +endif # BUILD_SRC + +# We process the rest of the Makefile if this is the final invocation of make +ifeq ($(skip-makefile),) + +srctree := $(if $(BUILD_SRC),$(BUILD_SRC),$(CURDIR)) +objtree := $(CURDIR) +src := $(srctree) +obj := $(objtree) + +export prefix bindir src obj + +# Shell quotes +bindir_SQ = $(subst ','\'',$(bindir)) +bindir_relative_SQ = $(subst ','\'',$(bindir_relative)) + +LIB_FILE = libtraceevent.a libtraceevent.so + +CONFIG_INCLUDES = +CONFIG_LIBS = +CONFIG_FLAGS = + +VERSION = $(EP_VERSION) +PATCHLEVEL = $(EP_PATCHLEVEL) +EXTRAVERSION = $(EP_EXTRAVERSION) + +OBJ = $@ +N = + +export Q VERBOSE + +EVENT_PARSE_VERSION = $(EP_VERSION).$(EP_PATCHLEVEL).$(EP_EXTRAVERSION) + +INCLUDES = -I. -I/usr/local/include $(CONFIG_INCLUDES) + +# Set compile option CFLAGS if not set elsewhere +CFLAGS ?= -g -Wall + +# Append required CFLAGS +override CFLAGS += $(CONFIG_FLAGS) $(INCLUDES) $(PLUGIN_DIR_SQ) +override CFLAGS += $(udis86-flags) + +ifeq ($(VERBOSE),1) + Q = + print_compile = + print_app_build = + print_fpic_compile = + print_shared_lib_compile = + print_plugin_obj_compile = + print_plugin_build = + print_install = +else + Q = @ + print_compile = echo ' CC '$(OBJ); + print_app_build = echo ' BUILD '$(OBJ); + print_fpic_compile = echo ' CC FPIC '$(OBJ); + print_shared_lib_compile = echo ' BUILD SHARED LIB '$(OBJ); + print_plugin_obj_compile = echo ' CC PLUGIN OBJ '$(OBJ); + print_plugin_build = echo ' CC PLUGI '$(OBJ); + print_static_lib_build = echo ' BUILD STATIC LIB '$(OBJ); + print_install = echo ' INSTALL '$1' to $(DESTDIR_SQ)$2'; +endif + +do_fpic_compile = \ + ($(print_fpic_compile) \ + $(CC) -c $(CFLAGS) $(EXT) -fPIC $< -o $@) + +do_app_build = \ + ($(print_app_build) \ + $(CC) $^ -rdynamic -o $@ $(CONFIG_LIBS) $(LIBS)) + +do_compile_shared_library = \ + ($(print_shared_lib_compile) \ + $(CC) --shared $^ -o $@) + +do_compile_plugin_obj = \ + ($(print_plugin_obj_compile) \ + $(CC) -c $(CFLAGS) -fPIC -o $@ $<) + +do_plugin_build = \ + ($(print_plugin_build) \ + $(CC) $(CFLAGS) -shared -nostartfiles -o $@ $<) + +do_build_static_lib = \ + ($(print_static_lib_build) \ + $(RM) $@; $(AR) rcs $@ $^) + + +define do_compile + $(print_compile) \ + $(CC) -c $(CFLAGS) $(EXT) $< -o $(obj)/$@; +endef + +$(obj)/%.o: $(src)/%.c + $(Q)$(call do_compile) + +%.o: $(src)/%.c + $(Q)$(call do_compile) + +PEVENT_LIB_OBJS = event-parse.o trace-seq.o parse-filter.o parse-utils.o + +ALL_OBJS = $(PEVENT_LIB_OBJS) + +CMD_TARGETS = $(LIB_FILE) + +TARGETS = $(CMD_TARGETS) + + +all: all_cmd + +all_cmd: $(CMD_TARGETS) + +libtraceevent.so: $(PEVENT_LIB_OBJS) + $(Q)$(do_compile_shared_library) + +libtraceevent.a: $(PEVENT_LIB_OBJS) + $(Q)$(do_build_static_lib) + +$(PEVENT_LIB_OBJS): %.o: $(src)/%.c + $(Q)$(do_fpic_compile) + +define make_version.h + (echo '/* This file is automatically generated. Do not modify. */'; \ + echo \#define VERSION_CODE $(shell \ + expr $(VERSION) \* 256 + $(PATCHLEVEL)); \ + echo '#define EXTRAVERSION ' $(EXTRAVERSION); \ + echo '#define VERSION_STRING "'$(VERSION).$(PATCHLEVEL).$(EXTRAVERSION)'"'; \ + echo '#define FILE_VERSION '$(FILE_VERSION); \ + ) > $1 +endef + +define update_version.h + ($(call make_version.h, $@.tmp); \ + if [ -r $@ ] && cmp -s $@ $@.tmp; then \ + rm -f $@.tmp; \ + else \ + echo ' UPDATE $@'; \ + mv -f $@.tmp $@; \ + fi); +endef + +ep_version.h: force + $(Q)$(N)$(call update_version.h) + +VERSION_FILES = ep_version.h + +define update_dir + (echo $1 > $@.tmp; \ + if [ -r $@ ] && cmp -s $@ $@.tmp; then \ + rm -f $@.tmp; \ + else \ + echo ' UPDATE $@'; \ + mv -f $@.tmp $@; \ + fi); +endef + +## make deps + +all_objs := $(sort $(ALL_OBJS)) +all_deps := $(all_objs:%.o=.%.d) + +define check_deps + $(CC) -M $(CFLAGS) $< > $@; +endef + +$(gui_deps): ks_version.h +$(non_gui_deps): tc_version.h + +$(all_deps): .%.d: $(src)/%.c + $(Q)$(call check_deps) + +$(all_objs) : %.o : .%.d + +dep_includes := $(wildcard $(all_deps)) + +ifneq ($(dep_includes),) + include $(dep_includes) +endif + +tags: force + $(RM) tags + find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px + +TAGS: force + $(RM) TAGS + find . -name '*.[ch]' | xargs etags + +define do_install + $(print_install) \ + if [ ! -d '$(DESTDIR_SQ)$2' ]; then \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \ + fi; \ + $(INSTALL) $1 '$(DESTDIR_SQ)$2' +endef + +install_lib: all_cmd install_plugins install_python + $(Q)$(call do_install,$(LIB_FILE),$(bindir_SQ)) + +install: install_lib + +clean: + $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES).*.d + $(RM) tags TAGS + +endif # skip-makefile + +PHONY += force +force: + +# Declare the contents of the .PHONY variable as phony. We keep that +# information in a variable so we can use it in if_changed and friends. +.PHONY: $(PHONY) diff --git a/event-parse.c b/event-parse.c new file mode 100644 index 0000000..47a3227 --- /dev/null +++ b/event-parse.c @@ -0,0 +1,4971 @@ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * The parts for function graph printing was taken and modified from the + * Linux Kernel that were written by + * - Copyright (C) 2009 Frederic Weisbecker, + * Frederic Weisbecker gave his permission to relicense the code to + * the Lesser General Public License. + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "event-parse.h" + +static const char *input_buf; +static unsigned long long input_buf_ptr; +static unsigned long long input_buf_siz; + +static int show_warning = 1; + +#define do_warning(fmt, ...) \ + do { \ + if (show_warning) \ + warning(fmt, ##__VA_ARGS__); \ + } while (0) + +static void init_input_buf(const char *buf, unsigned long long size) +{ + input_buf = buf; + input_buf_siz = size; + input_buf_ptr = 0; +} + +const char *pevent_get_input_buf(void) +{ + return input_buf; +} + +unsigned long long pevent_get_input_buf_ptr(void) +{ + return input_buf_ptr; +} + +struct event_handler { + struct event_handler *next; + int id; + const char *sys_name; + const char *event_name; + pevent_event_handler_func func; + void *context; +}; + +struct pevent_func_params { + struct pevent_func_params *next; + enum pevent_func_arg_type type; +}; + +struct pevent_function_handler { + struct pevent_function_handler *next; + enum pevent_func_arg_type ret_type; + char *name; + pevent_func_handler func; + struct pevent_func_params *params; + int nr_args; +}; + +static unsigned long long +process_defined_func(struct trace_seq *s, void *data, int size, + struct event_format *event, struct print_arg *arg); + +static void free_func_handle(struct pevent_function_handler *func); + +/** + * pevent_buffer_init - init buffer for parsing + * @buf: buffer to parse + * @size: the size of the buffer + * + * For use with pevent_read_token(), this initializes the internal + * buffer that pevent_read_token() will parse. + */ +void pevent_buffer_init(const char *buf, unsigned long long size) +{ + init_input_buf(buf, size); +} + +void breakpoint(void) +{ + static int x; + x++; +} + +struct print_arg *alloc_arg(void) +{ + struct print_arg *arg; + + arg = malloc_or_die(sizeof(*arg)); + if (!arg) + return NULL; + memset(arg, 0, sizeof(*arg)); + + return arg; +} + +struct cmdline { + char *comm; + int pid; +}; + +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; +} + +struct cmdline_list { + struct cmdline_list *next; + char *comm; + int pid; +}; + +static int cmdline_init(struct pevent *pevent) +{ + struct cmdline_list *cmdlist = pevent->cmdlist; + struct cmdline_list *item; + struct cmdline *cmdlines; + int i; + + cmdlines = malloc_or_die(sizeof(*cmdlines) * pevent->cmdline_count); + + i = 0; + while (cmdlist) { + cmdlines[i].pid = cmdlist->pid; + cmdlines[i].comm = cmdlist->comm; + i++; + item = cmdlist; + cmdlist = cmdlist->next; + free(item); + } + + qsort(cmdlines, pevent->cmdline_count, sizeof(*cmdlines), cmdline_cmp); + + pevent->cmdlines = cmdlines; + pevent->cmdlist = NULL; + + return 0; +} + +static char *find_cmdline(struct pevent *pevent, int pid) +{ + const struct cmdline *comm; + struct cmdline key; + + if (!pid) + return ""; + + if (!pevent->cmdlines) + cmdline_init(pevent); + + key.pid = pid; + + comm = bsearch(&key, pevent->cmdlines, pevent->cmdline_count, + sizeof(*pevent->cmdlines), cmdline_cmp); + + if (comm) + return comm->comm; + return "<...>"; +} + +/** + * pevent_pid_is_registered - return if a pid has a cmdline registered + * @pevent: handle for the pevent + * @pid: The pid to check if it has a cmdline registered with. + * + * Returns 1 if the pid has a cmdline mapped to it + * 0 otherwise. + */ +int pevent_pid_is_registered(struct pevent *pevent, int pid) +{ + const struct cmdline *comm; + struct cmdline key; + + if (!pid) + return 1; + + if (!pevent->cmdlines) + cmdline_init(pevent); + + key.pid = pid; + + comm = bsearch(&key, pevent->cmdlines, pevent->cmdline_count, + sizeof(*pevent->cmdlines), cmdline_cmp); + + if (comm) + return 1; + return 0; +} + +/* + * If the command lines have been converted to an array, then + * we must add this pid. This is much slower than when cmdlines + * are added before the array is initialized. + */ +static int add_new_comm(struct pevent *pevent, const char *comm, int pid) +{ + struct cmdline *cmdlines = pevent->cmdlines; + const struct cmdline *cmdline; + struct cmdline key; + + if (!pid) + return 0; + + /* avoid duplicates */ + key.pid = pid; + + cmdline = bsearch(&key, pevent->cmdlines, pevent->cmdline_count, + sizeof(*pevent->cmdlines), cmdline_cmp); + if (cmdline) { + errno = EEXIST; + return -1; + } + + cmdlines = realloc(cmdlines, sizeof(*cmdlines) * (pevent->cmdline_count + 1)); + if (!cmdlines) { + errno = ENOMEM; + return -1; + } + + cmdlines[pevent->cmdline_count].pid = pid; + cmdlines[pevent->cmdline_count].comm = strdup(comm); + if (!cmdlines[pevent->cmdline_count].comm) + die("malloc comm"); + + if (cmdlines[pevent->cmdline_count].comm) + pevent->cmdline_count++; + + qsort(cmdlines, pevent->cmdline_count, sizeof(*cmdlines), cmdline_cmp); + pevent->cmdlines = cmdlines; + + return 0; +} + +/** + * pevent_register_comm - register a pid / comm mapping + * @pevent: handle for the pevent + * @comm: the command line to register + * @pid: the pid to map the command line to + * + * This adds a mapping to search for command line names with + * a given pid. The comm is duplicated. + */ +int pevent_register_comm(struct pevent *pevent, const char *comm, int pid) +{ + struct cmdline_list *item; + + if (pevent->cmdlines) + return add_new_comm(pevent, comm, pid); + + item = malloc_or_die(sizeof(*item)); + item->comm = strdup(comm); + if (!item->comm) + die("malloc comm"); + item->pid = pid; + item->next = pevent->cmdlist; + + pevent->cmdlist = item; + pevent->cmdline_count++; + + return 0; +} + +struct func_map { + unsigned long long addr; + char *func; + char *mod; +}; + +struct func_list { + struct func_list *next; + unsigned long long addr; + char *func; + char *mod; +}; + +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; +} + +/* + * 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 int func_map_init(struct pevent *pevent) +{ + struct func_list *funclist; + struct func_list *item; + struct func_map *func_map; + int i; + + func_map = malloc_or_die(sizeof(*func_map) * (pevent->func_count + 1)); + funclist = pevent->funclist; + + i = 0; + while (funclist) { + func_map[i].func = funclist->func; + func_map[i].addr = funclist->addr; + func_map[i].mod = funclist->mod; + i++; + item = funclist; + funclist = funclist->next; + free(item); + } + + qsort(func_map, pevent->func_count, sizeof(*func_map), func_cmp); + + /* + * Add a special record at the end. + */ + func_map[pevent->func_count].func = NULL; + func_map[pevent->func_count].addr = 0; + func_map[pevent->func_count].mod = NULL; + + pevent->func_map = func_map; + pevent->funclist = NULL; + + return 0; +} + +static struct func_map * +find_func(struct pevent *pevent, unsigned long long addr) +{ + struct func_map *func; + struct func_map key; + + if (!pevent->func_map) + func_map_init(pevent); + + key.addr = addr; + + func = bsearch(&key, pevent->func_map, pevent->func_count, + sizeof(*pevent->func_map), func_bcmp); + + return func; +} + +/** + * pevent_find_function - find a function by a given address + * @pevent: handle for the pevent + * @addr: the address to find the function with + * + * Returns a pointer to the function stored that has the given + * address. Note, the address does not have to be exact, it + * will select the function that would contain the address. + */ +const char *pevent_find_function(struct pevent *pevent, unsigned long long addr) +{ + struct func_map *map; + + map = find_func(pevent, addr); + if (!map) + return NULL; + + return map->func; +} + +/** + * pevent_find_function_address - find a function address by a given address + * @pevent: handle for the pevent + * @addr: the address to find the function with + * + * Returns the address the function starts at. This can be used in + * conjunction with pevent_find_function to print both the function + * name and the function offset. + */ +unsigned long long +pevent_find_function_address(struct pevent *pevent, unsigned long long addr) +{ + struct func_map *map; + + map = find_func(pevent, addr); + if (!map) + return 0; + + return map->addr; +} + +/** + * pevent_register_function - register a function with a given address + * @pevent: handle for the pevent + * @function: the function name to register + * @addr: the address the function starts at + * @mod: the kernel module the function may be in (NULL for none) + * + * This registers a function name with an address and module. + * The @func passed in is duplicated. + */ +int pevent_register_function(struct pevent *pevent, char *func, + unsigned long long addr, char *mod) +{ + struct func_list *item; + + item = malloc_or_die(sizeof(*item)); + + item->next = pevent->funclist; + item->func = strdup(func); + if (mod) + item->mod = strdup(mod); + else + item->mod = NULL; + item->addr = addr; + + pevent->funclist = item; + + pevent->func_count++; + + return 0; +} + +/** + * pevent_print_funcs - print out the stored functions + * @pevent: handle for the pevent + * + * This prints out the stored functions. + */ +void pevent_print_funcs(struct pevent *pevent) +{ + int i; + + if (!pevent->func_map) + func_map_init(pevent); + + for (i = 0; i < (int)pevent->func_count; i++) { + printf("%016llx %s", + pevent->func_map[i].addr, + pevent->func_map[i].func); + if (pevent->func_map[i].mod) + printf(" [%s]\n", pevent->func_map[i].mod); + else + printf("\n"); + } +} + +struct printk_map { + unsigned long long addr; + char *printk; +}; + +struct printk_list { + struct printk_list *next; + unsigned long long addr; + char *printk; +}; + +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 void printk_map_init(struct pevent *pevent) +{ + struct printk_list *printklist; + struct printk_list *item; + struct printk_map *printk_map; + int i; + + printk_map = malloc_or_die(sizeof(*printk_map) * (pevent->printk_count + 1)); + + printklist = pevent->printklist; + + i = 0; + while (printklist) { + printk_map[i].printk = printklist->printk; + printk_map[i].addr = printklist->addr; + i++; + item = printklist; + printklist = printklist->next; + free(item); + } + + qsort(printk_map, pevent->printk_count, sizeof(*printk_map), printk_cmp); + + pevent->printk_map = printk_map; + pevent->printklist = NULL; +} + +static struct printk_map * +find_printk(struct pevent *pevent, unsigned long long addr) +{ + struct printk_map *printk; + struct printk_map key; + + if (!pevent->printk_map) + printk_map_init(pevent); + + key.addr = addr; + + printk = bsearch(&key, pevent->printk_map, pevent->printk_count, + sizeof(*pevent->printk_map), printk_cmp); + + return printk; +} + +/** + * pevent_register_print_string - register a string by its address + * @pevent: handle for the pevent + * @fmt: the string format to register + * @addr: the address the string was located at + * + * This registers a string by the address it was stored in the kernel. + * The @fmt passed in is duplicated. + */ +int pevent_register_print_string(struct pevent *pevent, char *fmt, + unsigned long long addr) +{ + struct printk_list *item; + + item = malloc_or_die(sizeof(*item)); + + item->next = pevent->printklist; + pevent->printklist = item; + item->printk = strdup(fmt); + item->addr = addr; + + pevent->printk_count++; + + return 0; +} + +/** + * pevent_print_printk - print out the stored strings + * @pevent: handle for the pevent + * + * This prints the string formats that were stored. + */ +void pevent_print_printk(struct pevent *pevent) +{ + int i; + + if (!pevent->printk_map) + printk_map_init(pevent); + + for (i = 0; i < (int)pevent->printk_count; i++) { + printf("%016llx %s\n", + pevent->printk_map[i].addr, + pevent->printk_map[i].printk); + } +} + +static struct event_format *alloc_event(void) +{ + struct event_format *event; + + event = malloc_or_die(sizeof(*event)); + memset(event, 0, sizeof(*event)); + + return event; +} + +static void add_event(struct pevent *pevent, struct event_format *event) +{ + int i; + + if (!pevent->events) + pevent->events = malloc_or_die(sizeof(event)); + else + pevent->events = + realloc(pevent->events, sizeof(event) * + (pevent->nr_events + 1)); + if (!pevent->events) + die("Can not allocate events"); + + for (i = 0; i < pevent->nr_events; i++) { + if (pevent->events[i]->id > event->id) + break; + } + if (i < pevent->nr_events) + memmove(&pevent->events[i + 1], + &pevent->events[i], + sizeof(event) * (pevent->nr_events - i)); + + pevent->events[i] = event; + pevent->nr_events++; + + event->pevent = pevent; +} + +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_flag_sym(struct print_flag_sym *fsym) +{ + struct print_flag_sym *next; + + while (fsym) { + next = fsym->next; + free(fsym->value); + free(fsym->str); + free(fsym); + fsym = next; + } +} + +static void free_arg(struct print_arg *arg) +{ + struct print_arg *farg; + + if (!arg) + return; + + switch (arg->type) { + case PRINT_ATOM: + free(arg->atom.atom); + break; + case PRINT_FIELD: + free(arg->field.name); + break; + case PRINT_FLAGS: + free_arg(arg->flags.field); + free(arg->flags.delim); + free_flag_sym(arg->flags.flags); + break; + case PRINT_SYMBOL: + free_arg(arg->symbol.field); + free_flag_sym(arg->symbol.symbols); + break; + case PRINT_TYPE: + free(arg->typecast.type); + free_arg(arg->typecast.item); + break; + case PRINT_STRING: + case PRINT_BSTRING: + free(arg->string.string); + break; + case PRINT_DYNAMIC_ARRAY: + free(arg->dynarray.index); + break; + case PRINT_OP: + free(arg->op.op); + free_arg(arg->op.left); + free_arg(arg->op.right); + break; + case PRINT_FUNC: + while (arg->func.args) { + farg = arg->func.args; + arg->func.args = farg->next; + free_arg(farg); + } + break; + + case PRINT_NULL: + default: + 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]; +} + +/** + * pevent_peek_char - peek at the next character that will be read + * + * Returns the next character read, or -1 if end of buffer. + */ +int pevent_peek_char(void) +{ + return __peek_char(); +} + +static enum event_type force_token(const char *str, char **tok); + +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(); + goto out; + + case EVENT_DQUOTE: + case EVENT_SQUOTE: + /* don't keep quotes */ + i--; + quote_ch = ch; + last_ch = 0; + concat: + 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--; + + /* + * For strings (double quotes) check the next token. + * If it is another string, concatinate the two. + */ + if (type == EVENT_DQUOTE) { + unsigned long long save_input_buf_ptr = input_buf_ptr; + + do { + ch = __read_char(); + } while (isspace(ch)); + if (ch == '"') + goto concat; + input_buf_ptr = save_input_buf_ptr; + } + + 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; + + if (type == EVENT_ITEM) { + /* + * Older versions of the kernel has a bug that + * creates invalid symbols and will break the mac80211 + * parsing. This is a work around to that bug. + * + * See Linux kernel commit: + * 811cb50baf63461ce0bdb234927046131fc7fa8b + */ + if (strcmp(*tok, "LOCAL_PR_FMT") == 0) { + free(*tok); + *tok = NULL; + return force_token("\"\%s\" ", tok); + } else if (strcmp(*tok, "STA_PR_FMT") == 0) { + free(*tok); + *tok = NULL; + return force_token("\" sta:%pM\" ", tok); + } else if (strcmp(*tok, "VIF_PR_FMT") == 0) { + free(*tok); + *tok = NULL; + return force_token("\" vif:%p(%d)\" ", tok); + } + } + + return type; +} + +static enum event_type force_token(const char *str, char **tok) +{ + const char *save_input_buf; + unsigned long long save_input_buf_ptr; + unsigned long long save_input_buf_siz; + enum event_type type; + + /* save off the current input pointers */ + save_input_buf = input_buf; + save_input_buf_ptr = input_buf_ptr; + save_input_buf_siz = input_buf_siz; + + init_input_buf(str, strlen(str)); + + type = __read_token(tok); + + /* reset back to original token */ + input_buf = save_input_buf; + input_buf_ptr = save_input_buf_ptr; + input_buf_siz = save_input_buf_siz; + + 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 */ + *tok = NULL; + return EVENT_NONE; +} + +/** + * pevent_read_token - access to utilites to use the pevent parser + * @tok: The token to return + * + * This will parse tokens from the string given by + * pevent_init_data(). + * + * Returns the token type. + */ +enum event_type pevent_read_token(char **tok) +{ + return read_token(tok); +} + +/** + * pevent_free_token - free a token returned by pevent_read_token + * @token: the token to free + */ +void pevent_free_token(char *token) +{ + free_token(token); +} + +/* 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); + *tok = NULL; + } + + /* not reached */ + *tok = NULL; + return EVENT_NONE; +} + +static int test_type(enum event_type type, enum event_type expect) +{ + if (type != expect) { + do_warning("Error: expected type %d but read %d", + expect, type); + return -1; + } + return 0; +} + +static int test_type_token(enum event_type type, const char *token, + enum event_type expect, const char *expect_tok) +{ + if (type != expect) { + do_warning("Error: expected type %d but read %d", + expect, type); + return -1; + } + + if (strcmp(token, expect_tok) != 0) { + do_warning("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, const 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 ret; +} + +static int read_expected(enum event_type expect, const char *str) +{ + return __read_expected(expect, str, 1); +} + +static int read_expected_item(enum event_type expect, const char *str) +{ + return __read_expected(expect, str, 0); +} + +static char *event_read_name(void) +{ + char *token; + + if (read_expected(EVENT_ITEM, "name") < 0) + return NULL; + + 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) + return -1; + + 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) == 0) + return 1; + + return 0; +} + +static int field_is_long(struct format_field *field) +{ + /* includes long long */ + if (strstr(field->type, "long")) + return 1; + + return 0; +} + +static int event_read_fields(struct event_format *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) + goto fail; + + free_token(token); + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto fail; + + last_token = token; + + field = malloc_or_die(sizeof(*field)); + memset(field, 0, sizeof(*field)); + field->event = event; + + /* 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); + free(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); + + if (type == EVENT_ITEM) + field->arraylen = strtoul(token, NULL, 0); + else + field->arraylen = 0; + + 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); + /* We only care about the last token */ + field->arraylen = strtoul(token, NULL, 0); + 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 (field_is_long(field)) + field->flags |= FIELD_IS_LONG; + + if (test_type_token(type, token, EVENT_OP, ";")) + goto fail; + free_token(token); + + if (read_expected(EVENT_ITEM, "offset") < 0) + goto fail_expect; + + 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) + goto fail_expect; + + if (read_expected(EVENT_ITEM, "size") < 0) + goto fail_expect; + + 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) + goto fail_expect; + + 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")) + goto fail; + + free_token(token); + + if (read_expected(EVENT_OP, ":") < 0) + goto fail_expect; + + if (read_expect_type(EVENT_ITEM, &token)) + goto fail; + + /* add signed type */ + + free_token(token); + if (read_expected(EVENT_OP, ";") < 0) + goto fail_expect; + + if (read_expect_type(EVENT_NEWLINE, &token)) + goto fail; + } + + free_token(token); + + if (field->flags & FIELD_IS_ARRAY) { + if (field->arraylen) + field->elementsize = field->size / field->arraylen; + else if (field->flags & FIELD_IS_STRING) + field->elementsize = 1; + else + field->elementsize = event->pevent->long_size; + } else + field->elementsize = field->size; + + *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_format *event) +{ + char *token; + int ret; + + if (read_expected_item(EVENT_ITEM, "format") < 0) + return -1; + + if (read_expected(EVENT_OP, ":") < 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; +} + +static enum event_type +process_arg_token(struct event_format *event, struct print_arg *arg, + char **tok, enum event_type type); + +static enum event_type +process_arg(struct event_format *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_op(struct event_format *event, struct print_arg *arg, char **tok); + +static enum event_type +process_cond(struct event_format *event, struct print_arg *top, char **tok) +{ + struct print_arg *arg, *left, *right; + enum event_type type; + char *token = NULL; + + arg = alloc_arg(); + left = alloc_arg(); + right = alloc_arg(); + + arg->type = PRINT_OP; + arg->op.left = left; + arg->op.right = right; + + *tok = NULL; + type = process_arg(event, left, &token); + + again: + /* Handle other operations in the arguments */ + if (type == EVENT_OP && strcmp(token, ":") != 0) { + type = process_op(event, left, &token); + goto again; + } + + if (test_type_token(type, token, EVENT_OP, ":")) + goto out_free; + + arg->op.op = token; + + type = process_arg(event, right, &token); + + top->op.right = arg; + + *tok = token; + return type; + +out_free: + /* Top may point to itself */ + top->op.right = NULL; + free_token(token); + free_arg(arg); + return EVENT_ERROR; +} + +static enum event_type +process_array(struct event_format *event, struct print_arg *top, char **tok) +{ + struct print_arg *arg; + enum event_type type; + char *token = NULL; + + arg = alloc_arg(); + + *tok = NULL; + type = process_arg(event, arg, &token); + if (test_type_token(type, token, EVENT_OP, "]")) + goto out_free; + + top->op.right = arg; + + free_token(token); + type = read_token_item(&token); + *tok = token; + + return type; + +out_free: + free_token(*tok); + *tok = NULL; + free_arg(arg); + return EVENT_ERROR; +} + +static int get_op_prio(char *op) +{ + if (!op[1]) { + switch (op[0]) { + case '~': + case '!': + return 4; + 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); +} + +/* Note, *tok does not get freed, but will most likely be saved */ +static enum event_type +process_op(struct event_format *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); + goto out_free; + } + switch (token[0]) { + case '~': + case '!': + case '+': + case '-': + break; + default: + do_warning("bad op token %s", token); + goto out_free; + + } + + /* make an empty left */ + left = alloc_arg(); + left->type = PRINT_NULL; + arg->op.left = left; + + right = alloc_arg(); + arg->op.right = right; + + /* do not free the token, it belongs to an op */ + *tok = NULL; + type = process_arg(event, right, tok); + + } else if (strcmp(token, "?") == 0) { + + left = alloc_arg(); + /* 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 || + strcmp(token, "==") == 0 || + strcmp(token, "!=") == 0) { + + left = alloc_arg(); + + /* 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); + + type = read_token_item(&token); + *tok = token; + + /* could just be a type pointer */ + if ((strcmp(arg->op.op, "*") == 0) && + type == EVENT_DELIM && (strcmp(token, ")") == 0)) { + if (left->type != PRINT_ATOM) + die("bad pointer type"); + left->atom.atom = realloc(left->atom.atom, + strlen(left->atom.atom) + 3); + strcat(left->atom.atom, " *"); + free(arg->op.op); + *arg = *left; + free(left); + + return type; + } + + right = alloc_arg(); + type = process_arg_token(event, right, tok, type); + arg->op.right = right; + + } else if (strcmp(token, "[") == 0) { + + left = alloc_arg(); + *left = *arg; + + arg->type = PRINT_OP; + arg->op.op = token; + arg->op.left = left; + + arg->op.prio = 0; + + type = process_array(event, arg, tok); + + } else { + do_warning("unknown op '%s'", token); + event->flags |= EVENT_FL_FAILED; + /* the arg is now the left side */ + goto out_free; + } + + if (type == EVENT_OP && strcmp(*tok, ":") != 0) { + 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; + + out_free: + free_token(token); + *tok = NULL; + return EVENT_ERROR; +} + +static enum event_type +process_entry(struct event_format *event __unused, struct print_arg *arg, + char **tok) +{ + enum event_type type; + char *field; + char *token; + + if (read_expected(EVENT_OP, "->") < 0) + goto out_err; + + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto out_free; + field = token; + + arg->type = PRINT_FIELD; + arg->field.name = field; + + type = read_token(&token); + *tok = token; + + return type; + + out_free: + free_token(token); + out_err: + *tok = NULL; + return EVENT_ERROR; +} + +static char *arg_eval (struct print_arg *arg); + +static unsigned long long +eval_type_str(unsigned long long val, const char *type, int pointer) +{ + int sign = 0; + char *ref; + int len; + + len = strlen(type); + + if (pointer) { + + if (type[len-1] != '*') { + do_warning("pointer expected with non pointer type"); + return val; + } + + ref = malloc_or_die(len); + memcpy(ref, type, len); + + /* chop off the " *" */ + ref[len - 2] = 0; + + val = eval_type_str(val, ref, 0); + free(ref); + return val; + } + + /* check if this is a pointer */ + if (type[len - 1] == '*') + return val; + + /* Try to figure out the arg size*/ + if (strncmp(type, "struct", 6) == 0) + /* all bets off */ + return val; + + if (strcmp(type, "u8") == 0) + return val & 0xff; + + if (strcmp(type, "u16") == 0) + return val & 0xffff; + + if (strcmp(type, "u32") == 0) + return val & 0xffffffff; + + if (strcmp(type, "u64") == 0 || + strcmp(type, "s64")) + return val; + + if (strcmp(type, "s8") == 0) + return (unsigned long long)(char)val & 0xff; + + if (strcmp(type, "s16") == 0) + return (unsigned long long)(short)val & 0xffff; + + if (strcmp(type, "s32") == 0) + return (unsigned long long)(int)val & 0xffffffff; + + if (strncmp(type, "unsigned ", 9) == 0) { + sign = 0; + type += 9; + } + + if (strcmp(type, "char") == 0) { + if (sign) + return (unsigned long long)(char)val & 0xff; + else + return val & 0xff; + } + + if (strcmp(type, "short") == 0) { + if (sign) + return (unsigned long long)(short)val & 0xffff; + else + return val & 0xffff; + } + + if (strcmp(type, "int") == 0) { + if (sign) + return (unsigned long long)(int)val & 0xffffffff; + else + return val & 0xffffffff; + } + + return val; +} + +/* + * Try to figure out the type. + */ +static unsigned long long +eval_type(unsigned long long val, struct print_arg *arg, int pointer) +{ + if (arg->type != PRINT_TYPE) + die("expected type argument"); + + return eval_type_str(val, arg->typecast.type, pointer); +} + +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); + val = eval_type(val, arg, 0); + 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; + case '-': + /* check for negative */ + if (arg->op.left->type == PRINT_NULL) + left = 0; + else + left = arg_num_eval(arg->op.left); + right = arg_num_eval(arg->op.right); + val = left - right; + break; + default: + die("unknown op '%s'", arg->op.op); + } + break; + + case PRINT_NULL: + case PRINT_FIELD ... PRINT_SYMBOL: + case PRINT_STRING: + case PRINT_BSTRING: + 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: + case PRINT_BSTRING: + default: + die("invalid eval type %d", arg->type); + break; + } + + return NULL; +} + +static enum event_type +process_fields(struct event_format *event, struct print_flag_sym **list, char **tok) +{ + enum event_type type; + struct print_arg *arg = NULL; + struct print_flag_sym *field; + char *token = *tok; + char *value; + + do { + free_token(token); + type = read_token_item(&token); + if (test_type_token(type, token, EVENT_OP, "{")) + break; + + arg = alloc_arg(); + + free_token(token); + type = process_arg(event, arg, &token); + if (test_type_token(type, token, EVENT_DELIM, ",")) + goto out_free; + + field = malloc_or_die(sizeof(*field)); + memset(field, 0, sizeof(field)); + + value = arg_eval(arg); + field->value = strdup(value); + + free_arg(arg); + arg = alloc_arg(); + + free_token(token); + type = process_arg(event, arg, &token); + if (test_type_token(type, token, EVENT_OP, "}")) + 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); + *tok = NULL; + + return EVENT_ERROR; +} + +static enum event_type +process_flags(struct event_format *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; + + field = alloc_arg(); + + type = process_arg(event, field, &token); + + /* Handle operations in the first argument */ + while (type == EVENT_OP) + type = process_op(event, field, &token); + + if (test_type_token(type, token, EVENT_DELIM, ",")) + goto out_free; + free_token(token); + + 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, ",")) + goto out_free; + + type = process_fields(event, &arg->flags.flags, &token); + if (test_type_token(type, token, EVENT_DELIM, ")")) + goto out_free; + + free_token(token); + type = read_token_item(tok); + return type; + + out_free: + free_token(token); + *tok = NULL; + return EVENT_ERROR; +} + +static enum event_type +process_symbols(struct event_format *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; + + field = alloc_arg(); + + type = process_arg(event, field, &token); + if (test_type_token(type, token, EVENT_DELIM, ",")) + goto out_free; + + arg->symbol.field = field; + + type = process_fields(event, &arg->symbol.symbols, &token); + if (test_type_token(type, token, EVENT_DELIM, ")")) + goto out_free; + + free_token(token); + type = read_token_item(tok); + return type; + + out_free: + free_token(token); + *tok = NULL; + return EVENT_ERROR; +} + +static enum event_type +process_dynamic_array(struct event_format *event, struct print_arg *arg, char **tok) +{ + struct format_field *field; + enum event_type type; + char *token; + + memset(arg, 0, sizeof(*arg)); + arg->type = PRINT_DYNAMIC_ARRAY; + + /* + * The item within the parenthesis is another field that holds + * the index into where the array starts. + */ + type = read_token(&token); + *tok = token; + if (type != EVENT_ITEM) + goto out_free; + + /* Find the field */ + + field = pevent_find_field(event, token); + if (!field) + goto out_free; + + arg->dynarray.field = field; + arg->dynarray.index = 0; + + if (read_expected(EVENT_DELIM, ")") < 0) + goto out_free; + + free_token(token); + type = read_token_item(&token); + *tok = token; + if (type != EVENT_OP || strcmp(token, "[") != 0) + return type; + + free_token(token); + arg = alloc_arg(); + type = process_arg(event, arg, &token); + if (type == EVENT_ERROR) + goto out_free; + + if (!test_type_token(type, token, EVENT_OP, "]")) + goto out_free; + + free_token(token); + type = read_token_item(tok); + return type; + + out_free: + free(arg); + free_token(token); + *tok = NULL; + return EVENT_ERROR; +} + +static enum event_type +process_paren(struct event_format *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) + goto out_free; + + if (type == EVENT_OP) + type = process_op(event, arg, &token); + + if (type == EVENT_ERROR) + goto out_free; + + if (test_type_token(type, token, EVENT_DELIM, ")")) + goto out_free; + + 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 = alloc_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; + + out_free: + free_token(token); + *tok = NULL; + return EVENT_ERROR; +} + + +static enum event_type +process_str(struct event_format *event __unused, struct print_arg *arg, char **tok) +{ + enum event_type type; + char *token; + + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto out_free; + + arg->type = PRINT_STRING; + arg->string.string = token; + arg->string.offset = -1; + + if (read_expected(EVENT_DELIM, ")") < 0) + goto out_err; + + type = read_token(&token); + *tok = token; + + return type; + + out_free: + free_token(token); + out_err: + *tok = NULL; + return EVENT_ERROR; +} + +static struct pevent_function_handler * +find_func_handler(struct pevent *pevent, char *func_name) +{ + struct pevent_function_handler *func; + + for (func = pevent->func_handlers; func; func = func->next) { + if (strcmp(func->name, func_name) == 0) + break; + } + + return func; +} + +static void remove_func_handler(struct pevent *pevent, char *func_name) +{ + struct pevent_function_handler *func; + struct pevent_function_handler **next; + + next = &pevent->func_handlers; + while ((func = *next)) { + if (strcmp(func->name, func_name) == 0) { + *next = func->next; + free_func_handle(func); + break; + } + next = &func->next; + } +} + +static enum event_type +process_func_handler(struct event_format *event, struct pevent_function_handler *func, + struct print_arg *arg, char **tok) +{ + struct print_arg **next_arg; + struct print_arg *farg; + enum event_type type; + char *token; + char *test; + int i; + + arg->type = PRINT_FUNC; + arg->func.func = func; + + *tok = NULL; + + next_arg = &(arg->func.args); + for (i = 0; i < func->nr_args; i++) { + farg = alloc_arg(); + type = process_arg(event, farg, &token); + if (i < (func->nr_args - 1)) + test = ","; + else + test = ")"; + + if (test_type_token(type, token, EVENT_DELIM, test)) { + free_arg(farg); + free_token(token); + return EVENT_ERROR; + } + + *next_arg = farg; + next_arg = &(farg->next); + free_token(token); + } + + type = read_token(&token); + *tok = token; + + return type; +} + +static enum event_type +process_function(struct event_format *event, struct print_arg *arg, + char *token, char **tok) +{ + struct pevent_function_handler *func; + + if (strcmp(token, "__print_flags") == 0) { + free_token(token); + return process_flags(event, arg, tok); + } + if (strcmp(token, "__print_symbolic") == 0) { + free_token(token); + return process_symbols(event, arg, tok); + } + if (strcmp(token, "__get_str") == 0) { + free_token(token); + return process_str(event, arg, tok); + } + if (strcmp(token, "__get_dynamic_array") == 0) { + free_token(token); + return process_dynamic_array(event, arg, tok); + } + + func = find_func_handler(event->pevent, token); + if (func) { + free_token(token); + return process_func_handler(event, func, arg, tok); + } + + do_warning("function %s not defined", token); + free_token(token); + return EVENT_ERROR; +} + +static enum event_type +process_arg_token(struct event_format *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); + break; + } + atom = token; + /* test the next token */ + type = read_token_item(&token); + + /* + * If the next token is a parenthesis, then this + * is a function. + */ + if (type == EVENT_DELIM && strcmp(token, "(") == 0) { + free_token(token); + token = NULL; + /* this will free atom. */ + type = process_function(event, arg, atom, &token); + break; + } + /* 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); + } + + 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); + + /* On error, the op is freed */ + if (type == EVENT_ERROR) + arg->op.op = NULL; + + /* return error type if errored */ + break; + + case EVENT_ERROR ... EVENT_NEWLINE: + default: + die("unexpected type %d", type); + } + *tok = token; + + return type; +} + +static int event_read_print_args(struct event_format *event, struct print_arg **list) +{ + enum event_type type = EVENT_ERROR; + struct print_arg *arg; + char *token; + int args = 0; + + do { + if (type == EVENT_NEWLINE) { + type = read_token_item(&token); + continue; + } + + arg = alloc_arg(); + + type = process_arg(event, arg, &token); + + if (type == EVENT_ERROR) { + free_token(token); + free_arg(arg); + return -1; + } + + *list = arg; + args++; + + if (type == EVENT_OP) { + type = process_op(event, arg, &token); + free_token(token); + if (type == EVENT_ERROR) { + *list = NULL; + free_arg(arg); + return -1; + } + 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 && type != EVENT_ERROR) + free_token(token); + + return args; +} + +static int event_read_print(struct event_format *event) +{ + enum event_type type; + char *token; + int ret; + + if (read_expected_item(EVENT_ITEM, "print") < 0) + return -1; + + if (read_expected(EVENT_ITEM, "fmt") < 0) + return -1; + + if (read_expected(EVENT_OP, ":") < 0) + return -1; + + if (read_expect_type(EVENT_DQUOTE, &token) < 0) + goto fail; + + concat: + 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; + + /* Handle concatenation of print lines */ + if (type == EVENT_DQUOTE) { + char *cat; + + cat = malloc_or_die(strlen(event->print_fmt.format) + + strlen(token) + 1); + strcpy(cat, event->print_fmt.format); + strcat(cat, token); + free_token(token); + free_token(event->print_fmt.format); + event->print_fmt.format = NULL; + token = cat; + goto concat; + } + + if (test_type_token(type, token, EVENT_DELIM, ",")) + goto fail; + + free_token(token); + + ret = event_read_print_args(event, &event->print_fmt.args); + if (ret < 0) + return -1; + + return ret; + + fail: + free_token(token); + return -1; +} + +/** + * pevent_find_common_field - return a common field by event + * @event: handle for the event + * @name: the name of the common field to return + * + * Returns a common field from the event by the given @name. + * This only searchs the common fields and not all field. + */ +struct format_field * +pevent_find_common_field(struct event_format *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; +} + +/** + * pevent_find_field - find a non-common field + * @event: handle for the event + * @name: the name of the non-common field + * + * Returns a non-common field by the given @name. + * This does not search common fields. + */ +struct format_field * +pevent_find_field(struct event_format *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; +} + +/** + * pevent_find_any_field - find any field by name + * @event: handle for the event + * @name: the name of the field + * + * Returns a field by the given @name. + * This searchs the common field names first, then + * the non-common ones if a common one was not found. + */ +struct format_field * +pevent_find_any_field(struct event_format *event, const char *name) +{ + struct format_field *format; + + format = pevent_find_common_field(event, name); + if (format) + return format; + return pevent_find_field(event, name); +} + +/** + * pevent_read_number - read a number from data + * @pevent: handle for the pevent + * @ptr: the raw data + * @size: the size of the data that holds the number + * + * Returns the number (converted to host) from the + * raw data. + */ +unsigned long long pevent_read_number(struct pevent *pevent, + const void *ptr, int size) +{ + switch (size) { + case 1: + return *(unsigned char *)ptr; + case 2: + return data2host2(pevent, ptr); + case 4: + return data2host4(pevent, ptr); + case 8: + return data2host8(pevent, ptr); + default: + /* BUG! */ + return 0; + } +} + +/** + * pevent_read_number_field - read a number from data + * @field: a handle to the field + * @data: the raw data to read + * @value: the value to place the number in + * + * Reads raw data according to a field offset and size, + * and translates it into @value. + * + * Returns 0 on success, -1 otherwise. + */ +int pevent_read_number_field(struct format_field *field, const void *data, + unsigned long long *value) +{ + if (!field) + return -1; + switch (field->size) { + case 1: + case 2: + case 4: + case 8: + *value = pevent_read_number(field->event->pevent, + data + field->offset, field->size); + return 0; + default: + return -1; + } +} + +static int get_common_info(struct pevent *pevent, + const char *type, int *offset, int *size) +{ + struct event_format *event; + struct format_field *field; + + /* + * All events should have the same common elements. + * Pick any event to find where the type is; + */ + if (!pevent->events) + die("no event_list!"); + + event = pevent->events[0]; + field = pevent_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(struct pevent *pevent, void *data, + int *size, int *offset, const char *name) +{ + int ret; + + if (!*size) { + ret = get_common_info(pevent, name, offset, size); + if (ret < 0) + return ret; + } + return pevent_read_number(pevent, data + *offset, *size); +} + +static int trace_parse_common_type(struct pevent *pevent, void *data) +{ + return __parse_common(pevent, data, + &pevent->type_size, &pevent->type_offset, + "common_type"); +} + +static int parse_common_pid(struct pevent *pevent, void *data) +{ + return __parse_common(pevent, data, + &pevent->pid_size, &pevent->pid_offset, + "common_pid"); +} + +static int parse_common_pc(struct pevent *pevent, void *data) +{ + return __parse_common(pevent, data, + &pevent->pc_size, &pevent->pc_offset, + "common_preempt_count"); +} + +static int parse_common_flags(struct pevent *pevent, void *data) +{ + return __parse_common(pevent, data, + &pevent->flags_size, &pevent->flags_offset, + "common_flags"); +} + +static int parse_common_lock_depth(struct pevent *pevent, void *data) +{ + int ret; + + ret = __parse_common(pevent, data, + &pevent->ld_size, &pevent->ld_offset, + "common_lock_depth"); + if (ret < 0) + return -1; + + return ret; +} + +static int events_id_cmp(const void *a, const void *b); + +/** + * pevent_find_event - find an event by given id + * @pevent: a handle to the pevent + * @id: the id of the event + * + * Returns an event that has a given @id. + */ +struct event_format *pevent_find_event(struct pevent *pevent, int id) +{ + struct event_format **eventptr; + struct event_format key; + struct event_format *pkey = &key; + + /* Check cache first */ + if (pevent->last_event && pevent->last_event->id == id) + return pevent->last_event; + + key.id = id; + + eventptr = bsearch(&pkey, pevent->events, pevent->nr_events, + sizeof(*pevent->events), events_id_cmp); + + if (eventptr) { + pevent->last_event = *eventptr; + return *eventptr; + } + + return NULL; +} + +/** + * pevent_find_event_by_name - find an event by given name + * @pevent: a handle to the pevent + * @sys: the system name to search for + * @name: the name of the event to search for + * + * This returns an event with a given @name and under the system + * @sys. If @sys is NULL the first event with @name is returned. + */ +struct event_format * +pevent_find_event_by_name(struct pevent *pevent, + const char *sys, const char *name) +{ + struct event_format *event; + int i; + + if (pevent->last_event && + strcmp(pevent->last_event->name, name) == 0 && + (!sys || strcmp(pevent->last_event->system, sys) == 0)) + return pevent->last_event; + + for (i = 0; i < pevent->nr_events; i++) { + event = pevent->events[i]; + if (strcmp(event->name, name) == 0) { + if (!sys) + break; + if (strcmp(event->system, sys) == 0) + break; + } + } + if (i == pevent->nr_events) + event = NULL; + + pevent->last_event = event; + return event; +} + +static unsigned long long +eval_num_arg(void *data, int size, struct event_format *event, struct print_arg *arg) +{ + struct pevent *pevent = event->pevent; + unsigned long long val = 0; + unsigned long long left, right; + struct print_arg *typearg = NULL; + struct print_arg *larg; + unsigned long offset; + unsigned int field_size; + + 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 = pevent_find_any_field(event, arg->field.name); + if (!arg->field.field) + die("field %s not found", arg->field.name); + } + /* must be a number */ + val = pevent_read_number(pevent, data + arg->field.field->offset, + arg->field.field->size); + break; + case PRINT_FLAGS: + case PRINT_SYMBOL: + break; + case PRINT_TYPE: + val = eval_num_arg(data, size, event, arg->typecast.item); + return eval_type(val, arg, 0); + case PRINT_STRING: + case PRINT_BSTRING: + return 0; + case PRINT_FUNC: { + struct trace_seq s; + trace_seq_init(&s); + val = process_defined_func(&s, data, size, event, arg); + trace_seq_destroy(&s); + return val; + } + case PRINT_OP: + if (strcmp(arg->op.op, "[") == 0) { + /* + * Arrays are special, since we don't want + * to read the arg as is. + */ + right = eval_num_arg(data, size, event, arg->op.right); + + /* handle typecasts */ + larg = arg->op.left; + while (larg->type == PRINT_TYPE) { + if (!typearg) + typearg = larg; + larg = larg->typecast.item; + } + + /* Default to long size */ + field_size = pevent->long_size; + + switch (larg->type) { + case PRINT_DYNAMIC_ARRAY: + offset = pevent_read_number(pevent, + data + larg->dynarray.field->offset, + larg->dynarray.field->size); + if (larg->dynarray.field->elementsize) + field_size = larg->dynarray.field->elementsize; + /* + * The actual length of the dynamic array is stored + * in the top half of the field, and the offset + * is in the bottom half of the 32 bit field. + */ + offset &= 0xffff; + offset += right; + break; + case PRINT_FIELD: + if (!larg->field.field) { + larg->field.field = + pevent_find_any_field(event, larg->field.name); + if (!larg->field.field) + die("field %s not found", larg->field.name); + } + field_size = larg->field.field->elementsize; + offset = larg->field.field->offset + + right * larg->field.field->elementsize; + break; + default: + goto default_op; /* oops, all bets off */ + } + val = pevent_read_number(pevent, + data + offset, field_size); + if (typearg) + val = eval_type(val, typearg, 1); + break; + } else if (strcmp(arg->op.op, "?") == 0) { + left = eval_num_arg(data, size, event, arg->op.left); + arg = arg->op.right; + if (left) + val = eval_num_arg(data, size, event, arg->op.left); + else + val = eval_num_arg(data, size, event, arg->op.right); + break; + } + default_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 '!': + switch (arg->op.op[1]) { + case 0: + val = !right; + break; + case '=': + val = left != right; + break; + default: + die("unknown op '%s'", arg->op.op); + } + break; + case '~': + val = ~right; + break; + 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; + case '-': + val = left - right; + break; + case '+': + 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 }, + { "BLOCK_IOPOLL_SOFTIRQ", 5 }, + { "TASKLET_SOFTIRQ", 6 }, + { "SCHED_SOFTIRQ", 7 }, + { "HRTIMER_SOFTIRQ", 8 }, + { "RCU_SOFTIRQ", 9 }, + + { "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_to_seq(struct trace_seq *s, const char *format, + int len_arg, const char *str) +{ + if (len_arg >= 0) + trace_seq_printf(s, format, len_arg, str); + else + trace_seq_printf(s, format, str); +} + +static void print_str_arg(struct trace_seq *s, void *data, int size, + struct event_format *event, const char *format, + int len_arg, struct print_arg *arg) +{ + struct pevent *pevent = event->pevent; + struct print_flag_sym *flag; + unsigned long long val, fval; + unsigned long addr; + char *str; + int print; + int len; + + switch (arg->type) { + case PRINT_NULL: + /* ?? */ + return; + case PRINT_ATOM: + print_str_to_seq(s, format, len_arg, arg->atom.atom); + return; + case PRINT_FIELD: + if (!arg->field.field) { + arg->field.field = pevent_find_any_field(event, arg->field.name); + if (!arg->field.field) + die("field %s not found", arg->field.name); + } + /* Zero sized fields, mean the rest of the data */ + len = arg->field.field->size ? : size - arg->field.field->offset; + + /* + * Some events pass in pointers. If this is not an array + * and the size is the same as long_size, assume that it + * is a pointer. + */ + if (!(arg->field.field->flags & FIELD_IS_ARRAY) && + arg->field.field->size == pevent->long_size) { + addr = *(unsigned long *)(data + arg->field.field->offset); + trace_seq_printf(s, "%lx", addr); + break; + } + str = malloc_or_die(len + 1); + memcpy(str, data + arg->field.field->offset, len); + str[len] = 0; + print_str_to_seq(s, format, len_arg, str); + 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) { + print_str_to_seq(s, format, len_arg, flag->str); + break; + } + if (fval && (val & fval) == fval) { + if (print && arg->flags.delim) + trace_seq_puts(s, arg->flags.delim); + print_str_to_seq(s, format, len_arg, 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) { + print_str_to_seq(s, format, len_arg, flag->str); + break; + } + } + break; + + case PRINT_TYPE: + break; + case PRINT_STRING: { + int str_offset; + + if (arg->string.offset == -1) { + struct format_field *f; + + f = pevent_find_any_field(event, arg->string.string); + arg->string.offset = f->offset; + } + str_offset = data2host4(pevent, data + arg->string.offset); + str_offset &= 0xffff; + print_str_to_seq(s, format, len_arg, ((char *)data) + str_offset); + break; + } + case PRINT_BSTRING: + trace_seq_printf(s, format, 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(s, data, size, event, + format, len_arg, arg->op.right->op.left); + else + print_str_arg(s, data, size, event, + format, len_arg, arg->op.right->op.right); + break; + case PRINT_FUNC: + process_defined_func(s, data, size, event, arg); + break; + default: + /* well... */ + break; + } +} + +static unsigned long long +process_defined_func(struct trace_seq *s, void *data, int size, + struct event_format *event, struct print_arg *arg) +{ + struct pevent_function_handler *func_handle = arg->func.func; + struct pevent_func_params *param; + unsigned long long *args; + unsigned long long ret; + struct print_arg *farg; + struct trace_seq str; + struct save_str { + struct save_str *next; + char *str; + } *strings = NULL, *string; + int i; + + if (!func_handle->nr_args) { + ret = (*func_handle->func)(s, NULL); + goto out; + } + + farg = arg->func.args; + param = func_handle->params; + + args = malloc_or_die(sizeof(*args) * func_handle->nr_args); + for (i = 0; i < func_handle->nr_args; i++) { + switch (param->type) { + case PEVENT_FUNC_ARG_INT: + case PEVENT_FUNC_ARG_LONG: + case PEVENT_FUNC_ARG_PTR: + args[i] = eval_num_arg(data, size, event, farg); + break; + case PEVENT_FUNC_ARG_STRING: + trace_seq_init(&str); + print_str_arg(&str, data, size, event, "%s", -1, farg); + trace_seq_terminate(&str); + string = malloc_or_die(sizeof(*string)); + string->next = strings; + string->str = strdup(str.buffer); + strings = string; + trace_seq_destroy(&str); + break; + default: + /* + * Something went totally wrong, this is not + * an input error, something in this code broke. + */ + die("Unexpected end of arguments\n"); + break; + } + farg = farg->next; + } + + ret = (*func_handle->func)(s, args); + free(args); + while (strings) { + string = strings; + strings = string->next; + free(string->str); + free(string); + } + + out: + /* TBD : handle return type here */ + return ret; +} + +static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struct event_format *event) +{ + struct pevent *pevent = event->pevent; + struct format_field *field, *ip_field; + struct print_arg *args, *arg, **next; + unsigned long long ip, val; + char *ptr; + void *bptr; + + field = pevent->bprint_buf_field; + ip_field = pevent->bprint_ip_field; + + if (!field) { + field = pevent_find_field(event, "buf"); + if (!field) + die("can't find buffer field for binary printk"); + ip_field = pevent_find_field(event, "ip"); + if (!ip_field) + die("can't find ip field for binary printk"); + pevent->bprint_buf_field = field; + pevent->bprint_ip_field = ip_field; + } + + ip = pevent_read_number(pevent, data + ip_field->offset, ip_field->size); + + /* + * The first arg is the IP pointer. + */ + args = alloc_arg(); + 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': + /* the pointers are always 4 bytes aligned */ + bptr = (void *)(((unsigned long)bptr + 3) & + ~3); + switch (ls) { + case 0: + ls = 4; + break; + case 1: + ls = pevent->long_size; + break; + case 2: + ls = 8; + default: + break; + } + val = pevent_read_number(pevent, bptr, ls); + bptr += ls; + arg = alloc_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 = alloc_arg(); + arg->next = NULL; + arg->type = PRINT_BSTRING; + 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; + + free_arg(args); + args = next; + } +} + +static char * +get_bprint_format(void *data, int size __unused, struct event_format *event) +{ + struct pevent *pevent = event->pevent; + unsigned long long addr; + struct format_field *field; + struct printk_map *printk; + char *format; + char *p; + + field = pevent->bprint_fmt_field; + + if (!field) { + field = pevent_find_field(event, "fmt"); + if (!field) + die("can't find format field for binary printk"); + pevent->bprint_fmt_field = field; + } + + addr = pevent_read_number(pevent, data + field->offset, field->size); + + printk = find_printk(pevent, 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 print_mac_arg(struct trace_seq *s, int mac, void *data, int size, + struct event_format *event, struct print_arg *arg) +{ + unsigned char *buf; + char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x"; + + if (arg->type == PRINT_FUNC) { + process_defined_func(s, data, size, event, arg); + return; + } + + if (arg->type != PRINT_FIELD) { + trace_seq_printf(s, "ARG TYPE NOT FIELD BUT %d", + arg->type); + return; + } + + if (mac == 'm') + fmt = "%.2x%.2x%.2x%.2x%.2x%.2x"; + if (!arg->field.field) { + arg->field.field = + pevent_find_any_field(event, arg->field.name); + if (!arg->field.field) + die("field %s not found", arg->field.name); + } + if (arg->field.field->size != 6) { + trace_seq_printf(s, "INVALIDMAC"); + return; + } + buf = data + arg->field.field->offset; + trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); +} + +static void print_event_fields(struct trace_seq *s, void *data, int size, + struct event_format *event) +{ + struct format_field *field; + unsigned long long val; + unsigned int offset, len, i; + + field = event->format.fields; + while (field) { + trace_seq_printf(s, " %s=", field->name); + if (field->flags & FIELD_IS_ARRAY) { + offset = field->offset; + len = field->size; + if (field->flags & FIELD_IS_DYNAMIC) { + val = pevent_read_number(event->pevent, data + offset, len); + offset = val; + len = offset >> 16; + offset &= 0xffff; + } + if (field->flags & FIELD_IS_STRING) { + trace_seq_printf(s, "%s", (char *)data + offset); + } else { + trace_seq_puts(s, "ARRAY["); + for (i = 0; i < len; i++) { + if (i) + trace_seq_puts(s, ", "); + trace_seq_printf(s, "%02x", + *((unsigned char *)data + offset + i)); + } + trace_seq_putc(s, ']'); + } + } else { + val = pevent_read_number(event->pevent, data + field->offset, + field->size); + if (field->flags & FIELD_IS_POINTER) { + trace_seq_printf(s, "0x%llx", val); + } else if (field->flags & FIELD_IS_SIGNED) { + switch (field->size) { + case 4: + /* + * If field is long then print it in hex. + * A long usually stores pointers. + */ + if (field->flags & FIELD_IS_LONG) + trace_seq_printf(s, "0x%x", (int)val); + else + trace_seq_printf(s, "%d", (int)val); + break; + case 2: + trace_seq_printf(s, "%2d", (short)val); + break; + case 1: + trace_seq_printf(s, "%1d", (char)val); + break; + default: + trace_seq_printf(s, "%lld", val); + } + } else { + if (field->flags & FIELD_IS_LONG) + trace_seq_printf(s, "0x%llx", val); + else + trace_seq_printf(s, "%llu", val); + } + } + field = field->next; + } +} + +static void pretty_print(struct trace_seq *s, void *data, int size, struct event_format *event) +{ + struct pevent *pevent = event->pevent; + 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_as_arg; + int len_arg; + int len; + int ls; + + if (event->flags & EVENT_FL_FAILED) { + trace_seq_printf(s, "[FAILED TO PARSE]"); + print_event_fields(s, data, size, event); + return; + } + + 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 == '\\') { + ptr++; + switch (*ptr) { + case 'n': + trace_seq_putc(s, '\n'); + break; + case 't': + trace_seq_putc(s, '\t'); + break; + case 'r': + trace_seq_putc(s, '\r'); + break; + case '\\': + trace_seq_putc(s, '\\'); + break; + default: + trace_seq_putc(s, *ptr); + break; + } + + } else if (*ptr == '%') { + saveptr = ptr; + show_func = 0; + len_as_arg = 0; + cont_process: + ptr++; + switch (*ptr) { + case '%': + trace_seq_putc(s, '%'); + break; + case '#': + /* FIXME: need to handle properly */ + goto cont_process; + case 'h': + ls--; + goto cont_process; + case 'l': + ls++; + goto cont_process; + case 'L': + ls = 2; + goto cont_process; + case '*': + /* The argument is the length. */ + if (!arg) + die("no argument match"); + len_arg = eval_num_arg(data, size, event, arg); + len_as_arg = 1; + arg = arg->next; + goto cont_process; + case '.': + case 'z': + case 'Z': + case '0' ... '9': + goto cont_process; + case 'p': + if (pevent->long_size == 4) + ls = 1; + else + ls = 2; + + if (*(ptr+1) == 'F' || + *(ptr+1) == 'f') { + ptr++; + show_func = *ptr; + } else if (*(ptr+1) == 'M' || *(ptr+1) == 'm') { + print_mac_arg(s, *(ptr+1), data, size, event, arg); + ptr++; + break; + } + + /* 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 > 31) + 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(pevent, val); + if (func) { + trace_seq_puts(s, func->func); + if (show_func == 'F') + trace_seq_printf(s, + "+0x%llx", + val - func->addr); + break; + } + } + if (pevent->long_size == 8 && ls) { + char *p; + + ls = 2; + /* make %l into %ll */ + p = strchr(format, 'l'); + if (p) + memmove(p, p+1, strlen(p)+1); + else if (strcmp(format, "%p") == 0) + strcpy(format, "0x%llx"); + } + switch (ls) { + case -2: + if (len_as_arg) + trace_seq_printf(s, format, len_arg, (char)val); + else + trace_seq_printf(s, format, (char)val); + break; + case -1: + if (len_as_arg) + trace_seq_printf(s, format, len_arg, (short)val); + else + trace_seq_printf(s, format, (short)val); + break; + case 0: + if (len_as_arg) + trace_seq_printf(s, format, len_arg, (int)val); + else + trace_seq_printf(s, format, (int)val); + break; + case 1: + if (len_as_arg) + trace_seq_printf(s, format, len_arg, (long)val); + else + trace_seq_printf(s, format, (long)val); + break; + case 2: + if (len_as_arg) + trace_seq_printf(s, format, len_arg, + (long long)val); + else + trace_seq_printf(s, format, (long long)val); + break; + default: + die("bad count (%d)", ls); + } + break; + case 's': + if (!arg) + die("no matching argument"); + + len = ((unsigned long)ptr + 1) - + (unsigned long)saveptr; + + /* should never happen */ + if (len > 31) + die("bad format!"); + + memcpy(format, saveptr, len); + format[len] = 0; + if (!len_as_arg) + len_arg = -1; + print_str_arg(s, data, size, event, + format, len_arg, arg); + arg = arg->next; + break; + default: + trace_seq_printf(s, ">%c<", *ptr); + + } + } else + trace_seq_putc(s, *ptr); + } + + if (args) { + free_args(args); + free(bprint_fmt); + } +} + +/** + * pevent_data_lat_fmt - parse the data for the latency format + * @pevent: a handle to the pevent + * @s: the trace_seq to write to + * @data: the raw data to read from + * @size: currently unused. + * + * This parses out the Latency format (interrupts disabled, + * need rescheduling, in hard/soft interrupt, preempt count + * and lock depth) and places it into the trace_seq. + */ +void pevent_data_lat_fmt(struct pevent *pevent, + struct trace_seq *s, struct record *record) +{ + static int check_lock_depth = 1; + static int lock_depth_exists; + unsigned int lat_flags; + unsigned int pc; + int lock_depth; + int hardirq; + int softirq; + void *data = record->data; + + lat_flags = parse_common_flags(pevent, data); + pc = parse_common_pc(pevent, data); + /* lock_depth may not always exist */ + if (check_lock_depth) { + struct format_field *field; + struct event_format *event; + + check_lock_depth = 0; + event = pevent->events[0]; + field = pevent_find_common_field(event, "common_lock_depth"); + if (field) + lock_depth_exists = 1; + } + if (lock_depth_exists) + lock_depth = parse_common_lock_depth(pevent, data); + + hardirq = lat_flags & TRACE_FLAG_HARDIRQ; + softirq = lat_flags & TRACE_FLAG_SOFTIRQ; + + trace_seq_printf(s, "%c%c%c", + (lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' : + (lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ? + 'X' : '.', + (lat_flags & TRACE_FLAG_NEED_RESCHED) ? + 'N' : '.', + (hardirq && softirq) ? 'H' : + hardirq ? 'h' : softirq ? 's' : '.'); + + if (pc) + trace_seq_printf(s, "%x", pc); + else + trace_seq_putc(s, '.'); + + if (lock_depth_exists) { + if (lock_depth < 0) + trace_seq_putc(s, '.'); + else + trace_seq_printf(s, "%d", lock_depth); + } + + trace_seq_terminate(s); +} + +/** + * pevent_data_type - parse out the given event type + * @pevent: a handle to the pevent + * @rec: the record to read from + * + * This returns the event id from the @rec. + */ +int pevent_data_type(struct pevent *pevent, struct record *rec) +{ + return trace_parse_common_type(pevent, rec->data); +} + +/** + * pevent_data_event_from_type - find the event by a given type + * @pevent: a handle to the pevent + * @type: the type of the event. + * + * This returns the event form a given @type; + */ +struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type) +{ + return pevent_find_event(pevent, type); +} + +/** + * pevent_data_pid - parse the PID from raw data + * @pevent: a handle to the pevent + * @rec: the record to parse + * + * This returns the PID from a raw data. + */ +int pevent_data_pid(struct pevent *pevent, struct record *rec) +{ + return parse_common_pid(pevent, rec->data); +} + +/** + * pevent_data_comm_from_pid - return the command line from PID + * @pevent: a handle to the pevent + * @pid: the PID of the task to search for + * + * This returns a pointer to the command line that has the given + * @pid. + */ +const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid) +{ + const char *comm; + + comm = find_cmdline(pevent, pid); + return comm; +} + +/** + * pevent_data_comm_from_pid - parse the data into the print format + * @s: the trace_seq to write to + * @event: the handle to the event + * @cpu: the cpu the event was recorded on + * @data: the raw data + * @size: the size of the raw data + * @nsecs: the timestamp of the event + * + * This parses the raw @data using the given @event information and + * writes the print format into the trace_seq. + */ +void pevent_event_info(struct trace_seq *s, struct event_format *event, + struct record *record) +{ + int print_pretty = 1; + + if (event->pevent->print_raw) + print_event_fields(s, record->data, record->size, event); + else { + + if (event->handler) + print_pretty = event->handler(s, record, event, + event->context); + + if (print_pretty) + pretty_print(s, record->data, record->size, event); + } + + trace_seq_terminate(s); +} + +void pevent_print_event(struct pevent *pevent, struct trace_seq *s, + struct record *record) +{ + static char *spaces = " "; /* 20 spaces */ + struct event_format *event; + unsigned long secs; + unsigned long usecs; + const char *comm; + void *data = record->data; + int type; + int pid; + int len; + + secs = record->ts / NSECS_PER_SEC; + usecs = record->ts - secs * NSECS_PER_SEC; + usecs = (usecs + 500) / NSECS_PER_USEC; + + if (record->size < 0) { + do_warning("ug! negative record size %d", record->size); + return; + } + + type = trace_parse_common_type(pevent, data); + + event = pevent_find_event(pevent, type); + if (!event) { + do_warning("ug! no event found for type %d", type); + return; + } + + pid = parse_common_pid(pevent, data); + comm = find_cmdline(pevent, pid); + + if (pevent->latency_format) { + trace_seq_printf(s, "%8.8s-%-5d %3d", + comm, pid, record->cpu); + pevent_data_lat_fmt(pevent, s, record); + } else + trace_seq_printf(s, "%16s-%-5d [%03d]", comm, pid, record->cpu); + + trace_seq_printf(s, " %5lu.%06lu: %s: ", secs, usecs, event->name); + + /* Space out the event names evenly. */ + len = strlen(event->name); + if (len < 20) + trace_seq_printf(s, "%.*s", 20 - len, spaces); + + pevent_event_info(s, event, record); +} + +static int events_id_cmp(const void *a, const void *b) +{ + struct event_format * const * ea = a; + struct event_format * const * eb = b; + + if ((*ea)->id < (*eb)->id) + return -1; + + if ((*ea)->id > (*eb)->id) + return 1; + + return 0; +} + +static int events_name_cmp(const void *a, const void *b) +{ + struct event_format * const * ea = a; + struct event_format * const * eb = b; + int res; + + res = strcmp((*ea)->name, (*eb)->name); + if (res) + return res; + + res = strcmp((*ea)->system, (*eb)->system); + if (res) + return res; + + return events_id_cmp(a, b); +} + +static int events_system_cmp(const void *a, const void *b) +{ + struct event_format * const * ea = a; + struct event_format * const * eb = b; + int res; + + res = strcmp((*ea)->system, (*eb)->system); + if (res) + return res; + + res = strcmp((*ea)->name, (*eb)->name); + if (res) + return res; + + return events_id_cmp(a, b); +} + +struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type sort_type) +{ + struct event_format **events; + int (*sort)(const void *a, const void *b); + + events = pevent->sort_events; + + if (events && pevent->last_type == sort_type) + return events; + + if (!events) { + events = malloc(sizeof(*events) * (pevent->nr_events + 1)); + if (!events) + return NULL; + + memcpy(events, pevent->events, sizeof(*events) * pevent->nr_events); + events[pevent->nr_events] = NULL; + + pevent->sort_events = events; + + /* the internal events are sorted by id */ + if (sort_type == EVENT_SORT_ID) { + pevent->last_type = sort_type; + return events; + } + } + + switch (sort_type) { + case EVENT_SORT_ID: + sort = events_id_cmp; + break; + case EVENT_SORT_NAME: + sort = events_name_cmp; + break; + case EVENT_SORT_SYSTEM: + sort = events_system_cmp; + break; + default: + return events; + } + + qsort(events, pevent->nr_events, sizeof(*events), sort); + pevent->last_type = sort_type; + + return events; +} + +static struct format_field ** +get_event_fields(const char *type, const char *name, + int count, struct format_field *list) +{ + struct format_field **fields; + struct format_field *field; + int i = 0; + + fields = malloc_or_die(sizeof(*fields) * (count + 1)); + for (field = list; field; field = field->next) { + fields[i++] = field; + if (i == count + 1) { + do_warning("event %s has more %s fields than specified", + name, type); + i--; + break; + } + } + + if (i != count) + do_warning("event %s has less %s fields than specified", + name, type); + + fields[i] = NULL; + + return fields; +} + +/** + * pevent_event_common_fields - return a list of common fields for an event + * @event: the event to return the common fields of. + * + * Returns an allocated array of fields. The last item in the array is NULL. + * The array must be freed with free(). + */ +struct format_field **pevent_event_common_fields(struct event_format *event) +{ + return get_event_fields("common", event->name, + event->format.nr_common, + event->format.common_fields); +} + +/** + * pevent_event_fields - return a list of event specific fields for an event + * @event: the event to return the fields of. + * + * Returns an allocated array of fields. The last item in the array is NULL. + * The array must be freed with free(). + */ +struct format_field **pevent_event_fields(struct event_format *event) +{ + return get_event_fields("event", event->name, + event->format.nr_fields, + event->format.fields); +} + +static void print_fields(struct trace_seq *s, struct print_flag_sym *field) +{ + trace_seq_printf(s, "{ %s, %s }", field->value, field->str); + if (field->next) { + trace_seq_puts(s, ", "); + print_fields(s, field->next); + } +} + +/* for debugging */ +static void print_args(struct print_arg *args) +{ + int print_paren = 1; + struct trace_seq s; + + 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); + trace_seq_init(&s); + print_fields(&s, args->flags.flags); + trace_seq_do_printf(&s); + trace_seq_destroy(&s); + printf(")"); + break; + case PRINT_SYMBOL: + printf("__print_symbolic("); + print_args(args->symbol.field); + printf(", "); + trace_seq_init(&s); + print_fields(&s, args->symbol.symbols); + trace_seq_do_printf(&s); + trace_seq_destroy(&s); + printf(")"); + break; + case PRINT_STRING: + case PRINT_BSTRING: + 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(const char *field, + int *offset, int *size, int mandatory) +{ + unsigned long long save_input_buf_ptr; + unsigned long long save_input_buf_siz; + char *token; + int type; + + save_input_buf_ptr = input_buf_ptr; + save_input_buf_siz = input_buf_siz; + + if (read_expected(EVENT_ITEM, "field") < 0) + return; + if (read_expected(EVENT_OP, ":") < 0) + return; + + /* type */ + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto fail; + free_token(token); + + /* + * If this is not a mandatory field, then test it first. + */ + if (mandatory) { + if (read_expected(EVENT_ITEM, field) < 0) + return; + } else { + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto fail; + if (strcmp(token, field) != 0) + goto discard; + free_token(token); + } + + if (read_expected(EVENT_OP, ";") < 0) + return; + if (read_expected(EVENT_ITEM, "offset") < 0) + return; + if (read_expected(EVENT_OP, ":") < 0) + return; + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto fail; + *offset = atoi(token); + free_token(token); + if (read_expected(EVENT_OP, ";") < 0) + return; + if (read_expected(EVENT_ITEM, "size") < 0) + return; + if (read_expected(EVENT_OP, ":") < 0) + return; + if (read_expect_type(EVENT_ITEM, &token) < 0) + goto fail; + *size = atoi(token); + free_token(token); + if (read_expected(EVENT_OP, ";") < 0) + return; + type = read_token(&token); + if (type != EVENT_NEWLINE) { + /* newer versions of the kernel have a "signed" type */ + if (type != EVENT_ITEM) + goto fail; + + if (strcmp(token, "signed") != 0) + goto fail; + + free_token(token); + + if (read_expected(EVENT_OP, ":") < 0) + return; + + if (read_expect_type(EVENT_ITEM, &token)) + goto fail; + + free_token(token); + if (read_expected(EVENT_OP, ";") < 0) + return; + + if (read_expect_type(EVENT_NEWLINE, &token)) + goto fail; + } + fail: + free_token(token); + return; + + discard: + input_buf_ptr = save_input_buf_ptr; + input_buf_siz = save_input_buf_siz; + *offset = 0; + *size = 0; + free_token(token); +} + +/** + * pevent_parse_header_page - parse the data stored in the header page + * @pevent: the handle to the pevent + * @buf: the buffer storing the header page format string + * @size: the size of @buf + * @long_size: the long size to use if there is no header + * + * This parses the header page format for information on the + * ring buffer used. The @buf should be copied from + * + * /sys/kernel/debug/tracing/events/header_page + */ +int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size, + int long_size) +{ + int ignore; + + if (!size) { + /* + * Old kernels did not have header page info. + * Sorry but we just use what we find here in user space. + */ + pevent->header_page_ts_size = sizeof(long long); + pevent->header_page_size_size = long_size; + pevent->header_page_data_offset = sizeof(long long) + long_size; + pevent->old_format = 1; + return -1; + } + init_input_buf(buf, size); + + parse_header_field("timestamp", &pevent->header_page_ts_offset, + &pevent->header_page_ts_size, 1); + parse_header_field("commit", &pevent->header_page_size_offset, + &pevent->header_page_size_size, 1); + parse_header_field("overwrite", &pevent->header_page_overwrite, + &ignore, 0); + parse_header_field("data", &pevent->header_page_data_offset, + &pevent->header_page_data_size, 1); + + return 0; +} + +static int event_matches(struct event_format *event, + int id, const char *sys_name, + const char *event_name) +{ + if (id >= 0 && id != event->id) + return 0; + + if (event_name && (strcmp(event_name, event->name) != 0)) + return 0; + + if (sys_name && (strcmp(sys_name, event->system) != 0)) + return 0; + + return 1; +} + +static void free_handler(struct event_handler *handle) +{ + free((void *)handle->sys_name); + free((void *)handle->event_name); + free(handle); +} + +static int find_event_handle(struct pevent *pevent, struct event_format *event) +{ + struct event_handler *handle, **next; + + for (next = &pevent->handlers; *next; + next = &(*next)->next) { + handle = *next; + if (event_matches(event, handle->id, + handle->sys_name, + handle->event_name)) + break; + } + + if (!(*next)) + return 0; + + pr_stat("overriding event (%d) %s:%s with new print handler", + event->id, event->system, event->name); + + event->handler = handle->func; + event->context = handle->context; + + *next = handle->next; + free_handler(handle); + + return 1; +} + +/** + * pevent_parse_event - parse the event format + * @pevent: the handle to the pevent + * @buf: the buffer storing the event format string + * @size: the size of @buf + * @sys: the system the event belongs to + * + * This parses the event format and creates an event structure + * to quickly parse raw data for a given event. + * + * These files currently come from: + * + * /sys/kernel/debug/tracing/events/.../.../format + */ +int pevent_parse_event(struct pevent *pevent, + const char *buf, unsigned long size, + const char *sys) +{ + struct event_format *event; + int ret; + + init_input_buf(buf, size); + + event = alloc_event(); + if (!event) + return -ENOMEM; + + event->name = event_read_name(); + if (!event->name) { + /* Bad event? */ + free(event); + return -1; + } + + if (strcmp(sys, "ftrace") == 0) { + + event->flags |= EVENT_FL_ISFTRACE; + + if (strcmp(event->name, "bprint") == 0) + event->flags |= EVENT_FL_ISBPRINT; + } + + event->id = event_read_id(); + if (event->id < 0) + die("failed to read event id"); + + event->system = strdup(sys); + + /* Add pevent to event so that it can be referenced */ + event->pevent = pevent; + + ret = event_read_format(event); + if (ret < 0) { + do_warning("failed to read event format for %s", event->name); + goto event_failed; + } + + /* + * If the event has an override, don't print warnings if the event + * print format fails to parse. + */ + if (find_event_handle(pevent, event)) + show_warning = 0; + + ret = event_read_print(event); + if (ret < 0) { + do_warning("failed to read event print fmt for %s", + event->name); + show_warning = 1; + goto event_failed; + } + show_warning = 1; + + add_event(pevent, event); + + if (!ret && (event->flags & EVENT_FL_ISFTRACE)) { + struct format_field *field; + struct print_arg *arg, **list; + + /* old ftrace had no args */ + + list = &event->print_fmt.args; + for (field = event->format.fields; field; field = field->next) { + arg = alloc_arg(); + *list = arg; + list = &arg->next; + arg->type = PRINT_FIELD; + arg->field.name = strdup(field->name); + arg->field.field = field; + } + return 0; + } + +#define PRINT_ARGS 0 + if (PRINT_ARGS && event->print_fmt.args) + print_args(event->print_fmt.args); + + return 0; + + event_failed: + event->flags |= EVENT_FL_FAILED; + /* still add it even if it failed */ + add_event(pevent, event); + return -1; +} + +int get_field_val(struct trace_seq *s, struct format_field *field, + const char *name, struct record *record, + unsigned long long *val, int err) +{ + if (!field) { + if (err) + trace_seq_printf(s, "", name); + return -1; + } + + if (pevent_read_number_field(field, record->data, val)) { + if (err) + trace_seq_printf(s, " %s=INVALID", name); + return -1; + } + + return 0; +} + +/** + * pevent_get_field_raw - return the raw pointer into the data field + * @s: The seq to print to on error + * @event: the event that the field is for + * @name: The name of the field + * @record: The record with the field name. + * @len: place to store the field length. + * @err: print default error if failed. + * + * Returns a pointer into record->data of the field and places + * the length of the field in @len. + * + * On failure, it returns NULL. + */ +void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, + const char *name, struct record *record, + int *len, int err) +{ + struct format_field *field; + void *data = record->data; + unsigned offset; + int dummy; + + if (!event) + return NULL; + + field = pevent_find_field(event, name); + + if (!field) { + if (err) + trace_seq_printf(s, "", name); + return NULL; + } + + /* Allow @len to be NULL */ + if (!len) + len = &dummy; + + offset = field->offset; + if (field->flags & FIELD_IS_DYNAMIC) { + offset = pevent_read_number(event->pevent, + data + offset, field->size); + *len = offset >> 16; + offset &= 0xffff; + } else + *len = field->size; + + return data + offset; +} + +/** + * pevent_get_field_val - find a field and return its value + * @s: The seq to print to on error + * @event: the event that the field is for + * @name: The name of the field + * @record: The record with the field name. + * @val: place to store the value of the field. + * @err: print default error if failed. + * + * Returns 0 on success -1 on field not found. + */ +int pevent_get_field_val(struct trace_seq *s, struct event_format *event, + const char *name, struct record *record, + unsigned long long *val, int err) +{ + struct format_field *field; + + if (!event) + return -1; + + field = pevent_find_field(event, name); + + return get_field_val(s, field, name, record, val, err); +} + +/** + * pevent_get_common_field_val - find a common field and return its value + * @s: The seq to print to on error + * @event: the event that the field is for + * @name: The name of the field + * @record: The record with the field name. + * @val: place to store the value of the field. + * @err: print default error if failed. + * + * Returns 0 on success -1 on field not found. + */ +int pevent_get_common_field_val(struct trace_seq *s, struct event_format *event, + const char *name, struct record *record, + unsigned long long *val, int err) +{ + struct format_field *field; + + if (!event) + return -1; + + field = pevent_find_common_field(event, name); + + return get_field_val(s, field, name, record, val, err); +} + +/** + * pevent_get_any_field_val - find a any field and return its value + * @s: The seq to print to on error + * @event: the event that the field is for + * @name: The name of the field + * @record: The record with the field name. + * @val: place to store the value of the field. + * @err: print default error if failed. + * + * Returns 0 on success -1 on field not found. + */ +int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event, + const char *name, struct record *record, + unsigned long long *val, int err) +{ + struct format_field *field; + + if (!event) + return -1; + + field = pevent_find_any_field(event, name); + + return get_field_val(s, field, name, record, val, err); +} + +/** + * pevent_print_num_field - print a field and a format + * @s: The seq to print to + * @fmt: The printf format to print the field with. + * @event: the event that the field is for + * @name: The name of the field + * @record: The record with the field name. + * @err: print default error if failed. + * + * Returns: 0 on success, -1 field not fould, or 1 if buffer is full. + */ +int pevent_print_num_field(struct trace_seq *s, const char *fmt, + struct event_format *event, const char *name, + struct record *record, int err) +{ + struct format_field *field = pevent_find_field(event, name); + unsigned long long val; + + if (!field) + goto failed; + + if (pevent_read_number_field(field, record->data, &val)) + goto failed; + + return trace_seq_printf(s, fmt, val); + + failed: + if (err) + trace_seq_printf(s, "CAN'T FIND FIELD \"%s\"", name); + return -1; +} + +static void free_func_handle(struct pevent_function_handler *func) +{ + struct pevent_func_params *params; + + free(func->name); + + while (func->params) { + params = func->params; + func->params = params->next; + free(params); + } + + free(func); +} + +/** + * pevent_register_print_function - register a helper function + * @pevent: the handle to the pevent + * @func: the function to process the helper function + * @name: the name of the helper function + * @parameters: A list of enum pevent_func_arg_type + * + * Some events may have helper functions in the print format arguments. + * This allows a plugin to dynmically create a way to process one + * of these functions. + * + * The @parameters is a variable list of pevent_func_arg_type enums that + * must end with PEVENT_FUNC_ARG_VOID. + */ +int pevent_register_print_function(struct pevent *pevent, + pevent_func_handler func, + enum pevent_func_arg_type ret_type, + char *name, ...) +{ + struct pevent_function_handler *func_handle; + struct pevent_func_params **next_param; + struct pevent_func_params *param; + enum pevent_func_arg_type type; + va_list ap; + + func_handle = find_func_handler(pevent, name); + if (func_handle) { + /* + * This is most like caused by the users own + * plugins updating the function. This overrides the + * system defaults. + */ + pr_stat("override of function helper '%s'", name); + remove_func_handler(pevent, name); + } + + func_handle = malloc_or_die(sizeof(*func_handle)); + memset(func_handle, 0, sizeof(*func_handle)); + + func_handle->ret_type = ret_type; + func_handle->name = strdup(name); + func_handle->func = func; + if (!func_handle->name) + die("Failed to allocate function name"); + + next_param = &(func_handle->params); + va_start(ap, name); + for (;;) { + type = va_arg(ap, enum pevent_func_arg_type); + if (type == PEVENT_FUNC_ARG_VOID) + break; + + if (type < 0 || type >= PEVENT_FUNC_ARG_MAX_TYPES) { + warning("Invalid argument type %d", type); + goto out_free; + } + + param = malloc_or_die(sizeof(*param)); + param->type = type; + param->next = NULL; + + *next_param = param; + next_param = &(param->next); + + func_handle->nr_args++; + } + va_end(ap); + + func_handle->next = pevent->func_handlers; + pevent->func_handlers = func_handle; + + return 0; + out_free: + va_end(ap); + free_func_handle(func_handle); + return -1; +} + +/** + * pevent_register_event_handle - register a way to parse an event + * @pevent: the handle to the pevent + * @id: the id of the event to register + * @sys_name: the system name the event belongs to + * @event_name: the name of the event + * @func: the function to call to parse the event information + * + * This function allows a developer to override the parsing of + * a given event. If for some reason the default print format + * is not sufficient, this function will register a function + * for an event to be used to parse the data instead. + * + * If @id is >= 0, then it is used to find the event. + * else @sys_name and @event_name are used. + */ +int pevent_register_event_handler(struct pevent *pevent, + int id, char *sys_name, char *event_name, + pevent_event_handler_func func, + void *context) +{ + struct event_format *event; + struct event_handler *handle; + + if (id >= 0) { + /* search by id */ + event = pevent_find_event(pevent, id); + if (!event) + goto not_found; + if (event_name && (strcmp(event_name, event->name) != 0)) + goto not_found; + if (sys_name && (strcmp(sys_name, event->system) != 0)) + goto not_found; + } else { + event = pevent_find_event_by_name(pevent, sys_name, event_name); + if (!event) + goto not_found; + } + + pr_stat("overriding event (%d) %s:%s with new print handler", + event->id, event->system, event->name); + + event->handler = func; + event->context = context; + return 0; + + not_found: + /* Save for later use. */ + handle = malloc_or_die(sizeof(*handle)); + memset(handle, 0, sizeof(handle)); + handle->id = id; + if (event_name) + handle->event_name = strdup(event_name); + if (sys_name) + handle->sys_name = strdup(sys_name); + + handle->func = func; + handle->next = pevent->handlers; + pevent->handlers = handle; + handle->context = context; + + return -1; +} + +/** + * pevent_alloc - create a pevent handle + */ +struct pevent *pevent_alloc(void) +{ + struct pevent *pevent; + + pevent = malloc(sizeof(*pevent)); + if (!pevent) + return NULL; + memset(pevent, 0, sizeof(*pevent)); + pevent->ref_count = 1; + + return pevent; +} + +void pevent_ref(struct pevent *pevent) +{ + pevent->ref_count++; +} + +static void free_format_fields(struct format_field *field) +{ + struct format_field *next; + + while (field) { + next = field->next; + free(field->type); + free(field->name); + free(field); + field = next; + } +} + +static void free_formats(struct format *format) +{ + free_format_fields(format->common_fields); + free_format_fields(format->fields); +} + +static void free_event(struct event_format *event) +{ + free(event->name); + free(event->system); + + free_formats(&event->format); + + free(event->print_fmt.format); + free_args(event->print_fmt.args); + + free(event); +} + +/** + * pevent_free - free a pevent handle + * @pevent: the pevent handle to free + */ +void pevent_free(struct pevent *pevent) +{ + struct cmdline_list *cmdlist = pevent->cmdlist, *cmdnext; + struct func_list *funclist = pevent->funclist, *funcnext; + struct printk_list *printklist = pevent->printklist, *printknext; + struct pevent_function_handler *func_handler; + struct event_handler *handle; + int i; + + pevent->ref_count--; + if (pevent->ref_count) + return; + + if (pevent->cmdlines) { + for (i = 0; i < pevent->cmdline_count; i++) + free(pevent->cmdlines[i].comm); + free(pevent->cmdlines); + } + + while (cmdlist) { + cmdnext = cmdlist->next; + free(cmdlist->comm); + free(cmdlist); + cmdlist = cmdnext; + } + + if (pevent->func_map) { + for (i = 0; i < pevent->func_count; i++) { + free(pevent->func_map[i].func); + free(pevent->func_map[i].mod); + } + free(pevent->func_map); + } + + while (funclist) { + funcnext = funclist->next; + free(funclist->func); + free(funclist->mod); + free(funclist); + funclist = funcnext; + } + + while (pevent->func_handlers) { + func_handler = pevent->func_handlers; + pevent->func_handlers = func_handler->next; + free_func_handle(func_handler); + } + + if (pevent->printk_map) { + for (i = 0; i < pevent->printk_count; i++) + free(pevent->printk_map[i].printk); + free(pevent->printk_map); + } + + while (printklist) { + printknext = printklist->next; + free(printklist->printk); + free(printklist); + printklist = printknext; + } + + for (i = 0; i < pevent->nr_events; i++) + free_event(pevent->events[i]); + + while (pevent->handlers) { + handle = pevent->handlers; + pevent->handlers = handle->next; + free_handler(handle); + } + + free(pevent->events); + free(pevent->sort_events); + + free(pevent); +} + +void pevent_unref(struct pevent *pevent) +{ + pevent_free(pevent); +} diff --git a/event-parse.h b/event-parse.h new file mode 100644 index 0000000..c32d715 --- /dev/null +++ b/event-parse.h @@ -0,0 +1,806 @@ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#ifndef _PARSE_EVENTS_H +#define _PARSE_EVENTS_H + +#include +#include + +#ifndef __unused +#define __unused __attribute__ ((unused)) +#endif + +/* ----------------------- trace_seq ----------------------- */ + + +#ifndef TRACE_SEQ_BUF_SIZE +#define TRACE_SEQ_BUF_SIZE 4096 +#endif + +#ifndef DEBUG_RECORD +#define DEBUG_RECORD 0 +#endif + +struct record { + unsigned long long ts; + unsigned long long offset; + long long missed_events; /* buffer dropped events before */ + int record_size; /* size of binary record */ + int size; /* size of data */ + void *data; + int cpu; + int ref_count; + int locked; /* Do not free, even if ref_count is zero */ + void *private; +#if DEBUG_RECORD + struct record *prev; + struct record *next; + long alloc_addr; +#endif +}; + +/* + * Trace sequences are used to allow a function to call several other functions + * to create a string of data to use (up to a max of PAGE_SIZE). + */ + +struct trace_seq { + char *buffer; + unsigned int buffer_size; + unsigned int len; + unsigned int readpos; +}; + +void trace_seq_init(struct trace_seq *s); +void trace_seq_destroy(struct trace_seq *s); + +extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); +extern int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) + __attribute__ ((format (printf, 2, 0))); + +extern int trace_seq_puts(struct trace_seq *s, const char *str); +extern int trace_seq_putc(struct trace_seq *s, unsigned char c); + +extern void trace_seq_terminate(struct trace_seq *s); + +extern int trace_seq_do_printf(struct trace_seq *s); + + +/* ----------------------- pevent ----------------------- */ + +struct pevent; +struct event_format; + +typedef int (*pevent_event_handler_func)(struct trace_seq *s, + struct record *record, + struct event_format *event, + void *context); + +typedef int (*pevent_plugin_load_func)(struct pevent *pevent); +typedef int (*pevent_plugin_unload_func)(void); + +struct plugin_option { + struct plugin_option *next; + void *handle; + char *file; + char *name; + char *plugin_alias; + char *description; + char *value; + void *private; + int set; +}; + +/* + * Plugin hooks that can be called: + * + * PEVENT_PLUGIN_LOADER: (required) + * The function name to initialized the plugin. + * + * int PEVENT_PLUGIN_LOADER(struct pevent *pevent) + * + * PEVENT_PLUGIN_UNLOADER: (optional) + * The function called just before unloading + * + * int PEVENT_PLUGIN_UNLOADER(void) + * + * PEVENT_PLUGIN_OPTIONS: (optional) + * Plugin options that can be set before loading + * + * struct plugin_option PEVENT_PLUGIN_OPTIONS[] = { + * { + * .name = "option-name", + * .plugin_alias = "overide-file-name", (optional) + * .description = "description of option to show users", + * }, + * { + * .name = NULL, + * }, + * }; + * + * Array must end with .name = NULL; + * + * + * .plugin_alias is used to give a shorter name to access + * the vairable. Useful if a plugin handles more than one event. + * + * PEVENT_PLUGIN_ALIAS: (optional) + * The name to use for finding options (uses filename if not defined) + */ +#define PEVENT_PLUGIN_LOADER pevent_plugin_loader +#define PEVENT_PLUGIN_UNLOADER pevent_plugin_unloader +#define PEVENT_PLUGIN_OPTIONS pevent_plugin_options +#define PEVENT_PLUGIN_ALIAS pevent_plugin_alias +#define _MAKE_STR(x) #x +#define MAKE_STR(x) _MAKE_STR(x) +#define PEVENT_PLUGIN_LOADER_NAME MAKE_STR(PEVENT_PLUGIN_LOADER) +#define PEVENT_PLUGIN_UNLOADER_NAME MAKE_STR(PEVENT_PLUGIN_UNLOADER) +#define PEVENT_PLUGIN_OPTIONS_NAME MAKE_STR(PEVENT_PLUGIN_OPTIONS) +#define PEVENT_PLUGIN_ALIAS_NAME MAKE_STR(PEVENT_PLUGIN_ALIAS) + +#define NSECS_PER_SEC 1000000000ULL +#define NSECS_PER_USEC 1000ULL + +enum format_flags { + FIELD_IS_ARRAY = 1, + FIELD_IS_POINTER = 2, + FIELD_IS_SIGNED = 4, + FIELD_IS_STRING = 8, + FIELD_IS_DYNAMIC = 16, + FIELD_IS_LONG = 32, +}; + +struct format_field { + struct format_field *next; + struct event_format *event; + char *type; + char *name; + int offset; + int size; + unsigned int arraylen; + unsigned int elementsize; + unsigned long flags; +}; + +struct format { + int nr_common; + int nr_fields; + struct format_field *common_fields; + struct format_field *fields; +}; + +struct print_arg_atom { + char *atom; +}; + +struct print_arg_string { + char *string; + int offset; +}; + +struct print_arg_field { + char *name; + struct format_field *field; +}; + +struct print_flag_sym { + struct print_flag_sym *next; + char *value; + char *str; +}; + +struct print_arg_typecast { + char *type; + struct print_arg *item; +}; + +struct print_arg_flags { + struct print_arg *field; + char *delim; + struct print_flag_sym *flags; +}; + +struct print_arg_symbol { + struct print_arg *field; + struct print_flag_sym *symbols; +}; + +struct print_arg_dynarray { + struct format_field *field; + struct print_arg *index; +}; + +struct print_arg; + +struct print_arg_op { + char *op; + int prio; + struct print_arg *left; + struct print_arg *right; +}; + +struct pevent_function_handler; + +struct print_arg_func { + struct pevent_function_handler *func; + struct print_arg *args; +}; + +enum print_arg_type { + PRINT_NULL, + PRINT_ATOM, + PRINT_FIELD, + PRINT_FLAGS, + PRINT_SYMBOL, + PRINT_TYPE, + PRINT_STRING, + PRINT_BSTRING, + PRINT_DYNAMIC_ARRAY, + PRINT_OP, + PRINT_FUNC, +}; + +struct print_arg { + struct print_arg *next; + enum print_arg_type type; + union { + struct print_arg_atom atom; + struct print_arg_field field; + struct print_arg_typecast typecast; + struct print_arg_flags flags; + struct print_arg_symbol symbol; + struct print_arg_func func; + struct print_arg_string string; + struct print_arg_op op; + struct print_arg_dynarray dynarray; + }; +}; + +struct print_fmt { + char *format; + struct print_arg *args; +}; + +struct event_format { + struct pevent *pevent; + char *name; + int id; + int flags; + struct format format; + struct print_fmt print_fmt; + char *system; + pevent_event_handler_func handler; + void *context; +}; + +enum { + EVENT_FL_ISFTRACE = 0x01, + EVENT_FL_ISPRINT = 0x02, + EVENT_FL_ISBPRINT = 0x04, + EVENT_FL_ISFUNCENT = 0x10, + EVENT_FL_ISFUNCRET = 0x20, + + EVENT_FL_FAILED = 0x80000000 +}; + +enum event_sort_type { + EVENT_SORT_ID, + EVENT_SORT_NAME, + EVENT_SORT_SYSTEM, +}; + +enum event_type { + EVENT_ERROR, + EVENT_NONE, + EVENT_SPACE, + EVENT_NEWLINE, + EVENT_OP, + EVENT_DELIM, + EVENT_ITEM, + EVENT_DQUOTE, + EVENT_SQUOTE, +}; + +typedef unsigned long long (*pevent_func_handler)(struct trace_seq *s, + unsigned long long *args); + +enum pevent_func_arg_type { + PEVENT_FUNC_ARG_VOID, + PEVENT_FUNC_ARG_INT, + PEVENT_FUNC_ARG_LONG, + PEVENT_FUNC_ARG_STRING, + PEVENT_FUNC_ARG_PTR, + PEVENT_FUNC_ARG_MAX_TYPES +}; + +struct cmdline; +struct cmdline_list; +struct func_map; +struct func_list; +struct event_handler; + +struct pevent { + int ref_count; + + 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; + int header_page_overwrite; + + int file_bigendian; + int host_bigendian; + + int latency_format; + + int old_format; + + int cpus; + int long_size; + + struct cmdline *cmdlines; + struct cmdline_list *cmdlist; + int cmdline_count; + + struct func_map *func_map; + struct func_list *funclist; + unsigned int func_count; + + struct printk_map *printk_map; + struct printk_list *printklist; + unsigned int printk_count; + + struct event_format **events; + int nr_events; + struct event_format **sort_events; + enum event_sort_type last_type; + + int type_offset; + int type_size; + + int pid_offset; + int pid_size; + + int pc_offset; + int pc_size; + + int flags_offset; + int flags_size; + + int ld_offset; + int ld_size; + + int print_raw; + + int test_filters; + + struct format_field *bprint_ip_field; + struct format_field *bprint_fmt_field; + struct format_field *bprint_buf_field; + + struct event_handler *handlers; + struct pevent_function_handler *func_handlers; + + /* cache */ + struct event_format *last_event; +}; + +/* Can be overridden */ +void die(const char *fmt, ...); +void *malloc_or_die(unsigned int size); +void warning(const char *fmt, ...); +void pr_stat(const char *fmt, ...); +void vpr_stat(const char *fmt, va_list ap); + +/* Always available */ +void __die(const char *fmt, ...); +void __warning(const char *fmt, ...); +void __pr_stat(const char *fmt, ...); + +void __vdie(const char *fmt, ...); +void __vwarning(const char *fmt, ...); +void __vpr_stat(const char *fmt, ...); + +static inline unsigned short +__data2host2(struct pevent *pevent, unsigned short data) +{ + unsigned short swap; + + if (pevent->host_bigendian == pevent->file_bigendian) + return data; + + swap = ((data & 0xffULL) << 8) | + ((data & (0xffULL << 8)) >> 8); + + return swap; +} + +static inline unsigned int +__data2host4(struct pevent *pevent, unsigned int data) +{ + unsigned int swap; + + if (pevent->host_bigendian == pevent->file_bigendian) + return data; + + swap = ((data & 0xffULL) << 24) | + ((data & (0xffULL << 8)) << 8) | + ((data & (0xffULL << 16)) >> 8) | + ((data & (0xffULL << 24)) >> 24); + + return swap; +} + +static inline unsigned long long +__data2host8(struct pevent *pevent, unsigned long long data) +{ + unsigned long long swap; + + if (pevent->host_bigendian == pevent->file_bigendian) + return data; + + swap = ((data & 0xffULL) << 56) | + ((data & (0xffULL << 8)) << 40) | + ((data & (0xffULL << 16)) << 24) | + ((data & (0xffULL << 24)) << 8) | + ((data & (0xffULL << 32)) >> 8) | + ((data & (0xffULL << 40)) >> 24) | + ((data & (0xffULL << 48)) >> 40) | + ((data & (0xffULL << 56)) >> 56); + + return swap; +} + +#define data2host2(pevent, ptr) __data2host2(pevent, *(unsigned short *)(ptr)) +#define data2host4(pevent, ptr) __data2host4(pevent, *(unsigned int *)(ptr)) +#define data2host8(pevent, ptr) \ +({ \ + unsigned long long __val; \ + \ + memcpy(&__val, (ptr), sizeof(unsigned long long)); \ + __data2host8(pevent, __val); \ +}) + +/* taken from kernel/trace/trace.h */ +enum trace_flag_type { + TRACE_FLAG_IRQS_OFF = 0x01, + TRACE_FLAG_IRQS_NOSUPPORT = 0x02, + TRACE_FLAG_NEED_RESCHED = 0x04, + TRACE_FLAG_HARDIRQ = 0x08, + TRACE_FLAG_SOFTIRQ = 0x10, +}; + +int pevent_register_comm(struct pevent *pevent, const char *comm, int pid); +int pevent_register_function(struct pevent *pevent, char *name, + unsigned long long addr, char *mod); +int pevent_register_print_string(struct pevent *pevent, char *fmt, + unsigned long long addr); +int pevent_pid_is_registered(struct pevent *pevent, int pid); + +void pevent_print_event(struct pevent *pevent, struct trace_seq *s, + struct record *record); + +int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size, + int long_size); + +int pevent_parse_event(struct pevent *pevent, const char *buf, + unsigned long size, const char *sys); + +void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, + const char *name, struct record *record, + int *len, int err); + +int pevent_get_field_val(struct trace_seq *s, struct event_format *event, + const char *name, struct record *record, + unsigned long long *val, int err); +int pevent_get_common_field_val(struct trace_seq *s, struct event_format *event, + const char *name, struct record *record, + unsigned long long *val, int err); +int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event, + const char *name, struct record *record, + unsigned long long *val, int err); + +int pevent_print_num_field(struct trace_seq *s, const char *fmt, + struct event_format *event, const char *name, + struct record *record, int err); + +int pevent_register_event_handler(struct pevent *pevent, int id, char *sys_name, char *event_name, + pevent_event_handler_func func, void *context); +int pevent_register_print_function(struct pevent *pevent, + pevent_func_handler func, + enum pevent_func_arg_type ret_type, + char *name, ...); + +struct format_field *pevent_find_common_field(struct event_format *event, const char *name); +struct format_field *pevent_find_field(struct event_format *event, const char *name); +struct format_field *pevent_find_any_field(struct event_format *event, const char *name); + +const char *pevent_find_function(struct pevent *pevent, unsigned long long addr); +unsigned long long +pevent_find_function_address(struct pevent *pevent, unsigned long long addr); +unsigned long long pevent_read_number(struct pevent *pevent, const void *ptr, int size); +int pevent_read_number_field(struct format_field *field, const void *data, + unsigned long long *value); + +struct event_format *pevent_find_event(struct pevent *pevent, int id); + +struct event_format * +pevent_find_event_by_name(struct pevent *pevent, const char *sys, const char *name); + +void pevent_data_lat_fmt(struct pevent *pevent, + struct trace_seq *s, struct record *record); +int pevent_data_type(struct pevent *pevent, struct record *rec); +struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type); +int pevent_data_pid(struct pevent *pevent, struct record *rec); +const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid); +void pevent_event_info(struct trace_seq *s, struct event_format *event, + struct record *record); + +struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type); +struct format_field **pevent_event_common_fields(struct event_format *event); +struct format_field **pevent_event_fields(struct event_format *event); + +static inline int pevent_get_cpus(struct pevent *pevent) +{ + return pevent->cpus; +} + +static inline void pevent_set_cpus(struct pevent *pevent, int cpus) +{ + pevent->cpus = cpus; +} + +static inline int pevent_get_long_size(struct pevent *pevent) +{ + return pevent->long_size; +} + +static inline void pevent_set_long_size(struct pevent *pevent, int long_size) +{ + pevent->long_size = long_size; +} + +static inline int pevent_is_file_bigendian(struct pevent *pevent) +{ + return pevent->file_bigendian; +} + +static inline void pevent_set_file_bigendian(struct pevent *pevent, int endian) +{ + pevent->file_bigendian = endian; +} + +static inline int pevent_is_host_bigendian(struct pevent *pevent) +{ + return pevent->host_bigendian; +} + +static inline void pevent_set_host_bigendian(struct pevent *pevent, int endian) +{ + pevent->host_bigendian = endian; +} + +static inline int pevent_is_latency_format(struct pevent *pevent) +{ + return pevent->latency_format; +} + +static inline void pevent_set_latency_format(struct pevent *pevent, int lat) +{ + pevent->latency_format = lat; +} + +struct pevent *pevent_alloc(void); +void pevent_free(struct pevent *pevent); +void pevent_ref(struct pevent *pevent); +void pevent_unref(struct pevent *pevent); + +/* access to the internal parser */ +void pevent_buffer_init(const char *buf, unsigned long long size); +enum event_type pevent_read_token(char **tok); +void pevent_free_token(char *token); +int pevent_peek_char(void); +const char *pevent_get_input_buf(void); +unsigned long long pevent_get_input_buf_ptr(void); + +/* for debugging */ +void pevent_print_funcs(struct pevent *pevent); +void pevent_print_printk(struct pevent *pevent); + +/* ----------------------- filtering ----------------------- */ + +enum filter_boolean_type { + FILTER_FALSE, + FILTER_TRUE, +}; + +enum filter_op_type { + FILTER_OP_AND = 1, + FILTER_OP_OR, + FILTER_OP_NOT, +}; + +enum filter_cmp_type { + FILTER_CMP_NONE, + FILTER_CMP_EQ, + FILTER_CMP_NE, + FILTER_CMP_GT, + FILTER_CMP_LT, + FILTER_CMP_GE, + FILTER_CMP_LE, + FILTER_CMP_MATCH, + FILTER_CMP_NOT_MATCH, + FILTER_CMP_REGEX, + FILTER_CMP_NOT_REGEX, +}; + +enum filter_exp_type { + FILTER_EXP_NONE, + FILTER_EXP_ADD, + FILTER_EXP_SUB, + FILTER_EXP_MUL, + FILTER_EXP_DIV, + FILTER_EXP_MOD, + FILTER_EXP_RSHIFT, + FILTER_EXP_LSHIFT, + FILTER_EXP_AND, + FILTER_EXP_OR, + FILTER_EXP_XOR, + FILTER_EXP_NOT, +}; + +enum filter_arg_type { + FILTER_ARG_NONE, + FILTER_ARG_BOOLEAN, + FILTER_ARG_VALUE, + FILTER_ARG_FIELD, + FILTER_ARG_EXP, + FILTER_ARG_OP, + FILTER_ARG_NUM, + FILTER_ARG_STR, +}; + +enum filter_value_type { + FILTER_NUMBER, + FILTER_STRING, + FILTER_CHAR +}; + +struct fliter_arg; + +struct filter_arg_boolean { + enum filter_boolean_type value; +}; + +struct filter_arg_field { + struct format_field *field; +}; + +struct filter_arg_value { + enum filter_value_type type; + union { + char *str; + unsigned long long val; + }; +}; + +struct filter_arg_op { + enum filter_op_type type; + struct filter_arg *left; + struct filter_arg *right; +}; + +struct filter_arg_exp { + enum filter_exp_type type; + struct filter_arg *left; + struct filter_arg *right; +}; + +struct filter_arg_num { + enum filter_cmp_type type; + struct filter_arg *left; + struct filter_arg *right; +}; + +struct filter_arg_str { + enum filter_cmp_type type; + struct format_field *field; + char *val; + char *buffer; + regex_t reg; +}; + +struct filter_arg { + enum filter_arg_type type; + union { + struct filter_arg_boolean bool; + struct filter_arg_field field; + struct filter_arg_value value; + struct filter_arg_op op; + struct filter_arg_exp exp; + struct filter_arg_num num; + struct filter_arg_str str; + }; +}; + +struct filter_type { + int event_id; + struct event_format *event; + struct filter_arg *filter; +}; + +struct event_filter { + struct pevent *pevent; + int filters; + struct filter_type *event_filters; +}; + +struct event_filter *pevent_filter_alloc(struct pevent *pevent); + +#define FILTER_NONE -2 +#define FILTER_NOEXIST -1 +#define FILTER_MISS 0 +#define FILTER_MATCH 1 + +enum filter_trivial_type { + FILTER_TRIVIAL_FALSE, + FILTER_TRIVIAL_TRUE, + FILTER_TRIVIAL_BOTH, +}; + +int pevent_filter_add_filter_str(struct event_filter *filter, + const char *filter_str, + char **error_str); + + +int pevent_filter_match(struct event_filter *filter, + struct record *record); + +int pevent_event_filtered(struct event_filter *filter, + int event_id); + +void pevent_filter_reset(struct event_filter *filter); + +void pevent_filter_clear_trivial(struct event_filter *filter, + enum filter_trivial_type type); + +void pevent_filter_free(struct event_filter *filter); + +char *pevent_filter_make_string(struct event_filter *filter, int event_id); + +int pevent_filter_remove_event(struct event_filter *filter, + int event_id); + +int pevent_filter_event_has_trivial(struct event_filter *filter, + int event_id, + enum filter_trivial_type type); + +int pevent_filter_copy(struct event_filter *dest, struct event_filter *source); + +int pevent_update_trivial(struct event_filter *dest, struct event_filter *source, + enum filter_trivial_type type); + +int pevent_filter_compare(struct event_filter *filter1, struct event_filter *filter2); + +#endif /* _PARSE_EVENTS_H */ diff --git a/event-utils.h b/event-utils.h new file mode 100644 index 0000000..a8fb48f --- /dev/null +++ b/event-utils.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#ifndef __UTIL_H +#define __UTIL_H + +#include + +static inline char *strim(char *string) +{ + char *ret; + + if (!string) + return NULL; + while (*string) { + if (!isspace(*string)) + break; + string++; + } + ret = string; + + string = ret + strlen(ret) - 1; + while (string > ret) { + if (!isspace(*string)) + break; + string--; + } + string[1] = 0; + + return ret; +} + +static inline int has_text(const char *text) +{ + if (!text) + return 0; + + while (*text) { + if (!isspace(*text)) + return 1; + text++; + } + + return 0; +} + +#endif diff --git a/parse-filter.c b/parse-filter.c new file mode 100644 index 0000000..1df9966 --- /dev/null +++ b/parse-filter.c @@ -0,0 +1,2262 @@ +/* + * Copyright (C) 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include +#include +#include + +#include "event-parse.h" +#include "event-utils.h" + +#define COMM "COMM" + +static struct format_field comm = { + .name = "COMM", +}; + +struct event_list { + struct event_list *next; + struct event_format *event; +}; + +#define MAX_ERR_STR_SIZE 256 + +static void show_error(char **error_str, const char *fmt, ...) +{ + unsigned long long index; + const char *input; + char *error; + va_list ap; + int len; + int i; + + if (!error_str) + return; + + input = pevent_get_input_buf(); + index = pevent_get_input_buf_ptr(); + len = input ? strlen(input) : 0; + + error = malloc_or_die(MAX_ERR_STR_SIZE + (len*2) + 3); + + if (len) { + strcpy(error, input); + error[len] = '\n'; + for (i = 1; i < len && i < index; i++) + error[len+i] = ' '; + error[len + i] = '^'; + error[len + i + 1] = '\n'; + len += i+2; + } + + va_start(ap, fmt); + vsnprintf(error + len, MAX_ERR_STR_SIZE, fmt, ap); + va_end(ap); + + *error_str = error; +} + +static void free_token(char *token) +{ + pevent_free_token(token); +} + +static enum event_type read_token(char **tok) +{ + enum event_type type; + char *token = NULL; + + do { + free_token(token); + type = pevent_read_token(&token); + } while (type == EVENT_NEWLINE || type == EVENT_SPACE); + + /* If token is = or ! check to see if the next char is ~ */ + if (token && + (strcmp(token, "=") == 0 || strcmp(token, "!") == 0) && + pevent_peek_char() == '~') { + /* append it */ + *tok = malloc(3); + sprintf(*tok, "%c%c", *token, '~'); + free_token(token); + /* Now remove the '~' from the buffer */ + pevent_read_token(&token); + free_token(token); + } else + *tok = token; + + return type; +} + +static int filter_cmp(const void *a, const void *b) +{ + const struct filter_type *ea = a; + const struct filter_type *eb = b; + + if (ea->event_id < eb->event_id) + return -1; + + if (ea->event_id > eb->event_id) + return 1; + + return 0; +} + +static struct filter_type * +find_filter_type(struct event_filter *filter, int id) +{ + struct filter_type *filter_type; + struct filter_type key; + + key.event_id = id; + + filter_type = bsearch(&key, filter->event_filters, + filter->filters, + sizeof(*filter->event_filters), + filter_cmp); + + return filter_type; +} + +static struct filter_type * +add_filter_type(struct event_filter *filter, int id) +{ + struct filter_type *filter_type; + int i; + + filter_type = find_filter_type(filter, id); + if (filter_type) + return filter_type; + + if (!filter->filters) + filter->event_filters = + malloc_or_die(sizeof(*filter->event_filters)); + else { + filter->event_filters = + realloc(filter->event_filters, + sizeof(*filter->event_filters) * + (filter->filters + 1)); + if (!filter->event_filters) + die("Could not allocate filter"); + } + + for (i = 0; i < filter->filters; i++) { + if (filter->event_filters[i].event_id > id) + break; + } + + if (i < filter->filters) + memmove(&filter->event_filters[i+1], + &filter->event_filters[i], + sizeof(*filter->event_filters) * + (filter->filters - i)); + + filter_type = &filter->event_filters[i]; + filter_type->event_id = id; + filter_type->event = pevent_find_event(filter->pevent, id); + filter_type->filter = NULL; + + filter->filters++; + + return filter_type; +} + +/** + * pevent_filter_alloc - create a new event filter + * @pevent: The pevent that this filter is associated with + */ +struct event_filter *pevent_filter_alloc(struct pevent *pevent) +{ + struct event_filter *filter; + + filter = malloc_or_die(sizeof(*filter)); + memset(filter, 0, sizeof(*filter)); + filter->pevent = pevent; + pevent_ref(pevent); + + return filter; +} + +static struct filter_arg *allocate_arg(void) +{ + struct filter_arg *arg; + + arg = malloc_or_die(sizeof(*arg)); + memset(arg, 0, sizeof(*arg)); + + return arg; +} + +static void free_arg(struct filter_arg *arg) +{ + if (!arg) + return; + + switch (arg->type) { + case FILTER_ARG_NONE: + case FILTER_ARG_BOOLEAN: + case FILTER_ARG_NUM: + break; + + case FILTER_ARG_STR: + free(arg->str.val); + regfree(&arg->str.reg); + free(arg->str.buffer); + break; + + case FILTER_ARG_OP: + free_arg(arg->op.left); + free_arg(arg->op.right); + default: + break; + } + + free(arg); +} + +static void add_event(struct event_list **events, + struct event_format *event) +{ + struct event_list *list; + + list = malloc_or_die(sizeof(*list)); + list->next = *events; + *events = list; + list->event = event; +} + +static int event_match(struct event_format *event, + regex_t *sreg, regex_t *ereg) +{ + if (sreg) { + return !regexec(sreg, event->system, 0, NULL, 0) && + !regexec(ereg, event->name, 0, NULL, 0); + } + + return !regexec(ereg, event->system, 0, NULL, 0) || + !regexec(ereg, event->name, 0, NULL, 0); +} + +static int +find_event(struct pevent *pevent, struct event_list **events, + char *sys_name, char *event_name) +{ + struct event_format *event; + regex_t ereg; + regex_t sreg; + int match = 0; + char *reg; + int ret; + int i; + + if (!event_name) { + /* if no name is given, then swap sys and name */ + event_name = sys_name; + sys_name = NULL; + } + + reg = malloc_or_die(strlen(event_name) + 3); + sprintf(reg, "^%s$", event_name); + + ret = regcomp(&ereg, reg, REG_ICASE|REG_NOSUB); + free(reg); + + if (ret) + return -1; + + if (sys_name) { + reg = malloc_or_die(strlen(sys_name) + 3); + sprintf(reg, "^%s$", sys_name); + ret = regcomp(&sreg, reg, REG_ICASE|REG_NOSUB); + free(reg); + if (ret) { + regfree(&ereg); + return -1; + } + } + + for (i = 0; i < pevent->nr_events; i++) { + event = pevent->events[i]; + if (event_match(event, sys_name ? &sreg : NULL, &ereg)) { + match = 1; + add_event(events, event); + } + } + + regfree(&ereg); + if (sys_name) + regfree(&sreg); + + if (!match) + return -1; + + return 0; +} + +static void free_events(struct event_list *events) +{ + struct event_list *event; + + while (events) { + event = events; + events = events->next; + free(event); + } +} + +static struct filter_arg * +create_arg_item(struct event_format *event, + const char *token, enum filter_arg_type type, + char **error_str) +{ + struct format_field *field; + struct filter_arg *arg; + + arg = allocate_arg(); + + switch (type) { + + case EVENT_SQUOTE: + case EVENT_DQUOTE: + arg->type = FILTER_ARG_VALUE; + arg->value.type = + type == EVENT_DQUOTE ? FILTER_STRING : FILTER_CHAR; + arg->value.str = strdup(token); + if (!arg->value.str) + die("malloc string"); + break; + case EVENT_ITEM: + /* if it is a number, then convert it */ + if (isdigit(token[0])) { + arg->type = FILTER_ARG_VALUE; + arg->value.type = FILTER_NUMBER; + arg->value.val = strtoull(token, NULL, 0); + break; + } + /* Consider this a field */ + field = pevent_find_any_field(event, token); + if (!field) { + if (strcmp(token, COMM) != 0) { + /* not a field, Make it false */ + arg->type = FILTER_ARG_BOOLEAN; + arg->bool.value = FILTER_FALSE; + break; + } + /* If token is 'COMM' then it is special */ + field = &comm; + } + arg->type = FILTER_ARG_FIELD; + arg->field.field = field; + break; + default: + free_arg(arg); + show_error(error_str, "expected a value but found %s", + token); + return NULL; + } + return arg; +} + +static struct filter_arg * +create_arg_op(enum filter_op_type btype) +{ + struct filter_arg *arg; + + arg = allocate_arg(); + arg->type = FILTER_ARG_OP; + arg->op.type = btype; + + return arg; +} + +static struct filter_arg * +create_arg_exp(enum filter_exp_type etype) +{ + struct filter_arg *arg; + + arg = allocate_arg(); + arg->type = FILTER_ARG_EXP; + arg->op.type = etype; + + return arg; +} + +static struct filter_arg * +create_arg_cmp(enum filter_exp_type etype) +{ + struct filter_arg *arg; + + arg = allocate_arg(); + /* Use NUM and change if necessary */ + arg->type = FILTER_ARG_NUM; + arg->op.type = etype; + + return arg; +} + +static int add_right(struct filter_arg *op, struct filter_arg *arg, + char **error_str) +{ + struct filter_arg *left; + char *str; + int op_type; + int ret; + + switch (op->type) { + case FILTER_ARG_EXP: + if (op->exp.right) + goto out_fail; + op->exp.right = arg; + break; + + case FILTER_ARG_OP: + if (op->op.right) + goto out_fail; + op->op.right = arg; + break; + + case FILTER_ARG_NUM: + if (op->op.right) + goto out_fail; + /* + * The arg must be num, str, or field + */ + switch (arg->type) { + case FILTER_ARG_VALUE: + case FILTER_ARG_FIELD: + break; + default: + show_error(error_str, + "Illegal rvalue"); + return -1; + } + + /* + * Depending on the type, we may need to + * convert this to a string or regex. + */ + switch (arg->value.type) { + case FILTER_CHAR: + /* + * A char should be converted to number if + * the string is 1 byte, and the compare + * is not a REGEX. + */ + if (strlen(arg->value.str) == 1 && + op->num.type != FILTER_CMP_REGEX && + op->num.type != FILTER_CMP_NOT_REGEX) { + arg->value.type = FILTER_NUMBER; + goto do_int; + } + /* fall through */ + case FILTER_STRING: + + /* convert op to a string arg */ + op_type = op->num.type; + left = op->num.left; + str = arg->value.str; + + /* reset the op for the new field */ + memset(op, 0, sizeof(*op)); + + /* + * If left arg was a field not found then + * NULL the entire op. + */ + if (left->type == FILTER_ARG_BOOLEAN) { + free_arg(left); + free_arg(arg); + op->type = FILTER_ARG_BOOLEAN; + op->bool.value = FILTER_FALSE; + break; + } + + /* Left arg must be a field */ + if (left->type != FILTER_ARG_FIELD) { + show_error(error_str, + "Illegal lvalue for string comparison"); + return -1; + } + + /* Make sure this is a valid string compare */ + switch (op_type) { + case FILTER_CMP_EQ: + op_type = FILTER_CMP_MATCH; + break; + case FILTER_CMP_NE: + op_type = FILTER_CMP_NOT_MATCH; + break; + + case FILTER_CMP_REGEX: + case FILTER_CMP_NOT_REGEX: + ret = regcomp(&op->str.reg, str, REG_ICASE|REG_NOSUB); + if (ret) { + show_error(error_str, + "RegEx '%s' did not compute", + str); + return -1; + } + break; + default: + show_error(error_str, + "Illegal comparison for string"); + return -1; + } + + op->type = FILTER_ARG_STR; + op->str.type = op_type; + op->str.field = left->field.field; + op->str.val = strdup(str); + if (!op->str.val) + die("malloc string"); + /* + * Need a buffer to copy data for tests + */ + op->str.buffer = malloc_or_die(op->str.field->size + 1); + /* Null terminate this buffer */ + op->str.buffer[op->str.field->size] = 0; + + /* We no longer have left or right args */ + free_arg(arg); + free_arg(left); + + break; + + case FILTER_NUMBER: + + do_int: + switch (op->num.type) { + case FILTER_CMP_REGEX: + case FILTER_CMP_NOT_REGEX: + show_error(error_str, + "Op not allowed with integers"); + return -1; + + default: + break; + } + + /* numeric compare */ + op->num.right = arg; + break; + default: + goto out_fail; + } + break; + default: + goto out_fail; + } + + return 0; + + out_fail: + show_error(error_str, + "Syntax error"); + return -1; +} + +static struct filter_arg * +rotate_op_right(struct filter_arg *a, struct filter_arg *b) +{ + struct filter_arg *arg; + + arg = a->op.right; + a->op.right = b; + return arg; +} + +static int add_left(struct filter_arg *op, struct filter_arg *arg) +{ + switch (op->type) { + case FILTER_ARG_EXP: + if (arg->type == FILTER_ARG_OP) + arg = rotate_op_right(arg, op); + op->exp.left = arg; + break; + + case FILTER_ARG_OP: + op->op.left = arg; + break; + case FILTER_ARG_NUM: + if (arg->type == FILTER_ARG_OP) + arg = rotate_op_right(arg, op); + + /* left arg of compares must be a field */ + if (arg->type != FILTER_ARG_FIELD && + arg->type != FILTER_ARG_BOOLEAN) + return -1; + op->num.left = arg; + break; + default: + return -1; + } + return 0; +} + +enum op_type { + OP_NONE, + OP_BOOL, + OP_NOT, + OP_EXP, + OP_CMP, +}; + +static enum op_type process_op(const char *token, + enum filter_op_type *btype, + enum filter_cmp_type *ctype, + enum filter_exp_type *etype) +{ + *btype = FILTER_OP_NOT; + *etype = FILTER_EXP_NONE; + *ctype = FILTER_CMP_NONE; + + if (strcmp(token, "&&") == 0) + *btype = FILTER_OP_AND; + else if (strcmp(token, "||") == 0) + *btype = FILTER_OP_OR; + else if (strcmp(token, "!") == 0) + return OP_NOT; + + if (*btype != FILTER_OP_NOT) + return OP_BOOL; + + /* Check for value expressions */ + if (strcmp(token, "+") == 0) { + *etype = FILTER_EXP_ADD; + } else if (strcmp(token, "-") == 0) { + *etype = FILTER_EXP_SUB; + } else if (strcmp(token, "*") == 0) { + *etype = FILTER_EXP_MUL; + } else if (strcmp(token, "/") == 0) { + *etype = FILTER_EXP_DIV; + } else if (strcmp(token, "%") == 0) { + *etype = FILTER_EXP_MOD; + } else if (strcmp(token, ">>") == 0) { + *etype = FILTER_EXP_RSHIFT; + } else if (strcmp(token, "<<") == 0) { + *etype = FILTER_EXP_LSHIFT; + } else if (strcmp(token, "&") == 0) { + *etype = FILTER_EXP_AND; + } else if (strcmp(token, "|") == 0) { + *etype = FILTER_EXP_OR; + } else if (strcmp(token, "^") == 0) { + *etype = FILTER_EXP_XOR; + } else if (strcmp(token, "~") == 0) + *etype = FILTER_EXP_NOT; + + if (*etype != FILTER_EXP_NONE) + return OP_EXP; + + /* Check for compares */ + if (strcmp(token, "==") == 0) + *ctype = FILTER_CMP_EQ; + else if (strcmp(token, "!=") == 0) + *ctype = FILTER_CMP_NE; + else if (strcmp(token, "<") == 0) + *ctype = FILTER_CMP_LT; + else if (strcmp(token, ">") == 0) + *ctype = FILTER_CMP_GT; + else if (strcmp(token, "<=") == 0) + *ctype = FILTER_CMP_LE; + else if (strcmp(token, ">=") == 0) + *ctype = FILTER_CMP_GE; + else if (strcmp(token, "=~") == 0) + *ctype = FILTER_CMP_REGEX; + else if (strcmp(token, "!~") == 0) + *ctype = FILTER_CMP_NOT_REGEX; + else + return OP_NONE; + + return OP_CMP; +} + +static int check_op_done(struct filter_arg *arg) +{ + switch (arg->type) { + case FILTER_ARG_EXP: + return arg->exp.right != NULL; + + case FILTER_ARG_OP: + return arg->op.right != NULL; + + case FILTER_ARG_NUM: + return arg->num.right != NULL; + + case FILTER_ARG_STR: + /* A string conversion is always done */ + return 1; + + case FILTER_ARG_BOOLEAN: + /* field not found, is ok */ + return 1; + + default: + return 0; + } +} + +enum filter_vals { + FILTER_VAL_NORM, + FILTER_VAL_FALSE, + FILTER_VAL_TRUE, +}; + +void reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child, + struct filter_arg *arg) +{ + struct filter_arg *other_child; + struct filter_arg **ptr; + + if (parent->type != FILTER_ARG_OP && + arg->type != FILTER_ARG_OP) + die("can not reparent other than OP"); + + /* Get the sibling */ + if (old_child->op.right == arg) { + ptr = &old_child->op.right; + other_child = old_child->op.left; + } else if (old_child->op.left == arg) { + ptr = &old_child->op.left; + other_child = old_child->op.right; + } else + die("Error in reparent op, find other child"); + + /* Detach arg from old_child */ + *ptr = NULL; + + /* Check for root */ + if (parent == old_child) { + free_arg(other_child); + *parent = *arg; + /* Free arg without recussion */ + free(arg); + return; + } + + if (parent->op.right == old_child) + ptr = &parent->op.right; + else if (parent->op.left == old_child) + ptr = &parent->op.left; + else + die("Error in reparent op"); + *ptr = arg; + + free_arg(old_child); +} + +enum filter_vals test_arg(struct filter_arg *parent, struct filter_arg *arg) +{ + enum filter_vals lval, rval; + + switch (arg->type) { + + /* bad case */ + case FILTER_ARG_BOOLEAN: + return FILTER_VAL_FALSE + arg->bool.value; + + /* good cases: */ + case FILTER_ARG_STR: + case FILTER_ARG_VALUE: + case FILTER_ARG_FIELD: + return FILTER_VAL_NORM; + + case FILTER_ARG_EXP: + lval = test_arg(arg, arg->exp.left); + if (lval != FILTER_VAL_NORM) + return lval; + rval = test_arg(arg, arg->exp.right); + if (rval != FILTER_VAL_NORM) + return rval; + return FILTER_VAL_NORM; + + case FILTER_ARG_NUM: + lval = test_arg(arg, arg->num.left); + if (lval != FILTER_VAL_NORM) + return lval; + rval = test_arg(arg, arg->num.right); + if (rval != FILTER_VAL_NORM) + return rval; + return FILTER_VAL_NORM; + + case FILTER_ARG_OP: + if (arg->op.type != FILTER_OP_NOT) { + lval = test_arg(arg, arg->op.left); + switch (lval) { + case FILTER_VAL_NORM: + break; + case FILTER_VAL_TRUE: + if (arg->op.type == FILTER_OP_OR) + return FILTER_VAL_TRUE; + rval = test_arg(arg, arg->op.right); + if (rval != FILTER_VAL_NORM) + return rval; + + reparent_op_arg(parent, arg, arg->op.right); + return FILTER_VAL_NORM; + + case FILTER_VAL_FALSE: + if (arg->op.type == FILTER_OP_AND) + return FILTER_VAL_FALSE; + rval = test_arg(arg, arg->op.right); + if (rval != FILTER_VAL_NORM) + return rval; + + reparent_op_arg(parent, arg, arg->op.right); + return FILTER_VAL_NORM; + } + } + + rval = test_arg(arg, arg->op.right); + switch (rval) { + case FILTER_VAL_NORM: + break; + case FILTER_VAL_TRUE: + if (arg->op.type == FILTER_OP_OR) + return FILTER_VAL_TRUE; + if (arg->op.type == FILTER_OP_NOT) + return FILTER_VAL_FALSE; + + reparent_op_arg(parent, arg, arg->op.left); + return FILTER_VAL_NORM; + + case FILTER_VAL_FALSE: + if (arg->op.type == FILTER_OP_AND) + return FILTER_VAL_FALSE; + if (arg->op.type == FILTER_OP_NOT) + return FILTER_VAL_TRUE; + + reparent_op_arg(parent, arg, arg->op.left); + return FILTER_VAL_NORM; + } + + return FILTER_VAL_NORM; + default: + die("bad arg in filter tree"); + } + return FILTER_VAL_NORM; +} + +/* Remove any unknown event fields */ +static struct filter_arg *collapse_tree(struct filter_arg *arg) +{ + enum filter_vals ret; + + ret = test_arg(arg, arg); + switch (ret) { + case FILTER_VAL_NORM: + return arg; + + case FILTER_VAL_TRUE: + case FILTER_VAL_FALSE: + free_arg(arg); + arg = allocate_arg(); + arg->type = FILTER_ARG_BOOLEAN; + arg->bool.value = ret == FILTER_VAL_TRUE; + } + + return arg; +} + +static int +process_filter(struct event_format *event, struct filter_arg **parg, + char **error_str, int not) +{ + enum event_type type; + char *token = NULL; + struct filter_arg *current_op = NULL; + struct filter_arg *current_exp = NULL; + struct filter_arg *left_item = NULL; + struct filter_arg *arg = NULL; + enum op_type op_type; + enum filter_op_type btype; + enum filter_exp_type etype; + enum filter_cmp_type ctype; + int ret; + + *parg = NULL; + + do { + free(token); + type = read_token(&token); + switch (type) { + case EVENT_SQUOTE: + case EVENT_DQUOTE: + case EVENT_ITEM: + arg = create_arg_item(event, token, type, error_str); + if (!arg) + goto fail; + if (!left_item) + left_item = arg; + else if (current_exp) { + ret = add_right(current_exp, arg, error_str); + if (ret < 0) + goto fail; + left_item = NULL; + /* Not's only one one expression */ + if (not) { + arg = NULL; + if (current_op) + goto fail_print; + free(token); + *parg = current_exp; + return 0; + } + } else + goto fail_print; + arg = NULL; + break; + + case EVENT_DELIM: + if (*token == ',') { + show_error(error_str, + "Illegal token ','"); + goto fail; + } + + if (*token == '(') { + if (left_item) { + show_error(error_str, + "Open paren can not come after item"); + goto fail; + } + if (current_exp) { + show_error(error_str, + "Open paren can not come after expression"); + goto fail; + } + + ret = process_filter(event, &arg, error_str, 0); + if (ret != 1) { + if (ret == 0) + show_error(error_str, + "Unbalanced number of '('"); + goto fail; + } + ret = 0; + + /* A not wants just one expression */ + if (not) { + if (current_op) + goto fail_print; + *parg = arg; + return 0; + } + + if (current_op) + ret = add_right(current_op, arg, error_str); + else + current_exp = arg; + + if (ret < 0) + goto fail; + + } else { /* ')' */ + if (!current_op && !current_exp) + goto fail_print; + + /* Make sure everything is finished at this level */ + if (current_exp && !check_op_done(current_exp)) + goto fail_print; + if (current_op && !check_op_done(current_op)) + goto fail_print; + + if (current_op) + *parg = current_op; + else + *parg = current_exp; + return 1; + } + break; + + case EVENT_OP: + op_type = process_op(token, &btype, &ctype, &etype); + + /* All expect a left arg except for NOT */ + switch (op_type) { + case OP_BOOL: + /* Logic ops need a left expression */ + if (!current_exp && !current_op) + goto fail_print; + /* fall through */ + case OP_NOT: + /* logic only processes ops and exp */ + if (left_item) + goto fail_print; + break; + case OP_EXP: + case OP_CMP: + if (!left_item) + goto fail_print; + break; + case OP_NONE: + show_error(error_str, + "Unknown op token %s", token); + goto fail; + } + + ret = 0; + switch (op_type) { + case OP_BOOL: + arg = create_arg_op(btype); + if (current_op) + ret = add_left(arg, current_op); + else + ret = add_left(arg, current_exp); + current_op = arg; + current_exp = NULL; + break; + + case OP_NOT: + arg = create_arg_op(btype); + if (current_op) + ret = add_right(current_op, arg, error_str); + if (ret < 0) + goto fail; + current_exp = arg; + ret = process_filter(event, &arg, error_str, 1); + if (ret < 0) + goto fail; + ret = add_right(current_exp, arg, error_str); + if (ret < 0) + goto fail; + break; + + case OP_EXP: + case OP_CMP: + if (op_type == OP_EXP) + arg = create_arg_exp(etype); + else + arg = create_arg_cmp(ctype); + + if (current_op) + ret = add_right(current_op, arg, error_str); + if (ret < 0) + goto fail; + ret = add_left(arg, left_item); + if (ret < 0) { + arg = NULL; + goto fail_print; + } + current_exp = arg; + break; + default: + break; + } + arg = NULL; + if (ret < 0) + goto fail_print; + break; + case EVENT_NONE: + break; + default: + goto fail_print; + } + } while (type != EVENT_NONE); + + if (!current_op && !current_exp) + goto fail_print; + + if (!current_op) + current_op = current_exp; + + current_op = collapse_tree(current_op); + + *parg = current_op; + + return 0; + + fail_print: + show_error(error_str, "Syntax error"); + fail: + free_arg(current_op); + free_arg(current_exp); + free_arg(arg); + free(token); + return -1; +} + +static int +process_event(struct event_format *event, const char *filter_str, + struct filter_arg **parg, char **error_str) +{ + int ret; + + pevent_buffer_init(filter_str, strlen(filter_str)); + + ret = process_filter(event, parg, error_str, 0); + if (ret == 1) { + show_error(error_str, + "Unbalanced number of ')'"); + return -1; + } + if (ret < 0) + return ret; + + /* If parg is NULL, then make it into FALSE */ + if (!*parg) { + *parg = allocate_arg(); + (*parg)->type = FILTER_ARG_BOOLEAN; + (*parg)->bool.value = FILTER_FALSE; + } + + return 0; +} + +static int filter_event(struct event_filter *filter, + struct event_format *event, + const char *filter_str, char **error_str) +{ + struct filter_type *filter_type; + struct filter_arg *arg; + int ret; + + if (filter_str) { + ret = process_event(event, filter_str, &arg, error_str); + if (ret < 0) + return ret; + + } else { + /* just add a TRUE arg */ + arg = allocate_arg(); + arg->type = FILTER_ARG_BOOLEAN; + arg->bool.value = FILTER_TRUE; + } + + filter_type = add_filter_type(filter, event->id); + if (filter_type->filter) + free_arg(filter_type->filter); + filter_type->filter = arg; + + return 0; +} + +/** + * pevent_filter_add_filter_str - add a new filter + * @filter: the event filter to add to + * @filter_str: the filter string that contains the filter + * @error_str: string containing reason for failed filter + * + * Returns 0 if the filter was successfully added + * -1 if there was an error. + * + * On error, if @error_str points to a string pointer, + * it is set to the reason that the filter failed. + * This string must be freed with "free". + */ +int pevent_filter_add_filter_str(struct event_filter *filter, + const char *filter_str, + char **error_str) +{ + struct pevent *pevent = filter->pevent; + struct event_list *event; + struct event_list *events = NULL; + const char *filter_start; + const char *next_event; + char *this_event; + char *event_name = NULL; + char *sys_name = NULL; + char *sp; + int rtn = 0; + int len; + int ret; + + /* clear buffer to reset show error */ + pevent_buffer_init("", 0); + + if (error_str) + *error_str = NULL; + + filter_start = strchr(filter_str, ':'); + if (filter_start) + len = filter_start - filter_str; + else + len = strlen(filter_str); + + + do { + next_event = strchr(filter_str, ','); + if (next_event && + (!filter_start || next_event < filter_start)) + len = next_event - filter_str; + else if (filter_start) + len = filter_start - filter_str; + else + len = strlen(filter_str); + + this_event = malloc_or_die(len + 1); + memcpy(this_event, filter_str, len); + this_event[len] = 0; + + if (next_event) + next_event++; + + filter_str = next_event; + + sys_name = strtok_r(this_event, "/", &sp); + event_name = strtok_r(NULL, "/", &sp); + + if (!sys_name) { + show_error(error_str, "No filter found"); + /* This can only happen when events is NULL, but still */ + free_events(events); + free(this_event); + return -1; + } + + /* Find this event */ + ret = find_event(pevent, &events, strim(sys_name), strim(event_name)); + if (ret < 0) { + if (event_name) + show_error(error_str, + "No event found under '%s.%s'", + sys_name, event_name); + else + show_error(error_str, + "No event found under '%s'", + sys_name); + free_events(events); + free(this_event); + return -1; + } + free(this_event); + } while (filter_str); + + /* Skip the ':' */ + if (filter_start) + filter_start++; + + /* filter starts here */ + for (event = events; event; event = event->next) { + ret = filter_event(filter, event->event, filter_start, + error_str); + /* Failures are returned if a parse error happened */ + if (ret < 0) + rtn = ret; + + if (ret >= 0 && pevent->test_filters) { + char *test; + test = pevent_filter_make_string(filter, event->event->id); + printf(" '%s: %s'\n", event->event->name, test); + free(test); + } + } + + free_events(events); + + if (rtn >= 0 && pevent->test_filters) + exit(0); + + return rtn; +} + +static void free_filter_type(struct filter_type *filter_type) +{ + free_arg(filter_type->filter); +} + +/** + * pevent_filter_remove_event - remove a filter for an event + * @filter: the event filter to remove from + * @event_id: the event to remove a filter for + * + * Removes the filter saved for an event defined by @event_id + * from the @filter. + * + * Returns 1: if an event was removed + * 0: if the event was not found + */ +int pevent_filter_remove_event(struct event_filter *filter, + int event_id) +{ + struct filter_type *filter_type; + unsigned long len; + + if (!filter->filters) + return 0; + + filter_type = find_filter_type(filter, event_id); + + if (!filter_type) + return 0; + + free_filter_type(filter_type); + + /* The filter_type points into the event_filters array */ + len = (unsigned long)(filter->event_filters + filter->filters) - + (unsigned long)(filter_type + 1); + + memmove(filter_type, filter_type + 1, len); + filter->filters--; + + memset(&filter->event_filters[filter->filters], 0, + sizeof(*filter_type)); + + return 1; +} + +/** + * pevent_filter_reset - clear all filters in a filter + * @filter: the event filter to reset + * + * Removes all filters from a filter and resets it. + */ +void pevent_filter_reset(struct event_filter *filter) +{ + int i; + + for (i = 0; i < filter->filters; i++) + free_filter_type(&filter->event_filters[i]); + + free(filter->event_filters); + filter->filters = 0; + filter->event_filters = NULL; +} + +void pevent_filter_free(struct event_filter *filter) +{ + pevent_unref(filter->pevent); + + pevent_filter_reset(filter); + + free(filter); +} + +static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg); + +static int copy_filter_type(struct event_filter *filter, + struct event_filter *source, + struct filter_type *filter_type) +{ + struct filter_arg *arg; + struct event_format *event; + const char *sys; + const char *name; + char *str; + + /* Can't assume that the pevent's are the same */ + sys = filter_type->event->system; + name = filter_type->event->name; + event = pevent_find_event_by_name(filter->pevent, sys, name); + if (!event) + return -1; + + str = arg_to_str(source, filter_type->filter); + if (!str) + return -1; + + if (strcmp(str, "TRUE") == 0 || strcmp(str, "FALSE") == 0) { + /* Add trivial event */ + arg = allocate_arg(); + arg->type = FILTER_ARG_BOOLEAN; + if (strcmp(str, "TRUE") == 0) + arg->bool.value = 1; + else + arg->bool.value = 0; + + filter_type = add_filter_type(filter, event->id); + filter_type->filter = arg; + + free(str); + return 0; + } + + filter_event(filter, event, str, NULL); + free(str); + + return 0; +} + +/** + * pevent_filter_copy - copy a filter using another filter + * @dest - the filter to copy to + * @source - the filter to copy from + * + * Returns 0 on success and -1 if not all filters were copied + */ +int pevent_filter_copy(struct event_filter *dest, struct event_filter *source) +{ + int ret = 0; + int i; + + pevent_filter_reset(dest); + + for (i = 0; i < source->filters; i++) { + if (copy_filter_type(dest, source, &source->event_filters[i])) + ret = -1; + } + return ret; +} + + +/** + * pevent_update_trivial - update the trivial filters with the given filter + * @dest - the filter to update + * @source - the filter as the source of the update + * @type - the type of trivial filter to update. + * + * Scan dest for trivial events matching @type to replace with the source. + * + * Returns 0 on success and -1 if there was a problem updating, but + * events may have still been updated on error. + */ +int pevent_update_trivial(struct event_filter *dest, struct event_filter *source, + enum filter_trivial_type type) +{ + struct pevent *src_pevent; + struct pevent *dest_pevent; + struct event_format *event; + struct filter_type *filter_type; + struct filter_arg *arg; + char *str; + int i; + + src_pevent = source->pevent; + dest_pevent = dest->pevent; + + /* Do nothing if either of the filters has nothing to filter */ + if (!dest->filters || !source->filters) + return 0; + + for (i = 0; i < dest->filters; i++) { + filter_type = &dest->event_filters[i]; + arg = filter_type->filter; + if (arg->type != FILTER_ARG_BOOLEAN) + continue; + if ((arg->bool.value && type == FILTER_TRIVIAL_FALSE) || + (!arg->bool.value && type == FILTER_TRIVIAL_TRUE)) + continue; + + event = filter_type->event; + + if (src_pevent != dest_pevent) { + /* do a look up */ + event = pevent_find_event_by_name(src_pevent, + event->system, + event->name); + if (!event) + return -1; + } + + str = pevent_filter_make_string(source, event->id); + if (!str) + continue; + + /* Don't bother if the filter is trivial too */ + if (strcmp(str, "TRUE") != 0 && strcmp(str, "FALSE") != 0) + filter_event(dest, event, str, NULL); + free(str); + } + return 0; +} + +/** + * pevent_filter_clear_trivial - clear TRUE and FALSE filters + * @filter: the filter to remove trivial filters from + * @type: remove only true, false, or both + * + * Removes filters that only contain a TRUE or FALES boolean arg. + */ +void pevent_filter_clear_trivial(struct event_filter *filter, + enum filter_trivial_type type) +{ + struct filter_type *filter_type; + int count = 0; + int *ids; + int i; + + if (!filter->filters) + return; + + /* + * Two steps, first get all ids with trivial filters. + * then remove those ids. + */ + for (i = 0; i < filter->filters; i++) { + filter_type = &filter->event_filters[i]; + if (filter_type->filter->type != FILTER_ARG_BOOLEAN) + continue; + switch (type) { + case FILTER_TRIVIAL_FALSE: + if (filter_type->filter->bool.value) + continue; + case FILTER_TRIVIAL_TRUE: + if (!filter_type->filter->bool.value) + continue; + default: + break; + } + if (count) + ids = realloc(ids, sizeof(*ids) * (count + 1)); + else + ids = malloc(sizeof(*ids)); + if (!ids) + die("Can't allocate ids"); + ids[count++] = filter_type->event_id; + } + + if (!count) + return; + + for (i = 0; i < count; i++) + pevent_filter_remove_event(filter, ids[i]); + + free(ids); +} + +/** + * pevent_filter_event_has_trivial - return true event contains trivial filter + * @filter: the filter with the information + * @event_id: the id of the event to test + * @type: trivial type to test for (TRUE, FALSE, EITHER) + * + * Returns 1 if the event contains a matching trivial type + * otherwise 0. + */ +int pevent_filter_event_has_trivial(struct event_filter *filter, + int event_id, + enum filter_trivial_type type) +{ + struct filter_type *filter_type; + + if (!filter->filters) + return 0; + + filter_type = find_filter_type(filter, event_id); + + if (!filter_type) + return 0; + + if (filter_type->filter->type != FILTER_ARG_BOOLEAN) + return 0; + + switch (type) { + case FILTER_TRIVIAL_FALSE: + return !filter_type->filter->bool.value; + + case FILTER_TRIVIAL_TRUE: + return filter_type->filter->bool.value; + default: + return 1; + } +} + +static int test_filter(struct event_format *event, + struct filter_arg *arg, struct record *record); + +static const char * +get_comm(struct event_format *event, struct record *record) +{ + const char *comm; + int pid; + + pid = pevent_data_pid(event->pevent, record); + comm = pevent_data_comm_from_pid(event->pevent, pid); + return comm; +} + +static unsigned long long +get_value(struct event_format *event, + struct format_field *field, struct record *record) +{ + unsigned long long val; + + /* Handle our dummy "comm" field */ + if (field == &comm) { + const char *name; + + name = get_comm(event, record); + return (unsigned long long)name; + } + + pevent_read_number_field(field, record->data, &val); + + if (!(field->flags & FIELD_IS_SIGNED)) + return val; + + switch (field->size) { + case 1: + return (char)val; + case 2: + return (short)val; + case 4: + return (int)val; + case 8: + return (long long)val; + } + return val; +} + +static unsigned long long +get_arg_value(struct event_format *event, struct filter_arg *arg, struct record *record); + +static unsigned long long +get_exp_value(struct event_format *event, struct filter_arg *arg, struct record *record) +{ + unsigned long long lval, rval; + + lval = get_arg_value(event, arg->exp.left, record); + rval = get_arg_value(event, arg->exp.right, record); + + switch (arg->exp.type) { + case FILTER_EXP_ADD: + return lval + rval; + + case FILTER_EXP_SUB: + return lval - rval; + + case FILTER_EXP_MUL: + return lval * rval; + + case FILTER_EXP_DIV: + return lval / rval; + + case FILTER_EXP_MOD: + return lval % rval; + + case FILTER_EXP_RSHIFT: + return lval >> rval; + + case FILTER_EXP_LSHIFT: + return lval << rval; + + case FILTER_EXP_AND: + return lval & rval; + + case FILTER_EXP_OR: + return lval | rval; + + case FILTER_EXP_XOR: + return lval ^ rval; + + case FILTER_EXP_NOT: + default: + die("error in exp"); + } + return 0; +} + +static unsigned long long +get_arg_value(struct event_format *event, struct filter_arg *arg, struct record *record) +{ + switch (arg->type) { + case FILTER_ARG_FIELD: + return get_value(event, arg->field.field, record); + + case FILTER_ARG_VALUE: + if (arg->value.type != FILTER_NUMBER) + die("must have number field!"); + return arg->value.val; + + case FILTER_ARG_EXP: + return get_exp_value(event, arg, record); + + default: + die("oops in filter"); + } + return 0; +} + +static int test_num(struct event_format *event, + struct filter_arg *arg, struct record *record) +{ + unsigned long long lval, rval; + + lval = get_arg_value(event, arg->num.left, record); + rval = get_arg_value(event, arg->num.right, record); + + switch (arg->num.type) { + case FILTER_CMP_EQ: + return lval == rval; + + case FILTER_CMP_NE: + return lval != rval; + + case FILTER_CMP_GT: + return lval > rval; + + case FILTER_CMP_LT: + return lval < rval; + + case FILTER_CMP_GE: + return lval >= rval; + + case FILTER_CMP_LE: + return lval <= rval; + + default: + /* ?? */ + return 0; + } +} + +static const char *get_field_str(struct filter_arg *arg, struct record *record) +{ + const char *val = record->data + arg->str.field->offset; + + /* + * We need to copy the data since we can't be sure the field + * is null terminated. + */ + if (*(val + arg->str.field->size - 1)) { + /* copy it */ + memcpy(arg->str.buffer, val, arg->str.field->size); + /* the buffer is already NULL terminated */ + val = arg->str.buffer; + } + return val; +} + +static int test_str(struct event_format *event, + struct filter_arg *arg, struct record *record) +{ + const char *val; + + if (arg->str.field == &comm) + val = get_comm(event, record); + else + val = get_field_str(arg, record); + + switch (arg->str.type) { + case FILTER_CMP_MATCH: + return strcmp(val, arg->str.val) == 0; + + case FILTER_CMP_NOT_MATCH: + return strcmp(val, arg->str.val) != 0; + + case FILTER_CMP_REGEX: + /* Returns zero on match */ + return !regexec(&arg->str.reg, val, 0, NULL, 0); + + case FILTER_CMP_NOT_REGEX: + return regexec(&arg->str.reg, val, 0, NULL, 0); + + default: + /* ?? */ + return 0; + } +} + +static int test_op(struct event_format *event, + struct filter_arg *arg, struct record *record) +{ + switch (arg->op.type) { + case FILTER_OP_AND: + return test_filter(event, arg->op.left, record) && + test_filter(event, arg->op.right, record); + + case FILTER_OP_OR: + return test_filter(event, arg->op.left, record) || + test_filter(event, arg->op.right, record); + + case FILTER_OP_NOT: + return !test_filter(event, arg->op.right, record); + + default: + /* ?? */ + return 0; + } +} + +static int test_filter(struct event_format *event, + struct filter_arg *arg, struct record *record) +{ + switch (arg->type) { + case FILTER_ARG_BOOLEAN: + /* easy case */ + return arg->bool.value; + + case FILTER_ARG_OP: + return test_op(event, arg, record); + + case FILTER_ARG_NUM: + return test_num(event, arg, record); + + case FILTER_ARG_STR: + return test_str(event, arg, record); + + case FILTER_ARG_EXP: + case FILTER_ARG_VALUE: + case FILTER_ARG_FIELD: + /* + * Expressions, fields and values evaluate + * to true if they return non zero + */ + return !!get_arg_value(event, arg, record); + + default: + die("oops!"); + /* ?? */ + return 0; + } +} + +/** + * pevent_event_filtered - return true if event has filter + * @filter: filter struct with filter information + * @event_id: event id to test if filter exists + * + * Returns 1 if filter found for @event_id + * otherwise 0; + */ +int pevent_event_filtered(struct event_filter *filter, + int event_id) +{ + struct filter_type *filter_type; + + if (!filter->filters) + return 0; + + filter_type = find_filter_type(filter, event_id); + + return filter_type ? 1 : 0; +} + +/** + * pevent_filter_match - test if a record matches a filter + * @filter: filter struct with filter information + * @record: the record to test against the filter + * + * Returns: + * 1 - filter found for event and @record matches + * 0 - filter found for event and @record does not match + * -1 - no filter found for @record's event + * -2 - if no filters exist + */ +int pevent_filter_match(struct event_filter *filter, + struct record *record) +{ + struct pevent *pevent = filter->pevent; + struct filter_type *filter_type; + int event_id; + + if (!filter->filters) + return FILTER_NONE; + + event_id = pevent_data_type(pevent, record); + + filter_type = find_filter_type(filter, event_id); + + if (!filter_type) + return FILTER_NOEXIST; + + return test_filter(filter_type->event, filter_type->filter, record) ? + FILTER_MATCH : FILTER_MISS; +} + +static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) +{ + char *str = NULL; + char *left = NULL; + char *right = NULL; + char *op = NULL; + int left_val = -1; + int right_val = -1; + int val; + int len; + + switch (arg->op.type) { + case FILTER_OP_AND: + op = "&&"; + /* fall through */ + case FILTER_OP_OR: + if (!op) + op = "||"; + + left = arg_to_str(filter, arg->op.left); + right = arg_to_str(filter, arg->op.right); + if (!left || !right) + break; + + /* Try to consolidate boolean values */ + if (strcmp(left, "TRUE") == 0) + left_val = 1; + else if (strcmp(left, "FALSE") == 0) + left_val = 0; + + if (strcmp(right, "TRUE") == 0) + right_val = 1; + else if (strcmp(right, "FALSE") == 0) + right_val = 0; + + if (left_val >= 0) { + if ((arg->op.type == FILTER_OP_AND && !left_val) || + (arg->op.type == FILTER_OP_OR && left_val)) { + /* Just return left value */ + str = left; + left = NULL; + break; + } + if (right_val >= 0) { + /* just evaluate this. */ + val = 0; + switch (arg->op.type) { + case FILTER_OP_AND: + val = left_val && right_val; + break; + case FILTER_OP_OR: + val = left_val || right_val; + break; + default: + break; + } + str = malloc_or_die(6); + if (val) + strcpy(str, "TRUE"); + else + strcpy(str, "FALSE"); + break; + } + } + if (right_val >= 0) { + if ((arg->op.type == FILTER_OP_AND && !right_val) || + (arg->op.type == FILTER_OP_OR && right_val)) { + /* Just return right value */ + str = right; + right = NULL; + break; + } + /* The right value is meaningless */ + str = left; + left = NULL; + break; + } + + len = strlen(left) + strlen(right) + strlen(op) + 10; + str = malloc_or_die(len); + snprintf(str, len, "(%s) %s (%s)", + left, op, right); + break; + + case FILTER_OP_NOT: + op = "!"; + right = arg_to_str(filter, arg->op.right); + if (!right) + break; + + /* See if we can consolidate */ + if (strcmp(right, "TRUE") == 0) + right_val = 1; + else if (strcmp(right, "FALSE") == 0) + right_val = 0; + if (right_val >= 0) { + /* just return the opposite */ + str = malloc_or_die(6); + if (right_val) + strcpy(str, "FALSE"); + else + strcpy(str, "TRUE"); + break; + } + len = strlen(right) + strlen(op) + 3; + str = malloc_or_die(len); + snprintf(str, len, "%s(%s)", op, right); + break; + + default: + /* ?? */ + break; + } + free(left); + free(right); + return str; +} + +static char *val_to_str(struct event_filter *filter, struct filter_arg *arg) +{ + char *str; + + str = malloc_or_die(30); + + snprintf(str, 30, "%lld", arg->value.val); + + return str; +} + +static char *field_to_str(struct event_filter *filter, struct filter_arg *arg) +{ + return strdup(arg->field.field->name); +} + +static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg) +{ + char *lstr; + char *rstr; + char *op; + char *str; + int len; + + lstr = arg_to_str(filter, arg->exp.left); + rstr = arg_to_str(filter, arg->exp.right); + + switch (arg->exp.type) { + case FILTER_EXP_ADD: + op = "+"; + break; + case FILTER_EXP_SUB: + op = "-"; + break; + case FILTER_EXP_MUL: + op = "*"; + break; + case FILTER_EXP_DIV: + op = "/"; + break; + case FILTER_EXP_MOD: + op = "%"; + break; + case FILTER_EXP_RSHIFT: + op = ">>"; + break; + case FILTER_EXP_LSHIFT: + op = "<<"; + break; + case FILTER_EXP_AND: + op = "&"; + break; + case FILTER_EXP_OR: + op = "|"; + break; + case FILTER_EXP_XOR: + op = "^"; + break; + default: + die("oops in exp"); + } + + len = strlen(op) + strlen(lstr) + strlen(rstr) + 4; + str = malloc_or_die(len); + snprintf(str, len, "%s %s %s", lstr, op, rstr); + free(lstr); + free(rstr); + + return str; +} + +static char *num_to_str(struct event_filter *filter, struct filter_arg *arg) +{ + char *lstr; + char *rstr; + char *str = NULL; + char *op = NULL; + int len; + + lstr = arg_to_str(filter, arg->num.left); + rstr = arg_to_str(filter, arg->num.right); + + switch (arg->num.type) { + case FILTER_CMP_EQ: + op = "=="; + /* fall through */ + case FILTER_CMP_NE: + if (!op) + op = "!="; + /* fall through */ + case FILTER_CMP_GT: + if (!op) + op = ">"; + /* fall through */ + case FILTER_CMP_LT: + if (!op) + op = "<"; + /* fall through */ + case FILTER_CMP_GE: + if (!op) + op = ">="; + /* fall through */ + case FILTER_CMP_LE: + if (!op) + op = "<="; + + len = strlen(lstr) + strlen(op) + strlen(rstr) + 4; + str = malloc_or_die(len); + sprintf(str, "%s %s %s", lstr, op, rstr); + + break; + + default: + /* ?? */ + break; + } + + free(lstr); + free(rstr); + return str; +} + +static char *str_to_str(struct event_filter *filter, struct filter_arg *arg) +{ + char *str = NULL; + char *op = NULL; + int len; + + switch (arg->str.type) { + case FILTER_CMP_MATCH: + op = "=="; + /* fall through */ + case FILTER_CMP_NOT_MATCH: + if (!op) + op = "!="; + /* fall through */ + case FILTER_CMP_REGEX: + if (!op) + op = "=~"; + /* fall through */ + case FILTER_CMP_NOT_REGEX: + if (!op) + op = "!~"; + + len = strlen(arg->str.field->name) + strlen(op) + + strlen(arg->str.val) + 6; + str = malloc_or_die(len); + snprintf(str, len, "%s %s \"%s\"", + arg->str.field->name, + op, arg->str.val); + break; + + default: + /* ?? */ + break; + } + return str; +} + +static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg) +{ + char *str; + + switch (arg->type) { + case FILTER_ARG_BOOLEAN: + str = malloc_or_die(6); + if (arg->bool.value) + strcpy(str, "TRUE"); + else + strcpy(str, "FALSE"); + return str; + + case FILTER_ARG_OP: + return op_to_str(filter, arg); + + case FILTER_ARG_NUM: + return num_to_str(filter, arg); + + case FILTER_ARG_STR: + return str_to_str(filter, arg); + + case FILTER_ARG_VALUE: + return val_to_str(filter, arg); + + case FILTER_ARG_FIELD: + return field_to_str(filter, arg); + + case FILTER_ARG_EXP: + return exp_to_str(filter, arg); + + default: + /* ?? */ + return NULL; + } + +} + +/** + * pevent_filter_make_string - return a string showing the filter + * @filter: filter struct with filter information + * @event_id: the event id to return the filter string with + * + * Returns a string that displays the filter contents. + * This string must be freed with free(str). + * NULL is returned if no filter is found. + */ +char * +pevent_filter_make_string(struct event_filter *filter, int event_id) +{ + struct filter_type *filter_type; + + if (!filter->filters) + return NULL; + + filter_type = find_filter_type(filter, event_id); + + if (!filter_type) + return NULL; + + return arg_to_str(filter, filter_type->filter); +} + +/** + * pevent_filter_compare - compare two filters and return if they are the same + * @filter1: Filter to compare with @filter2 + * @filter2: Filter to compare with @filter1 + * + * Returns: + * 1 if the two filters hold the same content. + * 0 if they do not. + */ +int pevent_filter_compare(struct event_filter *filter1, struct event_filter *filter2) +{ + struct filter_type *filter_type1; + struct filter_type *filter_type2; + char *str1, *str2; + int result; + int i; + + /* Do the easy checks first */ + if (filter1->filters != filter2->filters) + return 0; + if (!filter1->filters && !filter2->filters) + return 1; + + /* + * Now take a look at each of the events to see if they have the same + * filters to them. + */ + for (i = 0; i < filter1->filters; i++) { + filter_type1 = &filter1->event_filters[i]; + filter_type2 = find_filter_type(filter2, filter_type1->event_id); + if (!filter_type2) + break; + if (filter_type1->filter->type != filter_type2->filter->type) + break; + switch (filter_type1->filter->type) { + case FILTER_TRIVIAL_FALSE: + case FILTER_TRIVIAL_TRUE: + /* trivial types just need the type compared */ + continue; + default: + break; + } + /* The best way to compare complex filters is with strings */ + str1 = arg_to_str(filter1, filter_type1->filter); + str2 = arg_to_str(filter2, filter_type2->filter); + result = strcmp(str1, str2) != 0; + free(str1); + free(str2); + if (result) + break; + } + + if (i < filter1->filters) + return 0; + return 1; +} + diff --git a/parse-utils.c b/parse-utils.c new file mode 100644 index 0000000..f023a13 --- /dev/null +++ b/parse-utils.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include + +#define __weak __attribute__((weak)) + +void __vdie(const char *fmt, va_list ap) +{ + int ret = errno; + + if (errno) + perror("trace-cmd"); + else + ret = -1; + + fprintf(stderr, " "); + vfprintf(stderr, fmt, ap); + + fprintf(stderr, "\n"); + exit(ret); +} + +void __die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __vdie(fmt, ap); + va_end(ap); +} + +void __weak die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __vdie(fmt, ap); + va_end(ap); +} + +void __vwarning(const char *fmt, va_list ap) +{ + if (errno) + perror("trace-cmd"); + errno = 0; + + fprintf(stderr, " "); + vfprintf(stderr, fmt, ap); + + fprintf(stderr, "\n"); +} + +void __warning(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __vwarning(fmt, ap); + va_end(ap); +} + +void __weak warning(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __vwarning(fmt, ap); + va_end(ap); +} + +void __vpr_stat(const char *fmt, va_list ap) +{ + vprintf(fmt, ap); + printf("\n"); +} + +void __pr_stat(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __vpr_stat(fmt, ap); + va_end(ap); +} + +void __weak vpr_stat(const char *fmt, va_list ap) +{ + __vpr_stat(fmt, ap); +} + +void __weak pr_stat(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __vpr_stat(fmt, ap); + va_end(ap); +} + +void __weak *malloc_or_die(unsigned int size) +{ + void *data; + + data = malloc(size); + if (!data) + die("malloc"); + return data; +} diff --git a/trace-seq.c b/trace-seq.c new file mode 100644 index 0000000..d2f265b --- /dev/null +++ b/trace-seq.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2009 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include + +#include "event-parse.h" + +/* + * The TRACE_SEQ_POISON is to catch the use of using + * a trace_seq structure after it was destroyed. + */ +#define TRACE_SEQ_POISON ((void *)0xdeadbeef) +#define TRACE_SEQ_CHECK(s) \ +do { \ + if ((s)->buffer == TRACE_SEQ_POISON) \ + die("Usage of trace_seq after it was destroyed"); \ +} while (0) + +/** + * trace_seq_init - initialize the trace_seq structure + * @s: a pointer to the trace_seq structure to initialize + */ +void trace_seq_init(struct trace_seq *s) +{ + s->len = 0; + s->readpos = 0; + s->buffer_size = TRACE_SEQ_BUF_SIZE; + s->buffer = malloc_or_die(s->buffer_size); +} + +/** + * trace_seq_destroy - free up memory of a trace_seq + * @s: a pointer to the trace_seq to free the buffer + * + * Only frees the buffer, not the trace_seq struct itself. + */ +void trace_seq_destroy(struct trace_seq *s) +{ + if (!s) + return; + TRACE_SEQ_CHECK(s); + free(s->buffer); + s->buffer = TRACE_SEQ_POISON; +} + +static void expand_buffer(struct trace_seq *s) +{ + s->buffer_size += TRACE_SEQ_BUF_SIZE; + s->buffer = realloc(s->buffer, s->buffer_size); + if (!s->buffer) + die("Can't allocate trace_seq buffer memory"); +} + +/** + * trace_seq_printf - sequence printing of trace information + * @s: trace sequence descriptor + * @fmt: printf format string + * + * It returns 0 if the trace oversizes the buffer's free + * space, 1 otherwise. + * + * The tracer may use either sequence operations or its own + * copy to user routines. To simplify formating of a trace + * trace_seq_printf is used to store strings into a special + * buffer (@s). Then the output may be either used by + * the sequencer or pulled into another buffer. + */ +int +trace_seq_printf(struct trace_seq *s, const char *fmt, ...) +{ + va_list ap; + int len; + int ret; + + TRACE_SEQ_CHECK(s); + + try_again: + len = (s->buffer_size - 1) - s->len; + + va_start(ap, fmt); + ret = vsnprintf(s->buffer + s->len, len, fmt, ap); + va_end(ap); + + if (ret >= len) { + expand_buffer(s); + goto try_again; + } + + s->len += ret; + + return 1; +} + +/** + * trace_seq_vprintf - sequence printing of trace information + * @s: trace sequence descriptor + * @fmt: printf format string + * + * The tracer may use either sequence operations or its own + * copy to user routines. To simplify formating of a trace + * trace_seq_printf is used to store strings into a special + * buffer (@s). Then the output may be either used by + * the sequencer or pulled into another buffer. + */ +int +trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) +{ + int len; + int ret; + + TRACE_SEQ_CHECK(s); + + try_again: + len = (s->buffer_size - 1) - s->len; + + ret = vsnprintf(s->buffer + s->len, len, fmt, args); + + if (ret >= len) { + expand_buffer(s); + goto try_again; + } + + s->len += ret; + + return len; +} + +/** + * trace_seq_puts - trace sequence printing of simple string + * @s: trace sequence descriptor + * @str: simple string to record + * + * The tracer may use either the sequence operations or its own + * copy to user routines. This function records a simple string + * into a special buffer (@s) for later retrieval by a sequencer + * or other mechanism. + */ +int trace_seq_puts(struct trace_seq *s, const char *str) +{ + int len; + + TRACE_SEQ_CHECK(s); + + len = strlen(str); + + while (len > ((s->buffer_size - 1) - s->len)) + expand_buffer(s); + + memcpy(s->buffer + s->len, str, len); + s->len += len; + + return len; +} + +int trace_seq_putc(struct trace_seq *s, unsigned char c) +{ + TRACE_SEQ_CHECK(s); + + while (s->len >= (s->buffer_size - 1)) + expand_buffer(s); + + s->buffer[s->len++] = c; + + return 1; +} + +void trace_seq_terminate(struct trace_seq *s) +{ + TRACE_SEQ_CHECK(s); + + /* There's always one character left on the buffer */ + s->buffer[s->len] = 0; +} + +int trace_seq_do_printf(struct trace_seq *s) +{ + TRACE_SEQ_CHECK(s); + return printf("%.*s", s->len, s->buffer); +} -- cgit v1.2.3 From a488d1770faaa2b5a8ef2aab1c4dbb255c97221a Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 6 Apr 2012 00:47:55 +0200 Subject: events: Update tools/lib/traceevent to work with perf Some of the util functions of libtraceevent.a conflict with perf, such as die(), warning() and others. Move them into event-util.h that is not included by the perf tools. Also, as perf compiles with 'bool' the filter_arg->bool needs to be renamed to filter_arg->boolean. Signed-off-by: Steven Rostedt Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Borislav Petkov Cc: Jiri Olsa Cc: Arun Sharma Cc: Namhyung Kim Signed-off-by: Frederic Weisbecker --- event-parse.c | 1 + event-parse.h | 20 +++----------------- event-utils.h | 16 ++++++++++++++++ parse-filter.c | 32 ++++++++++++++++---------------- trace-seq.c | 1 + 5 files changed, 37 insertions(+), 33 deletions(-) diff --git a/event-parse.c b/event-parse.c index 47a3227..3ce75b5 100644 --- a/event-parse.c +++ b/event-parse.c @@ -33,6 +33,7 @@ #include #include "event-parse.h" +#include "event-utils.h" static const char *input_buf; static unsigned long long input_buf_ptr; diff --git a/event-parse.h b/event-parse.h index c32d715..2e0222d 100644 --- a/event-parse.h +++ b/event-parse.h @@ -167,6 +167,8 @@ enum format_flags { FIELD_IS_STRING = 8, FIELD_IS_DYNAMIC = 16, FIELD_IS_LONG = 32, + FIELD_IS_FLAG = 64, + FIELD_IS_SYMBOLIC = 128, }; struct format_field { @@ -406,22 +408,6 @@ struct pevent { struct event_format *last_event; }; -/* Can be overridden */ -void die(const char *fmt, ...); -void *malloc_or_die(unsigned int size); -void warning(const char *fmt, ...); -void pr_stat(const char *fmt, ...); -void vpr_stat(const char *fmt, va_list ap); - -/* Always available */ -void __die(const char *fmt, ...); -void __warning(const char *fmt, ...); -void __pr_stat(const char *fmt, ...); - -void __vdie(const char *fmt, ...); -void __vwarning(const char *fmt, ...); -void __vpr_stat(const char *fmt, ...); - static inline unsigned short __data2host2(struct pevent *pevent, unsigned short data) { @@ -734,7 +720,7 @@ struct filter_arg_str { struct filter_arg { enum filter_arg_type type; union { - struct filter_arg_boolean bool; + struct filter_arg_boolean boolean; struct filter_arg_field field; struct filter_arg_value value; struct filter_arg_op op; diff --git a/event-utils.h b/event-utils.h index a8fb48f..0829638 100644 --- a/event-utils.h +++ b/event-utils.h @@ -23,6 +23,22 @@ #include +/* Can be overridden */ +void die(const char *fmt, ...); +void *malloc_or_die(unsigned int size); +void warning(const char *fmt, ...); +void pr_stat(const char *fmt, ...); +void vpr_stat(const char *fmt, va_list ap); + +/* Always available */ +void __die(const char *fmt, ...); +void __warning(const char *fmt, ...); +void __pr_stat(const char *fmt, ...); + +void __vdie(const char *fmt, ...); +void __vwarning(const char *fmt, ...); +void __vpr_stat(const char *fmt, ...); + static inline char *strim(char *string) { char *ret; diff --git a/parse-filter.c b/parse-filter.c index 1df9966..fe37182 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -359,7 +359,7 @@ create_arg_item(struct event_format *event, if (strcmp(token, COMM) != 0) { /* not a field, Make it false */ arg->type = FILTER_ARG_BOOLEAN; - arg->bool.value = FILTER_FALSE; + arg->boolean.value = FILTER_FALSE; break; } /* If token is 'COMM' then it is special */ @@ -487,7 +487,7 @@ static int add_right(struct filter_arg *op, struct filter_arg *arg, free_arg(left); free_arg(arg); op->type = FILTER_ARG_BOOLEAN; - op->bool.value = FILTER_FALSE; + op->boolean.value = FILTER_FALSE; break; } @@ -772,7 +772,7 @@ enum filter_vals test_arg(struct filter_arg *parent, struct filter_arg *arg) /* bad case */ case FILTER_ARG_BOOLEAN: - return FILTER_VAL_FALSE + arg->bool.value; + return FILTER_VAL_FALSE + arg->boolean.value; /* good cases: */ case FILTER_ARG_STR: @@ -871,7 +871,7 @@ static struct filter_arg *collapse_tree(struct filter_arg *arg) free_arg(arg); arg = allocate_arg(); arg->type = FILTER_ARG_BOOLEAN; - arg->bool.value = ret == FILTER_VAL_TRUE; + arg->boolean.value = ret == FILTER_VAL_TRUE; } return arg; @@ -1116,7 +1116,7 @@ process_event(struct event_format *event, const char *filter_str, if (!*parg) { *parg = allocate_arg(); (*parg)->type = FILTER_ARG_BOOLEAN; - (*parg)->bool.value = FILTER_FALSE; + (*parg)->boolean.value = FILTER_FALSE; } return 0; @@ -1139,7 +1139,7 @@ static int filter_event(struct event_filter *filter, /* just add a TRUE arg */ arg = allocate_arg(); arg->type = FILTER_ARG_BOOLEAN; - arg->bool.value = FILTER_TRUE; + arg->boolean.value = FILTER_TRUE; } filter_type = add_filter_type(filter, event->id); @@ -1369,9 +1369,9 @@ static int copy_filter_type(struct event_filter *filter, arg = allocate_arg(); arg->type = FILTER_ARG_BOOLEAN; if (strcmp(str, "TRUE") == 0) - arg->bool.value = 1; + arg->boolean.value = 1; else - arg->bool.value = 0; + arg->boolean.value = 0; filter_type = add_filter_type(filter, event->id); filter_type->filter = arg; @@ -1442,8 +1442,8 @@ int pevent_update_trivial(struct event_filter *dest, struct event_filter *source arg = filter_type->filter; if (arg->type != FILTER_ARG_BOOLEAN) continue; - if ((arg->bool.value && type == FILTER_TRIVIAL_FALSE) || - (!arg->bool.value && type == FILTER_TRIVIAL_TRUE)) + if ((arg->boolean.value && type == FILTER_TRIVIAL_FALSE) || + (!arg->boolean.value && type == FILTER_TRIVIAL_TRUE)) continue; event = filter_type->event; @@ -1497,10 +1497,10 @@ void pevent_filter_clear_trivial(struct event_filter *filter, continue; switch (type) { case FILTER_TRIVIAL_FALSE: - if (filter_type->filter->bool.value) + if (filter_type->filter->boolean.value) continue; case FILTER_TRIVIAL_TRUE: - if (!filter_type->filter->bool.value) + if (!filter_type->filter->boolean.value) continue; default: break; @@ -1551,10 +1551,10 @@ int pevent_filter_event_has_trivial(struct event_filter *filter, switch (type) { case FILTER_TRIVIAL_FALSE: - return !filter_type->filter->bool.value; + return !filter_type->filter->boolean.value; case FILTER_TRIVIAL_TRUE: - return filter_type->filter->bool.value; + return filter_type->filter->boolean.value; default: return 1; } @@ -1783,7 +1783,7 @@ static int test_filter(struct event_format *event, switch (arg->type) { case FILTER_ARG_BOOLEAN: /* easy case */ - return arg->bool.value; + return arg->boolean.value; case FILTER_ARG_OP: return test_op(event, arg, record); @@ -2147,7 +2147,7 @@ static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg) switch (arg->type) { case FILTER_ARG_BOOLEAN: str = malloc_or_die(6); - if (arg->bool.value) + if (arg->boolean.value) strcpy(str, "TRUE"); else strcpy(str, "FALSE"); diff --git a/trace-seq.c b/trace-seq.c index d2f265b..b1ccc92 100644 --- a/trace-seq.c +++ b/trace-seq.c @@ -24,6 +24,7 @@ #include #include "event-parse.h" +#include "event-utils.h" /* * The TRACE_SEQ_POISON is to catch the use of using -- cgit v1.2.3 From 09082dcc049a711418cf65ed76c6719302bd0e19 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 6 Apr 2012 00:47:57 +0200 Subject: perf/events: Add flag to produce nsec output libtraceevent library prints out in usecs but perf wants to print out in nsecs. Add a flag that lets the user decide to print out in usec or nsec times. Signed-off-by: Steven Rostedt Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Borislav Petkov Cc: Jiri Olsa Cc: Arun Sharma Cc: Namhyung Kim Signed-off-by: Frederic Weisbecker --- event-parse.c | 15 ++++++++++++--- event-parse.h | 12 ++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/event-parse.c b/event-parse.c index 3ce75b5..c799c19 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3940,15 +3940,16 @@ void pevent_print_event(struct pevent *pevent, struct trace_seq *s, struct event_format *event; unsigned long secs; unsigned long usecs; + unsigned long nsecs; const char *comm; void *data = record->data; int type; int pid; int len; + int p; secs = record->ts / NSECS_PER_SEC; - usecs = record->ts - secs * NSECS_PER_SEC; - usecs = (usecs + 500) / NSECS_PER_USEC; + nsecs = record->ts - secs * NSECS_PER_SEC; if (record->size < 0) { do_warning("ug! negative record size %d", record->size); @@ -3973,7 +3974,15 @@ void pevent_print_event(struct pevent *pevent, struct trace_seq *s, } else trace_seq_printf(s, "%16s-%-5d [%03d]", comm, pid, record->cpu); - trace_seq_printf(s, " %5lu.%06lu: %s: ", secs, usecs, event->name); + if (pevent->flags & PEVENT_NSEC_OUTPUT) { + usecs = nsecs; + p = 9; + } else { + usecs = (nsecs + 500) / NSECS_PER_USEC; + p = 6; + } + + trace_seq_printf(s, " %5lu.%0*lu: %s: ", secs, p, usecs, event->name); /* Space out the event names evenly. */ len = strlen(event->name); diff --git a/event-parse.h b/event-parse.h index 2e0222d..8852827 100644 --- a/event-parse.h +++ b/event-parse.h @@ -334,6 +334,10 @@ enum pevent_func_arg_type { PEVENT_FUNC_ARG_MAX_TYPES }; +enum pevent_flag { + PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */ +}; + struct cmdline; struct cmdline_list; struct func_map; @@ -373,6 +377,7 @@ struct pevent { struct printk_list *printklist; unsigned int printk_count; + struct event_format **events; int nr_events; struct event_format **sort_events; @@ -397,6 +402,8 @@ struct pevent { int test_filters; + int flags; + struct format_field *bprint_ip_field; struct format_field *bprint_fmt_field; struct format_field *bprint_buf_field; @@ -408,6 +415,11 @@ struct pevent { struct event_format *last_event; }; +static inline void pevent_set_flag(struct pevent *pevent, int flag) +{ + pevent->flags |= flag; +} + static inline unsigned short __data2host2(struct pevent *pevent, unsigned short data) { -- cgit v1.2.3 From 16d918fa17dee6ac2e5bea34e3af00fa80659d6a Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Fri, 6 Apr 2012 00:47:58 +0200 Subject: perf/events: Add flag/symbol format_flags This is a port of commit eb9a42caa7a92 perf trace: Add flag/symbolic format_flags of the old trace-event-parse.c to the new event-parse.c that was written by Tom Zanussi and forward ported by Steven Rostedt. Signed-off-by: Tom Zanussi Signed-off-by: Steven Rostedt Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Borislav Petkov Cc: Jiri Olsa Cc: Arun Sharma Cc: Namhyung Kim Signed-off-by: Frederic Weisbecker --- event-parse.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/event-parse.c b/event-parse.c index c799c19..f012395 100644 --- a/event-parse.c +++ b/event-parse.c @@ -39,6 +39,9 @@ static const char *input_buf; static unsigned long long input_buf_ptr; static unsigned long long input_buf_siz; +static int is_flag_field; +static int is_symbolic_field; + static int show_warning = 1; #define do_warning(fmt, ...) \ @@ -1789,6 +1792,16 @@ process_entry(struct event_format *event __unused, struct print_arg *arg, arg->type = PRINT_FIELD; arg->field.name = field; + if (is_flag_field) { + arg->field.field = pevent_find_any_field(event, arg->field.name); + arg->field.field->flags |= FIELD_IS_FLAG; + is_flag_field = 0; + } else if (is_symbolic_field) { + arg->field.field = pevent_find_any_field(event, arg->field.name); + arg->field.field->flags |= FIELD_IS_SYMBOLIC; + is_symbolic_field = 0; + } + type = read_token(&token); *tok = token; @@ -2398,10 +2411,12 @@ process_function(struct event_format *event, struct print_arg *arg, if (strcmp(token, "__print_flags") == 0) { free_token(token); + is_flag_field = 1; return process_flags(event, arg, tok); } if (strcmp(token, "__print_symbolic") == 0) { free_token(token); + is_symbolic_field = 1; return process_symbols(event, arg, tok); } if (strcmp(token, "__get_str") == 0) { -- cgit v1.2.3 From d3df208664240338a066cbf1908a4e22137d61f1 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 6 Apr 2012 00:47:59 +0200 Subject: perf/events: Correct size given to memset This is a forward port of commit 5660ce34241ab perf tools: Correct size given to memset written by Julia Lawall and forward ported by Steven Rostedt. Signed-off-by: Julia Lawall Signed-off-by: Steven Rostedt Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Borislav Petkov Cc: Jiri Olsa Cc: Arun Sharma Cc: Namhyung Kim Signed-off-by: Frederic Weisbecker --- event-parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index f012395..16da20c 100644 --- a/event-parse.c +++ b/event-parse.c @@ -2076,7 +2076,7 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char ** goto out_free; field = malloc_or_die(sizeof(*field)); - memset(field, 0, sizeof(field)); + memset(field, 0, sizeof(*field)); value = arg_eval(arg); field->value = strdup(value); -- cgit v1.2.3 From 8a5e5f380e2c40b176b261cdccdd7af8074f6058 Mon Sep 17 00:00:00 2001 From: Vaibhav Nagarnaik Date: Fri, 6 Apr 2012 00:48:00 +0200 Subject: parse-events: Handle invalid opcode parsing gracefully If an invalid opcode is encountered, trace-cmd exits with an error. Instead it can be treated as a soft error where the event's print format is not parsed and its binary data is dumped out. This patch adds a return value to arg_num_eval() function to indicate if the parsing was successful. If not, then the error is considered soft and the parsing of the offending event fails. Cc: Michael Rubin Cc: David Sharp Signed-off-by: Vaibhav Nagarnaik Link: http://lkml.kernel.org/r/1310785241-3799-2-git-send-email-vnagarnaik@google.com Signed-off-by: Steven Rostedt Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Borislav Petkov Cc: Jiri Olsa Cc: Arun Sharma Cc: Namhyung Kim Signed-off-by: Frederic Weisbecker --- event-parse.c | 125 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 83 insertions(+), 42 deletions(-) diff --git a/event-parse.c b/event-parse.c index 16da20c..ef2c65f 100644 --- a/event-parse.c +++ b/event-parse.c @@ -1915,90 +1915,120 @@ eval_type(unsigned long long val, struct print_arg *arg, int pointer) return eval_type_str(val, arg->typecast.type, pointer); } -static long long arg_num_eval(struct print_arg *arg) +static int arg_num_eval(struct print_arg *arg, long long *val) { long long left, right; - long long val = 0; + int ret = 1; switch (arg->type) { case PRINT_ATOM: - val = strtoll(arg->atom.atom, NULL, 0); + *val = strtoll(arg->atom.atom, NULL, 0); break; case PRINT_TYPE: - val = arg_num_eval(arg->typecast.item); - val = eval_type(val, arg, 0); + ret = arg_num_eval(arg->typecast.item, val); + if (!ret) + break; + *val = eval_type(*val, arg, 0); break; case PRINT_OP: switch (arg->op.op[0]) { case '|': - left = arg_num_eval(arg->op.left); - right = arg_num_eval(arg->op.right); + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; if (arg->op.op[1]) - val = left || right; + *val = left || right; else - val = left | right; + *val = left | right; break; case '&': - left = arg_num_eval(arg->op.left); - right = arg_num_eval(arg->op.right); + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; if (arg->op.op[1]) - val = left && right; + *val = left && right; else - val = left & right; + *val = left & right; break; case '<': - left = arg_num_eval(arg->op.left); - right = arg_num_eval(arg->op.right); + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; switch (arg->op.op[1]) { case 0: - val = left < right; + *val = left < right; break; case '<': - val = left << right; + *val = left << right; break; case '=': - val = left <= right; + *val = left <= right; break; default: - die("unknown op '%s'", arg->op.op); + do_warning("unknown op '%s'", arg->op.op); + ret = 0; } break; case '>': - left = arg_num_eval(arg->op.left); - right = arg_num_eval(arg->op.right); + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; switch (arg->op.op[1]) { case 0: - val = left > right; + *val = left > right; break; case '>': - val = left >> right; + *val = left >> right; break; case '=': - val = left >= right; + *val = left >= right; break; default: - die("unknown op '%s'", arg->op.op); + do_warning("unknown op '%s'", arg->op.op); + ret = 0; } 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); + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; - val = left == right; + if (arg->op.op[1] != '=') { + do_warning("unknown op '%s'", arg->op.op); + ret = 0; + } else + *val = left == right; break; case '!': - left = arg_num_eval(arg->op.left); - right = arg_num_eval(arg->op.right); + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; switch (arg->op.op[1]) { case '=': - val = left != right; + *val = left != right; break; default: - die("unknown op '%s'", arg->op.op); + do_warning("unknown op '%s'", arg->op.op); + ret = 0; } break; case '-': @@ -2006,12 +2036,17 @@ static long long arg_num_eval(struct print_arg *arg) if (arg->op.left->type == PRINT_NULL) left = 0; else - left = arg_num_eval(arg->op.left); - right = arg_num_eval(arg->op.right); - val = left - right; + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; + *val = left - right; break; default: - die("unknown op '%s'", arg->op.op); + do_warning("unknown op '%s'", arg->op.op); + ret = 0; } break; @@ -2020,10 +2055,11 @@ static long long arg_num_eval(struct print_arg *arg) case PRINT_STRING: case PRINT_BSTRING: default: - die("invalid eval type %d", arg->type); + do_warning("invalid eval type %d", arg->type); + ret = 0; } - return val; + return ret; } static char *arg_eval (struct print_arg *arg) @@ -2037,7 +2073,8 @@ static char *arg_eval (struct print_arg *arg) case PRINT_TYPE: return arg_eval(arg->typecast.item); case PRINT_OP: - val = arg_num_eval(arg); + if (!arg_num_eval(arg, &val)) + break; sprintf(buf, "%lld", val); return buf; @@ -2079,6 +2116,8 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char ** memset(field, 0, sizeof(*field)); value = arg_eval(arg); + if (value == NULL) + goto out_free; field->value = strdup(value); free_arg(arg); @@ -2090,6 +2129,8 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char ** goto out_free; value = arg_eval(arg); + if (value == NULL) + goto out_free; field->str = strdup(value); free_arg(arg); arg = NULL; -- cgit v1.2.3 From 9e274d5b4041e955576f4087802ea661a7276045 Mon Sep 17 00:00:00 2001 From: Vaibhav Nagarnaik Date: Fri, 6 Apr 2012 00:48:01 +0200 Subject: parse-events: Handle opcode parsing error If an invalid opcode is encountered in parsing event print format, the trace-cmd calls exit() without parsing any other events. This patch adds handling for such an error where the get_op_prio() is called. If the return value is -1, then the event print format parsing is skipped and parsing continues. Cc: Michael Rubin Cc: David Sharp Signed-off-by: Vaibhav Nagarnaik Link: http://lkml.kernel.org/r/1311619257-4970-1-git-send-email-vnagarnaik@google.com Signed-off-by: Steven Rostedt Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Borislav Petkov Cc: Jiri Olsa Cc: Arun Sharma Cc: Namhyung Kim Signed-off-by: Frederic Weisbecker --- event-parse.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/event-parse.c b/event-parse.c index ef2c65f..cdb32c7 100644 --- a/event-parse.c +++ b/event-parse.c @@ -1592,7 +1592,7 @@ static int get_op_prio(char *op) case '?': return 16; default: - die("unknown op '%c'", op[0]); + do_warning("unknown op '%c'", op[0]); return -1; } } else { @@ -1613,22 +1613,22 @@ static int get_op_prio(char *op) } else if (strcmp(op, "||") == 0) { return 15; } else { - die("unknown op '%s'", op); + do_warning("unknown op '%s'", op); return -1; } } } -static void set_op_prio(struct print_arg *arg) +static int set_op_prio(struct print_arg *arg) { /* single ops are the greatest */ - if (!arg->op.left || arg->op.left->type == PRINT_NULL) { + if (!arg->op.left || arg->op.left->type == PRINT_NULL) arg->op.prio = 0; - return; - } + else + arg->op.prio = get_op_prio(arg->op.op); - arg->op.prio = get_op_prio(arg->op.op); + return arg->op.prio; } /* Note, *tok does not get freed, but will most likely be saved */ @@ -1710,7 +1710,10 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) arg->op.op = token; arg->op.left = left; - set_op_prio(arg); + if (set_op_prio(arg) == -1) { + event->flags |= EVENT_FL_FAILED; + goto out_free; + } type = read_token_item(&token); *tok = token; -- cgit v1.2.3 From 526c1269e332a036968d2507dd9843e85fd2b8de Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 6 Apr 2012 00:48:02 +0200 Subject: parse-events: Let pevent_free() take a NULL pointer The pevent_free() should ack like other free()s and allow a NULL pointer to be passed to it which makes error handling a bit easier for the users of pevent_free(). Signed-off-by: Steven Rostedt Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Borislav Petkov Cc: Jiri Olsa Cc: Arun Sharma Cc: Namhyung Kim Signed-off-by: Frederic Weisbecker --- event-parse.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/event-parse.c b/event-parse.c index cdb32c7..4d5092f 100644 --- a/event-parse.c +++ b/event-parse.c @@ -4960,13 +4960,20 @@ static void free_event(struct event_format *event) */ void pevent_free(struct pevent *pevent) { - struct cmdline_list *cmdlist = pevent->cmdlist, *cmdnext; - struct func_list *funclist = pevent->funclist, *funcnext; - struct printk_list *printklist = pevent->printklist, *printknext; + struct cmdline_list *cmdlist, *cmdnext; + struct func_list *funclist, *funcnext; + struct printk_list *printklist, *printknext; struct pevent_function_handler *func_handler; struct event_handler *handle; int i; + if (!pevent) + return; + + cmdlist = pevent->cmdlist; + funclist = pevent->funclist; + printklist = pevent->printklist; + pevent->ref_count--; if (pevent->ref_count) return; -- cgit v1.2.3 From 10fdba5c174f2dc3db9cddcd40587b8eb2fb0990 Mon Sep 17 00:00:00 2001 From: Vaibhav Nagarnaik Date: Fri, 6 Apr 2012 00:48:03 +0200 Subject: parse-events: Support '+' opcode in print format The '+' opcode is not supported in the arguments for the print format. This patch adds support for it. Cc: Michael Rubin Cc: David Sharp Signed-off-by: Vaibhav Nagarnaik Link: http://lkml.kernel.org/r/1310785241-3799-4-git-send-email-vnagarnaik@google.com Signed-off-by: Steven Rostedt Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Borislav Petkov Cc: Jiri Olsa Cc: Arun Sharma Cc: Namhyung Kim Signed-off-by: Frederic Weisbecker --- event-parse.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/event-parse.c b/event-parse.c index 4d5092f..476626a 100644 --- a/event-parse.c +++ b/event-parse.c @@ -2047,6 +2047,18 @@ static int arg_num_eval(struct print_arg *arg, long long *val) break; *val = left - right; break; + case '+': + if (arg->op.left->type == PRINT_NULL) + left = 0; + else + ret = arg_num_eval(arg->op.left, &left); + if (!ret) + break; + ret = arg_num_eval(arg->op.right, &right); + if (!ret) + break; + *val = left + right; + break; default: do_warning("unknown op '%s'", arg->op.op); ret = 0; -- cgit v1.2.3 From 39f39d0073b3beaef4d8f4f3c4b0d96350308f02 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 6 Apr 2012 00:48:04 +0200 Subject: parse-events: Allow '*' and '/' operations in TP_printk Add multiply and divide operations in the printk format. Signed-off-by: Steven Rostedt Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Borislav Petkov Cc: Jiri Olsa Cc: Arun Sharma Cc: Namhyung Kim Signed-off-by: Frederic Weisbecker --- event-parse.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/event-parse.c b/event-parse.c index 476626a..876b272 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3130,6 +3130,12 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg case '+': val = left + right; break; + case '/': + val = left / right; + break; + case '*': + val = left * right; + break; default: die("unknown op '%s'", arg->op.op); } -- cgit v1.2.3 From 5650cb40c0cd7fa61b3da3e5d718d3f3dbea1c4f Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 6 Apr 2012 00:48:05 +0200 Subject: parse-event: Fix memset pointer size bug in handle Fix memset(ptr, 0, sizeof(ptr)) to memset(ptr, 0, sizeof(*ptr)) Signed-off-by: Steven Rostedt Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Borislav Petkov Cc: Jiri Olsa Cc: Arun Sharma Cc: Namhyung Kim Signed-off-by: Frederic Weisbecker --- event-parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index 876b272..ec13174 100644 --- a/event-parse.c +++ b/event-parse.c @@ -4904,7 +4904,7 @@ int pevent_register_event_handler(struct pevent *pevent, not_found: /* Save for later use. */ handle = malloc_or_die(sizeof(*handle)); - memset(handle, 0, sizeof(handle)); + memset(handle, 0, sizeof(*handle)); handle->id = id; if (event_name) handle->event_name = strdup(event_name); -- cgit v1.2.3 From 5ebb163e093809946357c16f7ad4746b7c7b3e0d Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 6 Apr 2012 00:48:06 +0200 Subject: parse-events: Rename struct record to struct pevent_record As libtraceevent will be a library, having struct record is far too generic of a name to use. Renaming it to be consistent with the rest of the functions will be a better long term solution. Signed-off-by: Steven Rostedt Cc: Ingo Molnar Cc: Thomas Gleixner Cc: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Steven Rostedt Cc: Borislav Petkov Cc: Jiri Olsa Cc: Arun Sharma Cc: Namhyung Kim Signed-off-by: Frederic Weisbecker --- event-parse.c | 22 +++++++++++----------- event-parse.h | 30 +++++++++++++++--------------- parse-filter.c | 24 ++++++++++++------------ 3 files changed, 38 insertions(+), 38 deletions(-) diff --git a/event-parse.c b/event-parse.c index ec13174..9985349 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3872,7 +3872,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event * and lock depth) and places it into the trace_seq. */ void pevent_data_lat_fmt(struct pevent *pevent, - struct trace_seq *s, struct record *record) + struct trace_seq *s, struct pevent_record *record) { static int check_lock_depth = 1; static int lock_depth_exists; @@ -3933,7 +3933,7 @@ void pevent_data_lat_fmt(struct pevent *pevent, * * This returns the event id from the @rec. */ -int pevent_data_type(struct pevent *pevent, struct record *rec) +int pevent_data_type(struct pevent *pevent, struct pevent_record *rec) { return trace_parse_common_type(pevent, rec->data); } @@ -3957,7 +3957,7 @@ struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type * * This returns the PID from a raw data. */ -int pevent_data_pid(struct pevent *pevent, struct record *rec) +int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec) { return parse_common_pid(pevent, rec->data); } @@ -3991,7 +3991,7 @@ const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid) * writes the print format into the trace_seq. */ void pevent_event_info(struct trace_seq *s, struct event_format *event, - struct record *record) + struct pevent_record *record) { int print_pretty = 1; @@ -4011,7 +4011,7 @@ void pevent_event_info(struct trace_seq *s, struct event_format *event, } void pevent_print_event(struct pevent *pevent, struct trace_seq *s, - struct record *record) + struct pevent_record *record) { static char *spaces = " "; /* 20 spaces */ struct event_format *event; @@ -4587,7 +4587,7 @@ int pevent_parse_event(struct pevent *pevent, } int get_field_val(struct trace_seq *s, struct format_field *field, - const char *name, struct record *record, + const char *name, struct pevent_record *record, unsigned long long *val, int err) { if (!field) { @@ -4620,7 +4620,7 @@ int get_field_val(struct trace_seq *s, struct format_field *field, * On failure, it returns NULL. */ void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, - const char *name, struct record *record, + const char *name, struct pevent_record *record, int *len, int err) { struct format_field *field; @@ -4667,7 +4667,7 @@ void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, * Returns 0 on success -1 on field not found. */ int pevent_get_field_val(struct trace_seq *s, struct event_format *event, - const char *name, struct record *record, + const char *name, struct pevent_record *record, unsigned long long *val, int err) { struct format_field *field; @@ -4692,7 +4692,7 @@ int pevent_get_field_val(struct trace_seq *s, struct event_format *event, * Returns 0 on success -1 on field not found. */ int pevent_get_common_field_val(struct trace_seq *s, struct event_format *event, - const char *name, struct record *record, + const char *name, struct pevent_record *record, unsigned long long *val, int err) { struct format_field *field; @@ -4717,7 +4717,7 @@ int pevent_get_common_field_val(struct trace_seq *s, struct event_format *event, * Returns 0 on success -1 on field not found. */ int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event, - const char *name, struct record *record, + const char *name, struct pevent_record *record, unsigned long long *val, int err) { struct format_field *field; @@ -4743,7 +4743,7 @@ int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event, */ int pevent_print_num_field(struct trace_seq *s, const char *fmt, struct event_format *event, const char *name, - struct record *record, int err) + struct pevent_record *record, int err) { struct format_field *field = pevent_find_field(event, name); unsigned long long val; diff --git a/event-parse.h b/event-parse.h index 8852827..ac997bc 100644 --- a/event-parse.h +++ b/event-parse.h @@ -39,7 +39,7 @@ #define DEBUG_RECORD 0 #endif -struct record { +struct pevent_record { unsigned long long ts; unsigned long long offset; long long missed_events; /* buffer dropped events before */ @@ -51,8 +51,8 @@ struct record { int locked; /* Do not free, even if ref_count is zero */ void *private; #if DEBUG_RECORD - struct record *prev; - struct record *next; + struct pevent_record *prev; + struct pevent_record *next; long alloc_addr; #endif }; @@ -91,7 +91,7 @@ struct pevent; struct event_format; typedef int (*pevent_event_handler_func)(struct trace_seq *s, - struct record *record, + struct pevent_record *record, struct event_format *event, void *context); @@ -497,7 +497,7 @@ int pevent_register_print_string(struct pevent *pevent, char *fmt, int pevent_pid_is_registered(struct pevent *pevent, int pid); void pevent_print_event(struct pevent *pevent, struct trace_seq *s, - struct record *record); + struct pevent_record *record); int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size, int long_size); @@ -506,22 +506,22 @@ int pevent_parse_event(struct pevent *pevent, const char *buf, unsigned long size, const char *sys); void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, - const char *name, struct record *record, + const char *name, struct pevent_record *record, int *len, int err); int pevent_get_field_val(struct trace_seq *s, struct event_format *event, - const char *name, struct record *record, + const char *name, struct pevent_record *record, unsigned long long *val, int err); int pevent_get_common_field_val(struct trace_seq *s, struct event_format *event, - const char *name, struct record *record, + const char *name, struct pevent_record *record, unsigned long long *val, int err); int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event, - const char *name, struct record *record, + const char *name, struct pevent_record *record, unsigned long long *val, int err); int pevent_print_num_field(struct trace_seq *s, const char *fmt, struct event_format *event, const char *name, - struct record *record, int err); + struct pevent_record *record, int err); int pevent_register_event_handler(struct pevent *pevent, int id, char *sys_name, char *event_name, pevent_event_handler_func func, void *context); @@ -547,13 +547,13 @@ struct event_format * pevent_find_event_by_name(struct pevent *pevent, const char *sys, const char *name); void pevent_data_lat_fmt(struct pevent *pevent, - struct trace_seq *s, struct record *record); -int pevent_data_type(struct pevent *pevent, struct record *rec); + struct trace_seq *s, struct pevent_record *record); +int pevent_data_type(struct pevent *pevent, struct pevent_record *rec); struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type); -int pevent_data_pid(struct pevent *pevent, struct record *rec); +int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec); const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid); void pevent_event_info(struct trace_seq *s, struct event_format *event, - struct record *record); + struct pevent_record *record); struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type); struct format_field **pevent_event_common_fields(struct event_format *event); @@ -773,7 +773,7 @@ int pevent_filter_add_filter_str(struct event_filter *filter, int pevent_filter_match(struct event_filter *filter, - struct record *record); + struct pevent_record *record); int pevent_event_filtered(struct event_filter *filter, int event_id); diff --git a/parse-filter.c b/parse-filter.c index fe37182..2d40c5e 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -1561,10 +1561,10 @@ int pevent_filter_event_has_trivial(struct event_filter *filter, } static int test_filter(struct event_format *event, - struct filter_arg *arg, struct record *record); + struct filter_arg *arg, struct pevent_record *record); static const char * -get_comm(struct event_format *event, struct record *record) +get_comm(struct event_format *event, struct pevent_record *record) { const char *comm; int pid; @@ -1576,7 +1576,7 @@ get_comm(struct event_format *event, struct record *record) static unsigned long long get_value(struct event_format *event, - struct format_field *field, struct record *record) + struct format_field *field, struct pevent_record *record) { unsigned long long val; @@ -1607,10 +1607,10 @@ get_value(struct event_format *event, } static unsigned long long -get_arg_value(struct event_format *event, struct filter_arg *arg, struct record *record); +get_arg_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record); static unsigned long long -get_exp_value(struct event_format *event, struct filter_arg *arg, struct record *record) +get_exp_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record) { unsigned long long lval, rval; @@ -1656,7 +1656,7 @@ get_exp_value(struct event_format *event, struct filter_arg *arg, struct record } static unsigned long long -get_arg_value(struct event_format *event, struct filter_arg *arg, struct record *record) +get_arg_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record) { switch (arg->type) { case FILTER_ARG_FIELD: @@ -1677,7 +1677,7 @@ get_arg_value(struct event_format *event, struct filter_arg *arg, struct record } static int test_num(struct event_format *event, - struct filter_arg *arg, struct record *record) + struct filter_arg *arg, struct pevent_record *record) { unsigned long long lval, rval; @@ -1709,7 +1709,7 @@ static int test_num(struct event_format *event, } } -static const char *get_field_str(struct filter_arg *arg, struct record *record) +static const char *get_field_str(struct filter_arg *arg, struct pevent_record *record) { const char *val = record->data + arg->str.field->offset; @@ -1727,7 +1727,7 @@ static const char *get_field_str(struct filter_arg *arg, struct record *record) } static int test_str(struct event_format *event, - struct filter_arg *arg, struct record *record) + struct filter_arg *arg, struct pevent_record *record) { const char *val; @@ -1757,7 +1757,7 @@ static int test_str(struct event_format *event, } static int test_op(struct event_format *event, - struct filter_arg *arg, struct record *record) + struct filter_arg *arg, struct pevent_record *record) { switch (arg->op.type) { case FILTER_OP_AND: @@ -1778,7 +1778,7 @@ static int test_op(struct event_format *event, } static int test_filter(struct event_format *event, - struct filter_arg *arg, struct record *record) + struct filter_arg *arg, struct pevent_record *record) { switch (arg->type) { case FILTER_ARG_BOOLEAN: @@ -1843,7 +1843,7 @@ int pevent_event_filtered(struct event_filter *filter, * -2 - if no filters exist */ int pevent_filter_match(struct event_filter *filter, - struct record *record) + struct pevent_record *record) { struct pevent *pevent = filter->pevent; struct filter_type *filter_type; -- cgit v1.2.3 From 09068ae426d31814c428bd05d686b43adde7b8ee Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Wed, 23 May 2012 11:36:42 +0900 Subject: tools lib traceevent: Allow expressions in __print_symbolic() fields The __print_symbolic() function takes a sequence of key-value pairs for pretty-printing a constant. The new kvm:kvm_exit print fmt uses the expression: __print_symbolic(..., { 0x040 + 1, "DB excp" }, ...) Currently only atoms are supported and this print fmt fails to parse. This patch adds support for expressions instead of just atoms so that 0x040 + 1 is parsed successfully. Cc: Avi Kivity Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1337740619-27925-6-git-send-email-namhyung.kim@lge.com Signed-off-by: Namhyung Kim Signed-off-by: Stefan Hajnoczi Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/event-parse.c b/event-parse.c index 9985349..33450c9 100644 --- a/event-parse.c +++ b/event-parse.c @@ -2124,6 +2124,13 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char ** free_token(token); type = process_arg(event, arg, &token); + + if (type == EVENT_OP) + type = process_op(event, arg, &token); + + if (type == EVENT_ERROR) + goto out_free; + if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free; -- cgit v1.2.3 From 0549864933e717186803c77ba567d40fbbf3a0ec Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 May 2012 11:36:47 +0900 Subject: tools lib traceevent: Fix a possible memory leak If event_read_fields failed in the middle, each member of struct format_field should be freed also. Cc: Borislav Petkov Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1337740619-27925-11-git-send-email-namhyung.kim@lge.com Signed-off-by: Namhyung Kim Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index 33450c9..d598b37 100644 --- a/event-parse.c +++ b/event-parse.c @@ -1434,8 +1434,11 @@ static int event_read_fields(struct event_format *event, struct format_field **f fail: free_token(token); fail_expect: - if (field) + if (field) { + free(field->type); + free(field->name); free(field); + } return -1; } -- cgit v1.2.3 From f1e2a334c7efabe8594cb4fb776e343242ae5c50 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 May 2012 11:36:49 +0900 Subject: tools lib traceevent: Fix a possibly wrong memory dereference If set_op_prio() failed, the token will be freed at out_free, then arg->op.op would turn out to be a dangle pointer. After returning EVENT_ERROR from process_op(), free_arg() will be called and then it will finally see the dangling pointer. Cc: Borislav Petkov Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1337740619-27925-13-git-send-email-namhyung.kim@lge.com Signed-off-by: Namhyung Kim Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/event-parse.c b/event-parse.c index d598b37..445a43a 100644 --- a/event-parse.c +++ b/event-parse.c @@ -1715,6 +1715,8 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) if (set_op_prio(arg) == -1) { event->flags |= EVENT_FL_FAILED; + /* arg->op.op (= token) will be freed at out_free */ + arg->op.op = NULL; goto out_free; } -- cgit v1.2.3 From 86cdb132f6773996a88682abf94a9df68df95b0e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 May 2012 11:36:50 +0900 Subject: tools lib traceevent: Fix freeing arg on process_dynamic_array() The @arg paremeter should not be freed inside of process_XXX(), because it'd be freed from the caller of process_arg(). We can free it only after it was reused for local usage. Cc: Borislav Petkov Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1337740619-27925-14-git-send-email-namhyung.kim@lge.com Signed-off-by: Namhyung Kim Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/event-parse.c b/event-parse.c index 445a43a..3559027 100644 --- a/event-parse.c +++ b/event-parse.c @@ -2300,17 +2300,18 @@ process_dynamic_array(struct event_format *event, struct print_arg *arg, char ** arg = alloc_arg(); type = process_arg(event, arg, &token); if (type == EVENT_ERROR) - goto out_free; + goto out_free_arg; if (!test_type_token(type, token, EVENT_OP, "]")) - goto out_free; + goto out_free_arg; free_token(token); type = read_token_item(tok); return type; + out_free_arg: + free_arg(arg); out_free: - free(arg); free_token(token); *tok = NULL; return EVENT_ERROR; -- cgit v1.2.3 From 32f133d07a72547308bf6bdb2922506bed8572fb Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 May 2012 11:36:51 +0900 Subject: tools lib traceevent: Use proper function parameter type The param needs to be updated when setting args up so that the loop in process_defined_func() can see the correct param->type for the farg. Cc: Borislav Petkov Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1337740619-27925-15-git-send-email-namhyung.kim@lge.com Signed-off-by: Namhyung Kim Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 1 + 1 file changed, 1 insertion(+) diff --git a/event-parse.c b/event-parse.c index 3559027..5548282 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3375,6 +3375,7 @@ process_defined_func(struct trace_seq *s, void *data, int size, break; } farg = farg->next; + param = param->next; } ret = (*func_handle->func)(s, args); -- cgit v1.2.3 From 7321ab3f54c0042839337c894b82b094e039bd0b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 23 May 2012 11:36:56 +0900 Subject: tools lib traceevent: Fix signature of create_arg_item() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The @type should be a type of enum event_type not enum filter_arg_type. This fixes following warning: $ make COMPILE FPIC parse-events.o COMPILE FPIC parse-filter.o /home/namhyung/project/trace-cmd/parse-filter.c: In function ‘create_arg_item’: /home/namhyung/project/trace-cmd/parse-filter.c:343:9: warning: comparison between ‘enum filter_arg_type’ and ‘enum event_type’ [-Wenum-compare] /home/namhyung/project/trace-cmd/parse-filter.c:339:2: warning: case value ‘8’ not in enumerated type ‘enum filter_arg_type’ [-Wswitch] BUILD STATIC LIB libparsevent.a BUILD STATIC LIB libtracecmd.a BUILD trace-cmd /usr/bin/make -C /home/namhyung/project/trace-cmd/Documentation all make[1]: Nothing to be done for `all'. Note: to build the gui, type "make gui" Cc: Borislav Petkov Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1337740619-27925-20-git-send-email-namhyung.kim@lge.com Signed-off-by: Namhyung Kim Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- parse-filter.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/parse-filter.c b/parse-filter.c index 2d40c5e..e08d21f 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -325,9 +325,8 @@ static void free_events(struct event_list *events) } static struct filter_arg * -create_arg_item(struct event_format *event, - const char *token, enum filter_arg_type type, - char **error_str) +create_arg_item(struct event_format *event, const char *token, + enum event_type type, char **error_str) { struct format_field *field; struct filter_arg *arg; -- cgit v1.2.3 From d4d65ab9259d2193b1473ba720e0cd593d57a72a Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sat, 26 May 2012 12:41:31 +0900 Subject: tools lib traceevent: Silence compiler warning on 32bit build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gcc complains about casting a pointer to unsigned long long directly: SUBDIR ../lib/traceevent/ CC FPIC event-parse.o CC FPIC trace-seq.o CC FPIC parse-filter.o /home/namhyung/project/linux/tools/lib/traceevent/parse-filter.c: In function ‘get_value’: /home/namhyung/project/linux/tools/lib/traceevent/parse-filter.c:1588: warning: cast from pointer to integer of different size CC FPIC parse-utils.o BUILD STATIC LIB libtraceevent.a Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1338003691-3141-1-git-send-email-namhyung@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- parse-filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse-filter.c b/parse-filter.c index e08d21f..dfcfe2c 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -1584,7 +1584,7 @@ get_value(struct event_format *event, const char *name; name = get_comm(event, record); - return (unsigned long long)name; + return (unsigned long)name; } pevent_read_number_field(field, record->data, &val); -- cgit v1.2.3 From 7cd5ca336c1ec252558a2dedc541ae2fecdae9ba Mon Sep 17 00:00:00 2001 From: Konstantin Stepanyuk Date: Wed, 27 Jun 2012 15:52:14 -0600 Subject: tools lib traceevent: Fix clean target in Makefile Dependency files were not cleaned up. Add missing space to fix the issue. Signed-off-by: Konstantin Stepanyuk Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1340833934-18783-1-git-send-email-konstantin.stepanyuk@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 3d69aa9..423f4b8 100644 --- a/Makefile +++ b/Makefile @@ -290,7 +290,7 @@ install_lib: all_cmd install_plugins install_python install: install_lib clean: - $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES).*.d + $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d $(RM) tags TAGS endif # skip-makefile -- cgit v1.2.3 From 72956cabe9dbe0f53c744dd170eff27a2256e7a5 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 22 Jun 2012 17:10:14 +0900 Subject: tools lib traceevent: Teach [ce]tags about libtraceeevent error codes As we use a macro trick to sync each error codes with its description string, teach [ce]tags to process them properly. This patch modifies the libtraceevent's Makefile not a kernel one. Suggested-by: Steven Rostedt Signed-off-by: Namhyung Kim Link: http://lkml.kernel.org/n/tip-3101nsbg52glxdqih291qj74@git.kernel.org Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1340352615-20737-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 423f4b8..34a577e 100644 --- a/Makefile +++ b/Makefile @@ -270,11 +270,13 @@ endif tags: force $(RM) tags - find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px + find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px \ + --regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/' TAGS: force $(RM) TAGS - find . -name '*.[ch]' | xargs etags + find . -name '*.[ch]' | xargs etags \ + --regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/' define do_install $(print_install) \ -- cgit v1.2.3 From ccb262afdaa0701cc303df3975190cbea0951b38 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 22 Jun 2012 14:37:36 +0900 Subject: tools lib traceevent: Make dependency files regeneratable Ingo reported that libtraceevent doesn't clean out dependency (.d) files and it can cause a build error when the libgcc package upgraded: comet:~/tip/tools/perf> make -j SUBDIR ../lib/traceevent/ make[1]: *** No rule to make target `/usr/lib/gcc/x86_64-redhat-linux/4.7.0/include/stddef.h', needed by `event-parse.o'. Stop. make: *** [../lib/traceevent//libtraceevent.a] Error 2 So this patch makes the .d files depends on the source and header files also, so that it can be re-generated as needed. NOTE: This code is copied from the GNU make manual page (4.14 Generating Prerequisites Automatically). Reported-by: Ingo Molnar Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1340343462-15556-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 34a577e..46c2f6b 100644 --- a/Makefile +++ b/Makefile @@ -250,8 +250,12 @@ endef all_objs := $(sort $(ALL_OBJS)) all_deps := $(all_objs:%.o=.%.d) +# let .d file also depends on the source and header files define check_deps - $(CC) -M $(CFLAGS) $< > $@; + @set -e; $(RM) $@; \ + $(CC) -M $(CFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + $(RM) $@.$$$$ endef $(gui_deps): ks_version.h -- cgit v1.2.3 From 83ef3924e7b39bf2adebee0126356354502f51fb Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 22 Jun 2012 17:10:15 +0900 Subject: tools lib traceevent: Check string is really printable When libtraceevent parses format fields, it assumes that array of 1 byte is string but it's not always true. The kvm_emulate_insn contains 15 u8 array of insn that contains (binary) instructions. Thus when it's printed, it'll have broken output like below: kvm_emulate_insn: [FAILED TO PARSE] rip=3238197797 csbase=0 len=2 \ insn=<89>P^]& flags=5 failed=0 With this patch: kvm_emulate_insn: [FAILED TO PARSE] rip=3238197797 csbase=0 len=2 \ insn=ARRAY[89, 10, 5d, c3, 8d, b4, 26, 00, 00, 00, 00, 55, 89, e5, 3e] flags=5 failed=0 Suggested-by: Steven Rostedt Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1340352615-20737-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index 5548282..b203a50 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3589,6 +3589,16 @@ static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size, trace_seq_printf(s, fmt, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); } +static int is_printable_array(char *p, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len && p[i]; i++) + if (!isprint(p[i])) + return 0; + return 1; +} + static void print_event_fields(struct trace_seq *s, void *data, int size, struct event_format *event) { @@ -3608,7 +3618,8 @@ static void print_event_fields(struct trace_seq *s, void *data, int size, len = offset >> 16; offset &= 0xffff; } - if (field->flags & FIELD_IS_STRING) { + if (field->flags & FIELD_IS_STRING && + is_printable_array(data + offset, len)) { trace_seq_printf(s, "%s", (char *)data + offset); } else { trace_seq_puts(s, "ARRAY["); @@ -3619,6 +3630,7 @@ static void print_event_fields(struct trace_seq *s, void *data, int size, *((unsigned char *)data + offset + i)); } trace_seq_putc(s, ']'); + field->flags &= ~FIELD_IS_STRING; } } else { val = pevent_read_number(event->pevent, data + field->offset, -- cgit v1.2.3 From 5e1b8b0f985a2b405e5ab5e6a2e853f8c0bb0ba4 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 27 Jun 2012 09:41:40 +0900 Subject: tools lib traceevent: Use local variable 'field' Use local variable 'field' to reduce typing. It is needed by later patch not to exceed 80 column. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1340757701-10711-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/event-parse.c b/event-parse.c index b203a50..63d02be 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3214,6 +3214,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, { struct pevent *pevent = event->pevent; struct print_flag_sym *flag; + struct format_field *field; unsigned long long val, fval; unsigned long addr; char *str; @@ -3228,27 +3229,29 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, print_str_to_seq(s, format, len_arg, arg->atom.atom); return; case PRINT_FIELD: - if (!arg->field.field) { - arg->field.field = pevent_find_any_field(event, arg->field.name); - if (!arg->field.field) + field = arg->field.field; + if (!field) { + field = pevent_find_any_field(event, arg->field.name); + if (!field) die("field %s not found", arg->field.name); + arg->field.field = field; } /* Zero sized fields, mean the rest of the data */ - len = arg->field.field->size ? : size - arg->field.field->offset; + len = field->size ? : size - field->offset; /* * Some events pass in pointers. If this is not an array * and the size is the same as long_size, assume that it * is a pointer. */ - if (!(arg->field.field->flags & FIELD_IS_ARRAY) && - arg->field.field->size == pevent->long_size) { - addr = *(unsigned long *)(data + arg->field.field->offset); + if (!(field->flags & FIELD_IS_ARRAY) && + field->size == pevent->long_size) { + addr = *(unsigned long *)(data + field->offset); trace_seq_printf(s, "%lx", addr); break; } str = malloc_or_die(len + 1); - memcpy(str, data + arg->field.field->offset, len); + memcpy(str, data + field->offset, len); str[len] = 0; print_str_to_seq(s, format, len_arg, str); free(str); -- cgit v1.2.3 From 8b018f546e6f9c6ac620b5bf35f8dd3540d478b4 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 27 Jun 2012 09:41:41 +0900 Subject: tools lib traceevent: Add support for __print_hex() Since the __print_hex() function is used in print fmt now, add corresponding parser routines. This makes the output of perf script on the kvm_emulate_insn event not to fail any more. before: kvm_emulate_insn: [FAILED TO PARSE] rip=3238197797 ... after: kvm_emulate_insn: 0:c102fa25:89 10 (prot32) Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1340757701-10711-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- event-parse.h | 7 ++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index 63d02be..b1abd39 100644 --- a/event-parse.c +++ b/event-parse.c @@ -697,6 +697,10 @@ static void free_arg(struct print_arg *arg) free_arg(arg->symbol.field); free_flag_sym(arg->symbol.symbols); break; + case PRINT_HEX: + free_arg(arg->hex.field); + free_arg(arg->hex.size); + break; case PRINT_TYPE: free(arg->typecast.type); free_arg(arg->typecast.item); @@ -2259,6 +2263,45 @@ process_symbols(struct event_format *event, struct print_arg *arg, char **tok) return EVENT_ERROR; } +static enum event_type +process_hex(struct event_format *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_HEX; + + field = alloc_arg(); + type = process_arg(event, field, &token); + + if (test_type_token(type, token, EVENT_DELIM, ",")) + goto out_free; + + arg->hex.field = field; + + free_token(token); + + field = alloc_arg(); + type = process_arg(event, field, &token); + + if (test_type_token(type, token, EVENT_DELIM, ")")) + goto out_free; + + arg->hex.size = field; + + free_token(token); + type = read_token_item(tok); + return type; + + out_free: + free_arg(field); + free_token(token); + *tok = NULL; + return EVENT_ERROR; +} + static enum event_type process_dynamic_array(struct event_format *event, struct print_arg *arg, char **tok) { @@ -2488,6 +2531,10 @@ process_function(struct event_format *event, struct print_arg *arg, is_symbolic_field = 1; return process_symbols(event, arg, tok); } + if (strcmp(token, "__print_hex") == 0) { + free_token(token); + return process_hex(event, arg, tok); + } if (strcmp(token, "__get_str") == 0) { free_token(token); return process_str(event, arg, tok); @@ -2995,6 +3042,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg break; case PRINT_FLAGS: case PRINT_SYMBOL: + case PRINT_HEX: break; case PRINT_TYPE: val = eval_num_arg(data, size, event, arg->typecast.item); @@ -3218,8 +3266,9 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, unsigned long long val, fval; unsigned long addr; char *str; + unsigned char *hex; int print; - int len; + int i, len; switch (arg->type) { case PRINT_NULL: @@ -3284,6 +3333,23 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, } } break; + case PRINT_HEX: + field = arg->hex.field->field.field; + if (!field) { + str = arg->hex.field->field.name; + field = pevent_find_any_field(event, str); + if (!field) + die("field %s not found", str); + arg->hex.field->field.field = field; + } + hex = data + field->offset; + len = eval_num_arg(data, size, event, arg->hex.size); + for (i = 0; i < len; i++) { + if (i) + trace_seq_putc(s, ' '); + trace_seq_printf(s, "%02x", hex[i]); + } + break; case PRINT_TYPE: break; @@ -4294,6 +4360,13 @@ static void print_args(struct print_arg *args) trace_seq_destroy(&s); printf(")"); break; + case PRINT_HEX: + printf("__print_hex("); + print_args(args->hex.field); + printf(", "); + print_args(args->hex.size); + printf(")"); + break; case PRINT_STRING: case PRINT_BSTRING: printf("__get_str(%s)", args->string.string); diff --git a/event-parse.h b/event-parse.h index ac997bc..5772ad8 100644 --- a/event-parse.h +++ b/event-parse.h @@ -226,6 +226,11 @@ struct print_arg_symbol { struct print_flag_sym *symbols; }; +struct print_arg_hex { + struct print_arg *field; + struct print_arg *size; +}; + struct print_arg_dynarray { struct format_field *field; struct print_arg *index; @@ -253,6 +258,7 @@ enum print_arg_type { PRINT_FIELD, PRINT_FLAGS, PRINT_SYMBOL, + PRINT_HEX, PRINT_TYPE, PRINT_STRING, PRINT_BSTRING, @@ -270,6 +276,7 @@ struct print_arg { struct print_arg_typecast typecast; struct print_arg_flags flags; struct print_arg_symbol symbol; + struct print_arg_hex hex; struct print_arg_func func; struct print_arg_string string; struct print_arg_op op; -- cgit v1.2.3 From f80a835a3cca113cea439b75c9c10cb9a6d171e5 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 11 Jun 2012 15:28:53 +0900 Subject: tools lib traceevent: Replace malloc_or_die to plain malloc in alloc_event() Because the only caller of the alloc_event() (pevent_parse_event) checks return value properly, it can be changed to use plain malloc. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1339396133-9839-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index b1abd39..83f0a8a 100644 --- a/event-parse.c +++ b/event-parse.c @@ -616,7 +616,9 @@ static struct event_format *alloc_event(void) { struct event_format *event; - event = malloc_or_die(sizeof(*event)); + event = malloc(sizeof(*event)); + if (!event) + return NULL; memset(event, 0, sizeof(*event)); return event; -- cgit v1.2.3 From dee43d8067350734c7be309e2c935904ed84db42 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 22 May 2012 14:45:21 +0900 Subject: tools lib traceevent: Let filtering numbers by string use function names As a pointer can be converted into a function name, let the filters work with the function name as well as with the pointer number. If the comparison expects a string, then convert numbers into functions, but only when the number is the same size as a long. Signed-off-by: Steven Rostedt Link: http://lkml.kernel.org/n/tip-oxsa1qkr2eq7u8d7r0aapedu@git.kernel.org Signed-off-by: Namhyung Kim --- parse-filter.c | 45 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/parse-filter.c b/parse-filter.c index dfcfe2c..80d872a 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -1710,18 +1710,43 @@ static int test_num(struct event_format *event, static const char *get_field_str(struct filter_arg *arg, struct pevent_record *record) { - const char *val = record->data + arg->str.field->offset; + struct event_format *event; + struct pevent *pevent; + unsigned long long addr; + const char *val = NULL; + char hex[64]; - /* - * We need to copy the data since we can't be sure the field - * is null terminated. - */ - if (*(val + arg->str.field->size - 1)) { - /* copy it */ - memcpy(arg->str.buffer, val, arg->str.field->size); - /* the buffer is already NULL terminated */ - val = arg->str.buffer; + /* If the field is not a string convert it */ + if (arg->str.field->flags & FIELD_IS_STRING) { + val = record->data + arg->str.field->offset; + + /* + * We need to copy the data since we can't be sure the field + * is null terminated. + */ + if (*(val + arg->str.field->size - 1)) { + /* copy it */ + memcpy(arg->str.buffer, val, arg->str.field->size); + /* the buffer is already NULL terminated */ + val = arg->str.buffer; + } + + } else { + event = arg->str.field->event; + pevent = event->pevent; + addr = get_value(event, arg->str.field, record); + + if (arg->str.field->flags & (FIELD_IS_POINTER | FIELD_IS_LONG)) + /* convert to a kernel symbol */ + val = pevent_find_function(pevent, addr); + + if (val == NULL) { + /* just use the hex of the string name */ + snprintf(hex, 64, "0x%llx", addr); + val = hex; + } } + return val; } -- cgit v1.2.3 From 2d5718acee21a504f16cd9d72555d3648e959854 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 15 Nov 2011 18:47:48 -0500 Subject: tools lib traceevent: Add support for "%.*s" in bprintk events The arg notation of '*' in bprintks is not handled by the parser. Implement it so that they show up properly in the output and do not kill the tracer from reporting events. Reported-by: Johannes Berg Signed-off-by: Steven Rostedt Link: http://lkml.kernel.org/n/tip-t0ctq7t1xz3ud6wv4v886jou@git.kernel.org Signed-off-by: Namhyung Kim --- event-parse.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/event-parse.c b/event-parse.c index 83f0a8a..0644c2a 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3370,7 +3370,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, break; } case PRINT_BSTRING: - trace_seq_printf(s, format, arg->string.string); + print_str_to_seq(s, format, len_arg, arg->string.string); break; case PRINT_OP: /* @@ -3471,6 +3471,7 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc unsigned long long ip, val; char *ptr; void *bptr; + int vsize; field = pevent->bprint_buf_field; ip_field = pevent->bprint_ip_field; @@ -3519,6 +3520,8 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc goto process_again; case '0' ... '9': goto process_again; + case '.': + goto process_again; case 'p': ls = 1; /* fall through */ @@ -3526,23 +3529,29 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc case 'u': case 'x': case 'i': - /* the pointers are always 4 bytes aligned */ - bptr = (void *)(((unsigned long)bptr + 3) & - ~3); switch (ls) { case 0: - ls = 4; + vsize = 4; break; case 1: - ls = pevent->long_size; + vsize = pevent->long_size; break; case 2: - ls = 8; + vsize = 8; default: + vsize = ls; /* ? */ break; } - val = pevent_read_number(pevent, bptr, ls); - bptr += ls; + /* fall through */ + case '*': + if (*ptr == '*') + vsize = 4; + + /* the pointers are always 4 bytes aligned */ + bptr = (void *)(((unsigned long)bptr + 3) & + ~3); + val = pevent_read_number(pevent, bptr, vsize); + bptr += vsize; arg = alloc_arg(); arg->next = NULL; arg->type = PRINT_ATOM; @@ -3550,6 +3559,13 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc sprintf(arg->atom.atom, "%lld", val); *next = arg; next = &arg->next; + /* + * The '*' case means that an arg is used as the length. + * We need to continue to figure out for what. + */ + if (*ptr == '*') + goto process_again; + break; case 's': arg = alloc_arg(); -- cgit v1.2.3 From 82ccc7733662d2509ee5ce1c4a5277015e1022f6 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 22 May 2012 14:52:40 +0900 Subject: tools lib traceevent: Add support to show migrate disable counter The RT kernel added a migrate disable counter in all events. Add support to show this in the latency format. Signed-off-by: Steven Rostedt Link: http://lkml.kernel.org/n/tip-l6ulxyda952g7kua4pfsh73k@git.kernel.org Signed-off-by: Namhyung Kim --- event-parse.c | 57 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/event-parse.c b/event-parse.c index 0644c2a..872dd35 100644 --- a/event-parse.c +++ b/event-parse.c @@ -2884,7 +2884,7 @@ static int get_common_info(struct pevent *pevent, event = pevent->events[0]; field = pevent_find_common_field(event, type); if (!field) - die("field '%s' not found", type); + return -1; *offset = field->offset; *size = field->size; @@ -2935,15 +2935,16 @@ static int parse_common_flags(struct pevent *pevent, void *data) static int parse_common_lock_depth(struct pevent *pevent, void *data) { - int ret; - - ret = __parse_common(pevent, data, - &pevent->ld_size, &pevent->ld_offset, - "common_lock_depth"); - if (ret < 0) - return -1; + return __parse_common(pevent, data, + &pevent->ld_size, &pevent->ld_offset, + "common_lock_depth"); +} - return ret; +static int parse_common_migrate_disable(struct pevent *pevent, void *data) +{ + return __parse_common(pevent, data, + &pevent->ld_size, &pevent->ld_offset, + "common_migrate_disable"); } static int events_id_cmp(const void *a, const void *b); @@ -3988,10 +3989,13 @@ void pevent_data_lat_fmt(struct pevent *pevent, struct trace_seq *s, struct pevent_record *record) { static int check_lock_depth = 1; + static int check_migrate_disable = 1; static int lock_depth_exists; + static int migrate_disable_exists; unsigned int lat_flags; unsigned int pc; int lock_depth; + int migrate_disable; int hardirq; int softirq; void *data = record->data; @@ -3999,18 +4003,26 @@ void pevent_data_lat_fmt(struct pevent *pevent, lat_flags = parse_common_flags(pevent, data); pc = parse_common_pc(pevent, data); /* lock_depth may not always exist */ - if (check_lock_depth) { - struct format_field *field; - struct event_format *event; - - check_lock_depth = 0; - event = pevent->events[0]; - field = pevent_find_common_field(event, "common_lock_depth"); - if (field) - lock_depth_exists = 1; - } if (lock_depth_exists) lock_depth = parse_common_lock_depth(pevent, data); + else if (check_lock_depth) { + lock_depth = parse_common_lock_depth(pevent, data); + if (lock_depth < 0) + check_lock_depth = 0; + else + lock_depth_exists = 1; + } + + /* migrate_disable may not always exist */ + if (migrate_disable_exists) + migrate_disable = parse_common_migrate_disable(pevent, data); + else if (check_migrate_disable) { + migrate_disable = parse_common_migrate_disable(pevent, data); + if (migrate_disable < 0) + check_migrate_disable = 0; + else + migrate_disable_exists = 1; + } hardirq = lat_flags & TRACE_FLAG_HARDIRQ; softirq = lat_flags & TRACE_FLAG_SOFTIRQ; @@ -4029,6 +4041,13 @@ void pevent_data_lat_fmt(struct pevent *pevent, else trace_seq_putc(s, '.'); + if (migrate_disable_exists) { + if (migrate_disable < 0) + trace_seq_putc(s, '.'); + else + trace_seq_printf(s, "%d", migrate_disable); + } + if (lock_depth_exists) { if (lock_depth < 0) trace_seq_putc(s, '.'); -- cgit v1.2.3 From 3de8c72426bc1c290a80d1e90fc370d7402bc1d7 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 9 Jan 2012 15:58:09 -0500 Subject: tools lib traceevent: Fix %pM print format arg handling When %pM is used, the arg value must be a 6 byte character that will be printed as a 6 byte MAC address. But the code does a break over the main code which updates the current processing arg to point to the next arg. If there are other print arguments after a %pM, they will be off by one. The next arg will still be processing the %pM arg. Reported-by: Johannes Berg Signed-off-by: Steven Rostedt Link: http://lkml.kernel.org/n/tip-q3g0n1espikynsdkpbi6ue6t@git.kernel.org Signed-off-by: Namhyung Kim --- event-parse.c | 1 + 1 file changed, 1 insertion(+) diff --git a/event-parse.c b/event-parse.c index 872dd35..da06c33 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3858,6 +3858,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event } else if (*(ptr+1) == 'M' || *(ptr+1) == 'm') { print_mac_arg(s, *(ptr+1), data, size, event, arg); ptr++; + arg = arg->next; break; } -- cgit v1.2.3 From 67ea988d4fdb3d7aab579e34289bca371ccbace1 Mon Sep 17 00:00:00 2001 From: Wolfgang Mauerer Date: Thu, 22 Mar 2012 11:18:21 +0100 Subject: tools lib traceevent: Fix trace_printk for long integers On 32 bit systems, a conversion of the trace_printk format string "%lu" -> "%llu" is intended (similar for %lx etc.) when a trace was taken on a machine with 64 bit long integers. However, the current code computes the bogus transformation "%lu" -> "%u". Fix this. Besides that, the transformation is only required on systems that don't use 64 bits for long integers natively. Signed-off-by: Wolfgang Mauerer Signed-off-by: Steven Rostedt Link: http://lkml.kernel.org/r/1332411501-8059-3-git-send-email-wolfgang.mauerer@siemens.com Signed-off-by: Namhyung Kim --- event-parse.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/event-parse.c b/event-parse.c index da06c33..ddee5a8 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3895,14 +3895,15 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event break; } } - if (pevent->long_size == 8 && ls) { + if (pevent->long_size == 8 && ls && + sizeof(long) != 8) { char *p; ls = 2; /* make %l into %ll */ p = strchr(format, 'l'); if (p) - memmove(p, p+1, strlen(p)+1); + memmove(p+1, p, strlen(p)+1); else if (strcmp(format, "%p") == 0) strcpy(format, "0x%llx"); } -- cgit v1.2.3 From 95a3d398c9be084026a9735742b818f1fe6e8f05 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Apr 2012 11:54:29 +0900 Subject: tools lib traceevent: Fix printk_cmp() The printk_cmp function should use printk_map instead of func_map. Also rename the variables for consistency. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: David Ahern Link: http://lkml.kernel.org/r/1333940074-19052-3-git-send-email-namhyung.kim@lge.com Signed-off-by: Steven Rostedt Signed-off-by: Namhyung Kim --- event-parse.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/event-parse.c b/event-parse.c index ddee5a8..7815b8d 100644 --- a/event-parse.c +++ b/event-parse.c @@ -511,12 +511,12 @@ struct printk_list { static int printk_cmp(const void *a, const void *b) { - const struct func_map *fa = a; - const struct func_map *fb = b; + const struct printk_map *pa = a; + const struct printk_map *pb = b; - if (fa->addr < fb->addr) + if (pa->addr < pb->addr) return -1; - if (fa->addr > fb->addr) + if (pa->addr > pb->addr) return 1; return 0; -- cgit v1.2.3 From dc1f2c30383978b279609b0a8bd0b250be2b2122 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Apr 2012 11:54:30 +0900 Subject: tools lib traceevent: Introduce extend_token() The __read_token() function has some duplicated code to handle internal buffer overflow. Factor them out to new extend_token(). According to the man pages of realloc/free(3), they can handle NULL pointer input so that it can be ended up to compact the code. Also handle error path correctly. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: David Ahern Link: http://lkml.kernel.org/r/1333940074-19052-4-git-send-email-namhyung.kim@lge.com [rostedt@goodmis.org: added some extra whitespace] Signed-off-by: Steven Rostedt Signed-off-by: Namhyung Kim --- event-parse.c | 50 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/event-parse.c b/event-parse.c index 7815b8d..768fab5 100644 --- a/event-parse.c +++ b/event-parse.c @@ -781,6 +781,25 @@ int pevent_peek_char(void) return __peek_char(); } +static int extend_token(char **tok, char *buf, int size) +{ + char *newtok = realloc(*tok, size); + + if (!newtok) { + free(*tok); + *tok = NULL; + return -1; + } + + if (!*tok) + strcpy(newtok, buf); + else + strcat(newtok, buf); + *tok = newtok; + + return 0; +} + static enum event_type force_token(const char *str, char **tok); static enum event_type __read_token(char **tok) @@ -865,17 +884,10 @@ static enum event_type __read_token(char **tok) 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); + tok_size += BUFSIZ; - if (!*tok) + if (extend_token(tok, buf, tok_size) < 0) return EVENT_NONE; - tok_size += BUFSIZ; i = 0; } last_ch = ch; @@ -914,17 +926,10 @@ static enum event_type __read_token(char **tok) 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); + tok_size += BUFSIZ; - if (!*tok) + if (extend_token(tok, buf, tok_size) < 0) return EVENT_NONE; - tok_size += BUFSIZ; i = 0; } ch = __read_char(); @@ -933,14 +938,7 @@ static enum event_type __read_token(char **tok) 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) + if (extend_token(tok, buf, tok_size + i + 1) < 0) return EVENT_NONE; if (type == EVENT_ITEM) { -- cgit v1.2.3 From a2890f7152dd4e9d51e98d63eedfc9005c20fbde Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Apr 2012 11:54:31 +0900 Subject: tools lib traceevent: Handle strdup failure cases There were some places didn't check return value of the strdup and had unneeded/duplicated checks. Fix it. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: David Ahern Link: http://lkml.kernel.org/r/1333940074-19052-5-git-send-email-namhyung.kim@lge.com Signed-off-by: Steven Rostedt Signed-off-by: Namhyung Kim --- event-parse.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/event-parse.c b/event-parse.c index 768fab5..cd334d5 100644 --- a/event-parse.c +++ b/event-parse.c @@ -467,8 +467,10 @@ int pevent_register_function(struct pevent *pevent, char *func, item->mod = NULL; item->addr = addr; - pevent->funclist = item; + if (!item->func || (mod && !item->mod)) + die("malloc func"); + pevent->funclist = item; pevent->func_count++; return 0; @@ -583,10 +585,13 @@ int pevent_register_print_string(struct pevent *pevent, char *fmt, item = malloc_or_die(sizeof(*item)); item->next = pevent->printklist; - pevent->printklist = item; item->printk = strdup(fmt); item->addr = addr; + if (!item->printk) + die("malloc fmt"); + + pevent->printklist = item; pevent->printk_count++; return 0; @@ -2150,6 +2155,8 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char ** if (value == NULL) goto out_free; field->value = strdup(value); + if (field->value == NULL) + goto out_free; free_arg(arg); arg = alloc_arg(); @@ -2163,6 +2170,8 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char ** if (value == NULL) goto out_free; field->str = strdup(value); + if (field->str == NULL) + goto out_free; free_arg(arg); arg = NULL; @@ -3433,6 +3442,9 @@ process_defined_func(struct trace_seq *s, void *data, int size, string = malloc_or_die(sizeof(*string)); string->next = strings; string->str = strdup(str.buffer); + if (!string->str) + die("malloc str"); + strings = string; trace_seq_destroy(&str); break; @@ -3571,6 +3583,8 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc arg->next = NULL; arg->type = PRINT_BSTRING; arg->string.string = strdup(bptr); + if (!arg->string.string) + break; bptr += strlen(bptr) + 1; *next = arg; next = &arg->next; @@ -4666,6 +4680,8 @@ int pevent_parse_event(struct pevent *pevent, die("failed to read event id"); event->system = strdup(sys); + if (!event->system) + die("failed to allocate system"); /* Add pevent to event so that it can be referenced */ event->pevent = pevent; @@ -4707,6 +4723,10 @@ int pevent_parse_event(struct pevent *pevent, list = &arg->next; arg->type = PRINT_FIELD; arg->field.name = strdup(field->name); + if (!arg->field.name) { + do_warning("failed to allocate field name"); + goto event_failed; + } arg->field.field = field; } return 0; @@ -5050,6 +5070,11 @@ int pevent_register_event_handler(struct pevent *pevent, if (sys_name) handle->sys_name = strdup(sys_name); + if ((event_name && !handle->event_name) || + (sys_name && !handle->sys_name)) { + die("Failed to allocate event/sys name"); + } + handle->func = func; handle->next = pevent->handlers; pevent->handlers = handle; -- cgit v1.2.3 From ae6c0749ca74ba65e72662d5f34f0218d2c7be68 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Apr 2012 11:54:33 +0900 Subject: tools lib traceevent: Handle realloc() failure path The realloc can fail so that we should handle it properly. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: David Ahern Link: http://lkml.kernel.org/r/1333940074-19052-7-git-send-email-namhyung.kim@lge.com Signed-off-by: Steven Rostedt Signed-off-by: Namhyung Kim --- event-parse.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 16 deletions(-) diff --git a/event-parse.c b/event-parse.c index cd334d5..05eb6b4 100644 --- a/event-parse.c +++ b/event-parse.c @@ -1264,9 +1264,15 @@ static int event_read_fields(struct event_format *event, struct format_field **f field->flags |= FIELD_IS_POINTER; if (field->type) { - field->type = realloc(field->type, - strlen(field->type) + - strlen(last_token) + 2); + char *new_type; + new_type = realloc(field->type, + strlen(field->type) + + strlen(last_token) + 2); + if (!new_type) { + free(last_token); + goto fail; + } + field->type = new_type; strcat(field->type, " "); strcat(field->type, last_token); free(last_token); @@ -1291,6 +1297,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f if (strcmp(token, "[") == 0) { enum event_type last_type = type; char *brackets = token; + char *new_brackets; int len; field->flags |= FIELD_IS_ARRAY; @@ -1310,9 +1317,14 @@ static int event_read_fields(struct event_format *event, struct format_field **f len = 1; last_type = type; - brackets = realloc(brackets, - strlen(brackets) + - strlen(token) + len); + new_brackets = realloc(brackets, + strlen(brackets) + + strlen(token) + len); + if (!new_brackets) { + free(brackets); + goto fail; + } + brackets = new_brackets; if (len == 2) strcat(brackets, " "); strcat(brackets, token); @@ -1328,7 +1340,12 @@ static int event_read_fields(struct event_format *event, struct format_field **f free_token(token); - brackets = realloc(brackets, strlen(brackets) + 2); + new_brackets = realloc(brackets, strlen(brackets) + 2); + if (!new_brackets) { + free(brackets); + goto fail; + } + brackets = new_brackets; strcat(brackets, "]"); /* add brackets to type */ @@ -1339,10 +1356,16 @@ static int event_read_fields(struct event_format *event, struct format_field **f * the format: type [] item; */ if (type == EVENT_ITEM) { - field->type = realloc(field->type, - strlen(field->type) + - strlen(field->name) + - strlen(brackets) + 2); + char *new_type; + new_type = realloc(field->type, + strlen(field->type) + + strlen(field->name) + + strlen(brackets) + 2); + if (!new_type) { + free(brackets); + goto fail; + } + field->type = new_type; strcat(field->type, " "); strcat(field->type, field->name); free_token(field->name); @@ -1350,9 +1373,15 @@ static int event_read_fields(struct event_format *event, struct format_field **f field->name = token; type = read_token(&token); } else { - field->type = realloc(field->type, - strlen(field->type) + - strlen(brackets) + 1); + char *new_type; + new_type = realloc(field->type, + strlen(field->type) + + strlen(brackets) + 1); + if (!new_type) { + free(brackets); + goto fail; + } + field->type = new_type; strcat(field->type, brackets); } free(brackets); @@ -1735,10 +1764,16 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) /* could just be a type pointer */ if ((strcmp(arg->op.op, "*") == 0) && type == EVENT_DELIM && (strcmp(token, ")") == 0)) { + char *new_atom; + if (left->type != PRINT_ATOM) die("bad pointer type"); - left->atom.atom = realloc(left->atom.atom, + new_atom = realloc(left->atom.atom, strlen(left->atom.atom) + 3); + if (!new_atom) + goto out_free; + + left->atom.atom = new_atom; strcat(left->atom.atom, " *"); free(arg->op.op); *arg = *left; @@ -2597,7 +2632,16 @@ process_arg_token(struct event_format *event, struct print_arg *arg, } /* atoms can be more than one token long */ while (type == EVENT_ITEM) { - atom = realloc(atom, strlen(atom) + strlen(token) + 2); + char *new_atom; + new_atom = realloc(atom, + strlen(atom) + strlen(token) + 2); + if (!new_atom) { + free(atom); + *tok = NULL; + free_token(token); + return EVENT_ERROR; + } + atom = new_atom; strcat(atom, " "); strcat(atom, token); free_token(token); -- cgit v1.2.3 From 06012b427231f02af0430d8901e7257cfcd0e310 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 23 Apr 2012 13:58:33 +0900 Subject: tools lib traceevent: Pass string type argument to args It seems PEVENT_FUNC_ARG_STRING missed passing the allocated string to the args array. Fix it. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Namhyung Kim Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: David Ahern Link: http://lkml.kernel.org/r/1335157118-14658-7-git-send-email-namhyung.kim@lge.com Signed-off-by: Steven Rostedt Signed-off-by: Namhyung Kim --- event-parse.c | 1 + 1 file changed, 1 insertion(+) diff --git a/event-parse.c b/event-parse.c index 05eb6b4..337ca02 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3489,6 +3489,7 @@ process_defined_func(struct trace_seq *s, void *data, int size, if (!string->str) die("malloc str"); + args[i] = (unsigned long long)string->str; strings = string; trace_seq_destroy(&str); break; -- cgit v1.2.3 From c9690c460e0c0d51a9f985e65ee0100158f17579 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 23 Apr 2012 13:58:34 +0900 Subject: tools lib traceevent: Do not call add_event() again if allocation failed When memory allocation for the field name is failed, do not goto event_failed since we added the event already. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Namhyung Kim Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: David Ahern Link: http://lkml.kernel.org/r/1335157118-14658-8-git-send-email-namhyung.kim@lge.com Signed-off-by: Steven Rostedt Signed-off-by: Namhyung Kim --- event-parse.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index 337ca02..99b0cd4 100644 --- a/event-parse.c +++ b/event-parse.c @@ -4770,7 +4770,8 @@ int pevent_parse_event(struct pevent *pevent, arg->field.name = strdup(field->name); if (!arg->field.name) { do_warning("failed to allocate field name"); - goto event_failed; + event->flags |= EVENT_FL_FAILED; + return -1; } arg->field.field = field; } -- cgit v1.2.3 From f8716bec6a718824e653aa8c9d97550bf4fd33d0 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 23 Apr 2012 13:58:35 +0900 Subject: tools lib traceevent: Fix some comments Update and add missing argument descriptions and fix some typo on function comments. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Namhyung Kim Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: David Ahern Link: http://lkml.kernel.org/r/1335157118-14658-9-git-send-email-namhyung.kim@lge.com Signed-off-by: Steven Rostedt Signed-off-by: Namhyung Kim --- event-parse.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/event-parse.c b/event-parse.c index 99b0cd4..8630160 100644 --- a/event-parse.c +++ b/event-parse.c @@ -4037,8 +4037,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event * pevent_data_lat_fmt - parse the data for the latency format * @pevent: a handle to the pevent * @s: the trace_seq to write to - * @data: the raw data to read from - * @size: currently unused. + * @record: the record to read from * * This parses out the Latency format (interrupts disabled, * need rescheduling, in hard/soft interrupt, preempt count @@ -4173,10 +4172,7 @@ const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid) * pevent_data_comm_from_pid - parse the data into the print format * @s: the trace_seq to write to * @event: the handle to the event - * @cpu: the cpu the event was recorded on - * @data: the raw data - * @size: the size of the raw data - * @nsecs: the timestamp of the event + * @record: the record to read from * * This parses the raw @data using the given @event information and * writes the print format into the trace_seq. @@ -4944,7 +4940,7 @@ int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event, * @record: The record with the field name. * @err: print default error if failed. * - * Returns: 0 on success, -1 field not fould, or 1 if buffer is full. + * Returns: 0 on success, -1 field not found, or 1 if buffer is full. */ int pevent_print_num_field(struct trace_seq *s, const char *fmt, struct event_format *event, const char *name, @@ -4986,11 +4982,12 @@ static void free_func_handle(struct pevent_function_handler *func) * pevent_register_print_function - register a helper function * @pevent: the handle to the pevent * @func: the function to process the helper function + * @ret_type: the return type of the helper function * @name: the name of the helper function * @parameters: A list of enum pevent_func_arg_type * * Some events may have helper functions in the print format arguments. - * This allows a plugin to dynmically create a way to process one + * This allows a plugin to dynamically create a way to process one * of these functions. * * The @parameters is a variable list of pevent_func_arg_type enums that @@ -5061,12 +5058,13 @@ int pevent_register_print_function(struct pevent *pevent, } /** - * pevent_register_event_handle - register a way to parse an event + * pevent_register_event_handler - register a way to parse an event * @pevent: the handle to the pevent * @id: the id of the event to register * @sys_name: the system name the event belongs to * @event_name: the name of the event * @func: the function to call to parse the event information + * @context: the data to be passed to @func * * This function allows a developer to override the parsing of * a given event. If for some reason the default print format -- cgit v1.2.3 From ab81f1b48ecda5bbb0395b727baeb78b424ec701 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 23 Apr 2012 13:58:36 +0900 Subject: tools lib traceevent: Check result of malloc() during reading token The malloc can fail so the return value should be checked. For now, just use malloc_or_die(). Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Namhyung Kim Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: David Ahern Link: http://lkml.kernel.org/r/1335157118-14658-10-git-send-email-namhyung.kim@lge.com Signed-off-by: Steven Rostedt Signed-off-by: Namhyung Kim --- parse-filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parse-filter.c b/parse-filter.c index 80d872a..d54c2b4 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -96,7 +96,7 @@ static enum event_type read_token(char **tok) (strcmp(token, "=") == 0 || strcmp(token, "!") == 0) && pevent_peek_char() == '~') { /* append it */ - *tok = malloc(3); + *tok = malloc_or_die(3); sprintf(*tok, "%c%c", *token, '~'); free_token(token); /* Now remove the '~' from the buffer */ -- cgit v1.2.3 From e2d6520d87d120e5388ff43cfcf2167713034b9d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 23 Apr 2012 13:58:38 +0900 Subject: tools lib traceevent: Check return value of arg_to_str() The arg_to_str() can fail so we should handle that case properly. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Namhyung Kim Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: David Ahern Link: http://lkml.kernel.org/r/1335157118-14658-12-git-send-email-namhyung.kim@lge.com Signed-off-by: Steven Rostedt Signed-off-by: Namhyung Kim --- parse-filter.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/parse-filter.c b/parse-filter.c index d54c2b4..f020ac6 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -2026,11 +2026,13 @@ static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg) char *lstr; char *rstr; char *op; - char *str; + char *str = NULL; int len; lstr = arg_to_str(filter, arg->exp.left); rstr = arg_to_str(filter, arg->exp.right); + if (!lstr || !rstr) + goto out; switch (arg->exp.type) { case FILTER_EXP_ADD: @@ -2070,6 +2072,7 @@ static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg) len = strlen(op) + strlen(lstr) + strlen(rstr) + 4; str = malloc_or_die(len); snprintf(str, len, "%s %s %s", lstr, op, rstr); +out: free(lstr); free(rstr); @@ -2086,6 +2089,8 @@ static char *num_to_str(struct event_filter *filter, struct filter_arg *arg) lstr = arg_to_str(filter, arg->num.left); rstr = arg_to_str(filter, arg->num.right); + if (!lstr || !rstr) + goto out; switch (arg->num.type) { case FILTER_CMP_EQ: @@ -2122,6 +2127,7 @@ static char *num_to_str(struct event_filter *filter, struct filter_arg *arg) break; } +out: free(lstr); free(rstr); return str; @@ -2272,7 +2278,12 @@ int pevent_filter_compare(struct event_filter *filter1, struct event_filter *fil /* The best way to compare complex filters is with strings */ str1 = arg_to_str(filter1, filter_type1->filter); str2 = arg_to_str(filter2, filter_type2->filter); - result = strcmp(str1, str2) != 0; + if (str1 && str2) + result = strcmp(str1, str2) != 0; + else + /* bail out if allocation fails */ + result = 1; + free(str1); free(str2); if (result) -- cgit v1.2.3 From fb5a7241fb3469617acffd53327d663e2597dac7 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Tue, 24 Apr 2012 23:19:40 +0200 Subject: tools lib traceevent: Add missing break in make_bprint_args In the current code we assign vsize=8 and then fall through to the default and assign vsize=1. -> probably the break is missing here, otherwise we can remove the case. Signed-off-by: Peter Huewe Signed-off-by: Steven Rostedt Link: http://lkml.kernel.org/n/tip-3fxjy46h2tr9pl0spv7tems6@git.kernel.org Signed-off-by: Namhyung Kim --- event-parse.c | 1 + 1 file changed, 1 insertion(+) diff --git a/event-parse.c b/event-parse.c index 8630160..f880db4 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3594,6 +3594,7 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc break; case 2: vsize = 8; + break; default: vsize = ls; /* ? */ break; -- cgit v1.2.3 From a3eff12770ed6ca96a68f26ba551fd440f6f3f0f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 24 Apr 2012 10:29:44 +0900 Subject: tools lib traceevent: Cleanup realloc use The if branch is completely unnecessary since 'realloc' can handle NULL pointers for the first parameter. This patch is just an adoption of Ulrich Drepper's recent patch on perf tools. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ulrich Drepper Link: http://lkml.kernel.org/r/1335230984-7613-1-git-send-email-namhyung.kim@lge.com Signed-off-by: Steven Rostedt Signed-off-by: Namhyung Kim --- event-parse.c | 8 ++------ parse-filter.c | 24 ++++++++---------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/event-parse.c b/event-parse.c index f880db4..5f34aa3 100644 --- a/event-parse.c +++ b/event-parse.c @@ -633,12 +633,8 @@ static void add_event(struct pevent *pevent, struct event_format *event) { int i; - if (!pevent->events) - pevent->events = malloc_or_die(sizeof(event)); - else - pevent->events = - realloc(pevent->events, sizeof(event) * - (pevent->nr_events + 1)); + pevent->events = realloc(pevent->events, sizeof(event) * + (pevent->nr_events + 1)); if (!pevent->events) die("Can not allocate events"); diff --git a/parse-filter.c b/parse-filter.c index f020ac6..ad17855 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -148,17 +148,11 @@ add_filter_type(struct event_filter *filter, int id) if (filter_type) return filter_type; - if (!filter->filters) - filter->event_filters = - malloc_or_die(sizeof(*filter->event_filters)); - else { - filter->event_filters = - realloc(filter->event_filters, - sizeof(*filter->event_filters) * - (filter->filters + 1)); - if (!filter->event_filters) - die("Could not allocate filter"); - } + filter->event_filters = realloc(filter->event_filters, + sizeof(*filter->event_filters) * + (filter->filters + 1)); + if (!filter->event_filters) + die("Could not allocate filter"); for (i = 0; i < filter->filters; i++) { if (filter->event_filters[i].event_id > id) @@ -1480,7 +1474,7 @@ void pevent_filter_clear_trivial(struct event_filter *filter, { struct filter_type *filter_type; int count = 0; - int *ids; + int *ids = NULL; int i; if (!filter->filters) @@ -1504,10 +1498,8 @@ void pevent_filter_clear_trivial(struct event_filter *filter, default: break; } - if (count) - ids = realloc(ids, sizeof(*ids) * (count + 1)); - else - ids = malloc(sizeof(*ids)); + + ids = realloc(ids, sizeof(*ids) * (count + 1)); if (!ids) die("Can't allocate ids"); ids[count++] = filter_type->event_id; -- cgit v1.2.3 From 7ecca8dff9efa39ec30ba09cae819c519c59d75c Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 6 Jul 2012 16:21:32 +0900 Subject: tools lib traceevent: Detect build environment changes Cross compiling perf requires setting ARCH and CROSS_COMPILE variables, but libtraceevent couldn't detect the changes so it ends up believing no recompiling is required. Thus the linker failed like: LINK perf ../lib/traceevent//libtraceevent.a: member ../lib/traceevent//libtraceevent.a(event-parse.o) in archive is not an object collect2: ld returned 1 exit status make: *** [perf] Error 1 This patch fixes this by adding TRACEEVENT-CFLAGS file like PERF-CFLAGS to track those changes. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1341559297-25725-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 46c2f6b..14131cb 100644 --- a/Makefile +++ b/Makefile @@ -207,7 +207,7 @@ libtraceevent.so: $(PEVENT_LIB_OBJS) libtraceevent.a: $(PEVENT_LIB_OBJS) $(Q)$(do_build_static_lib) -$(PEVENT_LIB_OBJS): %.o: $(src)/%.c +$(PEVENT_LIB_OBJS): %.o: $(src)/%.c TRACEEVENT-CFLAGS $(Q)$(do_fpic_compile) define make_version.h @@ -272,6 +272,16 @@ ifneq ($(dep_includes),) include $(dep_includes) endif +### Detect environment changes +TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):$(ARCH):$(CROSS_COMPILE) + +TRACEEVENT-CFLAGS: force + @FLAGS='$(TRACK_CFLAGS)'; \ + if test x"$$FLAGS" != x"`cat TRACEEVENT-CFLAGS 2>/dev/null`" ; then \ + echo 1>&2 " * new build flags or cross compiler"; \ + echo "$$FLAGS" >TRACEEVENT-CFLAGS; \ + fi + tags: force $(RM) tags find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px \ @@ -297,7 +307,7 @@ install: install_lib clean: $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d - $(RM) tags TAGS + $(RM) TRACEEVENT-CFLAGS tags TAGS endif # skip-makefile -- cgit v1.2.3 From a13166425bbc268533b59689c5560c8670c9974a Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 6 Jul 2012 16:21:33 +0900 Subject: tools lib traceevent: Ignore TRACEEVENT-CFLAGS file The TRACEEVENT-CFLAGS file is used to detect any change on compiler flags. Just ignore it. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1341559297-25725-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..35f56be --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +TRACEEVENT-CFLAGS -- cgit v1.2.3 From 60153ed5837f26347ac7987b75de404273703071 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Tue, 7 Aug 2012 19:43:14 +0200 Subject: tools lib traceevent: Fix cast from pointer to integer for 32 bit Fixing the integer cast reported by the following warning: tools/lib/traceevent/event-parse.c:3488:14: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] Signed-off-by: Robert Richter Cc: Ingo Molnar Link: http://lkml.kernel.org/r/1344361396-7237-5-git-send-email-robert.richter@amd.com Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index 5f34aa3..b7c2c49 100644 --- a/event-parse.c +++ b/event-parse.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "event-parse.h" #include "event-utils.h" @@ -3485,7 +3486,7 @@ process_defined_func(struct trace_seq *s, void *data, int size, if (!string->str) die("malloc str"); - args[i] = (unsigned long long)string->str; + args[i] = (uintptr_t)string->str; strings = string; trace_seq_destroy(&str); break; -- cgit v1.2.3 From 13270dc4b4d79fdd563e23d61795222ac8c4529a Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 22 Aug 2012 16:00:28 +0900 Subject: tools lib traceevent: Do not link broken field arg for an old ftrace event Defer linking a newly allocated arg to print_fmt.args until all of its field is setup so that later access to ->field.name cannot be NULL. Signed-off-by: Namhyung Kim Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1345618831-9148-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/event-parse.c b/event-parse.c index b7c2c49..33fcd94 100644 --- a/event-parse.c +++ b/event-parse.c @@ -4754,20 +4754,20 @@ int pevent_parse_event(struct pevent *pevent, struct print_arg *arg, **list; /* old ftrace had no args */ - list = &event->print_fmt.args; for (field = event->format.fields; field; field = field->next) { arg = alloc_arg(); - *list = arg; - list = &arg->next; arg->type = PRINT_FIELD; arg->field.name = strdup(field->name); if (!arg->field.name) { do_warning("failed to allocate field name"); event->flags |= EVENT_FL_FAILED; + free_arg(arg); return -1; } arg->field.field = field; + *list = arg; + list = &arg->next; } return 0; } -- cgit v1.2.3 From c746d190db53c0cdf7bd4399eddab08efd70c70d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 22 Aug 2012 16:00:29 +0900 Subject: tools lib traceevent: Introduce pevent_errno Define and use error numbers for pevent_parse_event() and get rid of die() and do_warning() calls. If the function returns non-zero value, the caller can check the return code and do appropriate things. I chose the error numbers to be negative not to clash with standard errno, and as usual, 0 for success. Signed-off-by: Namhyung Kim Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1345618831-9148-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 50 ++++++++++++++++++++++++++++++-------------------- event-parse.h | 26 ++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/event-parse.c b/event-parse.c index 33fcd94..a46cae7 100644 --- a/event-parse.c +++ b/event-parse.c @@ -4686,9 +4686,8 @@ static int find_event_handle(struct pevent *pevent, struct event_format *event) * * /sys/kernel/debug/tracing/events/.../.../format */ -int pevent_parse_event(struct pevent *pevent, - const char *buf, unsigned long size, - const char *sys) +enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, + unsigned long size, const char *sys) { struct event_format *event; int ret; @@ -4697,17 +4696,16 @@ int pevent_parse_event(struct pevent *pevent, event = alloc_event(); if (!event) - return -ENOMEM; + return PEVENT_ERRNO__MEM_ALLOC_FAILED; event->name = event_read_name(); if (!event->name) { /* Bad event? */ - free(event); - return -1; + ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; + goto event_alloc_failed; } if (strcmp(sys, "ftrace") == 0) { - event->flags |= EVENT_FL_ISFTRACE; if (strcmp(event->name, "bprint") == 0) @@ -4715,20 +4713,28 @@ int pevent_parse_event(struct pevent *pevent, } event->id = event_read_id(); - if (event->id < 0) - die("failed to read event id"); + if (event->id < 0) { + ret = PEVENT_ERRNO__READ_ID_FAILED; + /* + * This isn't an allocation error actually. + * But as the ID is critical, just bail out. + */ + goto event_alloc_failed; + } event->system = strdup(sys); - if (!event->system) - die("failed to allocate system"); + if (!event->system) { + ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; + goto event_alloc_failed; + } /* Add pevent to event so that it can be referenced */ event->pevent = pevent; ret = event_read_format(event); if (ret < 0) { - do_warning("failed to read event format for %s", event->name); - goto event_failed; + ret = PEVENT_ERRNO__READ_FORMAT_FAILED; + goto event_parse_failed; } /* @@ -4740,10 +4746,9 @@ int pevent_parse_event(struct pevent *pevent, ret = event_read_print(event); if (ret < 0) { - do_warning("failed to read event print fmt for %s", - event->name); show_warning = 1; - goto event_failed; + ret = PEVENT_ERRNO__READ_PRINT_FAILED; + goto event_parse_failed; } show_warning = 1; @@ -4760,10 +4765,9 @@ int pevent_parse_event(struct pevent *pevent, arg->type = PRINT_FIELD; arg->field.name = strdup(field->name); if (!arg->field.name) { - do_warning("failed to allocate field name"); event->flags |= EVENT_FL_FAILED; free_arg(arg); - return -1; + return PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED; } arg->field.field = field; *list = arg; @@ -4778,11 +4782,17 @@ int pevent_parse_event(struct pevent *pevent, return 0; - event_failed: + event_parse_failed: event->flags |= EVENT_FL_FAILED; /* still add it even if it failed */ add_event(pevent, event); - return -1; + return ret; + + event_alloc_failed: + free(event->system); + free(event->name); + free(event); + return ret; } int get_field_val(struct trace_seq *s, struct format_field *field, diff --git a/event-parse.h b/event-parse.h index 5772ad8..3c48229 100644 --- a/event-parse.h +++ b/event-parse.h @@ -345,6 +345,28 @@ enum pevent_flag { PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */ }; +enum pevent_errno { + PEVENT_ERRNO__SUCCESS = 0, + + /* + * Choose an arbitrary negative big number not to clash with standard + * errno since SUS requires the errno has distinct positive values. + * See 'Issue 6' in the link below. + * + * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html + */ + __PEVENT_ERRNO__START = -100000, + + PEVENT_ERRNO__MEM_ALLOC_FAILED = __PEVENT_ERRNO__START, + PEVENT_ERRNO__PARSE_EVENT_FAILED, + PEVENT_ERRNO__READ_ID_FAILED, + PEVENT_ERRNO__READ_FORMAT_FAILED, + PEVENT_ERRNO__READ_PRINT_FAILED, + PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED, + + __PEVENT_ERRNO__END, +}; + struct cmdline; struct cmdline_list; struct func_map; @@ -509,8 +531,8 @@ void pevent_print_event(struct pevent *pevent, struct trace_seq *s, int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size, int long_size); -int pevent_parse_event(struct pevent *pevent, const char *buf, - unsigned long size, const char *sys); +enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, + unsigned long size, const char *sys); void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, -- cgit v1.2.3 From 9943d8d8b99d2047337240a5194fbdbc5e64d86e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 22 Aug 2012 16:00:30 +0900 Subject: tools lib traceevent: Introduce pevent_strerror The pevent_strerror() sets @buf to a string that describes the (libtraceevent-specific) error condition that is passed via @errnum. This is similar to strerror_r() and does same thing if @errnum has a standard errno value. To sync error string with its code, define PEVENT_ERRORS with _PE() macro and use it as suggested by Steven. Signed-off-by: Namhyung Kim Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1345618831-9148-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 43 +++++++++++++++++++++++++++++++++++++++++++ event-parse.h | 20 ++++++++++++++------ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/event-parse.c b/event-parse.c index a46cae7..1373e4c 100644 --- a/event-parse.c +++ b/event-parse.c @@ -4795,6 +4795,49 @@ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, return ret; } +#undef _PE +#define _PE(code, str) str +static const char * const pevent_error_str[] = { + PEVENT_ERRORS +}; +#undef _PE + +int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, + char *buf, size_t buflen) +{ + int idx; + const char *msg; + + if (errnum >= 0) { + strerror_r(errnum, buf, buflen); + return 0; + } + + if (errnum <= __PEVENT_ERRNO__START || + errnum >= __PEVENT_ERRNO__END) + return -1; + + idx = errnum - __PEVENT_ERRNO__START; + msg = pevent_error_str[idx]; + + switch (errnum) { + case PEVENT_ERRNO__MEM_ALLOC_FAILED: + case PEVENT_ERRNO__PARSE_EVENT_FAILED: + case PEVENT_ERRNO__READ_ID_FAILED: + case PEVENT_ERRNO__READ_FORMAT_FAILED: + case PEVENT_ERRNO__READ_PRINT_FAILED: + case PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED: + snprintf(buf, buflen, "%s", msg); + break; + + default: + /* cannot reach here */ + break; + } + + return 0; +} + int get_field_val(struct trace_seq *s, struct format_field *field, const char *name, struct pevent_record *record, unsigned long long *val, int err) diff --git a/event-parse.h b/event-parse.h index 3c48229..527df03 100644 --- a/event-parse.h +++ b/event-parse.h @@ -345,6 +345,16 @@ enum pevent_flag { PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */ }; +#define PEVENT_ERRORS \ + _PE(MEM_ALLOC_FAILED, "failed to allocate memory"), \ + _PE(PARSE_EVENT_FAILED, "failed to parse event"), \ + _PE(READ_ID_FAILED, "failed to read event id"), \ + _PE(READ_FORMAT_FAILED, "failed to read event format"), \ + _PE(READ_PRINT_FAILED, "failed to read event print fmt"), \ + _PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace") + +#undef _PE +#define _PE(__code, __str) PEVENT_ERRNO__ ## __code enum pevent_errno { PEVENT_ERRNO__SUCCESS = 0, @@ -357,15 +367,11 @@ enum pevent_errno { */ __PEVENT_ERRNO__START = -100000, - PEVENT_ERRNO__MEM_ALLOC_FAILED = __PEVENT_ERRNO__START, - PEVENT_ERRNO__PARSE_EVENT_FAILED, - PEVENT_ERRNO__READ_ID_FAILED, - PEVENT_ERRNO__READ_FORMAT_FAILED, - PEVENT_ERRNO__READ_PRINT_FAILED, - PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED, + PEVENT_ERRORS, __PEVENT_ERRNO__END, }; +#undef _PE struct cmdline; struct cmdline_list; @@ -583,6 +589,8 @@ int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec); const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid); void pevent_event_info(struct trace_seq *s, struct event_format *event, struct pevent_record *record); +int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, + char *buf, size_t buflen); struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type); struct format_field **pevent_event_common_fields(struct event_format *event); -- cgit v1.2.3 From 439d3017d1de3d17d9ce3d24b348f59adae7226c Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 22 Aug 2012 16:00:31 +0900 Subject: tools lib traceevent: Fix strerror_r() use in pevent_strerror glibc-2.16 starts to mark the function with attribute warn_unused_result so that it can cause a build warning. Since GNU version of strerror_r() can return a pointer to a string without setting @buf, check the return value and copy/truncate it to our buffer if needed. Signed-off-by: Namhyung Kim Acked-by: Kirill A. Shutemov Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Kirill A. Shutemov Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1345618831-9148-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 7 ++++++- event-utils.h | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index 1373e4c..f978c59 100644 --- a/event-parse.c +++ b/event-parse.c @@ -4809,7 +4809,12 @@ int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, const char *msg; if (errnum >= 0) { - strerror_r(errnum, buf, buflen); + msg = strerror_r(errnum, buf, buflen); + if (msg != buf) { + size_t len = strlen(msg); + char *c = mempcpy(buf, msg, min(buflen-1, len)); + *c = '\0'; + } return 0; } diff --git a/event-utils.h b/event-utils.h index 0829638..bc07500 100644 --- a/event-utils.h +++ b/event-utils.h @@ -39,6 +39,12 @@ void __vdie(const char *fmt, ...); void __vwarning(const char *fmt, ...); void __vpr_stat(const char *fmt, ...); +#define min(x, y) ({ \ + typeof(x) _min1 = (x); \ + typeof(y) _min2 = (y); \ + (void) (&_min1 == &_min2); \ + _min1 < _min2 ? _min1 : _min2; }) + static inline char *strim(char *string) { char *ret; -- cgit v1.2.3 From e1a4c7037b49415c253646d6ad458f9aa7a9c4c2 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 23 Aug 2012 16:37:00 +0900 Subject: tools lib traceevent: Fix off-by-one bug in pevent_strerror() As pevent_errno is defined using PEVENT_ERRORS which uses _PE macro magic, the first errno is bigger than __PEVENT_ERRNO_START by 1. So we need to subtract the 1 also when calculating the index of the error strings. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1345707420-21767-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index f978c59..b5b4d80 100644 --- a/event-parse.c +++ b/event-parse.c @@ -4822,7 +4822,7 @@ int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, errnum >= __PEVENT_ERRNO__END) return -1; - idx = errnum - __PEVENT_ERRNO__START; + idx = errnum - __PEVENT_ERRNO__START - 1; msg = pevent_error_str[idx]; switch (errnum) { -- cgit v1.2.3 From b6c6d9201ccd1bbde4574420c89b21b3b2a745e6 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 23 Aug 2012 11:22:01 -0400 Subject: tools lib traceevent: Modify header to work in C++ programs Replace keyword "private" to "priv" in event-parse.h to allow it to be used in C++ programs. Signed-off-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1345735321.5069.62.camel@gandalf.local.home Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/event-parse.h b/event-parse.h index 527df03..863a0bb 100644 --- a/event-parse.h +++ b/event-parse.h @@ -49,7 +49,7 @@ struct pevent_record { int cpu; int ref_count; int locked; /* Do not free, even if ref_count is zero */ - void *private; + void *priv; #if DEBUG_RECORD struct pevent_record *prev; struct pevent_record *next; @@ -106,7 +106,7 @@ struct plugin_option { char *plugin_alias; char *description; char *value; - void *private; + void *priv; int set; }; -- cgit v1.2.3 From 575ef9e1b8107044be4ce633c3f5a6662a740e9c Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 7 Sep 2012 11:49:45 +0900 Subject: tools lib traceevent: Get rid of die() from pretty_print() There are three cases that call die() in the pretty_print. 1. insufficient number of argument: cannot proceed anymore. 2. too long format conversion specifier: truncate and proceed. 3. bad size specifier in format string: skip and proceed. For all cases, convert die to do_warning, mark the event as EVENT_FL_FAILED and print error message at the last. Signed-off-by: Namhyung Kim Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1346986187-5170-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/event-parse.c b/event-parse.c index b5b4d80..6d5e759 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3889,8 +3889,11 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event goto cont_process; case '*': /* The argument is the length. */ - if (!arg) - die("no argument match"); + if (!arg) { + do_warning("no argument match"); + event->flags |= EVENT_FL_FAILED; + goto out_failed; + } len_arg = eval_num_arg(data, size, event, arg); len_as_arg = 1; arg = arg->next; @@ -3923,15 +3926,21 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event case 'x': case 'X': case 'u': - if (!arg) - die("no argument match"); + if (!arg) { + do_warning("no argument match"); + event->flags |= EVENT_FL_FAILED; + goto out_failed; + } len = ((unsigned long)ptr + 1) - (unsigned long)saveptr; /* should never happen */ - if (len > 31) - die("bad format!"); + if (len > 31) { + do_warning("bad format!"); + event->flags |= EVENT_FL_FAILED; + len = 31; + } memcpy(format, saveptr, len); format[len] = 0; @@ -3995,19 +4004,26 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event trace_seq_printf(s, format, (long long)val); break; default: - die("bad count (%d)", ls); + do_warning("bad count (%d)", ls); + event->flags |= EVENT_FL_FAILED; } break; case 's': - if (!arg) - die("no matching argument"); + if (!arg) { + do_warning("no matching argument"); + event->flags |= EVENT_FL_FAILED; + goto out_failed; + } len = ((unsigned long)ptr + 1) - (unsigned long)saveptr; /* should never happen */ - if (len > 31) - die("bad format!"); + if (len > 31) { + do_warning("bad format!"); + event->flags |= EVENT_FL_FAILED; + len = 31; + } memcpy(format, saveptr, len); format[len] = 0; @@ -4025,6 +4041,11 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event trace_seq_putc(s, *ptr); } + if (event->flags & EVENT_FL_FAILED) { +out_failed: + trace_seq_printf(s, "[FAILED TO PARSE]"); + } + if (args) { free_args(args); free(bprint_fmt); -- cgit v1.2.3 From 44c035a89e86b828989fe9ac3295947d39edfb4e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 7 Sep 2012 11:49:46 +0900 Subject: tools lib traceevent: Get rid of die() from pevent_register_event_handler If memory allocation for handler fails, return gracefully instead of calling die(). Note that casts to void * are needed because gcc complained about discarding 'const' qualifier during implicit argument cast. Signed-off-by: Namhyung Kim Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1346986187-5170-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/event-parse.c b/event-parse.c index 6d5e759..17fd01d 100644 --- a/event-parse.c +++ b/event-parse.c @@ -5183,7 +5183,12 @@ int pevent_register_event_handler(struct pevent *pevent, not_found: /* Save for later use. */ - handle = malloc_or_die(sizeof(*handle)); + handle = malloc(sizeof(*handle)); + if (!handle) { + do_warning("Failed to allocate event handler"); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } + memset(handle, 0, sizeof(*handle)); handle->id = id; if (event_name) @@ -5193,7 +5198,11 @@ int pevent_register_event_handler(struct pevent *pevent, if ((event_name && !handle->event_name) || (sys_name && !handle->sys_name)) { - die("Failed to allocate event/sys name"); + do_warning("Failed to allocate event/sys name"); + free((void *)handle->event_name); + free((void *)handle->sys_name); + free(handle); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; } handle->func = func; -- cgit v1.2.3 From 1db1b6e0e00f10784d36203e9a4167e2cac2bdf2 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 7 Sep 2012 11:49:47 +0900 Subject: tools lib traceevent: Get rid of die() from pevent_register_print_function If memory allocation for handler fails or argument type is not match, return gracefully instead of calling die(). Also add an new error code for the later case. Signed-off-by: Namhyung Kim Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1346986187-5170-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 26 ++++++++++++++++++++------ event-parse.h | 3 ++- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/event-parse.c b/event-parse.c index 17fd01d..4595aeb 100644 --- a/event-parse.c +++ b/event-parse.c @@ -5080,6 +5080,7 @@ int pevent_register_print_function(struct pevent *pevent, struct pevent_func_params *param; enum pevent_func_arg_type type; va_list ap; + int ret; func_handle = find_func_handler(pevent, name); if (func_handle) { @@ -5092,14 +5093,21 @@ int pevent_register_print_function(struct pevent *pevent, remove_func_handler(pevent, name); } - func_handle = malloc_or_die(sizeof(*func_handle)); + func_handle = malloc(sizeof(*func_handle)); + if (!func_handle) { + do_warning("Failed to allocate function handler"); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } memset(func_handle, 0, sizeof(*func_handle)); func_handle->ret_type = ret_type; func_handle->name = strdup(name); func_handle->func = func; - if (!func_handle->name) - die("Failed to allocate function name"); + if (!func_handle->name) { + do_warning("Failed to allocate function name"); + free(func_handle); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } next_param = &(func_handle->params); va_start(ap, name); @@ -5109,11 +5117,17 @@ int pevent_register_print_function(struct pevent *pevent, break; if (type < 0 || type >= PEVENT_FUNC_ARG_MAX_TYPES) { - warning("Invalid argument type %d", type); + do_warning("Invalid argument type %d", type); + ret = PEVENT_ERRNO__INVALID_ARG_TYPE; goto out_free; } - param = malloc_or_die(sizeof(*param)); + param = malloc(sizeof(*param)); + if (!param) { + do_warning("Failed to allocate function param"); + ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; + goto out_free; + } param->type = type; param->next = NULL; @@ -5131,7 +5145,7 @@ int pevent_register_print_function(struct pevent *pevent, out_free: va_end(ap); free_func_handle(func_handle); - return -1; + return ret; } /** diff --git a/event-parse.h b/event-parse.h index 863a0bb..3318963 100644 --- a/event-parse.h +++ b/event-parse.h @@ -351,7 +351,8 @@ enum pevent_flag { _PE(READ_ID_FAILED, "failed to read event id"), \ _PE(READ_FORMAT_FAILED, "failed to read event format"), \ _PE(READ_PRINT_FAILED, "failed to read event print fmt"), \ - _PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace") + _PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace"),\ + _PE(INVALID_ARG_TYPE, "invalid argument type") #undef _PE #define _PE(__code, __str) PEVENT_ERRNO__ ## __code -- cgit v1.2.3 From 95a7a2e4b65df74b0e3935154a60f31162adc95a Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Sat, 8 Sep 2012 03:43:22 +0300 Subject: tools lib traceevent: replace mempcpy with memcpy The mempcpy function is not supported by bionic in Android and will lead to compilation errors. Replacing mempcpy with memcpy so it will work in Android. Signed-off-by: Irina Tirdea Cc: David Ahern Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1347065004-15306-11-git-send-email-irina.tirdea@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/event-parse.c b/event-parse.c index 4595aeb..f4190b5 100644 --- a/event-parse.c +++ b/event-parse.c @@ -4833,8 +4833,8 @@ int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, msg = strerror_r(errnum, buf, buflen); if (msg != buf) { size_t len = strlen(msg); - char *c = mempcpy(buf, msg, min(buflen-1, len)); - *c = '\0'; + memcpy(buf, msg, min(buflen - 1, len)); + *(buf + min(buflen - 1, len)) = '\0'; } return 0; } -- cgit v1.2.3 From 12b2a2018a0e1e677513d87c1ef0d64801721a87 Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Tue, 11 Sep 2012 01:15:03 +0300 Subject: perf tools: Use __maybe_used for unused variables perf defines both __used and __unused variables to use for marking unused variables. The variable __used is defined to __attribute__((__unused__)), which contradicts the kernel definition to __attribute__((__used__)) for new gcc versions. On Android, __used is also defined in system headers and this leads to warnings like: warning: '__used__' attribute ignored __unused is not defined in the kernel and is not a standard definition. If __unused is included everywhere instead of __used, this leads to conflicts with glibc headers, since glibc has a variables with this name in its headers. The best approach is to use __maybe_unused, the definition used in the kernel for __attribute__((unused)). In this way there is only one definition in perf sources (instead of 2 definitions that point to the same thing: __used and __unused) and it works on both Linux and Android. This patch simply replaces all instances of __used and __unused with __maybe_unused. Signed-off-by: Irina Tirdea Acked-by: Pekka Enberg Cc: David Ahern Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1347315303-29906-7-git-send-email-irina.tirdea@intel.com [ committer note: fixed up conflict with a116e05 in builtin-sched.c ] Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 8 +++++--- event-parse.h | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/event-parse.c b/event-parse.c index f4190b5..2c54cdd 100644 --- a/event-parse.c +++ b/event-parse.c @@ -1824,7 +1824,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) } static enum event_type -process_entry(struct event_format *event __unused, struct print_arg *arg, +process_entry(struct event_format *event __maybe_unused, struct print_arg *arg, char **tok) { enum event_type type; @@ -2458,7 +2458,8 @@ process_paren(struct event_format *event, struct print_arg *arg, char **tok) static enum event_type -process_str(struct event_format *event __unused, struct print_arg *arg, char **tok) +process_str(struct event_format *event __maybe_unused, struct print_arg *arg, + char **tok) { enum event_type type; char *token; @@ -3653,7 +3654,8 @@ static void free_args(struct print_arg *args) } static char * -get_bprint_format(void *data, int size __unused, struct event_format *event) +get_bprint_format(void *data, int size __maybe_unused, + struct event_format *event) { struct pevent *pevent = event->pevent; unsigned long long addr; diff --git a/event-parse.h b/event-parse.h index 3318963..a4bbe24 100644 --- a/event-parse.h +++ b/event-parse.h @@ -24,8 +24,8 @@ #include #include -#ifndef __unused -#define __unused __attribute__ ((unused)) +#ifndef __maybe_unused +#define __maybe_unused __attribute__((unused)) #endif /* ----------------------- trace_seq ----------------------- */ -- cgit v1.2.3 From 7dafefdc3c97611036bab757f14f068de10a9f4c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 12 Sep 2012 14:29:40 -0300 Subject: tools lib traceevent: Define _GNU_SOURCE in Makefile For the reasons stated on: commit 0a84f00 Author: David Daney perf tools: Fix broken build by defining _GNU_SOURCE in Makefile Acked-by: Steven Rostedt Cc: David Ahern Cc: David Daney Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Steven Rostedt Link: http://lkml.kernel.org/n/tip-e2nofbmj4uf0ykgsytxvt9pu@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 2 +- event-parse.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 14131cb..04d959f 100644 --- a/Makefile +++ b/Makefile @@ -129,7 +129,7 @@ CFLAGS ?= -g -Wall # Append required CFLAGS override CFLAGS += $(CONFIG_FLAGS) $(INCLUDES) $(PLUGIN_DIR_SQ) -override CFLAGS += $(udis86-flags) +override CFLAGS += $(udis86-flags) -D_GNU_SOURCE ifeq ($(VERBOSE),1) Q = diff --git a/event-parse.c b/event-parse.c index 2c54cdd..77ebeb8 100644 --- a/event-parse.c +++ b/event-parse.c @@ -24,7 +24,6 @@ * Frederic Weisbecker gave his permission to relicense the code to * the Lesser General Public License. */ -#define _GNU_SOURCE #include #include #include -- cgit v1.2.3 From 8c5cc1de1f675f7fa555dda1d9d838758f4dc891 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 19 Sep 2012 15:58:41 +0900 Subject: tools lib traceevent: Fix error path on process_array() free_token() under out_free should be called with 'token' and no need to set *tok to NULL since it's set already. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1348037924-17568-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/event-parse.c b/event-parse.c index 77ebeb8..6270ee2 100644 --- a/event-parse.c +++ b/event-parse.c @@ -1595,8 +1595,7 @@ process_array(struct event_format *event, struct print_arg *top, char **tok) return type; out_free: - free_token(*tok); - *tok = NULL; + free_token(token); free_arg(arg); return EVENT_ERROR; } -- cgit v1.2.3 From ebba4450179952b8d76493ae503fb0efcb4179c7 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 19 Sep 2012 15:58:42 +0900 Subject: tools lib traceevent: Make sure that arg->op.right is set properly When process_op failed, @arg will be freed on a caller with type of PRINT_OP. Thus free_arg() will try to free ->op.right field which can have stale value if something bad happens in the middle. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1348037924-17568-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/event-parse.c b/event-parse.c index 6270ee2..27088c5 100644 --- a/event-parse.c +++ b/event-parse.c @@ -1719,6 +1719,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) arg->op.left = left; arg->op.prio = 0; + /* it will set arg->op.right */ type = process_cond(event, arg, tok); } else if (strcmp(token, ">>") == 0 || @@ -1745,6 +1746,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) arg->type = PRINT_OP; arg->op.op = token; arg->op.left = left; + arg->op.right = NULL; if (set_op_prio(arg) == -1) { event->flags |= EVENT_FL_FAILED; @@ -1792,6 +1794,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) arg->op.prio = 0; + /* it will set arg->op.right */ type = process_array(event, arg, tok); } else { -- cgit v1.2.3 From 307675f9cc102f3af03ce7571bcd4c8c288fcaa9 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 19 Sep 2012 15:58:43 +0900 Subject: tools lib traceevent: Free field if an error occurs on process_fields The field should be freed on error paths. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1348037924-17568-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/event-parse.c b/event-parse.c index 27088c5..a776ed5 100644 --- a/event-parse.c +++ b/event-parse.c @@ -2186,10 +2186,10 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char ** value = arg_eval(arg); if (value == NULL) - goto out_free; + goto out_free_field; field->value = strdup(value); if (field->value == NULL) - goto out_free; + goto out_free_field; free_arg(arg); arg = alloc_arg(); @@ -2197,14 +2197,14 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char ** free_token(token); type = process_arg(event, arg, &token); if (test_type_token(type, token, EVENT_OP, "}")) - goto out_free; + goto out_free_field; value = arg_eval(arg); if (value == NULL) - goto out_free; + goto out_free_field; field->str = strdup(value); if (field->str == NULL) - goto out_free; + goto out_free_field; free_arg(arg); arg = NULL; @@ -2218,6 +2218,8 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char ** *tok = token; return type; +out_free_field: + free_flag_sym(field); out_free: free_arg(arg); free_token(token); -- cgit v1.2.3 From 9f9a3a60c8f30acef0c36b99dd86951be13f5b92 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 19 Sep 2012 15:58:44 +0900 Subject: tools lib traceevent: Free field if an error occurs on process_flags/symbols The field should be freed on error paths. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1348037924-17568-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/event-parse.c b/event-parse.c index a776ed5..acf4038 100644 --- a/event-parse.c +++ b/event-parse.c @@ -2247,7 +2247,7 @@ process_flags(struct event_format *event, struct print_arg *arg, char **tok) type = process_op(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ",")) - goto out_free; + goto out_free_field; free_token(token); arg->flags.field = field; @@ -2269,7 +2269,9 @@ process_flags(struct event_format *event, struct print_arg *arg, char **tok) type = read_token_item(tok); return type; - out_free: +out_free_field: + free_arg(field); +out_free: free_token(token); *tok = NULL; return EVENT_ERROR; @@ -2289,7 +2291,7 @@ process_symbols(struct event_format *event, struct print_arg *arg, char **tok) type = process_arg(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ",")) - goto out_free; + goto out_free_field; arg->symbol.field = field; @@ -2301,7 +2303,9 @@ process_symbols(struct event_format *event, struct print_arg *arg, char **tok) type = read_token_item(tok); return type; - out_free: +out_free_field: + free_arg(field); +out_free: free_token(token); *tok = NULL; return EVENT_ERROR; -- cgit v1.2.3 From c0d35689bd7d8266826b93aa0a525a4dc4781855 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 12 Sep 2012 15:39:59 -0300 Subject: tools lib traceevent: Use asprintf were applicable Replacing the equivalent open coded malloc + sprintf bits. Reviewed-by: Namhyung Kim Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Steven Rostedt Link: http://lkml.kernel.org/n/tip-ghokwtdw2hgmmmn7oa9s03r4@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 58 +++++++++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/event-parse.c b/event-parse.c index acf4038..2e05d56 100644 --- a/event-parse.c +++ b/event-parse.c @@ -827,9 +827,9 @@ static enum event_type __read_token(char **tok) switch (type) { case EVENT_NEWLINE: case EVENT_DELIM: - *tok = malloc_or_die(2); - (*tok)[0] = ch; - (*tok)[1] = 0; + if (asprintf(tok, "%c", ch) < 0) + return EVENT_ERROR; + return type; case EVENT_OP: @@ -2777,10 +2777,8 @@ static int event_read_print(struct event_format *event) if (type == EVENT_DQUOTE) { char *cat; - cat = malloc_or_die(strlen(event->print_fmt.format) + - strlen(token) + 1); - strcpy(cat, event->print_fmt.format); - strcat(cat, token); + if (asprintf(&cat, "%s%s", event->print_fmt.format, token) < 0) + goto fail; free_token(token); free_token(event->print_fmt.format); event->print_fmt.format = NULL; @@ -3524,6 +3522,18 @@ process_defined_func(struct trace_seq *s, void *data, int size, return ret; } +static void free_args(struct print_arg *args) +{ + struct print_arg *next; + + while (args) { + next = args->next; + + free_arg(args); + args = next; + } +} + static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struct event_format *event) { struct pevent *pevent = event->pevent; @@ -3559,8 +3569,9 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc next = &arg->next; arg->type = PRINT_ATOM; - arg->atom.atom = malloc_or_die(32); - sprintf(arg->atom.atom, "%lld", ip); + + if (asprintf(&arg->atom.atom, "%lld", ip) < 0) + goto out_free; /* skip the first "%pf : " */ for (ptr = fmt + 6, bptr = data + field->offset; @@ -3617,8 +3628,10 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc arg = alloc_arg(); arg->next = NULL; arg->type = PRINT_ATOM; - arg->atom.atom = malloc_or_die(32); - sprintf(arg->atom.atom, "%lld", val); + if (asprintf(&arg->atom.atom, "%lld", val) < 0) { + free(arg); + goto out_free; + } *next = arg; next = &arg->next; /* @@ -3646,18 +3659,10 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc } return args; -} - -static void free_args(struct print_arg *args) -{ - struct print_arg *next; - - while (args) { - next = args->next; - free_arg(args); - args = next; - } +out_free: + free_args(args); + return NULL; } static char * @@ -3684,9 +3689,8 @@ get_bprint_format(void *data, int size __maybe_unused, printk = find_printk(pevent, addr); if (!printk) { - format = malloc_or_die(45); - sprintf(format, "%%pf : (NO FORMAT FOUND at %llx)\n", - addr); + if (asprintf(&format, "%%pf : (NO FORMAT FOUND at %llx)\n", addr) < 0) + return NULL; return format; } @@ -3694,8 +3698,8 @@ get_bprint_format(void *data, int size __maybe_unused, /* Remove any quotes. */ if (*p == '"') p++; - format = malloc_or_die(strlen(p) + 10); - sprintf(format, "%s : %s", "%pf", p); + if (asprintf(&format, "%s : %s", "%pf", p) < 0) + return NULL; /* remove ending quotes and new line since we will add one too */ p = format + strlen(format) - 1; if (*p == '"') -- cgit v1.2.3 From 91c8dca25312edd0d298dfe612c5fa336c3bbe42 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 12 Sep 2012 15:39:59 -0300 Subject: tools lib traceevent: Use calloc were applicable Replacing the equivalent open coded malloc + memset bits. Reviewed-by: Namhyung Kim Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Steven Rostedt Link: http://lkml.kernel.org/n/tip-598fjtjbzal4wxh7fp0yv0q1@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 44 ++++++++++++++------------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/event-parse.c b/event-parse.c index 2e05d56..2aeae55 100644 --- a/event-parse.c +++ b/event-parse.c @@ -117,14 +117,7 @@ void breakpoint(void) struct print_arg *alloc_arg(void) { - struct print_arg *arg; - - arg = malloc_or_die(sizeof(*arg)); - if (!arg) - return NULL; - memset(arg, 0, sizeof(*arg)); - - return arg; + return calloc(1, sizeof(struct print_arg)); } struct cmdline { @@ -619,14 +612,7 @@ void pevent_print_printk(struct pevent *pevent) static struct event_format *alloc_event(void) { - struct event_format *event; - - event = malloc(sizeof(*event)); - if (!event) - return NULL; - memset(event, 0, sizeof(*event)); - - return event; + return calloc(1, sizeof(struct event_format)); } static void add_event(struct pevent *pevent, struct event_format *event) @@ -1240,8 +1226,10 @@ static int event_read_fields(struct event_format *event, struct format_field **f last_token = token; - field = malloc_or_die(sizeof(*field)); - memset(field, 0, sizeof(*field)); + field = calloc(1, sizeof(*field)); + if (!field) + goto fail; + field->event = event; /* read the rest of the type */ @@ -2181,8 +2169,9 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char ** if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free; - field = malloc_or_die(sizeof(*field)); - memset(field, 0, sizeof(*field)); + field = calloc(1, sizeof(*field)); + if (!field) + goto out_free; value = arg_eval(arg); if (value == NULL) @@ -5106,12 +5095,11 @@ int pevent_register_print_function(struct pevent *pevent, remove_func_handler(pevent, name); } - func_handle = malloc(sizeof(*func_handle)); + func_handle = calloc(1, sizeof(*func_handle)); if (!func_handle) { do_warning("Failed to allocate function handler"); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } - memset(func_handle, 0, sizeof(*func_handle)); func_handle->ret_type = ret_type; func_handle->name = strdup(name); @@ -5210,13 +5198,12 @@ int pevent_register_event_handler(struct pevent *pevent, not_found: /* Save for later use. */ - handle = malloc(sizeof(*handle)); + handle = calloc(1, sizeof(*handle)); if (!handle) { do_warning("Failed to allocate event handler"); return PEVENT_ERRNO__MEM_ALLOC_FAILED; } - memset(handle, 0, sizeof(*handle)); handle->id = id; if (event_name) handle->event_name = strdup(event_name); @@ -5245,13 +5232,10 @@ int pevent_register_event_handler(struct pevent *pevent, */ struct pevent *pevent_alloc(void) { - struct pevent *pevent; + struct pevent *pevent = calloc(1, sizeof(*pevent)); - pevent = malloc(sizeof(*pevent)); - if (!pevent) - return NULL; - memset(pevent, 0, sizeof(*pevent)); - pevent->ref_count = 1; + if (pevent) + pevent->ref_count = 1; return pevent; } -- cgit v1.2.3 From 94353889cff17ff766a15a3c4c92904916f8d7b5 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 12 Sep 2012 15:39:59 -0300 Subject: tools lib traceevent: Fix afterlife gotos Instead of dying, just use do_warning and let the goto that is there to take place. Reviewed-by: Namhyung Kim Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Steven Rostedt Link: http://lkml.kernel.org/n/tip-aoaus46ngnt9oc2pt7ckot5d@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/event-parse.c b/event-parse.c index 2aeae55..2091991 100644 --- a/event-parse.c +++ b/event-parse.c @@ -1270,7 +1270,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f } if (!field->type) { - die("no type found"); + do_warning("%s: no type found", __func__); goto fail; } field->name = last_token; @@ -1317,7 +1317,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f free_token(token); type = read_token(&token); if (type == EVENT_NONE) { - die("failed to find token"); + do_warning("failed to find token"); goto fail; } } @@ -1669,7 +1669,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) if (arg->type == PRINT_OP && !arg->op.left) { /* handle single op */ if (token[1]) { - die("bad op token %s", token); + do_warning("bad op token %s", token); goto out_free; } switch (token[0]) { -- cgit v1.2.3 From 8b43424c32702852ea4d501f2a7aa7885e1543cb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 12 Sep 2012 17:30:50 -0300 Subject: tools lib traceevent: Remove some die() calls Cleaned event-parse.c this time, just propagate the errors and in handle them the call sites. Reviewed-by: Namhyung Kim Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Steven Rostedt Link: http://lkml.kernel.org/n/tip-9ebpr2vgfk2qs2841i99sa8y@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 249 +++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 175 insertions(+), 74 deletions(-) diff --git a/event-parse.c b/event-parse.c index 2091991..b3bc130 100644 --- a/event-parse.c +++ b/event-parse.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "event-parse.h" #include "event-utils.h" @@ -151,7 +152,9 @@ static int cmdline_init(struct pevent *pevent) struct cmdline *cmdlines; int i; - cmdlines = malloc_or_die(sizeof(*cmdlines) * pevent->cmdline_count); + cmdlines = malloc(sizeof(*cmdlines) * pevent->cmdline_count); + if (!cmdlines) + return -1; i = 0; while (cmdlist) { @@ -179,8 +182,8 @@ static char *find_cmdline(struct pevent *pevent, int pid) if (!pid) return ""; - if (!pevent->cmdlines) - cmdline_init(pevent); + if (!pevent->cmdlines && cmdline_init(pevent)) + return ""; key.pid = pid; @@ -208,8 +211,8 @@ int pevent_pid_is_registered(struct pevent *pevent, int pid) if (!pid) return 1; - if (!pevent->cmdlines) - cmdline_init(pevent); + if (!pevent->cmdlines && cmdline_init(pevent)) + return 0; key.pid = pid; @@ -251,10 +254,14 @@ static int add_new_comm(struct pevent *pevent, const char *comm, int pid) return -1; } - cmdlines[pevent->cmdline_count].pid = pid; cmdlines[pevent->cmdline_count].comm = strdup(comm); - if (!cmdlines[pevent->cmdline_count].comm) - die("malloc comm"); + if (!cmdlines[pevent->cmdline_count].comm) { + free(cmdlines); + errno = ENOMEM; + return -1; + } + + cmdlines[pevent->cmdline_count].pid = pid; if (cmdlines[pevent->cmdline_count].comm) pevent->cmdline_count++; @@ -281,10 +288,15 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid) if (pevent->cmdlines) return add_new_comm(pevent, comm, pid); - item = malloc_or_die(sizeof(*item)); + item = malloc(sizeof(*item)); + if (!item) + return -1; + item->comm = strdup(comm); - if (!item->comm) - die("malloc comm"); + if (!item->comm) { + free(item); + return -1; + } item->pid = pid; item->next = pevent->cmdlist; @@ -348,7 +360,10 @@ static int func_map_init(struct pevent *pevent) struct func_map *func_map; int i; - func_map = malloc_or_die(sizeof(*func_map) * (pevent->func_count + 1)); + func_map = malloc(sizeof(*func_map) * (pevent->func_count + 1)); + if (!func_map) + return -1; + funclist = pevent->funclist; i = 0; @@ -448,25 +463,36 @@ pevent_find_function_address(struct pevent *pevent, unsigned long long addr) int pevent_register_function(struct pevent *pevent, char *func, unsigned long long addr, char *mod) { - struct func_list *item; + struct func_list *item = malloc(sizeof(*item)); - item = malloc_or_die(sizeof(*item)); + if (!item) + return -1; item->next = pevent->funclist; item->func = strdup(func); - if (mod) + if (!item->func) + goto out_free; + + if (mod) { item->mod = strdup(mod); - else + if (!item->mod) + goto out_free_func; + } else item->mod = NULL; item->addr = addr; - if (!item->func || (mod && !item->mod)) - die("malloc func"); - pevent->funclist = item; pevent->func_count++; return 0; + +out_free_func: + free(item->func); + item->func = NULL; +out_free: + free(item); + errno = ENOMEM; + return -1; } /** @@ -517,14 +543,16 @@ static int printk_cmp(const void *a, const void *b) return 0; } -static void printk_map_init(struct pevent *pevent) +static int printk_map_init(struct pevent *pevent) { struct printk_list *printklist; struct printk_list *item; struct printk_map *printk_map; int i; - printk_map = malloc_or_die(sizeof(*printk_map) * (pevent->printk_count + 1)); + printk_map = malloc(sizeof(*printk_map) * (pevent->printk_count + 1)); + if (!printk_map) + return -1; printklist = pevent->printklist; @@ -542,6 +570,8 @@ static void printk_map_init(struct pevent *pevent) pevent->printk_map = printk_map; pevent->printklist = NULL; + + return 0; } static struct printk_map * @@ -550,8 +580,8 @@ find_printk(struct pevent *pevent, unsigned long long addr) struct printk_map *printk; struct printk_map key; - if (!pevent->printk_map) - printk_map_init(pevent); + if (!pevent->printk_map && printk_map_init(pevent)) + return NULL; key.addr = addr; @@ -573,21 +603,27 @@ find_printk(struct pevent *pevent, unsigned long long addr) int pevent_register_print_string(struct pevent *pevent, char *fmt, unsigned long long addr) { - struct printk_list *item; + struct printk_list *item = malloc(sizeof(*item)); - item = malloc_or_die(sizeof(*item)); + if (!item) + return -1; item->next = pevent->printklist; - item->printk = strdup(fmt); item->addr = addr; + item->printk = strdup(fmt); if (!item->printk) - die("malloc fmt"); + goto out_free; pevent->printklist = item; pevent->printk_count++; return 0; + +out_free: + free(item); + errno = ENOMEM; + return -1; } /** @@ -615,14 +651,15 @@ static struct event_format *alloc_event(void) return calloc(1, sizeof(struct event_format)); } -static void add_event(struct pevent *pevent, struct event_format *event) +static int add_event(struct pevent *pevent, struct event_format *event) { int i; + struct event_format **events = realloc(pevent->events, sizeof(event) * + (pevent->nr_events + 1)); + if (!events) + return -1; - pevent->events = realloc(pevent->events, sizeof(event) * - (pevent->nr_events + 1)); - if (!pevent->events) - die("Can not allocate events"); + pevent->events = events; for (i = 0; i < pevent->nr_events; i++) { if (pevent->events[i]->id > event->id) @@ -637,6 +674,8 @@ static void add_event(struct pevent *pevent, struct event_format *event) pevent->nr_events++; event->pevent = pevent; + + return 0; } static int event_item_type(enum event_type type) @@ -1751,8 +1790,10 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) type == EVENT_DELIM && (strcmp(token, ")") == 0)) { char *new_atom; - if (left->type != PRINT_ATOM) - die("bad pointer type"); + if (left->type != PRINT_ATOM) { + do_warning("bad pointer type"); + goto out_free; + } new_atom = realloc(left->atom.atom, strlen(left->atom.atom) + 3); if (!new_atom) @@ -1870,7 +1911,11 @@ eval_type_str(unsigned long long val, const char *type, int pointer) return val; } - ref = malloc_or_die(len); + ref = malloc(len); + if (!ref) { + do_warning("%s: not enough memory!", __func__); + return val; + } memcpy(ref, type, len); /* chop off the " *" */ @@ -1947,8 +1992,10 @@ eval_type_str(unsigned long long val, const char *type, int pointer) static unsigned long long eval_type(unsigned long long val, struct print_arg *arg, int pointer) { - if (arg->type != PRINT_TYPE) - die("expected type argument"); + if (arg->type != PRINT_TYPE) { + do_warning("expected type argument"); + return 0; + } return eval_type_str(val, arg->typecast.type, pointer); } @@ -2133,7 +2180,7 @@ static char *arg_eval (struct print_arg *arg) case PRINT_STRING: case PRINT_BSTRING: default: - die("invalid eval type %d", arg->type); + do_warning("invalid eval type %d", arg->type); break; } @@ -2431,8 +2478,10 @@ process_paren(struct event_format *event, struct print_arg *arg, char **tok) /* make this a typecast and contine */ /* prevous must be an atom */ - if (arg->type != PRINT_ATOM) - die("previous needed to be PRINT_ATOM"); + if (arg->type != PRINT_ATOM) { + do_warning("previous needed to be PRINT_ATOM"); + goto out_free; + } item_arg = alloc_arg(); @@ -2674,7 +2723,8 @@ process_arg_token(struct event_format *event, struct print_arg *arg, case EVENT_ERROR ... EVENT_NEWLINE: default: - die("unexpected type %d", type); + do_warning("unexpected type %d", type); + return EVENT_ERROR; } *tok = token; @@ -2921,8 +2971,10 @@ static int get_common_info(struct pevent *pevent, * All events should have the same common elements. * Pick any event to find where the type is; */ - if (!pevent->events) - die("no event_list!"); + if (!pevent->events) { + do_warning("no event_list!"); + return -1; + } event = pevent->events[0]; field = pevent_find_common_field(event, type); @@ -3080,7 +3132,8 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg if (!arg->field.field) { arg->field.field = pevent_find_any_field(event, arg->field.name); if (!arg->field.field) - die("field %s not found", arg->field.name); + goto out_warning_field; + } /* must be a number */ val = pevent_read_number(pevent, data + arg->field.field->offset, @@ -3141,8 +3194,10 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg if (!larg->field.field) { larg->field.field = pevent_find_any_field(event, larg->field.name); - if (!larg->field.field) - die("field %s not found", larg->field.name); + if (!larg->field.field) { + arg = larg; + goto out_warning_field; + } } field_size = larg->field.field->elementsize; offset = larg->field.field->offset + @@ -3178,7 +3233,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg val = left != right; break; default: - die("unknown op '%s'", arg->op.op); + goto out_warning_op; } break; case '~': @@ -3208,7 +3263,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg val = left <= right; break; default: - die("unknown op '%s'", arg->op.op); + goto out_warning_op; } break; case '>': @@ -3223,12 +3278,13 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg val = left >= right; break; default: - die("unknown op '%s'", arg->op.op); + goto out_warning_op; } break; case '=': if (arg->op.op[1] != '=') - die("unknown op '%s'", arg->op.op); + goto out_warning_op; + val = left == right; break; case '-': @@ -3244,13 +3300,21 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg val = left * right; break; default: - die("unknown op '%s'", arg->op.op); + goto out_warning_op; } break; default: /* not sure what to do there */ return 0; } return val; + +out_warning_op: + do_warning("%s: unknown op '%s'", __func__, arg->op.op); + return 0; + +out_warning_field: + do_warning("%s: field %s not found", __func__, arg->field.name); + return 0; } struct flag { @@ -3327,8 +3391,10 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, field = arg->field.field; if (!field) { field = pevent_find_any_field(event, arg->field.name); - if (!field) - die("field %s not found", arg->field.name); + if (!field) { + str = arg->field.name; + goto out_warning_field; + } arg->field.field = field; } /* Zero sized fields, mean the rest of the data */ @@ -3345,7 +3411,11 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, trace_seq_printf(s, "%lx", addr); break; } - str = malloc_or_die(len + 1); + str = malloc(len + 1); + if (!str) { + do_warning("%s: not enough memory!", __func__); + return; + } memcpy(str, data + field->offset, len); str[len] = 0; print_str_to_seq(s, format, len_arg, str); @@ -3385,7 +3455,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, str = arg->hex.field->field.name; field = pevent_find_any_field(event, str); if (!field) - die("field %s not found", str); + goto out_warning_field; arg->hex.field->field.field = field; } hex = data + field->offset; @@ -3437,6 +3507,11 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, /* well... */ break; } + + return; + +out_warning_field: + do_warning("%s: field %s not found", __func__, arg->field.name); } static unsigned long long @@ -3463,7 +3538,11 @@ process_defined_func(struct trace_seq *s, void *data, int size, farg = arg->func.args; param = func_handle->params; - args = malloc_or_die(sizeof(*args) * func_handle->nr_args); + ret = ULLONG_MAX; + args = malloc(sizeof(*args) * func_handle->nr_args); + if (!args) + goto out; + for (i = 0; i < func_handle->nr_args; i++) { switch (param->type) { case PEVENT_FUNC_ARG_INT: @@ -3475,12 +3554,18 @@ process_defined_func(struct trace_seq *s, void *data, int size, trace_seq_init(&str); print_str_arg(&str, data, size, event, "%s", -1, farg); trace_seq_terminate(&str); - string = malloc_or_die(sizeof(*string)); + string = malloc(sizeof(*string)); + if (!string) { + do_warning("%s(%d): malloc str", __func__, __LINE__); + goto out_free; + } string->next = strings; string->str = strdup(str.buffer); - if (!string->str) - die("malloc str"); - + if (!string->str) { + free(string); + do_warning("%s(%d): malloc str", __func__, __LINE__); + goto out_free; + } args[i] = (uintptr_t)string->str; strings = string; trace_seq_destroy(&str); @@ -3490,14 +3575,15 @@ process_defined_func(struct trace_seq *s, void *data, int size, * Something went totally wrong, this is not * an input error, something in this code broke. */ - die("Unexpected end of arguments\n"); - break; + do_warning("Unexpected end of arguments\n"); + goto out_free; } farg = farg->next; param = param->next; } ret = (*func_handle->func)(s, args); +out_free: free(args); while (strings) { string = strings; @@ -3538,11 +3624,15 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc if (!field) { field = pevent_find_field(event, "buf"); - if (!field) - die("can't find buffer field for binary printk"); + if (!field) { + do_warning("can't find buffer field for binary printk"); + return NULL; + } ip_field = pevent_find_field(event, "ip"); - if (!ip_field) - die("can't find ip field for binary printk"); + if (!ip_field) { + do_warning("can't find ip field for binary printk"); + return NULL; + } pevent->bprint_buf_field = field; pevent->bprint_ip_field = ip_field; } @@ -3637,7 +3727,7 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc arg->type = PRINT_BSTRING; arg->string.string = strdup(bptr); if (!arg->string.string) - break; + goto out_free; bptr += strlen(bptr) + 1; *next = arg; next = &arg->next; @@ -3669,8 +3759,10 @@ get_bprint_format(void *data, int size __maybe_unused, if (!field) { field = pevent_find_field(event, "fmt"); - if (!field) - die("can't find format field for binary printk"); + if (!field) { + do_warning("can't find format field for binary printk"); + return NULL; + } pevent->bprint_fmt_field = field; } @@ -3723,8 +3815,11 @@ static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size, if (!arg->field.field) { arg->field.field = pevent_find_any_field(event, arg->field.name); - if (!arg->field.field) - die("field %s not found", arg->field.name); + if (!arg->field.field) { + do_warning("%s: field %s not found", + __func__, arg->field.name); + return; + } } if (arg->field.field->size != 6) { trace_seq_printf(s, "INVALIDMAC"); @@ -4380,7 +4475,10 @@ get_event_fields(const char *type, const char *name, struct format_field *field; int i = 0; - fields = malloc_or_die(sizeof(*fields) * (count + 1)); + fields = malloc(sizeof(*fields) * (count + 1)); + if (!fields) + return NULL; + for (field = list; field; field = field->next) { fields[i++] = field; if (i == count + 1) { @@ -4775,7 +4873,8 @@ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, } show_warning = 1; - add_event(pevent, event); + if (add_event(pevent, event)) + goto event_alloc_failed; if (!ret && (event->flags & EVENT_FL_ISFTRACE)) { struct format_field *field; @@ -4808,7 +4907,9 @@ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, event_parse_failed: event->flags |= EVENT_FL_FAILED; /* still add it even if it failed */ - add_event(pevent, event); + if (add_event(pevent, event)) + goto event_alloc_failed; + return ret; event_alloc_failed: -- cgit v1.2.3 From ccd88951765c5e347c3be42d23d961d00207523f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 18 Sep 2012 11:13:15 -0300 Subject: tools lib traceevent: Carve out events format parsing routine The pevent_parse_event() routine will parse a events/sys/tp/format file and add an event_format instance to the pevent struct. This patch introduces a pevent_parse_format() routine with just the bits needed to parse the event/sys/tp/format file and just return the event_format instance, useful for when all we want is to parse the format file, without requiring the pevent struct. Acked-by: Steven Rostedt Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Steven Rostedt Link: http://lkml.kernel.org/n/tip-lge0afl47arh86om0m6a5bqr@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 96 ++++++++++++++++++++++++++++++++++++++++++++--------------- event-parse.h | 3 ++ 2 files changed, 75 insertions(+), 24 deletions(-) diff --git a/event-parse.c b/event-parse.c index b3bc130..1fa71ca 100644 --- a/event-parse.c +++ b/event-parse.c @@ -4794,8 +4794,7 @@ static int find_event_handle(struct pevent *pevent, struct event_format *event) } /** - * pevent_parse_event - parse the event format - * @pevent: the handle to the pevent + * __pevent_parse_format - parse the event format * @buf: the buffer storing the event format string * @size: the size of @buf * @sys: the system the event belongs to @@ -4807,15 +4806,16 @@ static int find_event_handle(struct pevent *pevent, struct event_format *event) * * /sys/kernel/debug/tracing/events/.../.../format */ -enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, - unsigned long size, const char *sys) +enum pevent_errno __pevent_parse_format(struct event_format **eventp, + struct pevent *pevent, const char *buf, + unsigned long size, const char *sys) { struct event_format *event; int ret; init_input_buf(buf, size); - event = alloc_event(); + *eventp = event = alloc_event(); if (!event) return PEVENT_ERRNO__MEM_ALLOC_FAILED; @@ -4849,9 +4849,6 @@ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, goto event_alloc_failed; } - /* Add pevent to event so that it can be referenced */ - event->pevent = pevent; - ret = event_read_format(event); if (ret < 0) { ret = PEVENT_ERRNO__READ_FORMAT_FAILED; @@ -4862,19 +4859,16 @@ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, * If the event has an override, don't print warnings if the event * print format fails to parse. */ - if (find_event_handle(pevent, event)) + if (pevent && find_event_handle(pevent, event)) show_warning = 0; ret = event_read_print(event); + show_warning = 1; + if (ret < 0) { - show_warning = 1; ret = PEVENT_ERRNO__READ_PRINT_FAILED; goto event_parse_failed; } - show_warning = 1; - - if (add_event(pevent, event)) - goto event_alloc_failed; if (!ret && (event->flags & EVENT_FL_ISFTRACE)) { struct format_field *field; @@ -4898,21 +4892,75 @@ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, return 0; } -#define PRINT_ARGS 0 - if (PRINT_ARGS && event->print_fmt.args) - print_args(event->print_fmt.args); - return 0; event_parse_failed: event->flags |= EVENT_FL_FAILED; - /* still add it even if it failed */ - if (add_event(pevent, event)) - goto event_alloc_failed; - return ret; event_alloc_failed: + free(event->system); + free(event->name); + free(event); + *eventp = NULL; + return ret; +} + +/** + * pevent_parse_format - parse the event format + * @buf: the buffer storing the event format string + * @size: the size of @buf + * @sys: the system the event belongs to + * + * This parses the event format and creates an event structure + * to quickly parse raw data for a given event. + * + * These files currently come from: + * + * /sys/kernel/debug/tracing/events/.../.../format + */ +enum pevent_errno pevent_parse_format(struct event_format **eventp, const char *buf, + unsigned long size, const char *sys) +{ + return __pevent_parse_format(eventp, NULL, buf, size, sys); +} + +/** + * pevent_parse_event - parse the event format + * @pevent: the handle to the pevent + * @buf: the buffer storing the event format string + * @size: the size of @buf + * @sys: the system the event belongs to + * + * This parses the event format and creates an event structure + * to quickly parse raw data for a given event. + * + * These files currently come from: + * + * /sys/kernel/debug/tracing/events/.../.../format + */ +enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, + unsigned long size, const char *sys) +{ + struct event_format *event = NULL; + int ret = __pevent_parse_format(&event, pevent, buf, size, sys); + + if (event == NULL) + return ret; + + /* Add pevent to event so that it can be referenced */ + event->pevent = pevent; + + if (add_event(pevent, event)) + goto event_add_failed; + +#define PRINT_ARGS 0 + if (PRINT_ARGS && event->print_fmt.args) + print_args(event->print_fmt.args); + + return 0; + +event_add_failed: free(event->system); free(event->name); free(event); @@ -5365,7 +5413,7 @@ static void free_formats(struct format *format) free_format_fields(format->fields); } -static void free_event(struct event_format *event) +void pevent_free_format(struct event_format *event) { free(event->name); free(event->system); @@ -5451,7 +5499,7 @@ void pevent_free(struct pevent *pevent) } for (i = 0; i < pevent->nr_events; i++) - free_event(pevent->events[i]); + pevent_free_format(pevent->events[i]); while (pevent->handlers) { handle = pevent->handlers; diff --git a/event-parse.h b/event-parse.h index a4bbe24..24a4bba 100644 --- a/event-parse.h +++ b/event-parse.h @@ -540,6 +540,9 @@ int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long siz enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, unsigned long size, const char *sys); +enum pevent_errno pevent_parse_format(struct event_format **eventp, const char *buf, + unsigned long size, const char *sys); +void pevent_free_format(struct event_format *event); void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event, const char *name, struct pevent_record *record, -- cgit v1.2.3 From b17b75e5117225682338c6f6f49da71bdee90a81 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 20 Sep 2012 11:09:19 +0900 Subject: tools lib traceevent: Handle alloc_arg failure Now alloc_arg returns NULL if memory allocation failed, it should be handled on callsites properly. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Steven Rostedt Link: http://lkml.kernel.org/r/87k3vpzbqo.fsf_-_@sejong.aot.lge.com Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/event-parse.c b/event-parse.c index 1fa71ca..17c9221 100644 --- a/event-parse.c +++ b/event-parse.c @@ -1565,6 +1565,14 @@ process_cond(struct event_format *event, struct print_arg *top, char **tok) left = alloc_arg(); right = alloc_arg(); + if (!arg || !left || !right) { + do_warning("%s: not enough memory!", __func__); + /* arg will be freed at out_free */ + free_arg(left); + free_arg(right); + goto out_free; + } + arg->type = PRINT_OP; arg->op.left = left; arg->op.right = right; @@ -1607,6 +1615,12 @@ process_array(struct event_format *event, struct print_arg *top, char **tok) char *token = NULL; arg = alloc_arg(); + if (!arg) { + do_warning("%s: not enough memory!", __func__); + /* '*tok' is set to top->op.op. No need to free. */ + *tok = NULL; + return EVENT_ERROR; + } *tok = NULL; type = process_arg(event, arg, &token); @@ -1725,10 +1739,16 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) /* make an empty left */ left = alloc_arg(); + if (!left) + goto out_warn_free; + left->type = PRINT_NULL; arg->op.left = left; right = alloc_arg(); + if (!right) + goto out_warn_free; + arg->op.right = right; /* do not free the token, it belongs to an op */ @@ -1738,6 +1758,9 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) } else if (strcmp(token, "?") == 0) { left = alloc_arg(); + if (!left) + goto out_warn_free; + /* copy the top arg to the left */ *left = *arg; @@ -1766,6 +1789,8 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) strcmp(token, "!=") == 0) { left = alloc_arg(); + if (!left) + goto out_warn_free; /* copy the top arg to the left */ *left = *arg; @@ -1797,7 +1822,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) new_atom = realloc(left->atom.atom, strlen(left->atom.atom) + 3); if (!new_atom) - goto out_free; + goto out_warn_free; left->atom.atom = new_atom; strcat(left->atom.atom, " *"); @@ -1809,12 +1834,18 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) } right = alloc_arg(); + if (!right) + goto out_warn_free; + type = process_arg_token(event, right, tok, type); arg->op.right = right; } else if (strcmp(token, "[") == 0) { left = alloc_arg(); + if (!left) + goto out_warn_free; + *left = *arg; arg->type = PRINT_OP; @@ -1847,7 +1878,9 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) return type; - out_free: +out_warn_free: + do_warning("%s: not enough memory!", __func__); +out_free: free_token(token); *tok = NULL; return EVENT_ERROR; @@ -2203,6 +2236,8 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char ** break; arg = alloc_arg(); + if (!arg) + goto out_free; free_token(token); type = process_arg(event, arg, &token); @@ -2229,6 +2264,8 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char ** free_arg(arg); arg = alloc_arg(); + if (!arg) + goto out_free; free_token(token); type = process_arg(event, arg, &token); @@ -2275,6 +2312,10 @@ process_flags(struct event_format *event, struct print_arg *arg, char **tok) arg->type = PRINT_FLAGS; field = alloc_arg(); + if (!field) { + do_warning("%s: not enough memory!", __func__); + goto out_free; + } type = process_arg(event, field, &token); @@ -2324,6 +2365,10 @@ process_symbols(struct event_format *event, struct print_arg *arg, char **tok) arg->type = PRINT_SYMBOL; field = alloc_arg(); + if (!field) { + do_warning("%s: not enough memory!", __func__); + goto out_free; + } type = process_arg(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ",")) @@ -2358,6 +2403,11 @@ process_hex(struct event_format *event, struct print_arg *arg, char **tok) arg->type = PRINT_HEX; field = alloc_arg(); + if (!field) { + do_warning("%s: not enough memory!", __func__); + goto out_free; + } + type = process_arg(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ",")) @@ -2368,6 +2418,12 @@ process_hex(struct event_format *event, struct print_arg *arg, char **tok) free_token(token); field = alloc_arg(); + if (!field) { + do_warning("%s: not enough memory!", __func__); + *tok = NULL; + return EVENT_ERROR; + } + type = process_arg(event, field, &token); if (test_type_token(type, token, EVENT_DELIM, ")")) @@ -2425,6 +2481,12 @@ process_dynamic_array(struct event_format *event, struct print_arg *arg, char ** free_token(token); arg = alloc_arg(); + if (!field) { + do_warning("%s: not enough memory!", __func__); + *tok = NULL; + return EVENT_ERROR; + } + type = process_arg(event, arg, &token); if (type == EVENT_ERROR) goto out_free_arg; @@ -2484,6 +2546,10 @@ process_paren(struct event_format *event, struct print_arg *arg, char **tok) } item_arg = alloc_arg(); + if (!item_arg) { + do_warning("%s: not enough memory!", __func__); + goto out_free; + } arg->type = PRINT_TYPE; arg->typecast.type = arg->atom.atom; @@ -2579,6 +2645,11 @@ process_func_handler(struct event_format *event, struct pevent_function_handler next_arg = &(arg->func.args); for (i = 0; i < func->nr_args; i++) { farg = alloc_arg(); + if (!farg) { + do_warning("%s: not enough memory!", __func__); + return EVENT_ERROR; + } + type = process_arg(event, farg, &token); if (i < (func->nr_args - 1)) test = ","; @@ -2745,6 +2816,10 @@ static int event_read_print_args(struct event_format *event, struct print_arg ** } arg = alloc_arg(); + if (!arg) { + do_warning("%s: not enough memory!", __func__); + return -1; + } type = process_arg(event, arg, &token); @@ -3643,6 +3718,10 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc * The first arg is the IP pointer. */ args = alloc_arg(); + if (!args) { + do_warning("%s(%d): not enough memory!", __func__, __LINE__); + return NULL; + } arg = args; arg->next = NULL; next = &arg->next; @@ -3705,6 +3784,11 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc val = pevent_read_number(pevent, bptr, vsize); bptr += vsize; arg = alloc_arg(); + if (!arg) { + do_warning("%s(%d): not enough memory!", + __func__, __LINE__); + goto out_free; + } arg->next = NULL; arg->type = PRINT_ATOM; if (asprintf(&arg->atom.atom, "%lld", val) < 0) { @@ -3723,6 +3807,11 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc break; case 's': arg = alloc_arg(); + if (!arg) { + do_warning("%s(%d): not enough memory!", + __func__, __LINE__); + goto out_free; + } arg->next = NULL; arg->type = PRINT_BSTRING; arg->string.string = strdup(bptr); @@ -4878,6 +4967,10 @@ enum pevent_errno __pevent_parse_format(struct event_format **eventp, list = &event->print_fmt.args; for (field = event->format.fields; field; field = field->next) { arg = alloc_arg(); + if (!arg) { + event->flags |= EVENT_FL_FAILED; + return PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED; + } arg->type = PRINT_FIELD; arg->field.name = strdup(field->name); if (!arg->field.name) { -- cgit v1.2.3 From 38343827083f972aed2b376dc96e5ff51c32bd94 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 25 Sep 2012 21:25:19 +0900 Subject: tools lib traceevent: Fix error path on pevent_parse_event If __pevent_parse_format() succeeded but add_event() failed, 'ret' didn't have a proper error code. Set it to PEVENT_ERRNO__MEM_ALLOC_FAILED. In addition, at that point 'event' also has fields and format information and they all need to be freed. Call pevent_free_format() to handle it. Signed-off-by: Namhyung Kim Cc: Ingo Molnar Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1348575919-4954-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/event-parse.c b/event-parse.c index 17c9221..47264b4 100644 --- a/event-parse.c +++ b/event-parse.c @@ -5044,8 +5044,10 @@ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, /* Add pevent to event so that it can be referenced */ event->pevent = pevent; - if (add_event(pevent, event)) + if (add_event(pevent, event)) { + ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; goto event_add_failed; + } #define PRINT_ARGS 0 if (PRINT_ARGS && event->print_fmt.args) @@ -5054,9 +5056,7 @@ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, return 0; event_add_failed: - free(event->system); - free(event->name); - free(event); + pevent_free_format(event); return ret; } -- cgit v1.2.3 From 0fb93867d68290f68d1eacf344175415bfc9cd83 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 1 Oct 2012 20:13:51 -0400 Subject: lib tools traceevent: Add back pevent assignment in __pevent_parse_format() Even though with the change of commit commit 2b29175 "tools lib traceevent: Carve out events format parsing routine", allowed __pevent_parse_format() to parse an event without the need of a pevent handler, the event still needs to assign the pevent handed to it. There's no problem with assigning it if the pevent is NULL, as the event->pevent would be NULL without the assignment. But function parsing handlers may be assigned to the pevent handler to help in parsing the event. If there's no pevent then there would not be any function handlers, but if the pevent isn't assigned first before parsing the event, it wont honor the function handlers that were assigned. Worse yet, the current code crashes if an event has a function that it tries to parse. For example: # perf record -e scsi:scsi_dispatch_cmd_timeout Segmentation fault (core dumped) This happens because the scsi_dispatch_cmd_timeout event format has the following: scsi_trace_parse_cdb(p, __get_dynamic_array(cmnd), REC->cmd_len) which hasn't been defined by the pevent code. Signed-off-by: Steven Rostedt Reviewed-by: Namhyung Kim Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/1349136831.22822.133.camel@gandalf.local.home Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/event-parse.c b/event-parse.c index 47264b4..f2989c5 100644 --- a/event-parse.c +++ b/event-parse.c @@ -2602,6 +2602,9 @@ find_func_handler(struct pevent *pevent, char *func_name) { struct pevent_function_handler *func; + if (!pevent) + return NULL; + for (func = pevent->func_handlers; func; func = func->next) { if (strcmp(func->name, func_name) == 0) break; @@ -4938,6 +4941,9 @@ enum pevent_errno __pevent_parse_format(struct event_format **eventp, goto event_alloc_failed; } + /* Add pevent to event so that it can be referenced */ + event->pevent = pevent; + ret = event_read_format(event); if (ret < 0) { ret = PEVENT_ERRNO__READ_FORMAT_FAILED; @@ -5041,9 +5047,6 @@ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, if (event == NULL) return ret; - /* Add pevent to event so that it can be referenced */ - event->pevent = pevent; - if (add_event(pevent, event)) { ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; goto event_add_failed; -- cgit v1.2.3 From 914b14a54ea9c117a5523f6e87e33d0bfaaeef02 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 1 Oct 2012 20:23:28 -0400 Subject: tools lib traceevent: Fix missed freeing of subargs in free_arg() in filter Some of args were missed in free_args(), as well as subargs. That is args like FILTER_ARG_NUM have left and right pointers to other args that also need to be freed. Signed-off-by: Steven Rostedt Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/1349137408.22822.135.camel@gandalf.local.home Signed-off-by: Arnaldo Carvalho de Melo --- parse-filter.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/parse-filter.c b/parse-filter.c index ad17855..5ea4326 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -209,7 +209,16 @@ static void free_arg(struct filter_arg *arg) switch (arg->type) { case FILTER_ARG_NONE: case FILTER_ARG_BOOLEAN: + break; + case FILTER_ARG_NUM: + free_arg(arg->num.left); + free_arg(arg->num.right); + break; + + case FILTER_ARG_EXP: + free_arg(arg->exp.left); + free_arg(arg->exp.right); break; case FILTER_ARG_STR: @@ -218,6 +227,12 @@ static void free_arg(struct filter_arg *arg) free(arg->str.buffer); break; + case FILTER_ARG_VALUE: + if (arg->value.type == FILTER_STRING || + arg->value.type == FILTER_CHAR) + free(arg->value.str); + break; + case FILTER_ARG_OP: free_arg(arg->op.left); free_arg(arg->op.right); -- cgit v1.2.3 From b4d21567f73465168893a0f5930bafd88cc87630 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 26 Oct 2012 17:55:48 +0900 Subject: tools lib traceevent: Do not generate dependency for system header files Ingo reported (again!) that 'make clean' on perf/traceevent does not work due to some reason with system header file. Quotes Ingo: "Note that the old dependency related build failure thought to be fixed in commit 860df5833e46 is back: make[1]: *** No rule to make target `/usr/lib/gcc/x86_64-redhat-linux/4.7.0/include/stddef.h', needed by `.trace-seq.d'. Stop. 'make clean' itself does not work in libtraceevent: comet:~/tip/tools/lib/traceevent> make clean make: *** No rule to make target `/usr/lib/gcc/x86_64-redhat-linux/4.7.0/include/stddef.h', needed by `.trace-seq.d'. Stop. So I had to clean it out manually: comet:~/tip/tools/lib/traceevent> git ls-files --others | xargs rm comet:~/tip/tools/lib/traceevent> and then things build fine." Try to fix it by excluding system headers from dependency generation. Signed-off-by: Namhyung Kim Reported-by: Ingo Molnar Cc: Borislav Petkov Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1351241752-2919-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 04d959f..a20e320 100644 --- a/Makefile +++ b/Makefile @@ -253,7 +253,7 @@ all_deps := $(all_objs:%.o=.%.d) # let .d file also depends on the source and header files define check_deps @set -e; $(RM) $@; \ - $(CC) -M $(CFLAGS) $< > $@.$$$$; \ + $(CC) -MM $(CFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ $(RM) $@.$$$$ endef -- cgit v1.2.3 From 412466082582317bc000c87f44189b455bd6b87b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 9 Nov 2012 15:18:57 -0300 Subject: tools lib traceevent: Add __maybe_unused to unused parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixing the build on 32-bit Fedora 14: tools/lib/traceevent/event-parse.c: In function ‘print_event_fields’: tools/lib/traceevent/event-parse.c:3934:69: error: unused parameter ‘size’ tools/lib/traceevent/event-parse.c: In function ‘pevent_strerror’: tools/lib/traceevent/event-parse.c:5074:36: error: unused parameter ‘pevent’ Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Steven Rostedt Link: http://lkml.kernel.org/n/tip-soe4gqcz8fd4ecik6exvyqox@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/event-parse.c b/event-parse.c index f2989c5..0dcc18c 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3931,7 +3931,8 @@ static int is_printable_array(char *p, unsigned int len) return 1; } -static void print_event_fields(struct trace_seq *s, void *data, int size, +static void print_event_fields(struct trace_seq *s, void *data, + int size __maybe_unused, struct event_format *event) { struct format_field *field; @@ -5070,8 +5071,8 @@ static const char * const pevent_error_str[] = { }; #undef _PE -int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, - char *buf, size_t buflen) +int pevent_strerror(struct pevent *pevent __maybe_unused, + enum pevent_errno errnum, char *buf, size_t buflen) { int idx; const char *msg; -- cgit v1.2.3 From 40274a82d7721357817d1073fb6211445b15043d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 9 Nov 2012 15:32:00 -0300 Subject: tools lib traceevent: Avoid comparisions between signed/unsigned Fixing this warning-as-error on f14 32-bit: tools/lib/traceevent/event-parse.c:5564:17: error: comparison between signed and unsigned integer expressions tools/lib/traceevent/event-parse.c:5586:17: error: comparison between signed and unsigned integer expressions Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Steven Rostedt Link: http://lkml.kernel.org/n/tip-stmix8hy4nu5ervpynn8yj2z@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/event-parse.c b/event-parse.c index 0dcc18c..6b647c1 100644 --- a/event-parse.c +++ b/event-parse.c @@ -5561,7 +5561,7 @@ void pevent_free(struct pevent *pevent) } if (pevent->func_map) { - for (i = 0; i < pevent->func_count; i++) { + for (i = 0; i < (int)pevent->func_count; i++) { free(pevent->func_map[i].func); free(pevent->func_map[i].mod); } @@ -5583,7 +5583,7 @@ void pevent_free(struct pevent *pevent) } if (pevent->printk_map) { - for (i = 0; i < pevent->printk_count; i++) + for (i = 0; i < (int)pevent->printk_count; i++) free(pevent->printk_map[i].printk); free(pevent->printk_map); } -- cgit v1.2.3 From 38ed8469f79d135824c821983ca978e9a0651ada Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 9 Nov 2012 15:42:26 -0300 Subject: tools lib traceevent: No need to check for < 0 on an unsigned enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gcc on f14 32-bit complains: tools/lib/traceevent/event-parse.c: In function ‘pevent_register_print_function’: tools/lib/traceevent/event-parse.c:5366:3: error: comparison of unsigned expression < 0 is always false This is because: enum pevent_func_arg_type type; this enum doesn't have any negative value, so gcc makes it an 'unsigned int'. Fix it by removing the < 0 test. Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Steven Rostedt Link: http://lkml.kernel.org/n/tip-6vnd6ud6fbpn48zax4a5ru01@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index 6b647c1..38d6595 100644 --- a/event-parse.c +++ b/event-parse.c @@ -5363,7 +5363,7 @@ int pevent_register_print_function(struct pevent *pevent, if (type == PEVENT_FUNC_ARG_VOID) break; - if (type < 0 || type >= PEVENT_FUNC_ARG_MAX_TYPES) { + if (type >= PEVENT_FUNC_ARG_MAX_TYPES) { do_warning("Invalid argument type %d", type); ret = PEVENT_ERRNO__INVALID_ARG_TYPE; goto out_free; -- cgit v1.2.3 From adae7feead75ae036b93a3ec0c99664f46deb263 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 9 Nov 2012 15:50:33 -0300 Subject: tools lib traceevent: Handle INVALID_ARG_TYPE errno in pevent_strerror MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gcc on f14 32-bit rightly complains: tools/lib/traceevent/event-parse.c:5097:2: error: enumeration value ‘PEVENT_ERRNO__INVALID_ARG_TYPE’ not handled in switch The entry for it is in the error strings array pevent_error_str[]: _PE(INVALID_ARG_TYPE, "invalid argument type") It was just not being handled on the pevent_strerror switch, fix it. Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Steven Rostedt Link: http://lkml.kernel.org/n/tip-c68zkvxw4289uqbosfkz963g@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 1 + 1 file changed, 1 insertion(+) diff --git a/event-parse.c b/event-parse.c index 38d6595..669953a 100644 --- a/event-parse.c +++ b/event-parse.c @@ -5101,6 +5101,7 @@ int pevent_strerror(struct pevent *pevent __maybe_unused, case PEVENT_ERRNO__READ_FORMAT_FAILED: case PEVENT_ERRNO__READ_PRINT_FAILED: case PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED: + case PEVENT_ERRNO__INVALID_ARG_TYPE: snprintf(buf, buflen, "%s", msg); break; -- cgit v1.2.3 From 6e0e90c59fc19746b64fd9dbdb1c8d915bdbfc29 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 9 Nov 2012 17:40:47 -0300 Subject: tools lib traceevent: Use 'const' in variables pointing to const strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixing the build on fedora 14, 32-bit: tools/lib/traceevent/event-parse.c: In function ‘find_cmdline’: tools/lib/traceevent/event-parse.c:183:3: error: return discards qualifiers from pointer target type tools/lib/traceevent/event-parse.c:186:3: error: return discards qualifiers from pointer target type tools/lib/traceevent/event-parse.c:195:2: error: return discards qualifiers from pointer target type tools/lib/traceevent/event-parse.c: In function ‘process_func_handler’: tools/lib/traceevent/event-parse.c:2658:9: error: assignment discards qualifiers from pointer target type tools/lib/traceevent/event-parse.c:2660:9: error: assignment discards qualifiers from pointer target type tools/lib/traceevent/event-parse.c: In function ‘print_mac_arg’: tools/lib/traceevent/event-parse.c:3892:14: error: initialization discards qualifiers from pointer target type tools/lib/traceevent/event-parse.c:3906:7: error: assignment discards qualifiers from pointer target type tools/lib/traceevent/event-parse.c: In function ‘pevent_print_event’: tools/lib/traceevent/event-parse.c:4412:24: error: initialization discards qualifiers from pointer target type Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Steven Rostedt Link: http://lkml.kernel.org/n/tip-0k5g8urwu7vwkgbcbt2x05fe@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/event-parse.c b/event-parse.c index 669953a..5a824e3 100644 --- a/event-parse.c +++ b/event-parse.c @@ -174,7 +174,7 @@ static int cmdline_init(struct pevent *pevent) return 0; } -static char *find_cmdline(struct pevent *pevent, int pid) +static const char *find_cmdline(struct pevent *pevent, int pid) { const struct cmdline *comm; struct cmdline key; @@ -2637,7 +2637,7 @@ process_func_handler(struct event_format *event, struct pevent_function_handler struct print_arg *farg; enum event_type type; char *token; - char *test; + const char *test; int i; arg->type = PRINT_FUNC; @@ -3889,7 +3889,7 @@ static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size, struct event_format *event, struct print_arg *arg) { unsigned char *buf; - char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x"; + const char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x"; if (arg->type == PRINT_FUNC) { process_defined_func(s, data, size, event, arg); @@ -4409,7 +4409,7 @@ void pevent_event_info(struct trace_seq *s, struct event_format *event, void pevent_print_event(struct pevent *pevent, struct trace_seq *s, struct pevent_record *record) { - static char *spaces = " "; /* 20 spaces */ + static const char *spaces = " "; /* 20 spaces */ struct event_format *event; unsigned long secs; unsigned long usecs; -- cgit v1.2.3 From 19d63a714055f783fa94986a97ec7660bb612a11 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Fri, 21 Dec 2012 15:00:58 -0500 Subject: tools lib traceevent: test correct variable after allocation we've tested the wrong variable for allocation failure, fix it to test the right one. Cc: Frederic Weisbecker Cc: Namhyung Kim Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1356120062-2648-1-git-send-email-sasha.levin@oracle.com Signed-off-by: Sasha Levin Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index 5a824e3..7538a1f 100644 --- a/event-parse.c +++ b/event-parse.c @@ -2481,7 +2481,7 @@ process_dynamic_array(struct event_format *event, struct print_arg *arg, char ** free_token(token); arg = alloc_arg(); - if (!field) { + if (!arg) { do_warning("%s: not enough memory!", __func__); *tok = NULL; return EVENT_ERROR; -- cgit v1.2.3 From 1845c4b28918f9e70b2e2e9efeab4b2342508381 Mon Sep 17 00:00:00 2001 From: Jon Stanley Date: Fri, 7 Sep 2012 16:32:46 -0400 Subject: tools lib traceevent: Update FSF postal address to be URL's. The FSF now prefers to use URL's in copyright headers rather than their postal address. This change updates the address to be per http://www.gnu.org/licenses/gpl-howto.html Signed-off-by: Jon Stanley Acked-by: Steven Rostedt Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1347049967-3143-1-git-send-email-jonstanley@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 3 +-- event-parse.h | 3 +-- event-utils.h | 3 +-- parse-filter.c | 3 +-- trace-seq.c | 3 +-- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/event-parse.c b/event-parse.c index 7538a1f..dc42e55 100644 --- a/event-parse.c +++ b/event-parse.c @@ -13,8 +13,7 @@ * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * diff --git a/event-parse.h b/event-parse.h index 24a4bba..7be7e89 100644 --- a/event-parse.h +++ b/event-parse.h @@ -13,8 +13,7 @@ * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ diff --git a/event-utils.h b/event-utils.h index bc07500..e76c9ac 100644 --- a/event-utils.h +++ b/event-utils.h @@ -13,8 +13,7 @@ * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ diff --git a/parse-filter.c b/parse-filter.c index 5ea4326..2500e75 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -13,8 +13,7 @@ * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ diff --git a/trace-seq.c b/trace-seq.c index b1ccc92..a57db80 100644 --- a/trace-seq.c +++ b/trace-seq.c @@ -13,8 +13,7 @@ * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * License along with this program; if not, see * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -- cgit v1.2.3 From 8781c466d87e6900c97d52a326b3d1d6ba075342 Mon Sep 17 00:00:00 2001 From: Jon Stanley Date: Sat, 8 Sep 2012 14:00:51 -0400 Subject: tools lib traceevent: Add copyright header Adding a missing copyright header to parse-utils.c. Assuminng that the license is LGPL like the rest of the trace-cmd library code. Signed-off-by: Jon Stanley Acked-by: Steven Rostedt Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1347127251-4695-1-git-send-email-jonstanley@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- parse-utils.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/parse-utils.c b/parse-utils.c index f023a13..bba701c 100644 --- a/parse-utils.c +++ b/parse-utils.c @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ #include #include #include -- cgit v1.2.3 From 8c6ea07dbd99be25af08ba63c350bfa733c929cc Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 15 Jan 2013 17:02:19 +0900 Subject: tools lib traceevent: Fix warning on '>=' operator Although the '>=' (and '<=') operator is handled properly in libtraceevent, it emitted following spurious warnings on perf test: $ perf test 5: parse events tests : ... Warning: unknown op '>=' Warning: unknown op '>=' Warning: unknown op '>=' Warning: unknown op '>=' Warning: unknown op '>=' Warning: unknown op '>=' ... Add the operator to the checks. Signed-off-by: Namhyung Kim Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1358236939-17393-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/event-parse.c b/event-parse.c index dc42e55..f504619 100644 --- a/event-parse.c +++ b/event-parse.c @@ -1784,6 +1784,8 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) strcmp(token, "/") == 0 || strcmp(token, "<") == 0 || strcmp(token, ">") == 0 || + strcmp(token, "<=") == 0 || + strcmp(token, ">=") == 0 || strcmp(token, "==") == 0 || strcmp(token, "!=") == 0) { -- cgit v1.2.3 From 49c4ca0cb97bbc2952f62a770387a186c45f6cc8 Mon Sep 17 00:00:00 2001 From: Tom Zanussi Date: Fri, 18 Jan 2013 13:51:25 -0600 Subject: perf tools: Reinstate 'signed' field flag for tracepoints For some reason the libtraceevent tracepoint-parsing code is missing the FIELD_IS_SIGNED flag-setting code, which causes problems for the Perl trace event binding at least, since it ends up unable to recognize negative numbers. Things like checking for negative return values therefore fail, causing scripts like rwtop to instead interpret the negative return value as a large positive value, which in turn get added to e.g. read totals with insanely invalid results. So set the FIELD_IS_SIGNED flag for tracepoint events that specify "signed:1". Before: # perf script record rw-by-pid # perf script report rw-by-pid read counts by pid: pid comm # reads bytes_requested bytes_read ------ -------------------- ----------- ---------- ---------- 753 Xorg 88 512000 7.74763251095801e+20 1619 firefox 42 462 2.58254417031934e+20 1232 gnome-shell 11 176 1.10680464442257e+20 1471 gnome-terminal 3 16366 18446744073709551615 1408 libsocialweb-co 2 32 18446744073709551613 After: # perf script report rw-by-pid read counts by pid: pid comm # reads bytes_requested bytes_read ------ -------------------- ----------- ---------- ---------- 753 Xorg 88 512000 2764 1619 firefox 42 462 126 1232 gnome-shell 11 176 40 1471 gnome-terminal 3 16366 10 1408 libsocialweb-co 2 32 8 Signed-off-by: Tom Zanussi Link: http://lkml.kernel.org/r/1471b5968821a455cf5168bb4567964e74ecf530.1358527965.git.tom.zanussi@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index f504619..bb8b3db 100644 --- a/event-parse.c +++ b/event-parse.c @@ -1462,7 +1462,8 @@ static int event_read_fields(struct event_format *event, struct format_field **f if (read_expect_type(EVENT_ITEM, &token)) goto fail; - /* add signed type */ + if (strtoul(token, NULL, 0)) + field->flags |= FIELD_IS_SIGNED; free_token(token); if (read_expected(EVENT_OP, ";") < 0) -- cgit v1.2.3 From 787eb875e25b244f4ea5f1785d62e7ebddb8c77b Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 24 Jan 2013 21:46:43 +0100 Subject: tools lib traceevent: Handle dynamic array's element size properly Fixing the dynamic array format field parsing. Currently the event_read_fields function could segfault while parsing dynamic array other than string type. The reason is the event->pevent does not need to be set and gets dereferenced unconditionaly. Also adding proper initialization of field->elementsize based on the parsed dynamic type. Signed-off-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Corey Ashford Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1359060403-32422-1-git-send-email-jolsa@redhat.com [ committer note: Made a char pointer parameter const, as requested by Steven ] Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/event-parse.c b/event-parse.c index bb8b3db..82b0606 100644 --- a/event-parse.c +++ b/event-parse.c @@ -1223,6 +1223,34 @@ static int field_is_long(struct format_field *field) return 0; } +static unsigned int type_size(const char *name) +{ + /* This covers all FIELD_IS_STRING types. */ + static struct { + const char *type; + unsigned int size; + } table[] = { + { "u8", 1 }, + { "u16", 2 }, + { "u32", 4 }, + { "u64", 8 }, + { "s8", 1 }, + { "s16", 2 }, + { "s32", 4 }, + { "s64", 8 }, + { "char", 1 }, + { }, + }; + int i; + + for (i = 0; table[i].type; i++) { + if (!strcmp(table[i].type, name)) + return table[i].size; + } + + return 0; +} + static int event_read_fields(struct event_format *event, struct format_field **fields) { struct format_field *field = NULL; @@ -1232,6 +1260,8 @@ static int event_read_fields(struct event_format *event, struct format_field **f int count = 0; do { + unsigned int size_dynamic = 0; + type = read_token(&token); if (type == EVENT_NEWLINE) { free_token(token); @@ -1390,6 +1420,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f field->type = new_type; strcat(field->type, " "); strcat(field->type, field->name); + size_dynamic = type_size(field->name); free_token(field->name); strcat(field->type, brackets); field->name = token; @@ -1478,10 +1509,14 @@ static int event_read_fields(struct event_format *event, struct format_field **f if (field->flags & FIELD_IS_ARRAY) { if (field->arraylen) field->elementsize = field->size / field->arraylen; + else if (field->flags & FIELD_IS_DYNAMIC) + field->elementsize = size_dynamic; else if (field->flags & FIELD_IS_STRING) field->elementsize = 1; - else - field->elementsize = event->pevent->long_size; + else if (field->flags & FIELD_IS_LONG) + field->elementsize = event->pevent ? + event->pevent->long_size : + sizeof(long); } else field->elementsize = field->size; -- cgit v1.2.3 From b2bc33de2a6299516460843543cc116eebf53c6d Mon Sep 17 00:00:00 2001 From: Jack Mitchell Date: Fri, 8 Mar 2013 11:21:52 +0000 Subject: libtraceevent: Remove hard coded include to /usr/local/include in Makefile having /usr/local/include hardcoded into the makefile is not necessary as this is automatically included by GCC. It also infects cross-compile builds with the host systems includes. Signed-off-by: Jack Mitchell Acked-by: Namhyung Kim Cc: Ingo Molnar Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1362741712-21308-1-git-send-email-ml@communistcode.co.uk Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a20e320..0b0a907 100644 --- a/Makefile +++ b/Makefile @@ -122,7 +122,7 @@ export Q VERBOSE EVENT_PARSE_VERSION = $(EP_VERSION).$(EP_PATCHLEVEL).$(EP_EXTRAVERSION) -INCLUDES = -I. -I/usr/local/include $(CONFIG_INCLUDES) +INCLUDES = -I. $(CONFIG_INCLUDES) # Set compile option CFLAGS if not set elsewhere CFLAGS ?= -g -Wall -- cgit v1.2.3 From 35bd58e724ccd482c19fd17285d50b5124adff07 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 4 Jun 2013 14:20:16 +0900 Subject: tools lib traceevent: Remove unused install targets The html_install, img_install, install_plugin and install_python are unused in the Makefile. Get rid of them. Signed-off-by: Namhyung Kim Acked-by: Steven Rostedt Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1370323231-14022-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 0b0a907..fd7510d 100644 --- a/Makefile +++ b/Makefile @@ -39,13 +39,8 @@ bindir_relative = bin bindir = $(prefix)/$(bindir_relative) man_dir = $(prefix)/share/man man_dir_SQ = '$(subst ','\'',$(man_dir))' -html_install = $(prefix)/share/kernelshark/html -html_install_SQ = '$(subst ','\'',$(html_install))' -img_install = $(prefix)/share/kernelshark/html/images -img_install_SQ = '$(subst ','\'',$(img_install))' -export man_dir man_dir_SQ html_install html_install_SQ INSTALL -export img_install img_install_SQ +export man_dir man_dir_SQ INSTALL export DESTDIR DESTDIR_SQ # copy a bit from Linux kbuild @@ -300,7 +295,7 @@ define do_install $(INSTALL) $1 '$(DESTDIR_SQ)$2' endef -install_lib: all_cmd install_plugins install_python +install_lib: all_cmd $(Q)$(call do_install,$(LIB_FILE),$(bindir_SQ)) install: install_lib -- cgit v1.2.3 From f8da167a191751717beb380de98d572c16fb3d76 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 4 Jun 2013 14:20:17 +0900 Subject: tools lib traceevent: Get rid of unused gui target It's came from trace-cmd's kernelshark which is not a part of libtraceevent. Signed-off-by: Namhyung Kim Acked-by: Steven Rostedt Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1370323231-14022-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Makefile b/Makefile index fd7510d..4505de8 100644 --- a/Makefile +++ b/Makefile @@ -71,10 +71,7 @@ $(if $(BUILD_OUTPUT),, \ all: sub-make -gui: force - $(call build_output, all_cmd) - -$(filter-out gui,$(MAKECMDGOALS)): sub-make +$(MAKECMDGOALS): sub-make sub-make: force $(call build_output, $(MAKECMDGOALS)) @@ -253,9 +250,6 @@ define check_deps $(RM) $@.$$$$ endef -$(gui_deps): ks_version.h -$(non_gui_deps): tc_version.h - $(all_deps): .%.d: $(src)/%.c $(Q)$(call check_deps) -- cgit v1.2.3 From cb46028b57a202b20843f1cd4245dbb2e4307980 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 4 Jun 2013 14:20:18 +0900 Subject: tools lib traceevent: Add const qualifier to string arguments If pevent_register_event_handler() received a string literal as @sys_name or @event_name parameter, it emitted a warning about const qualifier removal. Since they're not modified in the function we can make it have const qualifier. Signed-off-by: Namhyung Kim Acked-by: Steven Rostedt Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1370323231-14022-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 7 +++---- event-parse.h | 3 ++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/event-parse.c b/event-parse.c index 82b0606..d1c2a6a 100644 --- a/event-parse.c +++ b/event-parse.c @@ -5450,10 +5450,9 @@ int pevent_register_print_function(struct pevent *pevent, * If @id is >= 0, then it is used to find the event. * else @sys_name and @event_name are used. */ -int pevent_register_event_handler(struct pevent *pevent, - int id, char *sys_name, char *event_name, - pevent_event_handler_func func, - void *context) +int pevent_register_event_handler(struct pevent *pevent, int id, + const char *sys_name, const char *event_name, + pevent_event_handler_func func, void *context) { struct event_format *event; struct event_handler *handle; diff --git a/event-parse.h b/event-parse.h index 7be7e89..bfceab9 100644 --- a/event-parse.h +++ b/event-parse.h @@ -561,7 +561,8 @@ int pevent_print_num_field(struct trace_seq *s, const char *fmt, struct event_format *event, const char *name, struct pevent_record *record, int err); -int pevent_register_event_handler(struct pevent *pevent, int id, char *sys_name, char *event_name, +int pevent_register_event_handler(struct pevent *pevent, int id, + const char *sys_name, const char *event_name, pevent_event_handler_func func, void *context); int pevent_register_print_function(struct pevent *pevent, pevent_func_handler func, -- cgit v1.2.3 From e7034fb576911d9ce5195586b32e769d8a698783 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 4 Jun 2013 14:20:19 +0900 Subject: tools lib traceevent: Add trace_seq_reset() Sometimes it'd be useful if existing trace_seq can be reused. But currently it's impossible since there's no API to reset the trace_seq. Let's add trace_seq_reset() for this case. Signed-off-by: Namhyung Kim Acked-by: Steven Rostedt Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1370323231-14022-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.h | 1 + trace-seq.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/event-parse.h b/event-parse.h index bfceab9..39f0c1d 100644 --- a/event-parse.h +++ b/event-parse.h @@ -69,6 +69,7 @@ struct trace_seq { }; void trace_seq_init(struct trace_seq *s); +void trace_seq_reset(struct trace_seq *s); void trace_seq_destroy(struct trace_seq *s); extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) diff --git a/trace-seq.c b/trace-seq.c index a57db80..d7f2e68 100644 --- a/trace-seq.c +++ b/trace-seq.c @@ -48,6 +48,19 @@ void trace_seq_init(struct trace_seq *s) s->buffer = malloc_or_die(s->buffer_size); } +/** + * trace_seq_reset - re-initialize the trace_seq structure + * @s: a pointer to the trace_seq structure to reset + */ +void trace_seq_reset(struct trace_seq *s) +{ + if (!s) + return; + TRACE_SEQ_CHECK(s); + s->len = 0; + s->readpos = 0; +} + /** * trace_seq_destroy - free up memory of a trace_seq * @s: a pointer to the trace_seq to free the buffer -- cgit v1.2.3 From 32138403f1d0ea774f3a1b5a8dc3226592e199a7 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 4 Jun 2013 14:20:20 +0900 Subject: tools lib traceevent: Add page_size field to pevent The page size of traced system can be different than current system's because the recorded data file might be analyzed in a different machine. In this case we should use original page size of traced system when accessing the data file, so this information needs to be saved. Signed-off-by: Namhyung Kim Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1370323231-14022-6-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/event-parse.h b/event-parse.h index 39f0c1d..c37b202 100644 --- a/event-parse.h +++ b/event-parse.h @@ -400,6 +400,7 @@ struct pevent { int cpus; int long_size; + int page_size; struct cmdline *cmdlines; struct cmdline_list *cmdlist; @@ -621,6 +622,16 @@ static inline void pevent_set_long_size(struct pevent *pevent, int long_size) pevent->long_size = long_size; } +static inline int pevent_get_page_size(struct pevent *pevent) +{ + return pevent->page_size; +} + +static inline void pevent_set_page_size(struct pevent *pevent, int _page_size) +{ + pevent->page_size = _page_size; +} + static inline int pevent_is_file_bigendian(struct pevent *pevent) { return pevent->file_bigendian; -- cgit v1.2.3 From a51ee4f75ceed48c5baf7fc81fcb0889167f04d0 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 4 Jun 2013 14:20:21 +0900 Subject: tools lib traceevent: Port kbuffer parser routines kbuffer code is for parsing ftrace ring-buffer binary data and used for trace-cmd. Move the code here in order to be used more widely. Signed-off-by: Namhyung Kim Original-patch-by: Steven Rostedt Acked-by: Steven Rostedt Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1370323231-14022-7-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 1 + kbuffer-parse.c | 732 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ kbuffer.h | 67 ++++++ 3 files changed, 800 insertions(+) create mode 100644 kbuffer-parse.c create mode 100644 kbuffer.h diff --git a/Makefile b/Makefile index 4505de8..0794acc 100644 --- a/Makefile +++ b/Makefile @@ -181,6 +181,7 @@ $(obj)/%.o: $(src)/%.c $(Q)$(call do_compile) PEVENT_LIB_OBJS = event-parse.o trace-seq.o parse-filter.o parse-utils.o +PEVENT_LIB_OBJS += kbuffer-parse.o ALL_OBJS = $(PEVENT_LIB_OBJS) diff --git a/kbuffer-parse.c b/kbuffer-parse.c new file mode 100644 index 0000000..dcc6652 --- /dev/null +++ b/kbuffer-parse.c @@ -0,0 +1,732 @@ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include + +#include "kbuffer.h" + +#define MISSING_EVENTS (1 << 31) +#define MISSING_STORED (1 << 30) + +#define COMMIT_MASK ((1 << 27) - 1) + +enum { + KBUFFER_FL_HOST_BIG_ENDIAN = (1<<0), + KBUFFER_FL_BIG_ENDIAN = (1<<1), + KBUFFER_FL_LONG_8 = (1<<2), + KBUFFER_FL_OLD_FORMAT = (1<<3), +}; + +#define ENDIAN_MASK (KBUFFER_FL_HOST_BIG_ENDIAN | KBUFFER_FL_BIG_ENDIAN) + +/** kbuffer + * @timestamp - timestamp of current event + * @lost_events - # of lost events between this subbuffer and previous + * @flags - special flags of the kbuffer + * @subbuffer - pointer to the sub-buffer page + * @data - pointer to the start of data on the sub-buffer page + * @index - index from @data to the @curr event data + * @curr - offset from @data to the start of current event + * (includes metadata) + * @next - offset from @data to the start of next event + * @size - The size of data on @data + * @start - The offset from @subbuffer where @data lives + * + * @read_4 - Function to read 4 raw bytes (may swap) + * @read_8 - Function to read 8 raw bytes (may swap) + * @read_long - Function to read a long word (4 or 8 bytes with needed swap) + */ +struct kbuffer { + unsigned long long timestamp; + long long lost_events; + unsigned long flags; + void *subbuffer; + void *data; + unsigned int index; + unsigned int curr; + unsigned int next; + unsigned int size; + unsigned int start; + + unsigned int (*read_4)(void *ptr); + unsigned long long (*read_8)(void *ptr); + unsigned long long (*read_long)(struct kbuffer *kbuf, void *ptr); + int (*next_event)(struct kbuffer *kbuf); +}; + +static void *zmalloc(size_t size) +{ + return calloc(1, size); +} + +static int host_is_bigendian(void) +{ + unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 }; + unsigned int *ptr; + + ptr = (unsigned int *)str; + return *ptr == 0x01020304; +} + +static int do_swap(struct kbuffer *kbuf) +{ + return ((kbuf->flags & KBUFFER_FL_HOST_BIG_ENDIAN) + kbuf->flags) & + ENDIAN_MASK; +} + +static unsigned long long __read_8(void *ptr) +{ + unsigned long long data = *(unsigned long long *)ptr; + + return data; +} + +static unsigned long long __read_8_sw(void *ptr) +{ + unsigned long long data = *(unsigned long long *)ptr; + unsigned long long swap; + + swap = ((data & 0xffULL) << 56) | + ((data & (0xffULL << 8)) << 40) | + ((data & (0xffULL << 16)) << 24) | + ((data & (0xffULL << 24)) << 8) | + ((data & (0xffULL << 32)) >> 8) | + ((data & (0xffULL << 40)) >> 24) | + ((data & (0xffULL << 48)) >> 40) | + ((data & (0xffULL << 56)) >> 56); + + return swap; +} + +static unsigned int __read_4(void *ptr) +{ + unsigned int data = *(unsigned int *)ptr; + + return data; +} + +static unsigned int __read_4_sw(void *ptr) +{ + unsigned int data = *(unsigned int *)ptr; + unsigned int swap; + + swap = ((data & 0xffULL) << 24) | + ((data & (0xffULL << 8)) << 8) | + ((data & (0xffULL << 16)) >> 8) | + ((data & (0xffULL << 24)) >> 24); + + return swap; +} + +static unsigned long long read_8(struct kbuffer *kbuf, void *ptr) +{ + return kbuf->read_8(ptr); +} + +static unsigned int read_4(struct kbuffer *kbuf, void *ptr) +{ + return kbuf->read_4(ptr); +} + +static unsigned long long __read_long_8(struct kbuffer *kbuf, void *ptr) +{ + return kbuf->read_8(ptr); +} + +static unsigned long long __read_long_4(struct kbuffer *kbuf, void *ptr) +{ + return kbuf->read_4(ptr); +} + +static unsigned long long read_long(struct kbuffer *kbuf, void *ptr) +{ + return kbuf->read_long(kbuf, ptr); +} + +static int calc_index(struct kbuffer *kbuf, void *ptr) +{ + return (unsigned long)ptr - (unsigned long)kbuf->data; +} + +static int __next_event(struct kbuffer *kbuf); + +/** + * kbuffer_alloc - allocat a new kbuffer + * @size; enum to denote size of word + * @endian: enum to denote endianness + * + * Allocates and returns a new kbuffer. + */ +struct kbuffer * +kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian) +{ + struct kbuffer *kbuf; + int flags = 0; + + switch (size) { + case KBUFFER_LSIZE_4: + break; + case KBUFFER_LSIZE_8: + flags |= KBUFFER_FL_LONG_8; + break; + default: + return NULL; + } + + switch (endian) { + case KBUFFER_ENDIAN_LITTLE: + break; + case KBUFFER_ENDIAN_BIG: + flags |= KBUFFER_FL_BIG_ENDIAN; + break; + default: + return NULL; + } + + kbuf = zmalloc(sizeof(*kbuf)); + if (!kbuf) + return NULL; + + kbuf->flags = flags; + + if (host_is_bigendian()) + kbuf->flags |= KBUFFER_FL_HOST_BIG_ENDIAN; + + if (do_swap(kbuf)) { + kbuf->read_8 = __read_8_sw; + kbuf->read_4 = __read_4_sw; + } else { + kbuf->read_8 = __read_8; + kbuf->read_4 = __read_4; + } + + if (kbuf->flags & KBUFFER_FL_LONG_8) + kbuf->read_long = __read_long_8; + else + kbuf->read_long = __read_long_4; + + /* May be changed by kbuffer_set_old_format() */ + kbuf->next_event = __next_event; + + return kbuf; +} + +/** kbuffer_free - free an allocated kbuffer + * @kbuf: The kbuffer to free + * + * Can take NULL as a parameter. + */ +void kbuffer_free(struct kbuffer *kbuf) +{ + free(kbuf); +} + +static unsigned int type4host(struct kbuffer *kbuf, + unsigned int type_len_ts) +{ + if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) + return (type_len_ts >> 29) & 3; + else + return type_len_ts & 3; +} + +static unsigned int len4host(struct kbuffer *kbuf, + unsigned int type_len_ts) +{ + if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) + return (type_len_ts >> 27) & 7; + else + return (type_len_ts >> 2) & 7; +} + +static unsigned int type_len4host(struct kbuffer *kbuf, + unsigned int type_len_ts) +{ + if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) + return (type_len_ts >> 27) & ((1 << 5) - 1); + else + return type_len_ts & ((1 << 5) - 1); +} + +static unsigned int ts4host(struct kbuffer *kbuf, + unsigned int type_len_ts) +{ + if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) + return type_len_ts & ((1 << 27) - 1); + else + return type_len_ts >> 5; +} + +/* + * Linux 2.6.30 and earlier (not much ealier) had a different + * ring buffer format. It should be obsolete, but we handle it anyway. + */ +enum old_ring_buffer_type { + OLD_RINGBUF_TYPE_PADDING, + OLD_RINGBUF_TYPE_TIME_EXTEND, + OLD_RINGBUF_TYPE_TIME_STAMP, + OLD_RINGBUF_TYPE_DATA, +}; + +static unsigned int old_update_pointers(struct kbuffer *kbuf) +{ + unsigned long long extend; + unsigned int type_len_ts; + unsigned int type; + unsigned int len; + unsigned int delta; + unsigned int length; + void *ptr = kbuf->data + kbuf->curr; + + type_len_ts = read_4(kbuf, ptr); + ptr += 4; + + type = type4host(kbuf, type_len_ts); + len = len4host(kbuf, type_len_ts); + delta = ts4host(kbuf, type_len_ts); + + switch (type) { + case OLD_RINGBUF_TYPE_PADDING: + kbuf->next = kbuf->size; + return 0; + + case OLD_RINGBUF_TYPE_TIME_EXTEND: + extend = read_4(kbuf, ptr); + extend <<= TS_SHIFT; + extend += delta; + delta = extend; + ptr += 4; + break; + + case OLD_RINGBUF_TYPE_TIME_STAMP: + /* should never happen! */ + kbuf->curr = kbuf->size; + kbuf->next = kbuf->size; + kbuf->index = kbuf->size; + return -1; + default: + if (len) + length = len * 4; + else { + length = read_4(kbuf, ptr); + length -= 4; + ptr += 4; + } + break; + } + + kbuf->timestamp += delta; + kbuf->index = calc_index(kbuf, ptr); + kbuf->next = kbuf->index + length; + + return type; +} + +static int __old_next_event(struct kbuffer *kbuf) +{ + int type; + + do { + kbuf->curr = kbuf->next; + if (kbuf->next >= kbuf->size) + return -1; + type = old_update_pointers(kbuf); + } while (type == OLD_RINGBUF_TYPE_TIME_EXTEND || type == OLD_RINGBUF_TYPE_PADDING); + + return 0; +} + +static unsigned int +translate_data(struct kbuffer *kbuf, void *data, void **rptr, + unsigned long long *delta, int *length) +{ + unsigned long long extend; + unsigned int type_len_ts; + unsigned int type_len; + + type_len_ts = read_4(kbuf, data); + data += 4; + + type_len = type_len4host(kbuf, type_len_ts); + *delta = ts4host(kbuf, type_len_ts); + + switch (type_len) { + case KBUFFER_TYPE_PADDING: + *length = read_4(kbuf, data); + data += *length; + break; + + case KBUFFER_TYPE_TIME_EXTEND: + extend = read_4(kbuf, data); + data += 4; + extend <<= TS_SHIFT; + extend += *delta; + *delta = extend; + *length = 0; + break; + + case KBUFFER_TYPE_TIME_STAMP: + data += 12; + *length = 0; + break; + case 0: + *length = read_4(kbuf, data) - 4; + *length = (*length + 3) & ~3; + data += 4; + break; + default: + *length = type_len * 4; + break; + } + + *rptr = data; + + return type_len; +} + +static unsigned int update_pointers(struct kbuffer *kbuf) +{ + unsigned long long delta; + unsigned int type_len; + int length; + void *ptr = kbuf->data + kbuf->curr; + + type_len = translate_data(kbuf, ptr, &ptr, &delta, &length); + + kbuf->timestamp += delta; + kbuf->index = calc_index(kbuf, ptr); + kbuf->next = kbuf->index + length; + + return type_len; +} + +/** + * kbuffer_translate_data - read raw data to get a record + * @swap: Set to 1 if bytes in words need to be swapped when read + * @data: The raw data to read + * @size: Address to store the size of the event data. + * + * Returns a pointer to the event data. To determine the entire + * record size (record metadata + data) just add the difference between + * @data and the returned value to @size. + */ +void *kbuffer_translate_data(int swap, void *data, unsigned int *size) +{ + unsigned long long delta; + struct kbuffer kbuf; + int type_len; + int length; + void *ptr; + + if (swap) { + kbuf.read_8 = __read_8_sw; + kbuf.read_4 = __read_4_sw; + kbuf.flags = host_is_bigendian() ? 0 : KBUFFER_FL_BIG_ENDIAN; + } else { + kbuf.read_8 = __read_8; + kbuf.read_4 = __read_4; + kbuf.flags = host_is_bigendian() ? KBUFFER_FL_BIG_ENDIAN: 0; + } + + type_len = translate_data(&kbuf, data, &ptr, &delta, &length); + switch (type_len) { + case KBUFFER_TYPE_PADDING: + case KBUFFER_TYPE_TIME_EXTEND: + case KBUFFER_TYPE_TIME_STAMP: + return NULL; + }; + + *size = length; + + return ptr; +} + +static int __next_event(struct kbuffer *kbuf) +{ + int type; + + do { + kbuf->curr = kbuf->next; + if (kbuf->next >= kbuf->size) + return -1; + type = update_pointers(kbuf); + } while (type == KBUFFER_TYPE_TIME_EXTEND || type == KBUFFER_TYPE_PADDING); + + return 0; +} + +static int next_event(struct kbuffer *kbuf) +{ + return kbuf->next_event(kbuf); +} + +/** + * kbuffer_next_event - increment the current pointer + * @kbuf: The kbuffer to read + * @ts: Address to store the next record's timestamp (may be NULL to ignore) + * + * Increments the pointers into the subbuffer of the kbuffer to point to the + * next event so that the next kbuffer_read_event() will return a + * new event. + * + * Returns the data of the next event if a new event exists on the subbuffer, + * NULL otherwise. + */ +void *kbuffer_next_event(struct kbuffer *kbuf, unsigned long long *ts) +{ + int ret; + + if (!kbuf || !kbuf->subbuffer) + return NULL; + + ret = next_event(kbuf); + if (ret < 0) + return NULL; + + if (ts) + *ts = kbuf->timestamp; + + return kbuf->data + kbuf->index; +} + +/** + * kbuffer_load_subbuffer - load a new subbuffer into the kbuffer + * @kbuf: The kbuffer to load + * @subbuffer: The subbuffer to load into @kbuf. + * + * Load a new subbuffer (page) into @kbuf. This will reset all + * the pointers and update the @kbuf timestamp. The next read will + * return the first event on @subbuffer. + * + * Returns 0 on succes, -1 otherwise. + */ +int kbuffer_load_subbuffer(struct kbuffer *kbuf, void *subbuffer) +{ + unsigned long long flags; + void *ptr = subbuffer; + + if (!kbuf || !subbuffer) + return -1; + + kbuf->subbuffer = subbuffer; + + kbuf->timestamp = read_8(kbuf, ptr); + ptr += 8; + + kbuf->curr = 0; + + if (kbuf->flags & KBUFFER_FL_LONG_8) + kbuf->start = 16; + else + kbuf->start = 12; + + kbuf->data = subbuffer + kbuf->start; + + flags = read_long(kbuf, ptr); + kbuf->size = (unsigned int)flags & COMMIT_MASK; + + if (flags & MISSING_EVENTS) { + if (flags & MISSING_STORED) { + ptr = kbuf->data + kbuf->size; + kbuf->lost_events = read_long(kbuf, ptr); + } else + kbuf->lost_events = -1; + } else + kbuf->lost_events = 0; + + kbuf->index = 0; + kbuf->next = 0; + + next_event(kbuf); + + return 0; +} + +/** + * kbuffer_read_event - read the next event in the kbuffer subbuffer + * @kbuf: The kbuffer to read from + * @ts: The address to store the timestamp of the event (may be NULL to ignore) + * + * Returns a pointer to the data part of the current event. + * NULL if no event is left on the subbuffer. + */ +void *kbuffer_read_event(struct kbuffer *kbuf, unsigned long long *ts) +{ + if (!kbuf || !kbuf->subbuffer) + return NULL; + + if (kbuf->curr >= kbuf->size) + return NULL; + + if (ts) + *ts = kbuf->timestamp; + return kbuf->data + kbuf->index; +} + +/** + * kbuffer_timestamp - Return the timestamp of the current event + * @kbuf: The kbuffer to read from + * + * Returns the timestamp of the current (next) event. + */ +unsigned long long kbuffer_timestamp(struct kbuffer *kbuf) +{ + return kbuf->timestamp; +} + +/** + * kbuffer_read_at_offset - read the event that is at offset + * @kbuf: The kbuffer to read from + * @offset: The offset into the subbuffer + * @ts: The address to store the timestamp of the event (may be NULL to ignore) + * + * The @offset must be an index from the @kbuf subbuffer beginning. + * If @offset is bigger than the stored subbuffer, NULL will be returned. + * + * Returns the data of the record that is at @offset. Note, @offset does + * not need to be the start of the record, the offset just needs to be + * in the record (or beginning of it). + * + * Note, the kbuf timestamp and pointers are updated to the + * returned record. That is, kbuffer_read_event() will return the same + * data and timestamp, and kbuffer_next_event() will increment from + * this record. + */ +void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset, + unsigned long long *ts) +{ + void *data; + + if (offset < kbuf->start) + offset = 0; + else + offset -= kbuf->start; + + /* Reset the buffer */ + kbuffer_load_subbuffer(kbuf, kbuf->subbuffer); + + while (kbuf->curr < offset) { + data = kbuffer_next_event(kbuf, ts); + if (!data) + break; + } + + return data; +} + +/** + * kbuffer_subbuffer_size - the size of the loaded subbuffer + * @kbuf: The kbuffer to read from + * + * Returns the size of the subbuffer. Note, this size is + * where the last event resides. The stored subbuffer may actually be + * bigger due to padding and such. + */ +int kbuffer_subbuffer_size(struct kbuffer *kbuf) +{ + return kbuf->size; +} + +/** + * kbuffer_curr_index - Return the index of the record + * @kbuf: The kbuffer to read from + * + * Returns the index from the start of the data part of + * the subbuffer to the current location. Note this is not + * from the start of the subbuffer. An index of zero will + * point to the first record. Use kbuffer_curr_offset() for + * the actually offset (that can be used by kbuffer_read_at_offset()) + */ +int kbuffer_curr_index(struct kbuffer *kbuf) +{ + return kbuf->curr; +} + +/** + * kbuffer_curr_offset - Return the offset of the record + * @kbuf: The kbuffer to read from + * + * Returns the offset from the start of the subbuffer to the + * current location. + */ +int kbuffer_curr_offset(struct kbuffer *kbuf) +{ + return kbuf->curr + kbuf->start; +} + +/** + * kbuffer_event_size - return the size of the event data + * @kbuf: The kbuffer to read + * + * Returns the size of the event data (the payload not counting + * the meta data of the record) of the current event. + */ +int kbuffer_event_size(struct kbuffer *kbuf) +{ + return kbuf->next - kbuf->index; +} + +/** + * kbuffer_curr_size - return the size of the entire record + * @kbuf: The kbuffer to read + * + * Returns the size of the entire record (meta data and payload) + * of the current event. + */ +int kbuffer_curr_size(struct kbuffer *kbuf) +{ + return kbuf->next - kbuf->curr; +} + +/** + * kbuffer_missed_events - return the # of missed events from last event. + * @kbuf: The kbuffer to read from + * + * Returns the # of missed events (if recorded) before the current + * event. Note, only events on the beginning of a subbuffer can + * have missed events, all other events within the buffer will be + * zero. + */ +int kbuffer_missed_events(struct kbuffer *kbuf) +{ + /* Only the first event can have missed events */ + if (kbuf->curr) + return 0; + + return kbuf->lost_events; +} + +/** + * kbuffer_set_old_forma - set the kbuffer to use the old format parsing + * @kbuf: The kbuffer to set + * + * This is obsolete (or should be). The first kernels to use the + * new ring buffer had a slightly different ring buffer format + * (2.6.30 and earlier). It is still somewhat supported by kbuffer, + * but should not be counted on in the future. + */ +void kbuffer_set_old_format(struct kbuffer *kbuf) +{ + kbuf->flags |= KBUFFER_FL_OLD_FORMAT; + + kbuf->next_event = __old_next_event; +} diff --git a/kbuffer.h b/kbuffer.h new file mode 100644 index 0000000..c831f64 --- /dev/null +++ b/kbuffer.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2012 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#ifndef _KBUFFER_H +#define _KBUFFER_H + +#ifndef TS_SHIFT +#define TS_SHIFT 27 +#endif + +enum kbuffer_endian { + KBUFFER_ENDIAN_BIG, + KBUFFER_ENDIAN_LITTLE, +}; + +enum kbuffer_long_size { + KBUFFER_LSIZE_4, + KBUFFER_LSIZE_8, +}; + +enum { + KBUFFER_TYPE_PADDING = 29, + KBUFFER_TYPE_TIME_EXTEND = 30, + KBUFFER_TYPE_TIME_STAMP = 31, +}; + +struct kbuffer; + +struct kbuffer *kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian); +void kbuffer_free(struct kbuffer *kbuf); +int kbuffer_load_subbuffer(struct kbuffer *kbuf, void *subbuffer); +void *kbuffer_read_event(struct kbuffer *kbuf, unsigned long long *ts); +void *kbuffer_next_event(struct kbuffer *kbuf, unsigned long long *ts); +unsigned long long kbuffer_timestamp(struct kbuffer *kbuf); + +void *kbuffer_translate_data(int swap, void *data, unsigned int *size); + +void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset, unsigned long long *ts); + +int kbuffer_curr_index(struct kbuffer *kbuf); + +int kbuffer_curr_offset(struct kbuffer *kbuf); +int kbuffer_curr_size(struct kbuffer *kbuf); +int kbuffer_event_size(struct kbuffer *kbuf); +int kbuffer_missed_events(struct kbuffer *kbuf); +int kbuffer_subbuffer_size(struct kbuffer *kbuf); + +void kbuffer_set_old_format(struct kbuffer *kbuf); + +#endif /* _K_BUFFER_H */ -- cgit v1.2.3 From eb6e02a0f817d149de17f21d20b2b62f173cd1b1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 26 Aug 2013 13:04:04 -0300 Subject: tools lib traceevent: Fixup jobserver setup Getting rid of: make[1]: Entering directory `/home/git/linux/tools/lib/traceevent' make[2]: warning: jobserver unavailable: using -j1. Add `+' to parent make rule. make[1]: Entering directory `/home/git/linux/tools/lib/lk When running: make -j4 O=/tmp/build/perf -C tools/perf install-bin Acked-by: Borislav Petkov Cc: Adrian Hunter Cc: Borislav Petkov Cc: David Ahern Cc: Frederic Weisbecker Cc: Mike Galbraith Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Steven Rostedt Link: http://lkml.kernel.org/n/tip-pvr7uppe329gw9onchgdu0m6@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0794acc..ca6cb77 100644 --- a/Makefile +++ b/Makefile @@ -60,7 +60,7 @@ ifeq ($(BUILD_SRC),) ifneq ($(BUILD_OUTPUT),) define build_output - $(if $(VERBOSE:1=),@)$(MAKE) -C $(BUILD_OUTPUT) \ + $(if $(VERBOSE:1=),@)+$(MAKE) -C $(BUILD_OUTPUT) \ BUILD_SRC=$(CURDIR) -f $(CURDIR)/Makefile $1 endef -- cgit v1.2.3 From 4ffc2fc1387c586b982af8bc183367685731f19a Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 9 Oct 2013 11:49:27 +0200 Subject: tools: Harmonize the various build messages in perf, lib-traceevent, lib-lk The various build lines from libtraceevent and perf mix up during a parallel build and produce unaligned output like: CC builtin-buildid-list.o CC builtin-buildid-cache.o CC builtin-list.o CC FPIC trace-seq.o CC builtin-record.o CC FPIC parse-filter.o CC builtin-report.o CC builtin-stat.o CC FPIC parse-utils.o CC FPIC kbuffer-parse.o CC builtin-timechart.o CC builtin-top.o CC builtin-script.o BUILD STATIC LIB libtraceevent.a CC builtin-probe.o CC builtin-kmem.o CC builtin-lock.o To solve this, harmonize all the build message alignments to be similar to the kernel's kbuild output: prefixed by two spaces and 11-char wide. After the patch the output looks pretty tidy, even if output lines get mixed up: CC builtin-annotate.o FLAGS: * new build flags or cross compiler CC builtin-bench.o AR liblk.a CC bench/sched-messaging.o CC FPIC event-parse.o CC bench/sched-pipe.o CC FPIC trace-seq.o CC bench/mem-memcpy.o CC bench/mem-memset.o CC FPIC parse-filter.o CC builtin-diff.o CC builtin-evlist.o CC builtin-help.o Signed-off-by: Ingo Molnar Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1381312169-17354-3-git-send-email-mingo@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index ca6cb77..fc15020 100644 --- a/Makefile +++ b/Makefile @@ -134,14 +134,14 @@ ifeq ($(VERBOSE),1) print_install = else Q = @ - print_compile = echo ' CC '$(OBJ); - print_app_build = echo ' BUILD '$(OBJ); - print_fpic_compile = echo ' CC FPIC '$(OBJ); - print_shared_lib_compile = echo ' BUILD SHARED LIB '$(OBJ); - print_plugin_obj_compile = echo ' CC PLUGIN OBJ '$(OBJ); - print_plugin_build = echo ' CC PLUGI '$(OBJ); - print_static_lib_build = echo ' BUILD STATIC LIB '$(OBJ); - print_install = echo ' INSTALL '$1' to $(DESTDIR_SQ)$2'; + print_compile = echo ' CC '$(OBJ); + print_app_build = echo ' BUILD '$(OBJ); + print_fpic_compile = echo ' CC FPIC '$(OBJ); + print_shared_lib_compile = echo ' BUILD SHARED LIB '$(OBJ); + print_plugin_obj_compile = echo ' BUILD PLUGIN OBJ '$(OBJ); + print_plugin_build = echo ' BUILD PLUGIN '$(OBJ); + print_static_lib_build = echo ' BUILD STATIC LIB '$(OBJ); + print_install = echo ' INSTALL '$1' to $(DESTDIR_SQ)$2'; endif do_fpic_compile = \ @@ -268,7 +268,7 @@ TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):$(ARCH):$(CROSS_COMPILE) TRACEEVENT-CFLAGS: force @FLAGS='$(TRACK_CFLAGS)'; \ if test x"$$FLAGS" != x"`cat TRACEEVENT-CFLAGS 2>/dev/null`" ; then \ - echo 1>&2 " * new build flags or cross compiler"; \ + echo 1>&2 " FLAGS: * new build flags or cross compiler"; \ echo "$$FLAGS" >TRACEEVENT-CFLAGS; \ fi -- cgit v1.2.3 From 30f8dca212cabc2c8e3a4af0b50f19184e711a3a Mon Sep 17 00:00:00 2001 From: Yoshihiro YUNOMAE Date: Fri, 1 Nov 2013 17:53:53 -0400 Subject: tools lib traceevent: Add support for extracting trace_clock in report If trace-cmd extracts trace_clock, trace-cmd reads trace_clock data from the trace.dat and switches outputting format of timestamp for each trace_clock. Signed-off-by: Yoshihiro YUNOMAE Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20130424231305.14877.86147.stgit@yunodevel Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 50 +++++++++++++++++++++++++++++++++++++++----------- event-parse.h | 6 +++++- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/event-parse.c b/event-parse.c index d1c2a6a..deedff9 100644 --- a/event-parse.c +++ b/event-parse.c @@ -305,6 +305,11 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid) return 0; } +void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock) +{ + pevent->trace_clock = trace_clock; +} + struct func_map { unsigned long long addr; char *func; @@ -4443,8 +4448,21 @@ void pevent_event_info(struct trace_seq *s, struct event_format *event, trace_seq_terminate(s); } +static bool is_timestamp_in_us(char *trace_clock, bool use_trace_clock) +{ + if (!use_trace_clock) + return true; + + if (!strcmp(trace_clock, "local") || !strcmp(trace_clock, "global") + || !strcmp(trace_clock, "uptime") || !strcmp(trace_clock, "perf")) + return true; + + /* trace_clock is setting in tsc or counter mode */ + return false; +} + void pevent_print_event(struct pevent *pevent, struct trace_seq *s, - struct pevent_record *record) + struct pevent_record *record, bool use_trace_clock) { static const char *spaces = " "; /* 20 spaces */ struct event_format *event; @@ -4457,9 +4475,14 @@ void pevent_print_event(struct pevent *pevent, struct trace_seq *s, int pid; int len; int p; + bool use_usec_format; - secs = record->ts / NSECS_PER_SEC; - nsecs = record->ts - secs * NSECS_PER_SEC; + use_usec_format = is_timestamp_in_us(pevent->trace_clock, + use_trace_clock); + if (use_usec_format) { + secs = record->ts / NSECS_PER_SEC; + nsecs = record->ts - secs * NSECS_PER_SEC; + } if (record->size < 0) { do_warning("ug! negative record size %d", record->size); @@ -4484,15 +4507,20 @@ void pevent_print_event(struct pevent *pevent, struct trace_seq *s, } else trace_seq_printf(s, "%16s-%-5d [%03d]", comm, pid, record->cpu); - if (pevent->flags & PEVENT_NSEC_OUTPUT) { - usecs = nsecs; - p = 9; - } else { - usecs = (nsecs + 500) / NSECS_PER_USEC; - p = 6; - } + if (use_usec_format) { + if (pevent->flags & PEVENT_NSEC_OUTPUT) { + usecs = nsecs; + p = 9; + } else { + usecs = (nsecs + 500) / NSECS_PER_USEC; + p = 6; + } - trace_seq_printf(s, " %5lu.%0*lu: %s: ", secs, p, usecs, event->name); + trace_seq_printf(s, " %5lu.%0*lu: %s: ", + secs, p, usecs, event->name); + } else + trace_seq_printf(s, " %12llu: %s: ", + record->ts, event->name); /* Space out the event names evenly. */ len = strlen(event->name); diff --git a/event-parse.h b/event-parse.h index c37b202..7503edf 100644 --- a/event-parse.h +++ b/event-parse.h @@ -20,6 +20,7 @@ #ifndef _PARSE_EVENTS_H #define _PARSE_EVENTS_H +#include #include #include @@ -450,6 +451,8 @@ struct pevent { /* cache */ struct event_format *last_event; + + char *trace_clock; }; static inline void pevent_set_flag(struct pevent *pevent, int flag) @@ -527,6 +530,7 @@ enum trace_flag_type { }; int pevent_register_comm(struct pevent *pevent, const char *comm, int pid); +void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock); int pevent_register_function(struct pevent *pevent, char *name, unsigned long long addr, char *mod); int pevent_register_print_string(struct pevent *pevent, char *fmt, @@ -534,7 +538,7 @@ int pevent_register_print_string(struct pevent *pevent, char *fmt, int pevent_pid_is_registered(struct pevent *pevent, int pid); void pevent_print_event(struct pevent *pevent, struct trace_seq *s, - struct pevent_record *record); + struct pevent_record *record, bool use_trace_clock); int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size, int long_size); -- cgit v1.2.3 From 1f2c15d3cd850f68f3e2f70a90ac4574ad3fb80e Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 1 Nov 2013 17:53:54 -0400 Subject: tools lib traceevent: Update printk formats when entered Instead of cropping off the '"' and '\n"' from a printk format every time it is referenced, do it when it's added. This makes it easier to reference a printk_map and should speed things up a little. Signed-off-by: Steven Rostedt Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20131101215500.495619312@goodmis.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 29 ++++++++++++++--------------- event-parse.h | 2 +- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/event-parse.c b/event-parse.c index deedff9..856b791 100644 --- a/event-parse.c +++ b/event-parse.c @@ -604,10 +604,11 @@ find_printk(struct pevent *pevent, unsigned long long addr) * This registers a string by the address it was stored in the kernel. * The @fmt passed in is duplicated. */ -int pevent_register_print_string(struct pevent *pevent, char *fmt, +int pevent_register_print_string(struct pevent *pevent, const char *fmt, unsigned long long addr) { struct printk_list *item = malloc(sizeof(*item)); + char *p; if (!item) return -1; @@ -615,10 +616,21 @@ int pevent_register_print_string(struct pevent *pevent, char *fmt, item->next = pevent->printklist; item->addr = addr; + /* Strip off quotes and '\n' from the end */ + if (fmt[0] == '"') + fmt++; item->printk = strdup(fmt); if (!item->printk) goto out_free; + p = item->printk + strlen(item->printk) - 1; + if (*p == '"') + *p = 0; + + p -= 2; + if (strcmp(p, "\\n") == 0) + *p = 0; + pevent->printklist = item; pevent->printk_count++; @@ -3887,7 +3899,6 @@ get_bprint_format(void *data, int size __maybe_unused, struct format_field *field; struct printk_map *printk; char *format; - char *p; field = pevent->bprint_fmt_field; @@ -3909,20 +3920,8 @@ get_bprint_format(void *data, int size __maybe_unused, return format; } - p = printk->printk; - /* Remove any quotes. */ - if (*p == '"') - p++; - if (asprintf(&format, "%s : %s", "%pf", p) < 0) + if (asprintf(&format, "%s : %s", "%pf", printk->printk) < 0) return NULL; - /* 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; } diff --git a/event-parse.h b/event-parse.h index 7503edf..9ab6367 100644 --- a/event-parse.h +++ b/event-parse.h @@ -533,7 +533,7 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid); void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock); int pevent_register_function(struct pevent *pevent, char *name, unsigned long long addr, char *mod); -int pevent_register_print_string(struct pevent *pevent, char *fmt, +int pevent_register_print_string(struct pevent *pevent, const char *fmt, unsigned long long addr); int pevent_pid_is_registered(struct pevent *pevent, int pid); -- cgit v1.2.3 From 7d9e202d71c1b8c81c9c2eb30aeb2f95ed7926e4 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 1 Nov 2013 17:53:55 -0400 Subject: tools lib traceevent: If %s is a pointer, check printk formats If the format string of TP_printk() contains a %s, and the argument is not a string, check if the argument is a pointer that might match the printk_formats that were stored. Signed-off-by: Steven Rostedt Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20131101215500.698924777@goodmis.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index 856b791..013c8d3 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3505,6 +3505,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, struct pevent *pevent = event->pevent; struct print_flag_sym *flag; struct format_field *field; + struct printk_map *printk; unsigned long long val, fval; unsigned long addr; char *str; @@ -3540,7 +3541,12 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, if (!(field->flags & FIELD_IS_ARRAY) && field->size == pevent->long_size) { addr = *(unsigned long *)(data + field->offset); - trace_seq_printf(s, "%lx", addr); + /* Check if it matches a print format */ + printk = find_printk(pevent, addr); + if (printk) + trace_seq_puts(s, printk->printk); + else + trace_seq_printf(s, "%lx", addr); break; } str = malloc(len + 1); -- cgit v1.2.3 From 03687d9cebc62eca1b7c65f1c705ea0c9cd3e93b Mon Sep 17 00:00:00 2001 From: Howard Cochran Date: Fri, 1 Nov 2013 17:53:56 -0400 Subject: tools lib traceevent: Handle __print_hex(__get_dynamic_array(fieldname), len) The kernel has a few events with a format similar to this excerpt: field:unsigned int len; offset:12; size:4; signed:0; field:__data_loc unsigned char[] data_array; offset:16; size:4; signed:0; print fmt: "%s", __print_hex(__get_dynamic_array(data_array), REC->len) trace-cmd could already parse that arg correctly, but print_str_arg() was unable to handle the first parameter being a dynamic array. (It just printed a "field not found" warning). Teach print_str_arg's PRINT_HEX case to handle the nested PRINT_DYNAMIC_ARRAY correctly. The output now matches the kernel's own formatting for this case. Signed-off-by: Howard Cochran Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/1381503349-12271-1-git-send-email-hcochran@lexmark.com [ Removed "polish compare", we don't do that here ] Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/event-parse.c b/event-parse.c index 013c8d3..0a1ffe0 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3588,15 +3588,23 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, } break; case PRINT_HEX: - field = arg->hex.field->field.field; - if (!field) { - str = arg->hex.field->field.name; - field = pevent_find_any_field(event, str); - if (!field) - goto out_warning_field; - arg->hex.field->field.field = field; + if (arg->hex.field->type == PRINT_DYNAMIC_ARRAY) { + unsigned long offset; + offset = pevent_read_number(pevent, + data + arg->hex.field->dynarray.field->offset, + arg->hex.field->dynarray.field->size); + hex = data + (offset & 0xffff); + } else { + field = arg->hex.field->field.field; + if (!field) { + str = arg->hex.field->field.name; + field = pevent_find_any_field(event, str); + if (!field) + goto out_warning_field; + arg->hex.field->field.field = field; + } + hex = data + field->offset; } - hex = data + field->offset; len = eval_num_arg(data, size, event, arg->hex.size); for (i = 0; i < len; i++) { if (i) -- cgit v1.2.3 From 290894875b15be8dfcc0980b07bd74662748dd0a Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 1 Nov 2013 17:53:57 -0400 Subject: tools lib traceevent: Have bprintk output the same as the kernel does The trace_bprintk() in the kernel looks like: ring_buffer_producer_thread: Missed: 0 ring_buffer_producer_thread: Hit: 62174350 ring_buffer_producer_thread: Entries per millisec: 6296 ring_buffer_producer_thread: 158 ns per entry ring_buffer_producer_thread: Sleeping for 10 secs ring_buffer_producer_thread: Starting ring buffer hammer ring_buffer_producer_thread: End ring buffer hammer But the current output looks like this: ring_buffer_producer_thread : Time: 9407018 (usecs) ring_buffer_producer_thread : Overruns: 43285485 ring_buffer_producer_thread : Read: 4405365 (by events) ring_buffer_producer_thread : Entries: 0 ring_buffer_producer_thread : Total: 47690850 ring_buffer_producer_thread : Missed: 0 ring_buffer_producer_thread : Hit: 47690850 Remove the space between the function and the colon. Signed-off-by: Steven Rostedt Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20131101215501.272654481@goodmis.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/event-parse.c b/event-parse.c index 0a1ffe0..e1c743c 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3802,8 +3802,8 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc if (asprintf(&arg->atom.atom, "%lld", ip) < 0) goto out_free; - /* skip the first "%pf : " */ - for (ptr = fmt + 6, bptr = data + field->offset; + /* skip the first "%pf: " */ + for (ptr = fmt + 5, bptr = data + field->offset; bptr < data + size && *ptr; ptr++) { int ls = 0; @@ -3929,12 +3929,12 @@ get_bprint_format(void *data, int size __maybe_unused, printk = find_printk(pevent, addr); if (!printk) { - if (asprintf(&format, "%%pf : (NO FORMAT FOUND at %llx)\n", addr) < 0) + if (asprintf(&format, "%%pf: (NO FORMAT FOUND at %llx)\n", addr) < 0) return NULL; return format; } - if (asprintf(&format, "%s : %s", "%pf", printk->printk) < 0) + if (asprintf(&format, "%s: %s", "%pf", printk->printk) < 0) return NULL; return format; -- cgit v1.2.3 From 5e9382842f92c82d330c1fb506f1bc62478a482c Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 1 Nov 2013 17:53:58 -0400 Subject: tools lib traceevent: Check for spaces in character array Currently when using the raw format for fields, when looking at a character array, to determine if it is a string or not, we make sure all characters are "isprint()". If not, then we consider it a numeric array, and print the hex numbers of the characters instead. But it seems that '\n' fails the isprint() check! Add isspace() to the check as well, such that if all characters pass isprint() or isspace() it will assume the character array is a string. Reported-by: Xenia Ragiadakou Signed-off-by: Steven Rostedt Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Xenia Ragiadakou Link: http://lkml.kernel.org/r/20131101215501.465091682@goodmis.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index e1c743c..85cbbdd 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3981,7 +3981,7 @@ static int is_printable_array(char *p, unsigned int len) unsigned int i; for (i = 0; i < len && p[i]; i++) - if (!isprint(p[i])) + if (!isprint(p[i]) && !isspace(p[i])) return 0; return 1; } -- cgit v1.2.3 From 37b75ba756e650effa63599920a5c17cca9a503b Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 1 Nov 2013 17:53:59 -0400 Subject: tools lib traceevent: Add flags NOHANDLE and PRINTRAW to individual events Add the flags EVENT_FL_NOHANDLE and EVENT_FL_PRINTRAW to the event flags to have the event either ignore the register handler or to ignore the handler and also print the raw format respectively. This allows a tool to force a raw format or non handle for an event. Signed-off-by: Steven Rostedt Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20131101215501.655258742@goodmis.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 4 ++-- event-parse.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/event-parse.c b/event-parse.c index 85cbbdd..fc6f35f 100644 --- a/event-parse.c +++ b/event-parse.c @@ -4446,11 +4446,11 @@ void pevent_event_info(struct trace_seq *s, struct event_format *event, { int print_pretty = 1; - if (event->pevent->print_raw) + if (event->pevent->print_raw || (event->flags & EVENT_FL_PRINTRAW)) print_event_fields(s, record->data, record->size, event); else { - if (event->handler) + if (event->handler && !(event->flags & EVENT_FL_NOHANDLE)) print_pretty = event->handler(s, record, event, event->context); diff --git a/event-parse.h b/event-parse.h index 9ab6367..dc8539e 100644 --- a/event-parse.h +++ b/event-parse.h @@ -308,6 +308,8 @@ enum { EVENT_FL_ISBPRINT = 0x04, EVENT_FL_ISFUNCENT = 0x10, EVENT_FL_ISFUNCRET = 0x20, + EVENT_FL_NOHANDLE = 0x40, + EVENT_FL_PRINTRAW = 0x80, EVENT_FL_FAILED = 0x80000000 }; -- cgit v1.2.3 From 8de4822b3af0f4bac3551b6f4269ef96853f376b Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 1 Nov 2013 17:54:00 -0400 Subject: tools lib traceevent: Add pevent_print_func_field() helper function Add the pevent_print_func_field() that will look up a field that is expected to be a function pointer, and it will print the function name and offset of the address given by the field. Signed-off-by: Steven Rostedt Cc: Andrew Morton Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20131101215501.869542711@goodmis.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 42 ++++++++++++++++++++++++++++++++++++++++++ event-parse.h | 4 ++++ 2 files changed, 46 insertions(+) diff --git a/event-parse.c b/event-parse.c index fc6f35f..8f450ad 100644 --- a/event-parse.c +++ b/event-parse.c @@ -5367,6 +5367,48 @@ int pevent_print_num_field(struct trace_seq *s, const char *fmt, return -1; } +/** + * pevent_print_func_field - print a field and a format for function pointers + * @s: The seq to print to + * @fmt: The printf format to print the field with. + * @event: the event that the field is for + * @name: The name of the field + * @record: The record with the field name. + * @err: print default error if failed. + * + * Returns: 0 on success, -1 field not found, or 1 if buffer is full. + */ +int pevent_print_func_field(struct trace_seq *s, const char *fmt, + struct event_format *event, const char *name, + struct pevent_record *record, int err) +{ + struct format_field *field = pevent_find_field(event, name); + struct pevent *pevent = event->pevent; + unsigned long long val; + struct func_map *func; + char tmp[128]; + + if (!field) + goto failed; + + if (pevent_read_number_field(field, record->data, &val)) + goto failed; + + func = find_func(pevent, val); + + if (func) + snprintf(tmp, 128, "%s/0x%llx", func->func, func->addr - val); + else + sprintf(tmp, "0x%08llx", val); + + return trace_seq_printf(s, fmt, tmp); + + failed: + if (err) + trace_seq_printf(s, "CAN'T FIND FIELD \"%s\"", name); + return -1; +} + static void free_func_handle(struct pevent_function_handler *func) { struct pevent_func_params *params; diff --git a/event-parse.h b/event-parse.h index dc8539e..8d73d25 100644 --- a/event-parse.h +++ b/event-parse.h @@ -569,6 +569,10 @@ int pevent_print_num_field(struct trace_seq *s, const char *fmt, struct event_format *event, const char *name, struct pevent_record *record, int err); +int pevent_print_func_field(struct trace_seq *s, const char *fmt, + struct event_format *event, const char *name, + struct pevent_record *record, int err); + int pevent_register_event_handler(struct pevent *pevent, int id, const char *sys_name, const char *event_name, pevent_event_handler_func func, void *context); -- cgit v1.2.3 From ec1c1b002c3297e486bdcd86fd018927ef4e2996 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 11 Nov 2013 16:08:10 -0500 Subject: tools lib traceevent: Add direct access to dynamic arrays Jiri Olsa was writing a plugin for the cfg80211_tx_mlme_mgmt trace event, and was not able to get the implemented function working. The event's print fmt looks like: "netdev:%s(%d), ftype:0x%.2x", REC->name, REC->ifindex, __le16_to_cpup((__le16 *)__get_dynamic_array(frame)) As there's no helper function for __le16_to_cpup(), Jiri was creating one with a plugin. But unfortunately, it would not work even though he set up the plugin correctly. The problem is that the function parameters do not handle the helper function "__get_dynamic_array()", and that passes in a NULL pointer. Adding PRINT_DYNAMIC_ARRAY direct support to eval_num_arg() allows the use of __get_dynamic_array() in function parameters. Reported-by: Jiri Olsa Signed-off-by: Steven Rostedt Tested-by: Jiri Olsa Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20131111160810.0ba9df7d@gandalf.local.home Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/event-parse.c b/event-parse.c index 8f450ad..0362d57 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3435,6 +3435,19 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg goto out_warning_op; } break; + case PRINT_DYNAMIC_ARRAY: + /* Without [], we pass the address to the dynamic data */ + offset = pevent_read_number(pevent, + data + arg->dynarray.field->offset, + arg->dynarray.field->size); + /* + * The actual length of the dynamic array is stored + * in the top half of the field, and the offset + * is in the bottom half of the 32 bit field. + */ + offset &= 0xffff; + val = (unsigned long long)(data + offset); + break; default: /* not sure what to do there */ return 0; } -- cgit v1.2.3 From fc20b032b2fa4e3b2aabd779d0f9c1ab73da0d6e Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 18 Nov 2013 14:23:14 -0500 Subject: tools lib traceevent: Fix use of multiple options in processing field Jiri Olsa reported that the scsi_dispatch_cmd_done event failed to parse with: Error: expected type 5 but read 4 Error: expected type 5 but read 4 The problem is with this part of the print_fmt: __print_symbolic(((REC->result) >> 24) & 0xff, ... The __print_symbolic() helper function's first parameter is the field to use to determine what symbol to print based on the value of the result. The parser can handle one operation, but it can not handle multiple operations ('>>' and '&'). Add code to process all operations for the field argument for __print_symbolic() as well as __print_flags(). Reported-by: Jiri Olsa Signed-off-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20131118142314.27ca334b@gandalf.local.home Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/event-parse.c b/event-parse.c index 0362d57..8a5b65d 100644 --- a/event-parse.c +++ b/event-parse.c @@ -1606,6 +1606,24 @@ process_arg(struct event_format *event, struct print_arg *arg, char **tok) static enum event_type process_op(struct event_format *event, struct print_arg *arg, char **tok); +/* + * For __print_symbolic() and __print_flags, we need to completely + * evaluate the first argument, which defines what to print next. + */ +static enum event_type +process_field_arg(struct event_format *event, struct print_arg *arg, char **tok) +{ + enum event_type type; + + type = process_arg(event, arg, tok); + + while (type == EVENT_OP) { + type = process_op(event, arg, tok); + } + + return type; +} + static enum event_type process_cond(struct event_format *event, struct print_arg *top, char **tok) { @@ -2371,7 +2389,7 @@ process_flags(struct event_format *event, struct print_arg *arg, char **tok) goto out_free; } - type = process_arg(event, field, &token); + type = process_field_arg(event, field, &token); /* Handle operations in the first argument */ while (type == EVENT_OP) @@ -2424,7 +2442,8 @@ process_symbols(struct event_format *event, struct print_arg *arg, char **tok) goto out_free; } - type = process_arg(event, field, &token); + type = process_field_arg(event, field, &token); + if (test_type_token(type, token, EVENT_DELIM, ",")) goto out_free_field; -- cgit v1.2.3 From c53a6fd7a02d38ac6a90e82e0b38b791e31155fe Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 19 Nov 2013 16:14:51 -0300 Subject: tools lib traceevent: Fix conversion of pointer to integer of different size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gcc complaint on 32-bit system: /home/acme/git/linux/tools/lib/traceevent/event-parse.c: In function ‘eval_num_arg’: /home/acme/git/linux/tools/lib/traceevent/event-parse.c:3468:9: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] This is because the eval_num_arg returns everything as an 'unsigned long long', so it converts a void pointer to a wider integer, fix it by converting the void pointer to an integer of the same size, 'unsigned long', before casting it to 'unsigned long long'. Acked-by: Steven Rostedt Cc: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Mike Galbraith Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/n/tip-yllx4aqcg06v5n4vjpwiiuld@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index 8a5b65d..217c82e 100644 --- a/event-parse.c +++ b/event-parse.c @@ -3465,7 +3465,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg * is in the bottom half of the 32 bit field. */ offset &= 0xffff; - val = (unsigned long long)(data + offset); + val = (unsigned long long)((unsigned long)data + offset); break; default: /* not sure what to do there */ return 0; -- cgit v1.2.3 From 5327bd8eef8618f588c5017e1cc01d6c9cf60515 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 19 Nov 2013 18:29:37 -0500 Subject: tools lib traceevent: Use helper trace-seq in print functions like kernel does Jiri Olsa reported that his plugin for scsi was chopping off part of the output. Investigating this, I found that Jiri used the same functions as what is in the kernel, which adds the following: trace_seq_putc(p, 0); This adds a '\0' to the output string. The reason this works in the kernel is that the "p" that is passed to the function helper is a temporary trace_seq. But in the libtraceevent library, it's the pointer to the trace_seq used to output. By adding the '\0', it truncates the line and nothing added after that will be printed. We can solve this in two ways. One is to have the helper functions for the library not add the unnecessary '\0'. The other is to change the library to also use a helper trace_seq structure that gets copied to the main trace_seq just like the kernel does. The latter allows the helper functions in the plugins to be the same as the kernel, which is the better solution. Signed-off-by: Steven Rostedt Reported-by: Jiri Olsa Tested-by: Jiri Olsa Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Link: http://lkml.kernel.org/r/20131119182937.401668e3@gandalf.local.home Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/event-parse.c b/event-parse.c index 217c82e..900fca0 100644 --- a/event-parse.c +++ b/event-parse.c @@ -4099,6 +4099,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event unsigned long long val; struct func_map *func; const char *saveptr; + struct trace_seq p; char *bprint_fmt = NULL; char format[32]; int show_func; @@ -4306,8 +4307,12 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event format[len] = 0; if (!len_as_arg) len_arg = -1; - print_str_arg(s, data, size, event, + /* Use helper trace_seq */ + trace_seq_init(&p); + print_str_arg(&p, data, size, event, format, len_arg, arg); + trace_seq_terminate(&p); + trace_seq_puts(s, p.buffer); arg = arg->next; break; default: -- cgit v1.2.3 From 7e95ebdbbc3a96d3f4c3db30659b82cd64e36870 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:16 +0100 Subject: tools lib traceevent: Add plugin support Backporting plugin support for traceevent lib. Backported from Steven Rostedt's trace-cmd repo (HEAD 0f2c2fb): git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git It's now possible to use following interface to load plugins (shared objects) to enhance pevent object functionality. The plugin interface/hooks are as follows: (taken from event-parse.h comments) - 'pevent_plugin_loader' (required) The function name to initialized the plugin. int pevent_plugin_loader(struct pevent *pevent) - 'pevent_plugin_unloader' (optional) The function called just before unloading int pevent_plugin_unloader(void) - 'pevent_plugin_options' (optional) Plugin options that can be set before loading struct plugin_option pevent_plugin_options[] = { { .name = "option-name", .plugin_alias = "overide-file-name", (optional) .description = "description of option to show users", }, { .name = NULL, }, }; Array must end with .name = NULL; The plugin_alias (below) can be used to give a shorter name to access the variable. Useful if a plugin handles more than one event. NOTE options support is not backported yet. - 'pevent_plugin_alias' (optional) The name to use for finding options (uses filename if not defined) New traceevent functions are added to search and load available plugins: struct plugin_list* traceevent_load_plugins(struct pevent *pevent) - loads plusing for 'struct pevent' object and returns loaded plugins list void traceevent_unload_plugins(struct plugin_list *plugin_list); - unload plugin list Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-3-git-send-email-jolsa@redhat.com Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 6 +- event-parse.h | 5 ++ event-plugin.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 event-plugin.c diff --git a/Makefile b/Makefile index fc15020..2ccb5bc 100644 --- a/Makefile +++ b/Makefile @@ -180,7 +180,11 @@ $(obj)/%.o: $(src)/%.c %.o: $(src)/%.c $(Q)$(call do_compile) -PEVENT_LIB_OBJS = event-parse.o trace-seq.o parse-filter.o parse-utils.o +PEVENT_LIB_OBJS = event-parse.o +PEVENT_LIB_OBJS += event-plugin.o +PEVENT_LIB_OBJS += trace-seq.o +PEVENT_LIB_OBJS += parse-filter.o +PEVENT_LIB_OBJS += parse-utils.o PEVENT_LIB_OBJS += kbuffer-parse.o ALL_OBJS = $(PEVENT_LIB_OBJS) diff --git a/event-parse.h b/event-parse.h index 8d73d25..a288860 100644 --- a/event-parse.h +++ b/event-parse.h @@ -377,6 +377,11 @@ enum pevent_errno { }; #undef _PE +struct plugin_list; + +struct plugin_list *traceevent_load_plugins(struct pevent *pevent); +void traceevent_unload_plugins(struct plugin_list *plugin_list); + struct cmdline; struct cmdline_list; struct func_map; diff --git a/event-plugin.c b/event-plugin.c new file mode 100644 index 0000000..d272d87 --- /dev/null +++ b/event-plugin.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include +#include +#include +#include +#include +#include +#include +#include "event-parse.h" +#include "event-utils.h" + +#define LOCAL_PLUGIN_DIR ".traceevent/plugins" + +struct plugin_list { + struct plugin_list *next; + char *name; + void *handle; +}; + +static void +load_plugin(struct pevent *pevent, const char *path, + const char *file, void *data) +{ + struct plugin_list **plugin_list = data; + pevent_plugin_load_func func; + struct plugin_list *list; + const char *alias; + char *plugin; + void *handle; + + plugin = malloc_or_die(strlen(path) + strlen(file) + 2); + + strcpy(plugin, path); + strcat(plugin, "/"); + strcat(plugin, file); + + handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL); + if (!handle) { + warning("could not load plugin '%s'\n%s\n", + plugin, dlerror()); + goto out_free; + } + + alias = dlsym(handle, PEVENT_PLUGIN_ALIAS_NAME); + if (!alias) + alias = file; + + func = dlsym(handle, PEVENT_PLUGIN_LOADER_NAME); + if (!func) { + warning("could not find func '%s' in plugin '%s'\n%s\n", + PEVENT_PLUGIN_LOADER_NAME, plugin, dlerror()); + goto out_free; + } + + list = malloc_or_die(sizeof(*list)); + list->next = *plugin_list; + list->handle = handle; + list->name = plugin; + *plugin_list = list; + + pr_stat("registering plugin: %s", plugin); + func(pevent); + return; + + out_free: + free(plugin); +} + +static void +load_plugins_dir(struct pevent *pevent, const char *suffix, + const char *path, + void (*load_plugin)(struct pevent *pevent, + const char *path, + const char *name, + void *data), + void *data) +{ + struct dirent *dent; + struct stat st; + DIR *dir; + int ret; + + ret = stat(path, &st); + if (ret < 0) + return; + + if (!S_ISDIR(st.st_mode)) + return; + + dir = opendir(path); + if (!dir) + return; + + while ((dent = readdir(dir))) { + const char *name = dent->d_name; + + if (strcmp(name, ".") == 0 || + strcmp(name, "..") == 0) + continue; + + /* Only load plugins that end in suffix */ + if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0) + continue; + + load_plugin(pevent, path, name, data); + } + + closedir(dir); +} + +static void +load_plugins(struct pevent *pevent, const char *suffix, + void (*load_plugin)(struct pevent *pevent, + const char *path, + const char *name, + void *data), + void *data) +{ + char *home; + char *path; + char *envdir; + + /* + * If a system plugin directory was defined, + * check that first. + */ +#ifdef PLUGIN_DIR + load_plugins_dir(pevent, suffix, PLUGIN_DIR, load_plugin, data); +#endif + + /* + * Next let the environment-set plugin directory + * override the system defaults. + */ + envdir = getenv("TRACEEVENT_PLUGIN_DIR"); + if (envdir) + load_plugins_dir(pevent, suffix, envdir, load_plugin, data); + + /* + * Now let the home directory override the environment + * or system defaults. + */ + home = getenv("HOME"); + if (!home) + return; + + path = malloc_or_die(strlen(home) + strlen(LOCAL_PLUGIN_DIR) + 2); + + strcpy(path, home); + strcat(path, "/"); + strcat(path, LOCAL_PLUGIN_DIR); + + load_plugins_dir(pevent, suffix, path, load_plugin, data); + + free(path); +} + +struct plugin_list* +traceevent_load_plugins(struct pevent *pevent) +{ + struct plugin_list *list = NULL; + + load_plugins(pevent, ".so", load_plugin, &list); + return list; +} + +void +traceevent_unload_plugins(struct plugin_list *plugin_list) +{ + pevent_plugin_unload_func func; + struct plugin_list *list; + + while (plugin_list) { + list = plugin_list; + plugin_list = list->next; + func = dlsym(list->handle, PEVENT_PLUGIN_UNLOADER_NAME); + if (func) + func(); + dlclose(list->handle); + free(list->name); + free(list); + } +} -- cgit v1.2.3 From 8e4c961158b16969f55057d7928039bbd4031914 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:17 +0100 Subject: tools lib traceevent: Add plugin build support Backporting missing pieces of plugin building infrastructure: - Adding Makefile 'plugins' target to build all defined plugins - Adding Makefile 'install_plugins' target as 'install_lib' target dependency - Link plugin objects with shared object building Backported from Steven Rostedt's trace-cmd repo (HEAD 0f2c2fb): git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git Plugins are by default installed into following locations: '$(HOME)/.traceevent/plugins' - If we are installing under $(HOME) '$(prefix)/lib/traceevent/plugins' - Otherwise This path is propagated to the plugin object as a plugins search path. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-4-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 2ccb5bc..1526798 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,30 @@ man_dir_SQ = '$(subst ','\'',$(man_dir))' export man_dir man_dir_SQ INSTALL export DESTDIR DESTDIR_SQ +set_plugin_dir := 1 + +# Set plugin_dir to preffered global plugin location +# If we install under $HOME directory we go under +# $(HOME)/.traceevent/plugins +# +# We dont set PLUGIN_DIR in case we install under $HOME +# directory, because by default the code looks under: +# $(HOME)/.traceevent/plugins by default. +# +ifeq ($(plugin_dir),) +ifeq ($(prefix),$(HOME)) +override plugin_dir = $(HOME)/.traceevent/plugins +set_plugin_dir := 0 +else +override plugin_dir = $(prefix)/lib/traceevent/plugins +endif +endif + +ifeq ($(set_plugin_dir),1) +PLUGIN_DIR = -DPLUGIN_DIR="$(DESTDIR)/$(plugin_dir)" +PLUGIN_DIR_SQ = '$(subst ','\'',$(PLUGIN_DIR))' +endif + # copy a bit from Linux kbuild ifeq ("$(origin V)", "command line") @@ -96,6 +120,7 @@ export prefix bindir src obj # Shell quotes bindir_SQ = $(subst ','\'',$(bindir)) bindir_relative_SQ = $(subst ','\'',$(bindir_relative)) +plugin_dir_SQ = $(subst ','\'',$(plugin_dir)) LIB_FILE = libtraceevent.a libtraceevent.so @@ -138,8 +163,8 @@ else print_app_build = echo ' BUILD '$(OBJ); print_fpic_compile = echo ' CC FPIC '$(OBJ); print_shared_lib_compile = echo ' BUILD SHARED LIB '$(OBJ); - print_plugin_obj_compile = echo ' BUILD PLUGIN OBJ '$(OBJ); - print_plugin_build = echo ' BUILD PLUGIN '$(OBJ); + print_plugin_obj_compile = echo ' CC FPIC '$(OBJ); + print_plugin_build = echo ' BUILD PLUGIN '$(OBJ); print_static_lib_build = echo ' BUILD STATIC LIB '$(OBJ); print_install = echo ' INSTALL '$1' to $(DESTDIR_SQ)$2'; endif @@ -187,9 +212,11 @@ PEVENT_LIB_OBJS += parse-filter.o PEVENT_LIB_OBJS += parse-utils.o PEVENT_LIB_OBJS += kbuffer-parse.o -ALL_OBJS = $(PEVENT_LIB_OBJS) +PLUGINS := $(PLUGIN_OBJS:.o=.so) + +ALL_OBJS = $(PEVENT_LIB_OBJS) $(PLUGIN_OBJS) -CMD_TARGETS = $(LIB_FILE) +CMD_TARGETS = $(LIB_FILE) $(PLUGINS) TARGETS = $(CMD_TARGETS) @@ -204,9 +231,17 @@ libtraceevent.so: $(PEVENT_LIB_OBJS) libtraceevent.a: $(PEVENT_LIB_OBJS) $(Q)$(do_build_static_lib) +plugins: $(PLUGINS) + $(PEVENT_LIB_OBJS): %.o: $(src)/%.c TRACEEVENT-CFLAGS $(Q)$(do_fpic_compile) +$(PLUGIN_OBJS): %.o : $(src)/%.c + $(Q)$(do_compile_plugin_obj) + +$(PLUGINS): %.so: %.o + $(Q)$(do_plugin_build) + define make_version.h (echo '/* This file is automatically generated. Do not modify. */'; \ echo \#define VERSION_CODE $(shell \ @@ -294,9 +329,16 @@ define do_install $(INSTALL) $1 '$(DESTDIR_SQ)$2' endef -install_lib: all_cmd +install_lib: all_cmd install_plugins $(Q)$(call do_install,$(LIB_FILE),$(bindir_SQ)) +PLUGINS_INSTALL = $(subst .so,.install,$(PLUGINS)) + +$(PLUGINS_INSTALL): %.install : %.so force + $(Q)$(call do_install,$<,$(plugin_dir_SQ)) + +install_plugins: $(PLUGINS_INSTALL) + install: install_lib clean: -- cgit v1.2.3 From e297cc9ade21aab5746da69ef2fa182ed426404d Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:18 +0100 Subject: tools lib traceevent: Add traceevent_host_bigendian function Adding traceevent_host_bigendian function to get host endianity. It's used in following patches. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-5-git-send-email-jolsa@redhat.com Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/event-parse.h b/event-parse.h index a288860..54273c0 100644 --- a/event-parse.h +++ b/event-parse.h @@ -23,6 +23,7 @@ #include #include #include +#include #ifndef __maybe_unused #define __maybe_unused __attribute__((unused)) @@ -527,6 +528,15 @@ __data2host8(struct pevent *pevent, unsigned long long data) __data2host8(pevent, __val); \ }) +static inline int traceevent_host_bigendian(void) +{ + unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 }; + unsigned int val; + + memcpy(&val, str, 4); + return val == 0x01020304; +} + /* taken from kernel/trace/trace.h */ enum trace_flag_type { TRACE_FLAG_IRQS_OFF = 0x01, -- cgit v1.2.3 From 9112f4d14ff658ff254967618c1880e9ffa58050 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:19 +0100 Subject: tools lib traceevent: Change pevent_parse_format to include pevent handle Changing the pevent_parse_format interface to include the pevent handle. The goal is to always use pevent object when dealing with traceevent library. The reason is that we might need additional processing (like plugins), which is not possible otherwise. Patches follow to make this happen completely. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-6-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 56 +++++++++++++++++++++++++++++++++++--------------------- event-parse.h | 4 +++- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/event-parse.c b/event-parse.c index 900fca0..9849873 100644 --- a/event-parse.c +++ b/event-parse.c @@ -5121,8 +5121,38 @@ enum pevent_errno __pevent_parse_format(struct event_format **eventp, return ret; } +static enum pevent_errno +__pevent_parse_event(struct pevent *pevent, + struct event_format **eventp, + const char *buf, unsigned long size, + const char *sys) +{ + int ret = __pevent_parse_format(eventp, pevent, buf, size, sys); + struct event_format *event = *eventp; + + if (event == NULL) + return ret; + + if (pevent && add_event(pevent, event)) { + ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; + goto event_add_failed; + } + +#define PRINT_ARGS 0 + if (PRINT_ARGS && event->print_fmt.args) + print_args(event->print_fmt.args); + + return 0; + +event_add_failed: + pevent_free_format(event); + return ret; +} + /** * pevent_parse_format - parse the event format + * @pevent: the handle to the pevent + * @eventp: returned format * @buf: the buffer storing the event format string * @size: the size of @buf * @sys: the system the event belongs to @@ -5134,10 +5164,12 @@ enum pevent_errno __pevent_parse_format(struct event_format **eventp, * * /sys/kernel/debug/tracing/events/.../.../format */ -enum pevent_errno pevent_parse_format(struct event_format **eventp, const char *buf, +enum pevent_errno pevent_parse_format(struct pevent *pevent, + struct event_format **eventp, + const char *buf, unsigned long size, const char *sys) { - return __pevent_parse_format(eventp, NULL, buf, size, sys); + return __pevent_parse_event(pevent, eventp, buf, size, sys); } /** @@ -5158,25 +5190,7 @@ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, unsigned long size, const char *sys) { struct event_format *event = NULL; - int ret = __pevent_parse_format(&event, pevent, buf, size, sys); - - if (event == NULL) - return ret; - - if (add_event(pevent, event)) { - ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; - goto event_add_failed; - } - -#define PRINT_ARGS 0 - if (PRINT_ARGS && event->print_fmt.args) - print_args(event->print_fmt.args); - - return 0; - -event_add_failed: - pevent_free_format(event); - return ret; + return __pevent_parse_event(pevent, &event, buf, size, sys); } #undef _PE diff --git a/event-parse.h b/event-parse.h index 54273c0..620c27a 100644 --- a/event-parse.h +++ b/event-parse.h @@ -562,7 +562,9 @@ int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long siz enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, unsigned long size, const char *sys); -enum pevent_errno pevent_parse_format(struct event_format **eventp, const char *buf, +enum pevent_errno pevent_parse_format(struct pevent *pevent, + struct event_format **eventp, + const char *buf, unsigned long size, const char *sys); void pevent_free_format(struct event_format *event); -- cgit v1.2.3 From 061d43382c855430e9784da69ad269a643204ed1 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:20 +0100 Subject: tools lib traceevent: Harmonize the install messages in lib-traceevent Removing the 'to ...' part out of the install message, because it does not fit to the rest of the build messages we use. Before: INSTALL plugin_hrtimer.so to /home/jolsa/libexec/perf-core/traceevent/plugins INSTALL plugin_jbd2.so to /home/jolsa/libexec/perf-core/traceevent/plugins INSTALL plugin_kmem.so to /home/jolsa/libexec/perf-core/traceevent/plugins INSTALL plugin_kvm.so to /home/jolsa/libexec/perf-core/traceevent/plugins INSTALL plugin_mac80211.so to /home/jolsa/libexec/perf-core/traceevent/plugins INSTALL plugin_sched_switch.so to /home/jolsa/libexec/perf-core/traceevent/plugins INSTALL plugin_function.so to /home/jolsa/libexec/perf-core/traceevent/plugins INSTALL plugin_xen.so to /home/jolsa/libexec/perf-core/traceevent/plugins INSTALL plugin_scsi.so to /home/jolsa/libexec/perf-core/traceevent/plugins Now: INSTALL plugin_jbd2.so INSTALL plugin_hrtimer.so INSTALL plugin_kmem.so INSTALL plugin_kvm.so INSTALL plugin_mac80211.so INSTALL plugin_sched_switch.so INSTALL plugin_function.so INSTALL plugin_xen.so INSTALL plugin_scsi.so Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-7-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1526798..f946851 100644 --- a/Makefile +++ b/Makefile @@ -166,7 +166,7 @@ else print_plugin_obj_compile = echo ' CC FPIC '$(OBJ); print_plugin_build = echo ' BUILD PLUGIN '$(OBJ); print_static_lib_build = echo ' BUILD STATIC LIB '$(OBJ); - print_install = echo ' INSTALL '$1' to $(DESTDIR_SQ)$2'; + print_install = echo ' INSTALL '$1; endif do_fpic_compile = \ -- cgit v1.2.3 From ea53919925ce2f3f37e340720e1464261028f64c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:26 +0100 Subject: tools lib traceevent: Add jbd2 plugin Backporting jbd2 plugin. Backported from Steven Rostedt's trace-cmd repo (HEAD 0f2c2fb): git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git This plugin adds field resolving functions for following tracepoint events: jbd2:jbd2_checkpoint_stats jbd2:jbd2_run_stats The diff of 'perf script' output generated by old and new code: (data was generated by 'perf record -e 'jbd2:jbd2_run_stats,jbd2:jbd2_checkpoint_stats' -a') --- script.jbd2.old +++ script.jbd2.new - jbd2/dm-3-8 576 [000] 2983.748423: jbd2:jbd2_checkpoint_stats: [FAILED TO PARSE] dev=265289731 tid=0x3f2bbb chp_time=0x0 forced_to_close=0 written=0 dropped=2 + jbd2/dm-3-8 576 [000] 2983.748423: jbd2:jbd2_checkpoint_stats: dev 253,3 tid 4139963 chp_time 0 forced_to_close 0 written 0 dropped 2 - jbd2/dm-3-8 576 [000] 2983.852789: jbd2:jbd2_run_stats: [FAILED TO PARSE] dev=265289731 tid=0x3f2bc0 wait=0x0 request_delay=0x0 running=0x138d locked=0x0 flushing=0x0 logging=0x68 handle_count=2995 blocks=17 blocks_logged=18 + jbd2/dm-3-8 576 [000] 2983.852789: jbd2:jbd2_run_stats: dev 253,3 tid 4139968 wait 0 request_delay 0 running 5005 locked 0 flushing 0 logging 104 handle_count 2995 blocks 17 blocks_logged 18 This plugin also adds jbd2_dev_to_name function, which was removed from jdb2 tracepoints recently. Keeping it in for backward compatibility reasons. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-13-git-send-email-jolsa@redhat.com Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 2 ++ plugin_jbd2.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 plugin_jbd2.c diff --git a/Makefile b/Makefile index f946851..6c65400 100644 --- a/Makefile +++ b/Makefile @@ -212,6 +212,8 @@ PEVENT_LIB_OBJS += parse-filter.o PEVENT_LIB_OBJS += parse-utils.o PEVENT_LIB_OBJS += kbuffer-parse.o +PLUGIN_OBJS = plugin_jbd2.o + PLUGINS := $(PLUGIN_OBJS:.o=.so) ALL_OBJS = $(PEVENT_LIB_OBJS) $(PLUGIN_OBJS) diff --git a/plugin_jbd2.c b/plugin_jbd2.c new file mode 100644 index 0000000..5d85de7 --- /dev/null +++ b/plugin_jbd2.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include + +#include "event-parse.h" + +#define MINORBITS 20 +#define MINORMASK ((1U << MINORBITS) - 1) + +#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) +#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) + +unsigned long long process_jbd2_dev_to_name(struct trace_seq *s, + unsigned long long *args) +{ + unsigned int dev = args[0]; + + trace_seq_printf(s, "%d:%d", MAJOR(dev), MINOR(dev)); + return 0; +} + +unsigned long long process_jiffies_to_msecs(struct trace_seq *s, + unsigned long long *args) +{ + unsigned long long jiffies = args[0]; + + trace_seq_printf(s, "%lld", jiffies); + return jiffies; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_print_function(pevent, + process_jbd2_dev_to_name, + PEVENT_FUNC_ARG_STRING, + "jbd2_dev_to_name", + PEVENT_FUNC_ARG_INT, + PEVENT_FUNC_ARG_VOID); + + pevent_register_print_function(pevent, + process_jiffies_to_msecs, + PEVENT_FUNC_ARG_LONG, + "jiffies_to_msecs", + PEVENT_FUNC_ARG_LONG, + PEVENT_FUNC_ARG_VOID); + return 0; +} -- cgit v1.2.3 From 315e039b7982a9e52ca491abe8c2d474c21a31e4 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:27 +0100 Subject: tools lib traceevent: Add hrtimer plugin Backporting hrtimer plugin. Backported from Steven Rostedt's trace-cmd repo (HEAD 0f2c2fb): git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git This plugin adds function field resolving for following tracepoint events: timer:hrtimer_expire_entry timer:hrtimer_start The diff of 'perf script' output generated by old and new code: (data was generated by 'perf record -e 'timer:hrtimer*' -a') --- script.hrtimer.old +++ script.hrtimer.new - swapper 0 [000] 27405.519092: timer:hrtimer_start: [FAILED TO PARSE] hrtimer=0xffff88021e20e800 function=0xffffffff810c0e10 expires=27398383000000 softexpires=27398383000000 + swapper 0 [000] 27405.519103: timer:hrtimer_start: hrtimer=0xffff88021e20e800 function=tick_sched_timer expires=27398383000000 softexpires=27398383000000 - swapper 0 [001] 27405.519544: timer:hrtimer_expire_entry: [FAILED TO PARSE] hrtimer=0xffff880211334058 now=27398294182491 function=0xffffffff81086f20 + swapper 0 [001] 27405.519544: timer:hrtimer_expire_entry: hrtimer=0xffff880211334058 now=27398294182491 function=posix_timer_fn/0x0 Check the 'function' field is translated into the function name. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-14-git-send-email-jolsa@redhat.com Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 3 +- plugin_hrtimer.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 plugin_hrtimer.c diff --git a/Makefile b/Makefile index 6c65400..9ff2e25 100644 --- a/Makefile +++ b/Makefile @@ -212,7 +212,8 @@ PEVENT_LIB_OBJS += parse-filter.o PEVENT_LIB_OBJS += parse-utils.o PEVENT_LIB_OBJS += kbuffer-parse.o -PLUGIN_OBJS = plugin_jbd2.o +PLUGIN_OBJS = plugin_jbd2.o +PLUGIN_OBJS += plugin_hrtimer.o PLUGINS := $(PLUGIN_OBJS:.o=.so) diff --git a/plugin_hrtimer.c b/plugin_hrtimer.c new file mode 100644 index 0000000..e41d4cf --- /dev/null +++ b/plugin_hrtimer.c @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2009 Red Hat Inc, Steven Rostedt + * Copyright (C) 2009 Johannes Berg + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include + +#include "event-parse.h" + +static int timer_expire_handler(struct trace_seq *s, + struct pevent_record *record, + struct event_format *event, void *context) +{ + trace_seq_printf(s, "hrtimer="); + + if (pevent_print_num_field(s, "0x%llx", event, "timer", + record, 0) == -1) + pevent_print_num_field(s, "0x%llx", event, "hrtimer", + record, 1); + + trace_seq_printf(s, " now="); + + pevent_print_num_field(s, "%llu", event, "now", record, 1); + + pevent_print_func_field(s, " function=%s", event, "function", + record, 0); + return 0; +} + +static int timer_start_handler(struct trace_seq *s, + struct pevent_record *record, + struct event_format *event, void *context) +{ + struct pevent *pevent = event->pevent; + struct format_field *fn = pevent_find_field(event, "function"); + void *data = record->data; + + trace_seq_printf(s, "hrtimer="); + + if (pevent_print_num_field(s, "0x%llx", event, "timer", + record, 0) == -1) + pevent_print_num_field(s, "0x%llx", event, "hrtimer", + record, 1); + + if (!fn) { + trace_seq_printf(s, " function=MISSING"); + } else { + unsigned long long function; + const char *func; + + if (pevent_read_number_field(fn, data, &function)) + trace_seq_printf(s, " function=INVALID"); + + func = pevent_find_function(pevent, function); + + trace_seq_printf(s, " function=%s", func); + } + + trace_seq_printf(s, " expires="); + pevent_print_num_field(s, "%llu", event, "expires", record, 1); + + trace_seq_printf(s, " softexpires="); + pevent_print_num_field(s, "%llu", event, "softexpires", record, 1); + return 0; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_event_handler(pevent, -1, + "timer", "hrtimer_expire_entry", + timer_expire_handler, NULL); + + pevent_register_event_handler(pevent, -1, "timer", "hrtimer_start", + timer_start_handler, NULL); + return 0; +} -- cgit v1.2.3 From 193925f687ed9a8dad39e4dead43112f8e245958 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:28 +0100 Subject: tools lib traceevent: Add kmem plugin Backporting kmem plugin. Backported from Steven Rostedt's trace-cmd repo (HEAD 0f2c2fb): git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git This plugin adds call_site field resolving for following tracepoint events: kmem:kfree kmem:kmalloc kmem:kmalloc_node kmem:kmem_cache_alloc kmem:kmem_cache_alloc_node kmem:kmem_cache_free The diff of 'perf script' output generated by old and new code: (data was generated by 'perf record -e 'kmem:*' -a') --- script.kmem.old +++ script.kmem.new - perf 27846 [001] 29643.403319: kmem:kfree: call_site=ffffffff810e64f6 ptr=(nil) + perf 27846 [001] 29643.403238: kmem:kfree: (__audit_syscall_exit+0x1f6) call_site=ffffffff810e64f6 ptr=(nil) - perf 27846 [001] 29643.403337: kmem:kmem_cache_alloc: call_site=ffffffff812ff0c5 ptr=0xffff88020e155630 bytes_req=560 bytes_alloc=568 gfp_flags=GFP_KERNEL + perf 27846 [001] 29643.403337: kmem:kmem_cache_alloc: (radix_tree_preload+0x35) call_site=ffffffff812ff0c5 ptr=0xffff88020e155630 bytes_req=560 bytes_alloc=568 gfp_flags=GFP_KERNEL - perf 27846 [001] 29643.403342: kmem:kmem_cache_free: call_site=ffffffff8126ec61 ptr=0xffff88020dffe750 + perf 27846 [001] 29643.403342: kmem:kmem_cache_free: (jbd2_journal_stop+0x221) call_site=ffffffff8126ec61 ptr=0xffff88020dffe750 - firefox 954 [000] 29643.445477: kmem:kmem_cache_alloc_node: call_site=ffffffff8153c64e ptr=0xffff8801cecb4000 bytes_req=256 bytes_alloc=256 gfp_flags=GFP_KERNEL|GFP_REPEAT node=-1 + firefox 954 [000] 29643.445477: kmem:kmem_cache_alloc_node: (__alloc_skb+0x4e) call_site=ffffffff8153c64e ptr=0xffff8801cecb4000 bytes_req=256 bytes_alloc=256 gfp_flags=GFP_KERNEL|GFP_REPEAT node=-1 - perf 27846 [001] 29643.445510: kmem:kmalloc: call_site=ffffffff81250642 ptr=0xffff88020fd6c300 bytes_req=96 bytes_alloc=96 gfp_flags=GFP_NOFS|GFP_ZERO + perf 27846 [001] 29643.445510: kmem:kmalloc: (ext4_ext_find_extent+0x362) call_site=ffffffff81250642 ptr=0xffff88020fd6c300 bytes_req=96 bytes_alloc=96 gfp_flags=GFP_NOFS|GFP_ZERO - X 888 [002] 29643.445546: kmem:kmalloc_node: call_site=ffffffff8153c67e ptr=0xffff880103828e00 bytes_req=384 bytes_alloc=512 gfp_flags=GFP_KERNEL|GFP_NOWARN|GFP_REPEAT|GFP_NOMEMALLOC node=-1 + X 888 [002] 29643.445546: kmem:kmalloc_node: (__alloc_skb+0x7e) call_site=ffffffff8153c67e ptr=0xffff880103828e00 bytes_req=384 bytes_alloc=512 gfp_flags=GFP_KERNEL|GFP_NOWARN|GFP_REPEAT|GFP_NOMEMALLOC node=-1 Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-15-git-send-email-jolsa@redhat.com Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 1 + plugin_kmem.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 plugin_kmem.c diff --git a/Makefile b/Makefile index 9ff2e25..245d6b4 100644 --- a/Makefile +++ b/Makefile @@ -214,6 +214,7 @@ PEVENT_LIB_OBJS += kbuffer-parse.o PLUGIN_OBJS = plugin_jbd2.o PLUGIN_OBJS += plugin_hrtimer.o +PLUGIN_OBJS += plugin_kmem.o PLUGINS := $(PLUGIN_OBJS:.o=.so) diff --git a/plugin_kmem.c b/plugin_kmem.c new file mode 100644 index 0000000..7115c80 --- /dev/null +++ b/plugin_kmem.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2009 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include + +#include "event-parse.h" + +static int call_site_handler(struct trace_seq *s, struct pevent_record *record, + struct event_format *event, void *context) +{ + struct format_field *field; + unsigned long long val, addr; + void *data = record->data; + const char *func; + + field = pevent_find_field(event, "call_site"); + if (!field) + return 1; + + if (pevent_read_number_field(field, data, &val)) + return 1; + + func = pevent_find_function(event->pevent, val); + if (!func) + return 1; + + addr = pevent_find_function_address(event->pevent, val); + + trace_seq_printf(s, "(%s+0x%x) ", func, (int)(val - addr)); + return 1; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_event_handler(pevent, -1, "kmem", "kfree", + call_site_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kmem", "kmalloc", + call_site_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kmem", "kmalloc_node", + call_site_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kmem", "kmem_cache_alloc", + call_site_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kmem", + "kmem_cache_alloc_node", + call_site_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kmem", "kmem_cache_free", + call_site_handler, NULL); + return 0; +} -- cgit v1.2.3 From 22e19101ec6617c882b678f5389c6d1f5c38fbc7 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:29 +0100 Subject: tools lib traceevent: Add kvm plugin Backporting kvm plugin. Backported from Steven Rostedt's trace-cmd repo (HEAD 0f2c2fb): git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git This plugin adds field resolving functions for following tracepoint events: kvm:kvm_exit kvm:kvm_emulate_insn kvm:kvm_nested_vmexit kvm:kvm_nested_vmexit_inject kvmmmu:kvm_mmu_get_page kvmmmu:kvm_mmu_sync_page kvmmmu:kvm_mmu_unsync_page kvmmmu:kvm_mmu_zap_page kvmmmu:kvm_mmu_prepare_zap_page The diff of 'perf script' output generated by old and new code: (data was generated by 'perf record -e 'kvm:*,kvmmmu:*' -a') --- script.kvm.old +++ script.kvm.new qemu-system-x86 17414 [000] 6868.995053: kvm:kvm_exit: reason EPT_VIOLATION rip 0xfff0 info 184 0 qemu-system-x86 17414 [000] 6868.995109: kvm:kvm_emulate_insn: f0000:c46b:e4 71 (real) - qemu-system-x86 3006 [002] 10562.079422: kvmmmu:kvm_mmu_get_page: [FAILED TO PARSE] mmu_valid_gen=0x2 gfn=0 role=122884 root_count=0 unsync=0 created=1 + qemu-system-x86 3006 [002] 10562.079422: kvmmmu:kvm_mmu_get_page: new sp gfn 0 0/4 q0 direct --- !pge !nxe root 0 sync - qemu-system-x86 3006 [002] 10562.080502: kvmmmu:kvm_mmu_prepare_zap_page: [FAILED TO PARSE] mmu_valid_gen=0x2 gfn=0 role=122884 root_count=1 unsync=0 + qemu-system-x86 3006 [002] 10562.080502: kvmmmu:kvm_mmu_prepare_zap_page: 0/4 q0 direct --- !pge !nxe root 1 sync qemu-system-x86 3290 [002] 10708.755312: kvmmmu:fast_page_fault: [FAILED TO PARSE] vcpu_id=0 gva=4094486080 error_code=3 sptep=0xffff88019f1e3670 old_spte=336391285 new_spte=336391287 retry=1 - insmod 2576 [001] 781.731666: kvmmmu:kvm_mmu_sync_page: [FAILED TO PARSE] mmu_valid_gen=0x1 gfn=2 role=24624 root_count=10 unsync=1 + insmod 2576 [001] 781.731666: kvmmmu:kvm_mmu_sync_page: 3/0 q0 --- !pge !nxe root 10 unsync - insmod 2576 [001] 781.731668: kvmmmu:kvm_mmu_unsync_page: [FAILED TO PARSE] mmu_valid_gen=0x1 gfn=2 role=24624 root_count=10 unsync=1 + insmod 2576 [001] 781.731668: kvmmmu:kvm_mmu_unsync_page: 3/0 q0 --- !pge !nxe root 10 unsync Note: - kvm_mmu_zap_page is replaced by kvm_mmu_prepare_zap_page in current kernel, keeping it for backward compatibility - some of the tracepoints keep the same output even with the plugin handling: kvm:kvm_exit, kvm:kvm_emulate_insn - the 'kvmmmu:fast_page_fault' is still broken because of missing is_writable_pte function and is fixed in another patch - ommited following tracepoints from backport because the output was buggy kvm:kvm_nested_vmexit kvm:kvm_nested_vmexit_inject Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-16-git-send-email-jolsa@redhat.com Signed-off-by: Jiri Olsa Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 1 + plugin_kvm.c | 419 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 420 insertions(+) create mode 100644 plugin_kvm.c diff --git a/Makefile b/Makefile index 245d6b4..226a8f9 100644 --- a/Makefile +++ b/Makefile @@ -215,6 +215,7 @@ PEVENT_LIB_OBJS += kbuffer-parse.o PLUGIN_OBJS = plugin_jbd2.o PLUGIN_OBJS += plugin_hrtimer.o PLUGIN_OBJS += plugin_kmem.o +PLUGIN_OBJS += plugin_kvm.o PLUGINS := $(PLUGIN_OBJS:.o=.so) diff --git a/plugin_kvm.c b/plugin_kvm.c new file mode 100644 index 0000000..be9d9c6 --- /dev/null +++ b/plugin_kvm.c @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2009 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include +#include + +#include "event-parse.h" + +#ifdef HAVE_UDIS86 + +#include + +static ud_t ud; + +static void init_disassembler(void) +{ + ud_init(&ud); + ud_set_syntax(&ud, UD_SYN_ATT); +} + +static const char *disassemble(unsigned char *insn, int len, uint64_t rip, + int cr0_pe, int eflags_vm, + int cs_d, int cs_l) +{ + int mode; + + if (!cr0_pe) + mode = 16; + else if (eflags_vm) + mode = 16; + else if (cs_l) + mode = 64; + else if (cs_d) + mode = 32; + else + mode = 16; + + ud_set_pc(&ud, rip); + ud_set_mode(&ud, mode); + ud_set_input_buffer(&ud, insn, len); + ud_disassemble(&ud); + return ud_insn_asm(&ud); +} + +#else + +static void init_disassembler(void) +{ +} + +static const char *disassemble(unsigned char *insn, int len, uint64_t rip, + int cr0_pe, int eflags_vm, + int cs_d, int cs_l) +{ + static char out[15*3+1]; + int i; + + for (i = 0; i < len; ++i) + sprintf(out + i * 3, "%02x ", insn[i]); + out[len*3-1] = '\0'; + return out; +} + +#endif + + +#define VMX_EXIT_REASONS \ + _ER(EXCEPTION_NMI, 0) \ + _ER(EXTERNAL_INTERRUPT, 1) \ + _ER(TRIPLE_FAULT, 2) \ + _ER(PENDING_INTERRUPT, 7) \ + _ER(NMI_WINDOW, 8) \ + _ER(TASK_SWITCH, 9) \ + _ER(CPUID, 10) \ + _ER(HLT, 12) \ + _ER(INVD, 13) \ + _ER(INVLPG, 14) \ + _ER(RDPMC, 15) \ + _ER(RDTSC, 16) \ + _ER(VMCALL, 18) \ + _ER(VMCLEAR, 19) \ + _ER(VMLAUNCH, 20) \ + _ER(VMPTRLD, 21) \ + _ER(VMPTRST, 22) \ + _ER(VMREAD, 23) \ + _ER(VMRESUME, 24) \ + _ER(VMWRITE, 25) \ + _ER(VMOFF, 26) \ + _ER(VMON, 27) \ + _ER(CR_ACCESS, 28) \ + _ER(DR_ACCESS, 29) \ + _ER(IO_INSTRUCTION, 30) \ + _ER(MSR_READ, 31) \ + _ER(MSR_WRITE, 32) \ + _ER(MWAIT_INSTRUCTION, 36) \ + _ER(MONITOR_INSTRUCTION, 39) \ + _ER(PAUSE_INSTRUCTION, 40) \ + _ER(MCE_DURING_VMENTRY, 41) \ + _ER(TPR_BELOW_THRESHOLD, 43) \ + _ER(APIC_ACCESS, 44) \ + _ER(EOI_INDUCED, 45) \ + _ER(EPT_VIOLATION, 48) \ + _ER(EPT_MISCONFIG, 49) \ + _ER(INVEPT, 50) \ + _ER(PREEMPTION_TIMER, 52) \ + _ER(WBINVD, 54) \ + _ER(XSETBV, 55) \ + _ER(APIC_WRITE, 56) \ + _ER(INVPCID, 58) + +#define SVM_EXIT_REASONS \ + _ER(EXIT_READ_CR0, 0x000) \ + _ER(EXIT_READ_CR3, 0x003) \ + _ER(EXIT_READ_CR4, 0x004) \ + _ER(EXIT_READ_CR8, 0x008) \ + _ER(EXIT_WRITE_CR0, 0x010) \ + _ER(EXIT_WRITE_CR3, 0x013) \ + _ER(EXIT_WRITE_CR4, 0x014) \ + _ER(EXIT_WRITE_CR8, 0x018) \ + _ER(EXIT_READ_DR0, 0x020) \ + _ER(EXIT_READ_DR1, 0x021) \ + _ER(EXIT_READ_DR2, 0x022) \ + _ER(EXIT_READ_DR3, 0x023) \ + _ER(EXIT_READ_DR4, 0x024) \ + _ER(EXIT_READ_DR5, 0x025) \ + _ER(EXIT_READ_DR6, 0x026) \ + _ER(EXIT_READ_DR7, 0x027) \ + _ER(EXIT_WRITE_DR0, 0x030) \ + _ER(EXIT_WRITE_DR1, 0x031) \ + _ER(EXIT_WRITE_DR2, 0x032) \ + _ER(EXIT_WRITE_DR3, 0x033) \ + _ER(EXIT_WRITE_DR4, 0x034) \ + _ER(EXIT_WRITE_DR5, 0x035) \ + _ER(EXIT_WRITE_DR6, 0x036) \ + _ER(EXIT_WRITE_DR7, 0x037) \ + _ER(EXIT_EXCP_BASE, 0x040) \ + _ER(EXIT_INTR, 0x060) \ + _ER(EXIT_NMI, 0x061) \ + _ER(EXIT_SMI, 0x062) \ + _ER(EXIT_INIT, 0x063) \ + _ER(EXIT_VINTR, 0x064) \ + _ER(EXIT_CR0_SEL_WRITE, 0x065) \ + _ER(EXIT_IDTR_READ, 0x066) \ + _ER(EXIT_GDTR_READ, 0x067) \ + _ER(EXIT_LDTR_READ, 0x068) \ + _ER(EXIT_TR_READ, 0x069) \ + _ER(EXIT_IDTR_WRITE, 0x06a) \ + _ER(EXIT_GDTR_WRITE, 0x06b) \ + _ER(EXIT_LDTR_WRITE, 0x06c) \ + _ER(EXIT_TR_WRITE, 0x06d) \ + _ER(EXIT_RDTSC, 0x06e) \ + _ER(EXIT_RDPMC, 0x06f) \ + _ER(EXIT_PUSHF, 0x070) \ + _ER(EXIT_POPF, 0x071) \ + _ER(EXIT_CPUID, 0x072) \ + _ER(EXIT_RSM, 0x073) \ + _ER(EXIT_IRET, 0x074) \ + _ER(EXIT_SWINT, 0x075) \ + _ER(EXIT_INVD, 0x076) \ + _ER(EXIT_PAUSE, 0x077) \ + _ER(EXIT_HLT, 0x078) \ + _ER(EXIT_INVLPG, 0x079) \ + _ER(EXIT_INVLPGA, 0x07a) \ + _ER(EXIT_IOIO, 0x07b) \ + _ER(EXIT_MSR, 0x07c) \ + _ER(EXIT_TASK_SWITCH, 0x07d) \ + _ER(EXIT_FERR_FREEZE, 0x07e) \ + _ER(EXIT_SHUTDOWN, 0x07f) \ + _ER(EXIT_VMRUN, 0x080) \ + _ER(EXIT_VMMCALL, 0x081) \ + _ER(EXIT_VMLOAD, 0x082) \ + _ER(EXIT_VMSAVE, 0x083) \ + _ER(EXIT_STGI, 0x084) \ + _ER(EXIT_CLGI, 0x085) \ + _ER(EXIT_SKINIT, 0x086) \ + _ER(EXIT_RDTSCP, 0x087) \ + _ER(EXIT_ICEBP, 0x088) \ + _ER(EXIT_WBINVD, 0x089) \ + _ER(EXIT_MONITOR, 0x08a) \ + _ER(EXIT_MWAIT, 0x08b) \ + _ER(EXIT_MWAIT_COND, 0x08c) \ + _ER(EXIT_NPF, 0x400) \ + _ER(EXIT_ERR, -1) + +#define _ER(reason, val) { #reason, val }, +struct str_values { + const char *str; + int val; +}; + +static struct str_values vmx_exit_reasons[] = { + VMX_EXIT_REASONS + { NULL, -1} +}; + +static struct str_values svm_exit_reasons[] = { + SVM_EXIT_REASONS + { NULL, -1} +}; + +static struct isa_exit_reasons { + unsigned isa; + struct str_values *strings; +} isa_exit_reasons[] = { + { .isa = 1, .strings = vmx_exit_reasons }, + { .isa = 2, .strings = svm_exit_reasons }, + { } +}; + +static const char *find_exit_reason(unsigned isa, int val) +{ + struct str_values *strings = NULL; + int i; + + for (i = 0; isa_exit_reasons[i].strings; ++i) + if (isa_exit_reasons[i].isa == isa) { + strings = isa_exit_reasons[i].strings; + break; + } + if (!strings) + return "UNKNOWN-ISA"; + for (i = 0; strings[i].val >= 0; i++) + if (strings[i].val == val) + break; + if (strings[i].str) + return strings[i].str; + return "UNKNOWN"; +} + +static int kvm_exit_handler(struct trace_seq *s, struct pevent_record *record, + struct event_format *event, void *context) +{ + unsigned long long isa; + unsigned long long val; + unsigned long long info1 = 0, info2 = 0; + + if (pevent_get_field_val(s, event, "exit_reason", record, &val, 1) < 0) + return -1; + + if (pevent_get_field_val(s, event, "isa", record, &isa, 0) < 0) + isa = 1; + + trace_seq_printf(s, "reason %s", find_exit_reason(isa, val)); + + pevent_print_num_field(s, " rip 0x%lx", event, "guest_rip", record, 1); + + if (pevent_get_field_val(s, event, "info1", record, &info1, 0) >= 0 + && pevent_get_field_val(s, event, "info2", record, &info2, 0) >= 0) + trace_seq_printf(s, " info %llx %llx", info1, info2); + + return 0; +} + +#define KVM_EMUL_INSN_F_CR0_PE (1 << 0) +#define KVM_EMUL_INSN_F_EFL_VM (1 << 1) +#define KVM_EMUL_INSN_F_CS_D (1 << 2) +#define KVM_EMUL_INSN_F_CS_L (1 << 3) + +static int kvm_emulate_insn_handler(struct trace_seq *s, + struct pevent_record *record, + struct event_format *event, void *context) +{ + unsigned long long rip, csbase, len, flags, failed; + int llen; + uint8_t *insn; + const char *disasm; + + if (pevent_get_field_val(s, event, "rip", record, &rip, 1) < 0) + return -1; + + if (pevent_get_field_val(s, event, "csbase", record, &csbase, 1) < 0) + return -1; + + if (pevent_get_field_val(s, event, "len", record, &len, 1) < 0) + return -1; + + if (pevent_get_field_val(s, event, "flags", record, &flags, 1) < 0) + return -1; + + if (pevent_get_field_val(s, event, "failed", record, &failed, 1) < 0) + return -1; + + insn = pevent_get_field_raw(s, event, "insn", record, &llen, 1); + if (!insn) + return -1; + + disasm = disassemble(insn, len, rip, + flags & KVM_EMUL_INSN_F_CR0_PE, + flags & KVM_EMUL_INSN_F_EFL_VM, + flags & KVM_EMUL_INSN_F_CS_D, + flags & KVM_EMUL_INSN_F_CS_L); + + trace_seq_printf(s, "%llx:%llx: %s%s", csbase, rip, disasm, + failed ? " FAIL" : ""); + return 0; +} + +union kvm_mmu_page_role { + unsigned word; + struct { + unsigned glevels:4; + unsigned level:4; + unsigned quadrant:2; + unsigned pad_for_nice_hex_output:6; + unsigned direct:1; + unsigned access:3; + unsigned invalid:1; + unsigned cr4_pge:1; + unsigned nxe:1; + }; +}; + +static int kvm_mmu_print_role(struct trace_seq *s, struct pevent_record *record, + struct event_format *event, void *context) +{ + unsigned long long val; + static const char *access_str[] = { + "---", "--x", "w--", "w-x", "-u-", "-ux", "wu-", "wux" + }; + union kvm_mmu_page_role role; + + if (pevent_get_field_val(s, event, "role", record, &val, 1) < 0) + return -1; + + role.word = (int)val; + + /* + * We can only use the structure if file is of the same + * endianess. + */ + if (pevent_is_file_bigendian(event->pevent) == + pevent_is_host_bigendian(event->pevent)) { + + trace_seq_printf(s, "%u/%u q%u%s %s%s %spge %snxe", + role.level, + role.glevels, + role.quadrant, + role.direct ? " direct" : "", + access_str[role.access], + role.invalid ? " invalid" : "", + role.cr4_pge ? "" : "!", + role.nxe ? "" : "!"); + } else + trace_seq_printf(s, "WORD: %08x", role.word); + + pevent_print_num_field(s, " root %u ", event, + "root_count", record, 1); + + if (pevent_get_field_val(s, event, "unsync", record, &val, 1) < 0) + return -1; + + trace_seq_printf(s, "%s%c", val ? "unsync" : "sync", 0); + return 0; +} + +static int kvm_mmu_get_page_handler(struct trace_seq *s, + struct pevent_record *record, + struct event_format *event, void *context) +{ + unsigned long long val; + + if (pevent_get_field_val(s, event, "created", record, &val, 1) < 0) + return -1; + + trace_seq_printf(s, "%s ", val ? "new" : "existing"); + + if (pevent_get_field_val(s, event, "gfn", record, &val, 1) < 0) + return -1; + + trace_seq_printf(s, "sp gfn %llx ", val); + return kvm_mmu_print_role(s, record, event, context); +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + init_disassembler(); + + pevent_register_event_handler(pevent, -1, "kvm", "kvm_exit", + kvm_exit_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kvm", "kvm_emulate_insn", + kvm_emulate_insn_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_get_page", + kvm_mmu_get_page_handler, NULL); + + pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_sync_page", + kvm_mmu_print_role, NULL); + + pevent_register_event_handler(pevent, -1, + "kvmmmu", "kvm_mmu_unsync_page", + kvm_mmu_print_role, NULL); + + pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_zap_page", + kvm_mmu_print_role, NULL); + + pevent_register_event_handler(pevent, -1, "kvmmmu", + "kvm_mmu_prepare_zap_page", kvm_mmu_print_role, + NULL); + return 0; +} -- cgit v1.2.3 From ea33e3bb399ed73e96c1a1a5f5ada1bba3cc78a0 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:30 +0100 Subject: tools lib traceevent: Add mac80211 plugin Backporting mac80211 plugin. Backported from Steven Rostedt's trace-cmd repo (HEAD 0f2c2fb): git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git This plugin adds changed field resolving for mac80211:drv_bss_info_changed tracepoint event. The diff of 'perf script' output generated by old and new code: (data was generated by 'perf record -e 'mac80211:drv_bss_info_changed' -a') --- script.mac80211.old +++ script.mac80211.new - ifconfig 3711 [000] 1290.446492: mac80211:drv_bss_info_changed: phy0 vif:wlan0(2) changed:0x309f + ifconfig 3711 [000] 1290.446492: mac80211:drv_bss_info_changed: phy0 vif:wlan0(2) + assoc:0 aid:2 cts:0 shortpre:0 shortslot:0 dtimper:1 + bcnint:102 assoc_cap:0x431 basic_rates:0xf enable_beacon:0 + ht_operation_mode:0 Omitting the mac80211:drv_config tracepoint handling because the kernel tracepoint changed its prototype and the plugin handler is no longer working. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-17-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 1 + plugin_mac80211.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 plugin_mac80211.c diff --git a/Makefile b/Makefile index 226a8f9..d0e0ca1 100644 --- a/Makefile +++ b/Makefile @@ -216,6 +216,7 @@ PLUGIN_OBJS = plugin_jbd2.o PLUGIN_OBJS += plugin_hrtimer.o PLUGIN_OBJS += plugin_kmem.o PLUGIN_OBJS += plugin_kvm.o +PLUGIN_OBJS += plugin_mac80211.o PLUGINS := $(PLUGIN_OBJS:.o=.so) diff --git a/plugin_mac80211.c b/plugin_mac80211.c new file mode 100644 index 0000000..558a3b9 --- /dev/null +++ b/plugin_mac80211.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2009 Johannes Berg + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include + +#include "event-parse.h" + +#define INDENT 65 + +static void print_string(struct trace_seq *s, struct event_format *event, + const char *name, const void *data) +{ + struct format_field *f = pevent_find_field(event, name); + int offset; + int length; + + if (!f) { + trace_seq_printf(s, "NOTFOUND:%s", name); + return; + } + + offset = f->offset; + length = f->size; + + if (!strncmp(f->type, "__data_loc", 10)) { + unsigned long long v; + if (pevent_read_number_field(f, data, &v)) { + trace_seq_printf(s, "invalid_data_loc"); + return; + } + offset = v & 0xffff; + length = v >> 16; + } + + trace_seq_printf(s, "%.*s", length, (char *)data + offset); +} + +#define SF(fn) pevent_print_num_field(s, fn ":%d", event, fn, record, 0) +#define SFX(fn) pevent_print_num_field(s, fn ":%#x", event, fn, record, 0) +#define SP() trace_seq_putc(s, ' ') + +static int drv_bss_info_changed(struct trace_seq *s, + struct pevent_record *record, + struct event_format *event, void *context) +{ + void *data = record->data; + + print_string(s, event, "wiphy_name", data); + trace_seq_printf(s, " vif:"); + print_string(s, event, "vif_name", data); + pevent_print_num_field(s, "(%d)", event, "vif_type", record, 1); + + trace_seq_printf(s, "\n%*s", INDENT, ""); + SF("assoc"); SP(); + SF("aid"); SP(); + SF("cts"); SP(); + SF("shortpre"); SP(); + SF("shortslot"); SP(); + SF("dtimper"); SP(); + trace_seq_printf(s, "\n%*s", INDENT, ""); + SF("bcnint"); SP(); + SFX("assoc_cap"); SP(); + SFX("basic_rates"); SP(); + SF("enable_beacon"); + trace_seq_printf(s, "\n%*s", INDENT, ""); + SF("ht_operation_mode"); + + return 0; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_event_handler(pevent, -1, "mac80211", + "drv_bss_info_changed", + drv_bss_info_changed, NULL); + return 0; +} -- cgit v1.2.3 From 4f6857aa52bb5fcdc567d2b615a6afcd6ece81a0 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:31 +0100 Subject: tools lib traceevent: Add sched_switch plugin Backporting sched_switch plugin. Backported from Steven Rostedt's trace-cmd repo (HEAD 0f2c2fb): git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git This plugin adds fields resolving for sched:sched_switch tracepoint event. The diff of 'perf script' output generated by old and new code: (data was generated by 'perf record -e sched:sched_switch -a') --- script.sched_switch.old +++ script.sched_switch.new - perf 577 [002] 30965.311852: sched:sched_switch: prev_comm=perf prev_pid=577 prev_prio=120 prev_state=S ==> next_comm=swapper/2 next_pid=0 next_prio=120 + perf 577 [002] 30965.311852: sched:sched_switch: perf:577 [120] S ==> swapper/2:0 [120] Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-18-git-send-email-jolsa@redhat.com Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 1 + plugin_sched_switch.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 plugin_sched_switch.c diff --git a/Makefile b/Makefile index d0e0ca1..1c319a8 100644 --- a/Makefile +++ b/Makefile @@ -217,6 +217,7 @@ PLUGIN_OBJS += plugin_hrtimer.o PLUGIN_OBJS += plugin_kmem.o PLUGIN_OBJS += plugin_kvm.o PLUGIN_OBJS += plugin_mac80211.o +PLUGIN_OBJS += plugin_sched_switch.o PLUGINS := $(PLUGIN_OBJS:.o=.so) diff --git a/plugin_sched_switch.c b/plugin_sched_switch.c new file mode 100644 index 0000000..fea3724 --- /dev/null +++ b/plugin_sched_switch.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include + +#include "event-parse.h" + +static void write_state(struct trace_seq *s, int val) +{ + const char states[] = "SDTtZXxW"; + int found = 0; + int i; + + for (i = 0; i < (sizeof(states) - 1); i++) { + if (!(val & (1 << i))) + continue; + + if (found) + trace_seq_putc(s, '|'); + + found = 1; + trace_seq_putc(s, states[i]); + } + + if (!found) + trace_seq_putc(s, 'R'); +} + +static void write_and_save_comm(struct format_field *field, + struct pevent_record *record, + struct trace_seq *s, int pid) +{ + const char *comm; + int len; + + comm = (char *)(record->data + field->offset); + len = s->len; + trace_seq_printf(s, "%.*s", + field->size, comm); + + /* make sure the comm has a \0 at the end. */ + trace_seq_terminate(s); + comm = &s->buffer[len]; + + /* Help out the comm to ids. This will handle dups */ + pevent_register_comm(field->event->pevent, comm, pid); +} + +static int sched_wakeup_handler(struct trace_seq *s, + struct pevent_record *record, + struct event_format *event, void *context) +{ + struct format_field *field; + unsigned long long val; + + if (pevent_get_field_val(s, event, "pid", record, &val, 1)) + return trace_seq_putc(s, '!'); + + field = pevent_find_any_field(event, "comm"); + if (field) { + write_and_save_comm(field, record, s, val); + trace_seq_putc(s, ':'); + } + trace_seq_printf(s, "%lld", val); + + if (pevent_get_field_val(s, event, "prio", record, &val, 0) == 0) + trace_seq_printf(s, " [%lld]", val); + + if (pevent_get_field_val(s, event, "success", record, &val, 1) == 0) + trace_seq_printf(s, " success=%lld", val); + + if (pevent_get_field_val(s, event, "target_cpu", record, &val, 0) == 0) + trace_seq_printf(s, " CPU:%03llu", val); + + return 0; +} + +static int sched_switch_handler(struct trace_seq *s, + struct pevent_record *record, + struct event_format *event, void *context) +{ + struct format_field *field; + unsigned long long val; + + if (pevent_get_field_val(s, event, "prev_pid", record, &val, 1)) + return trace_seq_putc(s, '!'); + + field = pevent_find_any_field(event, "prev_comm"); + if (field) { + write_and_save_comm(field, record, s, val); + trace_seq_putc(s, ':'); + } + trace_seq_printf(s, "%lld ", val); + + if (pevent_get_field_val(s, event, "prev_prio", record, &val, 0) == 0) + trace_seq_printf(s, "[%lld] ", val); + + if (pevent_get_field_val(s, event, "prev_state", record, &val, 0) == 0) + write_state(s, val); + + trace_seq_puts(s, " ==> "); + + if (pevent_get_field_val(s, event, "next_pid", record, &val, 1)) + return trace_seq_putc(s, '!'); + + field = pevent_find_any_field(event, "next_comm"); + if (field) { + write_and_save_comm(field, record, s, val); + trace_seq_putc(s, ':'); + } + trace_seq_printf(s, "%lld", val); + + if (pevent_get_field_val(s, event, "next_prio", record, &val, 0) == 0) + trace_seq_printf(s, " [%lld]", val); + + return 0; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_event_handler(pevent, -1, "sched", "sched_switch", + sched_switch_handler, NULL); + + pevent_register_event_handler(pevent, -1, "sched", "sched_wakeup", + sched_wakeup_handler, NULL); + + pevent_register_event_handler(pevent, -1, "sched", "sched_wakeup_new", + sched_wakeup_handler, NULL); + return 0; +} -- cgit v1.2.3 From 44eac050927966477ac41bf3a27fd7622eb09b7d Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:32 +0100 Subject: tools lib traceevent: Add function plugin Backporting function plugin. Backported from Steven Rostedt's trace-cmd repo (HEAD 0f2c2fb): git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/trace-cmd.git This plugin adds function and parent function fields resolving for ftrace:function tracepoint event. The diff of 'perf script' output generated by old and new code: (data was generated by 'perf record -e ftrace:function ls') --- script.function.old +++ script.function.new - ls 10781 [001] 32667.291379: ftrace:function: ffffffff811adb80 <-- ffffffff811afc48 - ls 10781 [001] 32667.291379: ftrace:function: ffffffff811b35d0 <-- ffffffff811adb9b - ls 10781 [001] 32667.291380: ftrace:function: ffffffff811b3520 <-- ffffffff811b35e8 - ls 10781 [001] 32667.291380: ftrace:function: ffffffff811b2720 <-- ffffffff811b3549 - ls 10781 [001] 32667.291381: ftrace:function: ffffffff81297e10 <-- ffffffff811b356c - ls 10781 [001] 32667.291381: ftrace:function: ffffffff81298f40 <-- ffffffff81297e2c - ls 10781 [001] 32667.291382: ftrace:function: ffffffff81076160 <-- ffffffff811afbf0 - ls 10781 [001] 32667.291383: ftrace:function: ffffffff811c3eb0 <-- ffffffff811afbfc - ls 10781 [001] 32667.291383: ftrace:function: ffffffff8164e100 <-- ffffffff811c3ed8 - ls 10781 [001] 32667.291384: ftrace:function: ffffffff811a5d10 <-- ffffffff811c3f53 - ls 10781 [001] 32667.291384: ftrace:function: ffffffff811e8e70 <-- ffffffff811a5d58 - ls 10781 [001] 32667.291385: ftrace:function: ffffffff811f38e0 <-- ffffffff811a5d63 - ls 10781 [001] 32667.291385: ftrace:function: ffffffff811a9ff0 <-- ffffffff811a5d6b - ls 10781 [001] 32667.291386: ftrace:function: ffffffff811a9fa0 <-- ffffffff811aa015 - ls 10781 [001] 32667.291387: ftrace:function: ffffffff810851c0 <-- ffffffff811aa053 - ls 10781 [001] 32667.291387: ftrace:function: ffffffff81090e00 <-- ffffffff81085211 + ls 10781 [001] 32667.291379: ftrace:function: would_dump <-- setup_new_exec + ls 10781 [001] 32667.291379: ftrace:function: inode_permission <-- would_dump + ls 10781 [001] 32667.291380: ftrace:function: __inode_permission <-- inode_permission + ls 10781 [001] 32667.291380: ftrace:function: generic_permission <-- __inode_permission + ls 10781 [001] 32667.291381: ftrace:function: security_inode_permission <-- __inode_permission + ls 10781 [001] 32667.291381: ftrace:function: cap_inode_permission <-- security_inode_permission + ls 10781 [001] 32667.291382: ftrace:function: flush_signal_handlers <-- setup_new_exec + ls 10781 [001] 32667.291383: ftrace:function: do_close_on_exec <-- setup_new_exec + ls 10781 [001] 32667.291383: ftrace:function: _raw_spin_lock <-- do_close_on_exec + ls 10781 [001] 32667.291384: ftrace:function: filp_close <-- do_close_on_exec + ls 10781 [001] 32667.291384: ftrace:function: dnotify_flush <-- filp_close + ls 10781 [001] 32667.291385: ftrace:function: locks_remove_posix <-- filp_close + ls 10781 [001] 32667.291385: ftrace:function: fput <-- filp_close + ls 10781 [001] 32667.291386: ftrace:function: file_sb_list_del <-- fput + ls 10781 [001] 32667.291387: ftrace:function: task_work_add <-- fput + ls 10781 [001] 32667.291387: ftrace:function: kick_process <-- task_work_add Removing options support as it's not backported yet. Currently this plugin supports 2 options: 'parent' to display parent function 'indent' to show function call indents Enabling both of them by default. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-19-git-send-email-jolsa@redhat.com Signed-off-by: Steven Rostedt Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 1 + plugin_function.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 plugin_function.c diff --git a/Makefile b/Makefile index 1c319a8..21f9b8f 100644 --- a/Makefile +++ b/Makefile @@ -218,6 +218,7 @@ PLUGIN_OBJS += plugin_kmem.o PLUGIN_OBJS += plugin_kvm.o PLUGIN_OBJS += plugin_mac80211.o PLUGIN_OBJS += plugin_sched_switch.o +PLUGIN_OBJS += plugin_function.o PLUGINS := $(PLUGIN_OBJS:.o=.so) diff --git a/plugin_function.c b/plugin_function.c new file mode 100644 index 0000000..8deb22e --- /dev/null +++ b/plugin_function.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include +#include +#include + +#include "event-parse.h" +#include "event-utils.h" + +static struct func_stack { + int index; + int size; + char **stack; +} *fstack; + +static int cpus = -1; + +#define STK_BLK 10 + +static void add_child(struct func_stack *stack, const char *child, int pos) +{ + int i; + + if (!child) + return; + + if (pos < stack->size) + free(stack->stack[pos]); + else { + if (!stack->stack) + stack->stack = malloc_or_die(sizeof(char *) * STK_BLK); + else + stack->stack = realloc(stack->stack, sizeof(char *) * + (stack->size + STK_BLK)); + for (i = stack->size; i < stack->size + STK_BLK; i++) + stack->stack[i] = NULL; + stack->size += STK_BLK; + } + + stack->stack[pos] = strdup(child); +} + +static int get_index(const char *parent, const char *child, int cpu) +{ + int i; + + if (cpu < 0) + return 0; + + if (cpu > cpus) { + if (fstack) + fstack = realloc(fstack, sizeof(*fstack) * (cpu + 1)); + else + fstack = malloc_or_die(sizeof(*fstack) * (cpu + 1)); + + /* Account for holes in the cpu count */ + for (i = cpus + 1; i <= cpu; i++) + memset(&fstack[i], 0, sizeof(fstack[i])); + cpus = cpu; + } + + for (i = 0; i < fstack[cpu].size && fstack[cpu].stack[i]; i++) { + if (strcmp(parent, fstack[cpu].stack[i]) == 0) { + add_child(&fstack[cpu], child, i+1); + return i; + } + } + + /* Not found */ + add_child(&fstack[cpu], parent, 0); + add_child(&fstack[cpu], child, 1); + return 0; +} + +static int function_handler(struct trace_seq *s, struct pevent_record *record, + struct event_format *event, void *context) +{ + struct pevent *pevent = event->pevent; + unsigned long long function; + unsigned long long pfunction; + const char *func; + const char *parent; + int i, index; + + if (pevent_get_field_val(s, event, "ip", record, &function, 1)) + return trace_seq_putc(s, '!'); + + func = pevent_find_function(pevent, function); + + if (pevent_get_field_val(s, event, "parent_ip", record, &pfunction, 1)) + return trace_seq_putc(s, '!'); + + parent = pevent_find_function(pevent, pfunction); + + index = get_index(parent, func, record->cpu); + + for (i = 0; i < index; i++) + trace_seq_printf(s, " "); + + if (func) + trace_seq_printf(s, "%s", func); + else + trace_seq_printf(s, "0x%llx", function); + + trace_seq_printf(s, " <-- "); + if (parent) + trace_seq_printf(s, "%s", parent); + else + trace_seq_printf(s, "0x%llx", pfunction); + + return 0; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_event_handler(pevent, -1, "ftrace", "function", + function_handler, NULL); + return 0; +} + +void PEVENT_PLUGIN_UNLOADER(void) +{ + int i, x; + + for (i = 0; i <= cpus; i++) { + for (x = 0; x < fstack[i].size && fstack[i].stack[x]; x++) + free(fstack[i].stack[x]); + free(fstack[i].stack); + } + + free(fstack); + fstack = NULL; + cpus = -1; +} -- cgit v1.2.3 From b8aad550bf029822a75e4375a1a1dd5f35385c36 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:33 +0100 Subject: tools lib traceevent: Add xen plugin Adding xen plugin. This plugin adds fields resolving for following tracepoint events: xen:xen_mc_entry xen:xen_mc_extend_args The diff of 'perf script' output generated by old and new code: (data was generated by 'perf record -e 'xen:*' ls') --- script.xen.old +++ script.xen.new - swapper 0 [002] 136.267492: xen:xen_mc_entry: [FAILED TO PARSE] op=3 nargs=2 args=ARRAY[18, 00, 00, 00, 00, 00, 00, 00, 00, e0, d4, 4b, 04, 88, ff, ff, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00] + swapper 0 [002] 136.267492: xen:xen_mc_entry: op 3(stack_switch) args [18, 0, 0, 0, 0, 0] - perf 1970 [008] 136.273319: xen:xen_mc_extend_args: [FAILED TO PARSE] op=1 args=16 res=1 + perf 1970 [008] 136.273319: xen:xen_mc_extend_args: extending op 1(mmu_update) by 16 bytes res ??? NOTE We still do not handle the 'sizeof' and fail to parse following xen tracepoints: xen:xen_mmu_set_pte xen:xen_mmu_set_pte_atomic xen:xen_mmu_set_domain_pte xen:xen_mmu_set_pte_at xen:xen_mmu_set_pmd xen:xen_mmu_set_pud xen:xen_mmu_set_pgd xen:xen_mmu_ptep_modify_prot_start xen:xen_mmu_ptep_modify_prot_commit Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jeremy Fitzhardinge Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-20-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 1 + plugin_xen.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 plugin_xen.c diff --git a/Makefile b/Makefile index 21f9b8f..54af60a 100644 --- a/Makefile +++ b/Makefile @@ -219,6 +219,7 @@ PLUGIN_OBJS += plugin_kvm.o PLUGIN_OBJS += plugin_mac80211.o PLUGIN_OBJS += plugin_sched_switch.o PLUGIN_OBJS += plugin_function.o +PLUGIN_OBJS += plugin_xen.o PLUGINS := $(PLUGIN_OBJS:.o=.so) diff --git a/plugin_xen.c b/plugin_xen.c new file mode 100644 index 0000000..e779429 --- /dev/null +++ b/plugin_xen.c @@ -0,0 +1,130 @@ +#include +#include +#include +#include "event-parse.h" + +#define __HYPERVISOR_set_trap_table 0 +#define __HYPERVISOR_mmu_update 1 +#define __HYPERVISOR_set_gdt 2 +#define __HYPERVISOR_stack_switch 3 +#define __HYPERVISOR_set_callbacks 4 +#define __HYPERVISOR_fpu_taskswitch 5 +#define __HYPERVISOR_sched_op_compat 6 +#define __HYPERVISOR_dom0_op 7 +#define __HYPERVISOR_set_debugreg 8 +#define __HYPERVISOR_get_debugreg 9 +#define __HYPERVISOR_update_descriptor 10 +#define __HYPERVISOR_memory_op 12 +#define __HYPERVISOR_multicall 13 +#define __HYPERVISOR_update_va_mapping 14 +#define __HYPERVISOR_set_timer_op 15 +#define __HYPERVISOR_event_channel_op_compat 16 +#define __HYPERVISOR_xen_version 17 +#define __HYPERVISOR_console_io 18 +#define __HYPERVISOR_physdev_op_compat 19 +#define __HYPERVISOR_grant_table_op 20 +#define __HYPERVISOR_vm_assist 21 +#define __HYPERVISOR_update_va_mapping_otherdomain 22 +#define __HYPERVISOR_iret 23 /* x86 only */ +#define __HYPERVISOR_vcpu_op 24 +#define __HYPERVISOR_set_segment_base 25 /* x86/64 only */ +#define __HYPERVISOR_mmuext_op 26 +#define __HYPERVISOR_acm_op 27 +#define __HYPERVISOR_nmi_op 28 +#define __HYPERVISOR_sched_op 29 +#define __HYPERVISOR_callback_op 30 +#define __HYPERVISOR_xenoprof_op 31 +#define __HYPERVISOR_event_channel_op 32 +#define __HYPERVISOR_physdev_op 33 +#define __HYPERVISOR_hvm_op 34 +#define __HYPERVISOR_tmem_op 38 + +/* Architecture-specific hypercall definitions. */ +#define __HYPERVISOR_arch_0 48 +#define __HYPERVISOR_arch_1 49 +#define __HYPERVISOR_arch_2 50 +#define __HYPERVISOR_arch_3 51 +#define __HYPERVISOR_arch_4 52 +#define __HYPERVISOR_arch_5 53 +#define __HYPERVISOR_arch_6 54 +#define __HYPERVISOR_arch_7 55 + +#define N(x) [__HYPERVISOR_##x] = "("#x")" +static const char *xen_hypercall_names[] = { + N(set_trap_table), + N(mmu_update), + N(set_gdt), + N(stack_switch), + N(set_callbacks), + N(fpu_taskswitch), + N(sched_op_compat), + N(dom0_op), + N(set_debugreg), + N(get_debugreg), + N(update_descriptor), + N(memory_op), + N(multicall), + N(update_va_mapping), + N(set_timer_op), + N(event_channel_op_compat), + N(xen_version), + N(console_io), + N(physdev_op_compat), + N(grant_table_op), + N(vm_assist), + N(update_va_mapping_otherdomain), + N(iret), + N(vcpu_op), + N(set_segment_base), + N(mmuext_op), + N(acm_op), + N(nmi_op), + N(sched_op), + N(callback_op), + N(xenoprof_op), + N(event_channel_op), + N(physdev_op), + N(hvm_op), + +/* Architecture-specific hypercall definitions. */ + N(arch_0), + N(arch_1), + N(arch_2), + N(arch_3), + N(arch_4), + N(arch_5), + N(arch_6), + N(arch_7), +}; +#undef N + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +static const char *xen_hypercall_name(unsigned op) +{ + if (op < ARRAY_SIZE(xen_hypercall_names) && + xen_hypercall_names[op] != NULL) + return xen_hypercall_names[op]; + + return ""; +} + +unsigned long long process_xen_hypercall_name(struct trace_seq *s, + unsigned long long *args) +{ + unsigned int op = args[0]; + + trace_seq_printf(s, "%s", xen_hypercall_name(op)); + return 0; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_print_function(pevent, + process_xen_hypercall_name, + PEVENT_FUNC_ARG_STRING, + "xen_hypercall_name", + PEVENT_FUNC_ARG_INT, + PEVENT_FUNC_ARG_VOID); + return 0; +} -- cgit v1.2.3 From 56ba3eaa22678dc1e54e45678cffad9ea4a7a2ad Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:34 +0100 Subject: tools lib traceevent: Add scsi plugin Adding scsi plugin. This plugin adds fields resolving functions for following tracepoint events: scsi:scsi_dispatch_cmd_start scsi:scsi_dispatch_cmd_error scsi:scsi_dispatch_cmd_done scsi:scsi_dispatch_cmd_timeout The diff of 'perf script' output generated by old and new code: (data was generated by 'perf record -e 'scsi:scsi_dispatch_cmd*' -a) - swapper 0 [000] 6620.491019: scsi:scsi_dispatch_cmd_done: [FAILED TO PARSE] host_no=0 channel=0 id=0 lun=0 result=0 opcode=53 cmd_len=10 data_sglen=0 prot_sglen=0 prot_op=0 cmnd=5 + swapper 0 [000] 6620.491019: scsi:scsi_dispatch_cmd_done: host_no=0 channel=0 id=0 lun=0 data_sgl=0 prot_sgl=0 prot_op=SCSI_PROT_NORMAL cmnd=(SYNCHRONIZE_CACHE - raw=35 00 00 00 00 00 00 00 00 00) result=(driver=DRIVER_OK host=DID_OK message=COMMAND_COMPLETE status=SAM_STAT_GOOD) - kworker/0:0 21554 [000] 6620.491126: scsi:scsi_dispatch_cmd_start: [FAILED TO PARSE] host_no=0 channel=0 id=0 lun=0 opcode=42 cmd_len=10 data_sglen=1 prot_sglen=0 prot_op=0 cmnd=* + kworker/0:0 21554 [000] 6620.491126: scsi:scsi_dispatch_cmd_start: host_no=0 channel=0 id=0 lun=0 data_sgl=1 prot_sgl=0 prot_op=SCSI_PROT_NORMAL cmnd=(WRITE_10 lba=570899168 txlen=8 protect=0 raw=2a 00 22 07 3a e0 00 00 08 00) - jbd2/dm-3-8 593 [002] 6621.607992: scsi:scsi_dispatch_cmd_error: [FAILED TO PARSE] host_no=0 channel=0 id=0 lun=0 rtn=4182 opcode=53 cmd_len=10 data_sglen=0 prot_sglen=0 prot_op=0 cmnd=5 + jbd2/dm-3-8 593 [002] 6621.607992: scsi:scsi_dispatch_cmd_error: host_no=0 channel=0 id=0 lun=0 data_sgl=0 prot_sgl=0 prot_op=SCSI_PROT_NORMAL cmnd=(SYNCHRONIZE_CACHE - raw=35 00 00 00 00 00 00 00 00 00) rtn=4182 NOTE I couldn't generate scsi_dispatch_cmd_timeout tracepoint, but it's similar to the rest, so I believe it's ok. Signed-off-by: Jiri Olsa Cc: "Martin K. Petersen" Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Martin K. Petersen Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-21-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 1 + plugin_scsi.c | 423 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 424 insertions(+) create mode 100644 plugin_scsi.c diff --git a/Makefile b/Makefile index 54af60a..671f969 100644 --- a/Makefile +++ b/Makefile @@ -220,6 +220,7 @@ PLUGIN_OBJS += plugin_mac80211.o PLUGIN_OBJS += plugin_sched_switch.o PLUGIN_OBJS += plugin_function.o PLUGIN_OBJS += plugin_xen.o +PLUGIN_OBJS += plugin_scsi.o PLUGINS := $(PLUGIN_OBJS:.o=.so) diff --git a/plugin_scsi.c b/plugin_scsi.c new file mode 100644 index 0000000..6fb8e3e --- /dev/null +++ b/plugin_scsi.c @@ -0,0 +1,423 @@ +#include +#include +#include +#include "event-parse.h" + +typedef unsigned long sector_t; +typedef uint64_t u64; +typedef unsigned int u32; + +/* + * SCSI opcodes + */ +#define TEST_UNIT_READY 0x00 +#define REZERO_UNIT 0x01 +#define REQUEST_SENSE 0x03 +#define FORMAT_UNIT 0x04 +#define READ_BLOCK_LIMITS 0x05 +#define REASSIGN_BLOCKS 0x07 +#define INITIALIZE_ELEMENT_STATUS 0x07 +#define READ_6 0x08 +#define WRITE_6 0x0a +#define SEEK_6 0x0b +#define READ_REVERSE 0x0f +#define WRITE_FILEMARKS 0x10 +#define SPACE 0x11 +#define INQUIRY 0x12 +#define RECOVER_BUFFERED_DATA 0x14 +#define MODE_SELECT 0x15 +#define RESERVE 0x16 +#define RELEASE 0x17 +#define COPY 0x18 +#define ERASE 0x19 +#define MODE_SENSE 0x1a +#define START_STOP 0x1b +#define RECEIVE_DIAGNOSTIC 0x1c +#define SEND_DIAGNOSTIC 0x1d +#define ALLOW_MEDIUM_REMOVAL 0x1e + +#define READ_FORMAT_CAPACITIES 0x23 +#define SET_WINDOW 0x24 +#define READ_CAPACITY 0x25 +#define READ_10 0x28 +#define WRITE_10 0x2a +#define SEEK_10 0x2b +#define POSITION_TO_ELEMENT 0x2b +#define WRITE_VERIFY 0x2e +#define VERIFY 0x2f +#define SEARCH_HIGH 0x30 +#define SEARCH_EQUAL 0x31 +#define SEARCH_LOW 0x32 +#define SET_LIMITS 0x33 +#define PRE_FETCH 0x34 +#define READ_POSITION 0x34 +#define SYNCHRONIZE_CACHE 0x35 +#define LOCK_UNLOCK_CACHE 0x36 +#define READ_DEFECT_DATA 0x37 +#define MEDIUM_SCAN 0x38 +#define COMPARE 0x39 +#define COPY_VERIFY 0x3a +#define WRITE_BUFFER 0x3b +#define READ_BUFFER 0x3c +#define UPDATE_BLOCK 0x3d +#define READ_LONG 0x3e +#define WRITE_LONG 0x3f +#define CHANGE_DEFINITION 0x40 +#define WRITE_SAME 0x41 +#define UNMAP 0x42 +#define READ_TOC 0x43 +#define READ_HEADER 0x44 +#define GET_EVENT_STATUS_NOTIFICATION 0x4a +#define LOG_SELECT 0x4c +#define LOG_SENSE 0x4d +#define XDWRITEREAD_10 0x53 +#define MODE_SELECT_10 0x55 +#define RESERVE_10 0x56 +#define RELEASE_10 0x57 +#define MODE_SENSE_10 0x5a +#define PERSISTENT_RESERVE_IN 0x5e +#define PERSISTENT_RESERVE_OUT 0x5f +#define VARIABLE_LENGTH_CMD 0x7f +#define REPORT_LUNS 0xa0 +#define SECURITY_PROTOCOL_IN 0xa2 +#define MAINTENANCE_IN 0xa3 +#define MAINTENANCE_OUT 0xa4 +#define MOVE_MEDIUM 0xa5 +#define EXCHANGE_MEDIUM 0xa6 +#define READ_12 0xa8 +#define WRITE_12 0xaa +#define READ_MEDIA_SERIAL_NUMBER 0xab +#define WRITE_VERIFY_12 0xae +#define VERIFY_12 0xaf +#define SEARCH_HIGH_12 0xb0 +#define SEARCH_EQUAL_12 0xb1 +#define SEARCH_LOW_12 0xb2 +#define SECURITY_PROTOCOL_OUT 0xb5 +#define READ_ELEMENT_STATUS 0xb8 +#define SEND_VOLUME_TAG 0xb6 +#define WRITE_LONG_2 0xea +#define EXTENDED_COPY 0x83 +#define RECEIVE_COPY_RESULTS 0x84 +#define ACCESS_CONTROL_IN 0x86 +#define ACCESS_CONTROL_OUT 0x87 +#define READ_16 0x88 +#define WRITE_16 0x8a +#define READ_ATTRIBUTE 0x8c +#define WRITE_ATTRIBUTE 0x8d +#define VERIFY_16 0x8f +#define SYNCHRONIZE_CACHE_16 0x91 +#define WRITE_SAME_16 0x93 +#define SERVICE_ACTION_IN 0x9e +/* values for service action in */ +#define SAI_READ_CAPACITY_16 0x10 +#define SAI_GET_LBA_STATUS 0x12 +/* values for VARIABLE_LENGTH_CMD service action codes + * see spc4r17 Section D.3.5, table D.7 and D.8 */ +#define VLC_SA_RECEIVE_CREDENTIAL 0x1800 +/* values for maintenance in */ +#define MI_REPORT_IDENTIFYING_INFORMATION 0x05 +#define MI_REPORT_TARGET_PGS 0x0a +#define MI_REPORT_ALIASES 0x0b +#define MI_REPORT_SUPPORTED_OPERATION_CODES 0x0c +#define MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS 0x0d +#define MI_REPORT_PRIORITY 0x0e +#define MI_REPORT_TIMESTAMP 0x0f +#define MI_MANAGEMENT_PROTOCOL_IN 0x10 +/* value for MI_REPORT_TARGET_PGS ext header */ +#define MI_EXT_HDR_PARAM_FMT 0x20 +/* values for maintenance out */ +#define MO_SET_IDENTIFYING_INFORMATION 0x06 +#define MO_SET_TARGET_PGS 0x0a +#define MO_CHANGE_ALIASES 0x0b +#define MO_SET_PRIORITY 0x0e +#define MO_SET_TIMESTAMP 0x0f +#define MO_MANAGEMENT_PROTOCOL_OUT 0x10 +/* values for variable length command */ +#define XDREAD_32 0x03 +#define XDWRITE_32 0x04 +#define XPWRITE_32 0x06 +#define XDWRITEREAD_32 0x07 +#define READ_32 0x09 +#define VERIFY_32 0x0a +#define WRITE_32 0x0b +#define WRITE_SAME_32 0x0d + +#define SERVICE_ACTION16(cdb) (cdb[1] & 0x1f) +#define SERVICE_ACTION32(cdb) ((cdb[8] << 8) | cdb[9]) + +static const char * +scsi_trace_misc(struct trace_seq *, unsigned char *, int); + +static const char * +scsi_trace_rw6(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + sector_t lba = 0, txlen = 0; + + lba |= ((cdb[1] & 0x1F) << 16); + lba |= (cdb[2] << 8); + lba |= cdb[3]; + txlen = cdb[4]; + + trace_seq_printf(p, "lba=%llu txlen=%llu", + (unsigned long long)lba, (unsigned long long)txlen); + trace_seq_putc(p, 0); + return ret; +} + +static const char * +scsi_trace_rw10(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + sector_t lba = 0, txlen = 0; + + lba |= (cdb[2] << 24); + lba |= (cdb[3] << 16); + lba |= (cdb[4] << 8); + lba |= cdb[5]; + txlen |= (cdb[7] << 8); + txlen |= cdb[8]; + + trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", + (unsigned long long)lba, (unsigned long long)txlen, + cdb[1] >> 5); + + if (cdb[0] == WRITE_SAME) + trace_seq_printf(p, " unmap=%u", cdb[1] >> 3 & 1); + + trace_seq_putc(p, 0); + return ret; +} + +static const char * +scsi_trace_rw12(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + sector_t lba = 0, txlen = 0; + + lba |= (cdb[2] << 24); + lba |= (cdb[3] << 16); + lba |= (cdb[4] << 8); + lba |= cdb[5]; + txlen |= (cdb[6] << 24); + txlen |= (cdb[7] << 16); + txlen |= (cdb[8] << 8); + txlen |= cdb[9]; + + trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", + (unsigned long long)lba, (unsigned long long)txlen, + cdb[1] >> 5); + trace_seq_putc(p, 0); + return ret; +} + +static const char * +scsi_trace_rw16(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + sector_t lba = 0, txlen = 0; + + lba |= ((u64)cdb[2] << 56); + lba |= ((u64)cdb[3] << 48); + lba |= ((u64)cdb[4] << 40); + lba |= ((u64)cdb[5] << 32); + lba |= (cdb[6] << 24); + lba |= (cdb[7] << 16); + lba |= (cdb[8] << 8); + lba |= cdb[9]; + txlen |= (cdb[10] << 24); + txlen |= (cdb[11] << 16); + txlen |= (cdb[12] << 8); + txlen |= cdb[13]; + + trace_seq_printf(p, "lba=%llu txlen=%llu protect=%u", + (unsigned long long)lba, (unsigned long long)txlen, + cdb[1] >> 5); + + if (cdb[0] == WRITE_SAME_16) + trace_seq_printf(p, " unmap=%u", cdb[1] >> 3 & 1); + + trace_seq_putc(p, 0); + return ret; +} + +static const char * +scsi_trace_rw32(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len, *cmd; + sector_t lba = 0, txlen = 0; + u32 ei_lbrt = 0; + + switch (SERVICE_ACTION32(cdb)) { + case READ_32: + cmd = "READ"; + break; + case VERIFY_32: + cmd = "VERIFY"; + break; + case WRITE_32: + cmd = "WRITE"; + break; + case WRITE_SAME_32: + cmd = "WRITE_SAME"; + break; + default: + trace_seq_printf(p, "UNKNOWN"); + goto out; + } + + lba |= ((u64)cdb[12] << 56); + lba |= ((u64)cdb[13] << 48); + lba |= ((u64)cdb[14] << 40); + lba |= ((u64)cdb[15] << 32); + lba |= (cdb[16] << 24); + lba |= (cdb[17] << 16); + lba |= (cdb[18] << 8); + lba |= cdb[19]; + ei_lbrt |= (cdb[20] << 24); + ei_lbrt |= (cdb[21] << 16); + ei_lbrt |= (cdb[22] << 8); + ei_lbrt |= cdb[23]; + txlen |= (cdb[28] << 24); + txlen |= (cdb[29] << 16); + txlen |= (cdb[30] << 8); + txlen |= cdb[31]; + + trace_seq_printf(p, "%s_32 lba=%llu txlen=%llu protect=%u ei_lbrt=%u", + cmd, (unsigned long long)lba, + (unsigned long long)txlen, cdb[10] >> 5, ei_lbrt); + + if (SERVICE_ACTION32(cdb) == WRITE_SAME_32) + trace_seq_printf(p, " unmap=%u", cdb[10] >> 3 & 1); + +out: + trace_seq_putc(p, 0); + return ret; +} + +static const char * +scsi_trace_unmap(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + unsigned int regions = cdb[7] << 8 | cdb[8]; + + trace_seq_printf(p, "regions=%u", (regions - 8) / 16); + trace_seq_putc(p, 0); + return ret; +} + +static const char * +scsi_trace_service_action_in(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len, *cmd; + sector_t lba = 0; + u32 alloc_len = 0; + + switch (SERVICE_ACTION16(cdb)) { + case SAI_READ_CAPACITY_16: + cmd = "READ_CAPACITY_16"; + break; + case SAI_GET_LBA_STATUS: + cmd = "GET_LBA_STATUS"; + break; + default: + trace_seq_printf(p, "UNKNOWN"); + goto out; + } + + lba |= ((u64)cdb[2] << 56); + lba |= ((u64)cdb[3] << 48); + lba |= ((u64)cdb[4] << 40); + lba |= ((u64)cdb[5] << 32); + lba |= (cdb[6] << 24); + lba |= (cdb[7] << 16); + lba |= (cdb[8] << 8); + lba |= cdb[9]; + alloc_len |= (cdb[10] << 24); + alloc_len |= (cdb[11] << 16); + alloc_len |= (cdb[12] << 8); + alloc_len |= cdb[13]; + + trace_seq_printf(p, "%s lba=%llu alloc_len=%u", cmd, + (unsigned long long)lba, alloc_len); + +out: + trace_seq_putc(p, 0); + return ret; +} + +static const char * +scsi_trace_varlen(struct trace_seq *p, unsigned char *cdb, int len) +{ + switch (SERVICE_ACTION32(cdb)) { + case READ_32: + case VERIFY_32: + case WRITE_32: + case WRITE_SAME_32: + return scsi_trace_rw32(p, cdb, len); + default: + return scsi_trace_misc(p, cdb, len); + } +} + +static const char * +scsi_trace_misc(struct trace_seq *p, unsigned char *cdb, int len) +{ + const char *ret = p->buffer + p->len; + + trace_seq_printf(p, "-"); + trace_seq_putc(p, 0); + return ret; +} + +const char * +scsi_trace_parse_cdb(struct trace_seq *p, unsigned char *cdb, int len) +{ + switch (cdb[0]) { + case READ_6: + case WRITE_6: + return scsi_trace_rw6(p, cdb, len); + case READ_10: + case VERIFY: + case WRITE_10: + case WRITE_SAME: + return scsi_trace_rw10(p, cdb, len); + case READ_12: + case VERIFY_12: + case WRITE_12: + return scsi_trace_rw12(p, cdb, len); + case READ_16: + case VERIFY_16: + case WRITE_16: + case WRITE_SAME_16: + return scsi_trace_rw16(p, cdb, len); + case UNMAP: + return scsi_trace_unmap(p, cdb, len); + case SERVICE_ACTION_IN: + return scsi_trace_service_action_in(p, cdb, len); + case VARIABLE_LENGTH_CMD: + return scsi_trace_varlen(p, cdb, len); + default: + return scsi_trace_misc(p, cdb, len); + } +} + +unsigned long long process_scsi_trace_parse_cdb(struct trace_seq *s, + unsigned long long *args) +{ + scsi_trace_parse_cdb(s, (unsigned char *) args[1], args[2]); + return 0; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_print_function(pevent, + process_scsi_trace_parse_cdb, + PEVENT_FUNC_ARG_STRING, + "scsi_trace_parse_cdb", + PEVENT_FUNC_ARG_PTR, + PEVENT_FUNC_ARG_PTR, + PEVENT_FUNC_ARG_INT, + PEVENT_FUNC_ARG_VOID); + return 0; +} -- cgit v1.2.3 From 3f13470fc9ae700463bed8ecdc03afbdfce0076e Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:35 +0100 Subject: tools lib traceevent: Add cfg80211 plugin Adding cfg80211 plugin. This plugin adds handler for __le16_to_cpup function t properly parse following tracepoint events: cfg80211:cfg80211_tx_mlme_mgmt cfg80211:cfg80211_rx_mlme_mgmt cfg80211:cfg80211_rx_unprot_mlme_mgmt The diff of 'perf script' output generated by old and new code: (data was generated by 'perf record -e 'cfg80211:*' -a') --- script.cfg80211.old +++ script.cfg80211.new - ifconfig 2705 [003] 662.896560: cfg80211:cfg80211_tx_mlme_mgmt: [FAILED TO PARSE] name=wlan0 ifindex=3 frame=ARRAY[c0, 00, 00, 00, 00, 3a, 98, a0, 30, 51, 10, 0b, a9, c6, f4, 74, 00, 3a, 98, a0, 30, 51, 00, 00, 03, 00] + ifconfig 2705 [003] 662.896560: cfg80211:cfg80211_tx_mlme_mgmt: netdev:wlan0(3), ftype:0xc0 - kworker/u16:0 1697 [002] 664.808210: cfg80211:cfg80211_rx_mlme_mgmt: [FAILED TO PARSE] name=wlan0 ifindex=3 frame=ARRAY[b0, 00, da, 00, 10, 0b, a9, c6, f4, 74, d8, 24, bd, a1, 26, 31, d8, 24, bd, a1, 26, 31, 10, b7, 00, 00, 02, 00, 00, 00] + kworker/u16:0 1697 [002] 664.808210: cfg80211:cfg80211_rx_mlme_mgmt: netdev:wlan0(3), ftype:0xb0 Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-22-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 1 + plugin_cfg80211.c | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 plugin_cfg80211.c diff --git a/Makefile b/Makefile index 671f969..0d9cbb4 100644 --- a/Makefile +++ b/Makefile @@ -221,6 +221,7 @@ PLUGIN_OBJS += plugin_sched_switch.o PLUGIN_OBJS += plugin_function.o PLUGIN_OBJS += plugin_xen.o PLUGIN_OBJS += plugin_scsi.o +PLUGIN_OBJS += plugin_cfg80211.o PLUGINS := $(PLUGIN_OBJS:.o=.so) diff --git a/plugin_cfg80211.c b/plugin_cfg80211.c new file mode 100644 index 0000000..dcab8e8 --- /dev/null +++ b/plugin_cfg80211.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include "event-parse.h" + +static unsigned long long +process___le16_to_cpup(struct trace_seq *s, + unsigned long long *args) +{ + uint16_t *val = (uint16_t *) args[0]; + return val ? (long long) le16toh(*val) : 0; +} + +int PEVENT_PLUGIN_LOADER(struct pevent *pevent) +{ + pevent_register_print_function(pevent, + process___le16_to_cpup, + PEVENT_FUNC_ARG_INT, + "__le16_to_cpup", + PEVENT_FUNC_ARG_PTR, + PEVENT_FUNC_ARG_VOID); + return 0; +} -- cgit v1.2.3 From 72c89ea4c35920268a68a6da86487afac73cf405 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:36 +0100 Subject: tools lib traceevent: Remove malloc_or_die from event-plugin.c Removing malloc_or_die calls from event-plugin.c, replacing them with standard malloc and error path. Suggested-by: Namhyung Kim Signed-off-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-23-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- event-plugin.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/event-plugin.c b/event-plugin.c index d272d87..125f567 100644 --- a/event-plugin.c +++ b/event-plugin.c @@ -47,7 +47,11 @@ load_plugin(struct pevent *pevent, const char *path, char *plugin; void *handle; - plugin = malloc_or_die(strlen(path) + strlen(file) + 2); + plugin = malloc(strlen(path) + strlen(file) + 2); + if (!plugin) { + warning("could not allocate plugin memory\n"); + return; + } strcpy(plugin, path); strcat(plugin, "/"); @@ -71,7 +75,12 @@ load_plugin(struct pevent *pevent, const char *path, goto out_free; } - list = malloc_or_die(sizeof(*list)); + list = malloc(sizeof(*list)); + if (!list) { + warning("could not allocate plugin memory\n"); + goto out_free; + } + list->next = *plugin_list; list->handle = handle; list->name = plugin; @@ -163,7 +172,11 @@ load_plugins(struct pevent *pevent, const char *suffix, if (!home) return; - path = malloc_or_die(strlen(home) + strlen(LOCAL_PLUGIN_DIR) + 2); + path = malloc(strlen(home) + strlen(LOCAL_PLUGIN_DIR) + 2); + if (!path) { + warning("could not allocate plugin memory\n"); + return; + } strcpy(path, home); strcat(path, "/"); -- cgit v1.2.3 From 360ed3dcd155a277faa5d5f7a877b69c12767b8e Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:37 +0100 Subject: tools lib traceevent: Use static functions in jbd2 plugin There's no need for following functions to be global: process_jbd2_dev_to_name process_jiffies_to_msecs Make them static. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-24-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- plugin_jbd2.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/plugin_jbd2.c b/plugin_jbd2.c index 5d85de7..2f93f81 100644 --- a/plugin_jbd2.c +++ b/plugin_jbd2.c @@ -29,8 +29,9 @@ #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) -unsigned long long process_jbd2_dev_to_name(struct trace_seq *s, - unsigned long long *args) +static unsigned long long +process_jbd2_dev_to_name(struct trace_seq *s, + unsigned long long *args) { unsigned int dev = args[0]; @@ -38,8 +39,9 @@ unsigned long long process_jbd2_dev_to_name(struct trace_seq *s, return 0; } -unsigned long long process_jiffies_to_msecs(struct trace_seq *s, - unsigned long long *args) +static unsigned long long +process_jiffies_to_msecs(struct trace_seq *s, + unsigned long long *args) { unsigned long long jiffies = args[0]; -- cgit v1.2.3 From d05a9f1cde38977bf92b2f9319863bbb14f2099d Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:38 +0100 Subject: tools lib traceevent: Use pevent_print_func_field in hrtimer_start handler The pevent_print_func_field function encompasses all the functionality used in the hrtimer_start handler. Change the handler to use this function. This also unifies the function field output with the hrtimer_expire_entry handler. Suggested-by: Steven Rostedt Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-25-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- plugin_hrtimer.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/plugin_hrtimer.c b/plugin_hrtimer.c index e41d4cf..0b0ebf3 100644 --- a/plugin_hrtimer.c +++ b/plugin_hrtimer.c @@ -48,10 +48,6 @@ static int timer_start_handler(struct trace_seq *s, struct pevent_record *record, struct event_format *event, void *context) { - struct pevent *pevent = event->pevent; - struct format_field *fn = pevent_find_field(event, "function"); - void *data = record->data; - trace_seq_printf(s, "hrtimer="); if (pevent_print_num_field(s, "0x%llx", event, "timer", @@ -59,19 +55,8 @@ static int timer_start_handler(struct trace_seq *s, pevent_print_num_field(s, "0x%llx", event, "hrtimer", record, 1); - if (!fn) { - trace_seq_printf(s, " function=MISSING"); - } else { - unsigned long long function; - const char *func; - - if (pevent_read_number_field(fn, data, &function)) - trace_seq_printf(s, " function=INVALID"); - - func = pevent_find_function(pevent, function); - - trace_seq_printf(s, " function=%s", func); - } + pevent_print_func_field(s, " function=%s", event, "function", + record, 0); trace_seq_printf(s, " expires="); pevent_print_num_field(s, "%llu", event, "expires", record, 1); -- cgit v1.2.3 From 0cc4ddba143bb475235ba527e9012b34ad5540a2 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:39 +0100 Subject: tools lib traceevent: Several cleanups for function plugin Several cleanups suggested by Namhyung: * Remove index field from struct func_stack as it's not needed. * Rename get_index into add_and_get_index. * Use '%*X' format string capability instead of the loop Suggested-by: Namhyung Kim Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-26-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- plugin_function.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/plugin_function.c b/plugin_function.c index 8deb22e..87acf9c 100644 --- a/plugin_function.c +++ b/plugin_function.c @@ -25,7 +25,6 @@ #include "event-utils.h" static struct func_stack { - int index; int size; char **stack; } *fstack; @@ -57,7 +56,7 @@ static void add_child(struct func_stack *stack, const char *child, int pos) stack->stack[pos] = strdup(child); } -static int get_index(const char *parent, const char *child, int cpu) +static int add_and_get_index(const char *parent, const char *child, int cpu) { int i; @@ -97,7 +96,7 @@ static int function_handler(struct trace_seq *s, struct pevent_record *record, unsigned long long pfunction; const char *func; const char *parent; - int i, index; + int index; if (pevent_get_field_val(s, event, "ip", record, &function, 1)) return trace_seq_putc(s, '!'); @@ -109,10 +108,9 @@ static int function_handler(struct trace_seq *s, struct pevent_record *record, parent = pevent_find_function(pevent, pfunction); - index = get_index(parent, func, record->cpu); + index = add_and_get_index(parent, func, record->cpu); - for (i = 0; i < index; i++) - trace_seq_printf(s, " "); + trace_seq_printf(s, "%*s", index*3, ""); if (func) trace_seq_printf(s, "%s", func); -- cgit v1.2.3 From c948e9b4f39196fad0dd631c8891228aec10b339 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:40 +0100 Subject: tools lib traceevent: Remove malloc_or_die from plugin_function.c Removing malloc_or_die calls from plugin_function.c, replacing them and factoring the code with standard realloc and error path. Suggested-by: Namhyung Kim Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-27-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- plugin_function.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/plugin_function.c b/plugin_function.c index 87acf9c..aad92ad 100644 --- a/plugin_function.c +++ b/plugin_function.c @@ -43,11 +43,17 @@ static void add_child(struct func_stack *stack, const char *child, int pos) if (pos < stack->size) free(stack->stack[pos]); else { - if (!stack->stack) - stack->stack = malloc_or_die(sizeof(char *) * STK_BLK); - else - stack->stack = realloc(stack->stack, sizeof(char *) * - (stack->size + STK_BLK)); + char **ptr; + + ptr = realloc(stack->stack, sizeof(char *) * + (stack->size + STK_BLK)); + if (!ptr) { + warning("could not allocate plugin memory\n"); + return; + } + + stack->stack = ptr; + for (i = stack->size; i < stack->size + STK_BLK; i++) stack->stack[i] = NULL; stack->size += STK_BLK; @@ -64,10 +70,15 @@ static int add_and_get_index(const char *parent, const char *child, int cpu) return 0; if (cpu > cpus) { - if (fstack) - fstack = realloc(fstack, sizeof(*fstack) * (cpu + 1)); - else - fstack = malloc_or_die(sizeof(*fstack) * (cpu + 1)); + struct func_stack *ptr; + + ptr = realloc(fstack, sizeof(*fstack) * (cpu + 1)); + if (!ptr) { + warning("could not allocate plugin memory\n"); + return 0; + } + + fstack = ptr; /* Account for holes in the cpu count */ for (i = cpus + 1; i <= cpu; i++) -- cgit v1.2.3 From 81c4e9b8b5d000203e3050376a1641b172f28052 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Tue, 3 Dec 2013 14:09:41 +0100 Subject: tools lib traceevent: Update kvm plugin with is_writable_pte helper Adding is_writable_pte print helper function, so the kvmmmu:fast_page_fault print format gets resolved properly. The diff of 'perf script' output generated by old and new code: (data was generated by 'perf record -e 'kvm:*,kvmmmu:*' -a') --- script.kvm.old +++ script.kvm.new - qemu-system-x86 3290 [002] 10708.755312: kvmmmu:fast_page_fault: [FAILED TO PARSE] vcpu_id=0 gva=4094486080 error_code=3 sptep=0xffff88019f1e3670 old_spte=336391285 new_spte=336391287 retry=1 + qemu-system-x86 3290 [002] 10708.755312: kvmmmu:fast_page_fault: vcpu 0 gva f40ce640 error_code P|W sptep 0xffff88019f1e3670 old 0x140cec75 new 140cec77 spurious 0 fixed 1 Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386076182-14484-28-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- plugin_kvm.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/plugin_kvm.c b/plugin_kvm.c index be9d9c6..a0e282c 100644 --- a/plugin_kvm.c +++ b/plugin_kvm.c @@ -389,6 +389,16 @@ static int kvm_mmu_get_page_handler(struct trace_seq *s, return kvm_mmu_print_role(s, record, event, context); } +#define PT_WRITABLE_SHIFT 1 +#define PT_WRITABLE_MASK (1ULL << PT_WRITABLE_SHIFT) + +static unsigned long long +process_is_writable_pte(struct trace_seq *s, unsigned long long *args) +{ + unsigned long pte = args[0]; + return pte & PT_WRITABLE_MASK; +} + int PEVENT_PLUGIN_LOADER(struct pevent *pevent) { init_disassembler(); @@ -415,5 +425,12 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent) pevent_register_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_prepare_zap_page", kvm_mmu_print_role, NULL); + + pevent_register_print_function(pevent, + process_is_writable_pte, + PEVENT_FUNC_ARG_INT, + "is_writable_pte", + PEVENT_FUNC_ARG_LONG, + PEVENT_FUNC_ARG_VOID); return 0; } -- cgit v1.2.3 From 1b2fca7a59e1344d9770c115aa3b82229071055d Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 18 Nov 2013 21:38:20 -0500 Subject: tools lib traceevent: Report better error message on bad function args When Jiri Olsa was writing a function callback for scsi_trace_parse_cdb(), he thought that the traceevent library had a bug in it because he was getting this error: Error: expected ')' but read ',' Error: expected ')' but read ',' Error: expected ')' but read ',' Error: expected ')' but read ',' But in truth, he didn't have the write number of arguments for the function callback, and the error was the library detecting the discrepancy. A better error message would have prevented the confusion: Error: function 'scsi_trace_parse_cdb()' only expects 2 arguments but event scsi_dispatch_cmd_timeout has more Error: function 'scsi_trace_parse_cdb()' only expects 2 arguments but event scsi_dispatch_cmd_start has more Error: function 'scsi_trace_parse_cdb()' only expects 2 arguments but event scsi_dispatch_cmd_error has more Error: function 'scsi_trace_parse_cdb()' only expects 2 arguments but event scsi_dispatch_cmd_done has more Or Error: function 'scsi_trace_parse_cdb()' expects 4 arguments but event scsi_dispatch_cmd_timeout only uses 3 Error: function 'scsi_trace_parse_cdb()' expects 4 arguments but event scsi_dispatch_cmd_start only uses 3 Error: function 'scsi_trace_parse_cdb()' expects 4 arguments but event scsi_dispatch_cmd_error only uses 3 Error: function 'scsi_trace_parse_cdb()' expects 4 arguments but event scsi_dispatch_cmd_done only uses 3 Signed-off-by: Steven Rostedt Link: http://lkml.kernel.org/n/tip-a4c34w62vl0diitvxb7bt3er@git.kernel.org Signed-off-by: Jiri Olsa Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/event-parse.c b/event-parse.c index 9849873..22566c2 100644 --- a/event-parse.c +++ b/event-parse.c @@ -2710,7 +2710,6 @@ process_func_handler(struct event_format *event, struct pevent_function_handler struct print_arg *farg; enum event_type type; char *token; - const char *test; int i; arg->type = PRINT_FUNC; @@ -2727,15 +2726,19 @@ process_func_handler(struct event_format *event, struct pevent_function_handler } type = process_arg(event, farg, &token); - if (i < (func->nr_args - 1)) - test = ","; - else - test = ")"; - - if (test_type_token(type, token, EVENT_DELIM, test)) { - free_arg(farg); - free_token(token); - return EVENT_ERROR; + if (i < (func->nr_args - 1)) { + if (type != EVENT_DELIM || strcmp(token, ",") != 0) { + warning("Error: function '%s()' expects %d arguments but event %s only uses %d", + func->name, func->nr_args, + event->name, i + 1); + goto err; + } + } else { + if (type != EVENT_DELIM || strcmp(token, ")") != 0) { + warning("Error: function '%s()' only expects %d arguments but event %s has more", + func->name, func->nr_args, event->name); + goto err; + } } *next_arg = farg; @@ -2747,6 +2750,11 @@ process_func_handler(struct event_format *event, struct pevent_function_handler *tok = token; return type; + +err: + free_arg(farg); + free_token(token); + return EVENT_ERROR; } static enum event_type -- cgit v1.2.3 From 42926ea16c907f19ec92ee4ec3bd98d8ba038a14 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Dec 2013 14:34:00 +0900 Subject: tools lib traceevent: Get rid of malloc_or_die() in pevent_filter_alloc() It returns NULL when allocation fails so the users should check the return value from now on. Signed-off-by: Namhyung Kim Reviewed-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386567251-22751-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- parse-filter.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/parse-filter.c b/parse-filter.c index 2500e75..b3a61d4 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -182,7 +182,10 @@ struct event_filter *pevent_filter_alloc(struct pevent *pevent) { struct event_filter *filter; - filter = malloc_or_die(sizeof(*filter)); + filter = malloc(sizeof(*filter)); + if (filter == NULL) + return NULL; + memset(filter, 0, sizeof(*filter)); filter->pevent = pevent; pevent_ref(pevent); -- cgit v1.2.3 From 4422a087882981ac8b7024b097b5c5ec80d6d360 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Dec 2013 14:34:04 +0900 Subject: tools lib traceevent: Get rid of malloc_or_die() in add_event() Make it return error value since its only caller find_event() now can handle allocation error properly. Signed-off-by: Namhyung Kim Reviewed-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386567251-22751-8-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- parse-filter.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/parse-filter.c b/parse-filter.c index b3a61d4..2b73abf 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -245,15 +245,19 @@ static void free_arg(struct filter_arg *arg) free(arg); } -static void add_event(struct event_list **events, +static int add_event(struct event_list **events, struct event_format *event) { struct event_list *list; - list = malloc_or_die(sizeof(*list)); + list = malloc(sizeof(*list)); + if (list == NULL) + return -1; + list->next = *events; *events = list; list->event = event; + return 0; } static int event_match(struct event_format *event, @@ -276,6 +280,7 @@ find_event(struct pevent *pevent, struct event_list **events, regex_t ereg; regex_t sreg; int match = 0; + int fail = 0; char *reg; int ret; int i; @@ -310,7 +315,10 @@ find_event(struct pevent *pevent, struct event_list **events, event = pevent->events[i]; if (event_match(event, sys_name ? &sreg : NULL, &ereg)) { match = 1; - add_event(events, event); + if (add_event(events, event) < 0) { + fail = 1; + break; + } } } @@ -320,6 +328,8 @@ find_event(struct pevent *pevent, struct event_list **events, if (!match) return -1; + if (fail) + return -2; return 0; } -- cgit v1.2.3 From 303798929e1e55c03b21c5d10577bf82a93c6f07 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Dec 2013 14:34:05 +0900 Subject: tools lib traceevent: Get rid of die() in create_arg_item() Signed-off-by: Namhyung Kim Reviewed-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386567251-22751-9-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- parse-filter.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/parse-filter.c b/parse-filter.c index 2b73abf..53e48eb 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -362,8 +362,11 @@ create_arg_item(struct event_format *event, const char *token, arg->value.type = type == EVENT_DQUOTE ? FILTER_STRING : FILTER_CHAR; arg->value.str = strdup(token); - if (!arg->value.str) - die("malloc string"); + if (!arg->value.str) { + free_arg(arg); + show_error(error_str, "failed to allocate string filter arg"); + return NULL; + } break; case EVENT_ITEM: /* if it is a number, then convert it */ -- cgit v1.2.3 From 045f55dc62ee3931afe8fbb4519e713b2e7bae27 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Dec 2013 14:34:08 +0900 Subject: tools lib traceevent: Get rid of malloc_or_die() in pevent_filter_add_filter_str() Signed-off-by: Namhyung Kim Reviewed-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386567251-22751-12-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- parse-filter.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/parse-filter.c b/parse-filter.c index 53e48eb..a4d5bb2 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -1226,7 +1226,13 @@ int pevent_filter_add_filter_str(struct event_filter *filter, else len = strlen(filter_str); - this_event = malloc_or_die(len + 1); + this_event = malloc(len + 1); + if (this_event == NULL) { + show_error(error_str, "Memory allocation failure"); + /* This can only happen when events is NULL, but still */ + free_events(events); + return -1; + } memcpy(this_event, filter_str, len); this_event[len] = 0; -- cgit v1.2.3 From e108b0d61c5e13ae0600137fd32a525204dcf057 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Dec 2013 14:34:09 +0900 Subject: tools lib traceevent: Get rid of die() in pevent_filter_clear_trivial() Change the function signature to return error code and not call die() anymore. Signed-off-by: Namhyung Kim Reviewed-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386567251-22751-13-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.h | 2 +- parse-filter.c | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/event-parse.h b/event-parse.h index 620c27a..6e23f19 100644 --- a/event-parse.h +++ b/event-parse.h @@ -860,7 +860,7 @@ int pevent_event_filtered(struct event_filter *filter, void pevent_filter_reset(struct event_filter *filter); -void pevent_filter_clear_trivial(struct event_filter *filter, +int pevent_filter_clear_trivial(struct event_filter *filter, enum filter_trivial_type type); void pevent_filter_free(struct event_filter *filter); diff --git a/parse-filter.c b/parse-filter.c index a4d5bb2..ab402fb 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -1504,8 +1504,10 @@ int pevent_update_trivial(struct event_filter *dest, struct event_filter *source * @type: remove only true, false, or both * * Removes filters that only contain a TRUE or FALES boolean arg. + * + * Returns 0 on success and -1 if there was a problem. */ -void pevent_filter_clear_trivial(struct event_filter *filter, +int pevent_filter_clear_trivial(struct event_filter *filter, enum filter_trivial_type type) { struct filter_type *filter_type; @@ -1514,13 +1516,15 @@ void pevent_filter_clear_trivial(struct event_filter *filter, int i; if (!filter->filters) - return; + return 0; /* * Two steps, first get all ids with trivial filters. * then remove those ids. */ for (i = 0; i < filter->filters; i++) { + int *new_ids; + filter_type = &filter->event_filters[i]; if (filter_type->filter->type != FILTER_ARG_BOOLEAN) continue; @@ -1535,19 +1539,24 @@ void pevent_filter_clear_trivial(struct event_filter *filter, break; } - ids = realloc(ids, sizeof(*ids) * (count + 1)); - if (!ids) - die("Can't allocate ids"); + new_ids = realloc(ids, sizeof(*ids) * (count + 1)); + if (!new_ids) { + free(ids); + return -1; + } + + ids = new_ids; ids[count++] = filter_type->event_id; } if (!count) - return; + return 0; for (i = 0; i < count; i++) pevent_filter_remove_event(filter, ids[i]); free(ids); + return 0; } /** -- cgit v1.2.3 From b17a1e3d6ff0c14e4ca14bab837a47b4e57c4861 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 12 Dec 2013 16:36:04 +0900 Subject: tools lib traceevent: Get rid of malloc_or_die() in show_error() Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386833777-3790-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- parse-filter.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/parse-filter.c b/parse-filter.c index ab402fb..d4b0bac 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -56,7 +56,21 @@ static void show_error(char **error_str, const char *fmt, ...) index = pevent_get_input_buf_ptr(); len = input ? strlen(input) : 0; - error = malloc_or_die(MAX_ERR_STR_SIZE + (len*2) + 3); + error = malloc(MAX_ERR_STR_SIZE + (len*2) + 3); + if (error == NULL) { + /* + * Maybe it's due to len is too long. + * Retry without the input buffer part. + */ + len = 0; + + error = malloc(MAX_ERR_STR_SIZE); + if (error == NULL) { + /* no memory */ + *error_str = NULL; + return; + } + } if (len) { strcpy(error, input); -- cgit v1.2.3 From 88ce4af7642ebdcdf18f26238e13f91bd0a52a3f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 12 Dec 2013 16:36:05 +0900 Subject: tools lib traceevent: Get rid of die in add_filter_type() The realloc() should check return value and not to overwrite previous pointer in case of error. Signed-off-by: Namhyung Kim Reviewed-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386833777-3790-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- parse-filter.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/parse-filter.c b/parse-filter.c index d4b0bac..767de4f 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -161,11 +161,13 @@ add_filter_type(struct event_filter *filter, int id) if (filter_type) return filter_type; - filter->event_filters = realloc(filter->event_filters, - sizeof(*filter->event_filters) * - (filter->filters + 1)); - if (!filter->event_filters) - die("Could not allocate filter"); + filter_type = realloc(filter->event_filters, + sizeof(*filter->event_filters) * + (filter->filters + 1)); + if (!filter_type) + return NULL; + + filter->event_filters = filter_type; for (i = 0; i < filter->filters; i++) { if (filter->event_filters[i].event_id > id) @@ -1180,6 +1182,12 @@ static int filter_event(struct event_filter *filter, } filter_type = add_filter_type(filter, event->id); + if (filter_type == NULL) { + show_error(error_str, "failed to add a new filter: %s", + filter_str ? filter_str : "true"); + return -1; + } + if (filter_type->filter) free_arg(filter_type->filter); filter_type->filter = arg; @@ -1417,6 +1425,9 @@ static int copy_filter_type(struct event_filter *filter, arg->boolean.value = 0; filter_type = add_filter_type(filter, event->id); + if (filter_type == NULL) + return -1; + filter_type->filter = arg; free(str); -- cgit v1.2.3 From 888d6e2e5bc41402d39f8c18b65e6af3de7cb254 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 12 Dec 2013 16:36:06 +0900 Subject: tools lib traceevent: Get rid of malloc_or_die() allocate_arg() Also check return value and handle it. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386833777-3790-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- parse-filter.c | 48 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/parse-filter.c b/parse-filter.c index 767de4f..ab9cefe 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -211,12 +211,7 @@ struct event_filter *pevent_filter_alloc(struct pevent *pevent) static struct filter_arg *allocate_arg(void) { - struct filter_arg *arg; - - arg = malloc_or_die(sizeof(*arg)); - memset(arg, 0, sizeof(*arg)); - - return arg; + return calloc(1, sizeof(struct filter_arg)); } static void free_arg(struct filter_arg *arg) @@ -369,6 +364,10 @@ create_arg_item(struct event_format *event, const char *token, struct filter_arg *arg; arg = allocate_arg(); + if (arg == NULL) { + show_error(error_str, "failed to allocate filter arg"); + return NULL; + } switch (type) { @@ -422,6 +421,9 @@ create_arg_op(enum filter_op_type btype) struct filter_arg *arg; arg = allocate_arg(); + if (!arg) + return NULL; + arg->type = FILTER_ARG_OP; arg->op.type = btype; @@ -434,6 +436,9 @@ create_arg_exp(enum filter_exp_type etype) struct filter_arg *arg; arg = allocate_arg(); + if (!arg) + return NULL; + arg->type = FILTER_ARG_EXP; arg->op.type = etype; @@ -446,6 +451,9 @@ create_arg_cmp(enum filter_exp_type etype) struct filter_arg *arg; arg = allocate_arg(); + if (!arg) + return NULL; + /* Use NUM and change if necessary */ arg->type = FILTER_ARG_NUM; arg->op.type = etype; @@ -909,8 +917,10 @@ static struct filter_arg *collapse_tree(struct filter_arg *arg) case FILTER_VAL_FALSE: free_arg(arg); arg = allocate_arg(); - arg->type = FILTER_ARG_BOOLEAN; - arg->boolean.value = ret == FILTER_VAL_TRUE; + if (arg) { + arg->type = FILTER_ARG_BOOLEAN; + arg->boolean.value = ret == FILTER_VAL_TRUE; + } } return arg; @@ -1057,6 +1067,8 @@ process_filter(struct event_format *event, struct filter_arg **parg, switch (op_type) { case OP_BOOL: arg = create_arg_op(btype); + if (arg == NULL) + goto fail_alloc; if (current_op) ret = add_left(arg, current_op); else @@ -1067,6 +1079,8 @@ process_filter(struct event_format *event, struct filter_arg **parg, case OP_NOT: arg = create_arg_op(btype); + if (arg == NULL) + goto fail_alloc; if (current_op) ret = add_right(current_op, arg, error_str); if (ret < 0) @@ -1086,6 +1100,8 @@ process_filter(struct event_format *event, struct filter_arg **parg, arg = create_arg_exp(etype); else arg = create_arg_cmp(ctype); + if (arg == NULL) + goto fail_alloc; if (current_op) ret = add_right(current_op, arg, error_str); @@ -1119,11 +1135,16 @@ process_filter(struct event_format *event, struct filter_arg **parg, current_op = current_exp; current_op = collapse_tree(current_op); + if (current_op == NULL) + goto fail_alloc; *parg = current_op; return 0; + fail_alloc: + show_error(error_str, "failed to allocate filter arg"); + goto fail; fail_print: show_error(error_str, "Syntax error"); fail: @@ -1154,6 +1175,10 @@ process_event(struct event_format *event, const char *filter_str, /* If parg is NULL, then make it into FALSE */ if (!*parg) { *parg = allocate_arg(); + if (*parg == NULL) { + show_error(error_str, "failed to allocate filter arg"); + return -1; + } (*parg)->type = FILTER_ARG_BOOLEAN; (*parg)->boolean.value = FILTER_FALSE; } @@ -1177,6 +1202,10 @@ static int filter_event(struct event_filter *filter, } else { /* just add a TRUE arg */ arg = allocate_arg(); + if (arg == NULL) { + show_error(error_str, "failed to allocate filter arg"); + return -1; + } arg->type = FILTER_ARG_BOOLEAN; arg->boolean.value = FILTER_TRUE; } @@ -1418,6 +1447,9 @@ static int copy_filter_type(struct event_filter *filter, if (strcmp(str, "TRUE") == 0 || strcmp(str, "FALSE") == 0) { /* Add trivial event */ arg = allocate_arg(); + if (arg == NULL) + return -1; + arg->type = FILTER_ARG_BOOLEAN; if (strcmp(str, "TRUE") == 0) arg->boolean.value = 1; -- cgit v1.2.3 From 61f24d3e6ca274292170bcf74246eb1057ce09e2 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 12 Dec 2013 16:36:07 +0900 Subject: tools lib traceevent: Get rid of malloc_or_die() in read_token() Signed-off-by: Namhyung Kim Reviewed-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386833777-3790-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- parse-filter.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/parse-filter.c b/parse-filter.c index ab9cefe..246ee81 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -109,7 +109,11 @@ static enum event_type read_token(char **tok) (strcmp(token, "=") == 0 || strcmp(token, "!") == 0) && pevent_peek_char() == '~') { /* append it */ - *tok = malloc_or_die(3); + *tok = malloc(3); + if (*tok == NULL) { + free_token(token); + return EVENT_ERROR; + } sprintf(*tok, "%c%c", *token, '~'); free_token(token); /* Now remove the '~' from the buffer */ @@ -1123,6 +1127,8 @@ process_filter(struct event_format *event, struct filter_arg **parg, break; case EVENT_NONE: break; + case EVENT_ERROR: + goto fail_alloc; default: goto fail_print; } -- cgit v1.2.3 From 7760108bda50130b6d72138328383fecdb8a7444 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 12 Dec 2013 16:36:08 +0900 Subject: tools lib traceevent: Get rid of malloc_or_die() in find_event() Make it return pevent_errno to distinguish malloc allocation failure. Since it'll be returned to user later, add more error code. Signed-off-by: Namhyung Kim Reviewed-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386833777-3790-6-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.h | 4 +++- parse-filter.c | 27 +++++++++++++++++++-------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/event-parse.h b/event-parse.h index 6e23f19..abdfd3c 100644 --- a/event-parse.h +++ b/event-parse.h @@ -356,7 +356,9 @@ enum pevent_flag { _PE(READ_FORMAT_FAILED, "failed to read event format"), \ _PE(READ_PRINT_FAILED, "failed to read event print fmt"), \ _PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace"),\ - _PE(INVALID_ARG_TYPE, "invalid argument type") + _PE(INVALID_ARG_TYPE, "invalid argument type"), \ + _PE(INVALID_EVENT_NAME, "invalid event name"), \ + _PE(EVENT_NOT_FOUND, "No event found") #undef _PE #define _PE(__code, __str) PEVENT_ERRNO__ ## __code diff --git a/parse-filter.c b/parse-filter.c index 246ee81..a0ab040 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -287,7 +287,7 @@ static int event_match(struct event_format *event, !regexec(ereg, event->name, 0, NULL, 0); } -static int +static enum pevent_errno find_event(struct pevent *pevent, struct event_list **events, char *sys_name, char *event_name) { @@ -306,23 +306,31 @@ find_event(struct pevent *pevent, struct event_list **events, sys_name = NULL; } - reg = malloc_or_die(strlen(event_name) + 3); + reg = malloc(strlen(event_name) + 3); + if (reg == NULL) + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + sprintf(reg, "^%s$", event_name); ret = regcomp(&ereg, reg, REG_ICASE|REG_NOSUB); free(reg); if (ret) - return -1; + return PEVENT_ERRNO__INVALID_EVENT_NAME; if (sys_name) { - reg = malloc_or_die(strlen(sys_name) + 3); + reg = malloc(strlen(sys_name) + 3); + if (reg == NULL) { + regfree(&ereg); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } + sprintf(reg, "^%s$", sys_name); ret = regcomp(&sreg, reg, REG_ICASE|REG_NOSUB); free(reg); if (ret) { regfree(&ereg); - return -1; + return PEVENT_ERRNO__INVALID_EVENT_NAME; } } @@ -342,9 +350,9 @@ find_event(struct pevent *pevent, struct event_list **events, regfree(&sreg); if (!match) - return -1; + return PEVENT_ERRNO__EVENT_NOT_FOUND; if (fail) - return -2; + return PEVENT_ERRNO__MEM_ALLOC_FAILED; return 0; } @@ -1312,7 +1320,10 @@ int pevent_filter_add_filter_str(struct event_filter *filter, /* Find this event */ ret = find_event(pevent, &events, strim(sys_name), strim(event_name)); if (ret < 0) { - if (event_name) + if (ret == PEVENT_ERRNO__MEM_ALLOC_FAILED) + show_error(error_str, + "Memory allocation failure"); + else if (event_name) show_error(error_str, "No event found under '%s.%s'", sys_name, event_name); -- cgit v1.2.3 From 6f2b58e7be3394f2f39691bef847dec147d7edc2 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 12 Dec 2013 16:36:09 +0900 Subject: tools lib traceevent: Get rid of die() in add_right() Refactor it to return appropriate pevent_errno value. Signed-off-by: Namhyung Kim Reviewed-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386833777-3790-7-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.h | 8 +++++++- parse-filter.c | 34 +++++++++++++++++++--------------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/event-parse.h b/event-parse.h index abdfd3c..89e4dfd 100644 --- a/event-parse.h +++ b/event-parse.h @@ -358,7 +358,13 @@ enum pevent_flag { _PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace"),\ _PE(INVALID_ARG_TYPE, "invalid argument type"), \ _PE(INVALID_EVENT_NAME, "invalid event name"), \ - _PE(EVENT_NOT_FOUND, "No event found") + _PE(EVENT_NOT_FOUND, "no event found"), \ + _PE(SYNTAX_ERROR, "syntax error"), \ + _PE(ILLEGAL_RVALUE, "illegal rvalue"), \ + _PE(ILLEGAL_LVALUE, "illegal lvalue for string comparison"), \ + _PE(INVALID_REGEX, "regex did not compute"), \ + _PE(ILLEGAL_STRING_CMP, "illegal comparison for string"), \ + _PE(ILLEGAL_INTEGER_CMP,"illegal comparison for integer") #undef _PE #define _PE(__code, __str) PEVENT_ERRNO__ ## __code diff --git a/parse-filter.c b/parse-filter.c index a0ab040..c08ce59 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -473,8 +473,8 @@ create_arg_cmp(enum filter_exp_type etype) return arg; } -static int add_right(struct filter_arg *op, struct filter_arg *arg, - char **error_str) +static enum pevent_errno +add_right(struct filter_arg *op, struct filter_arg *arg, char **error_str) { struct filter_arg *left; char *str; @@ -505,9 +505,8 @@ static int add_right(struct filter_arg *op, struct filter_arg *arg, case FILTER_ARG_FIELD: break; default: - show_error(error_str, - "Illegal rvalue"); - return -1; + show_error(error_str, "Illegal rvalue"); + return PEVENT_ERRNO__ILLEGAL_RVALUE; } /* @@ -554,7 +553,7 @@ static int add_right(struct filter_arg *op, struct filter_arg *arg, if (left->type != FILTER_ARG_FIELD) { show_error(error_str, "Illegal lvalue for string comparison"); - return -1; + return PEVENT_ERRNO__ILLEGAL_LVALUE; } /* Make sure this is a valid string compare */ @@ -573,25 +572,31 @@ static int add_right(struct filter_arg *op, struct filter_arg *arg, show_error(error_str, "RegEx '%s' did not compute", str); - return -1; + return PEVENT_ERRNO__INVALID_REGEX; } break; default: show_error(error_str, "Illegal comparison for string"); - return -1; + return PEVENT_ERRNO__ILLEGAL_STRING_CMP; } op->type = FILTER_ARG_STR; op->str.type = op_type; op->str.field = left->field.field; op->str.val = strdup(str); - if (!op->str.val) - die("malloc string"); + if (!op->str.val) { + show_error(error_str, "Failed to allocate string filter"); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } /* * Need a buffer to copy data for tests */ - op->str.buffer = malloc_or_die(op->str.field->size + 1); + op->str.buffer = malloc(op->str.field->size + 1); + if (!op->str.buffer) { + show_error(error_str, "Failed to allocate string filter"); + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + } /* Null terminate this buffer */ op->str.buffer[op->str.field->size] = 0; @@ -609,7 +614,7 @@ static int add_right(struct filter_arg *op, struct filter_arg *arg, case FILTER_CMP_NOT_REGEX: show_error(error_str, "Op not allowed with integers"); - return -1; + return PEVENT_ERRNO__ILLEGAL_INTEGER_CMP; default: break; @@ -629,9 +634,8 @@ static int add_right(struct filter_arg *op, struct filter_arg *arg, return 0; out_fail: - show_error(error_str, - "Syntax error"); - return -1; + show_error(error_str, "Syntax error"); + return PEVENT_ERRNO__SYNTAX_ERROR; } static struct filter_arg * -- cgit v1.2.3 From 682a8b67269fb934ef0a105ee1b605b1db6447da Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 12 Dec 2013 16:36:10 +0900 Subject: tools lib traceevent: Make add_left() return pevent_errno So that it can propagate error properly. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386833777-3790-8-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- parse-filter.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/parse-filter.c b/parse-filter.c index c08ce59..774c3e4 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -648,7 +648,7 @@ rotate_op_right(struct filter_arg *a, struct filter_arg *b) return arg; } -static int add_left(struct filter_arg *op, struct filter_arg *arg) +static enum pevent_errno add_left(struct filter_arg *op, struct filter_arg *arg) { switch (op->type) { case FILTER_ARG_EXP: @@ -667,11 +667,11 @@ static int add_left(struct filter_arg *op, struct filter_arg *arg) /* left arg of compares must be a field */ if (arg->type != FILTER_ARG_FIELD && arg->type != FILTER_ARG_BOOLEAN) - return -1; + return PEVENT_ERRNO__INVALID_ARG_TYPE; op->num.left = arg; break; default: - return -1; + return PEVENT_ERRNO__INVALID_ARG_TYPE; } return 0; } -- cgit v1.2.3 From 0992c6b5c2d2ee0f0f462feeb23a59fc8cce487e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 12 Dec 2013 16:36:11 +0900 Subject: tools lib traceevent: Get rid of die() in reparent_op_arg() To do that, make the function returns the error code. Also pass error_str so that it can set proper error message when error occurred. Signed-off-by: Namhyung Kim Reviewed-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386833777-3790-9-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.h | 5 +++- parse-filter.c | 94 +++++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/event-parse.h b/event-parse.h index 89e4dfd..5e4392d 100644 --- a/event-parse.h +++ b/event-parse.h @@ -364,7 +364,10 @@ enum pevent_flag { _PE(ILLEGAL_LVALUE, "illegal lvalue for string comparison"), \ _PE(INVALID_REGEX, "regex did not compute"), \ _PE(ILLEGAL_STRING_CMP, "illegal comparison for string"), \ - _PE(ILLEGAL_INTEGER_CMP,"illegal comparison for integer") + _PE(ILLEGAL_INTEGER_CMP,"illegal comparison for integer"), \ + _PE(REPARENT_NOT_OP, "cannot reparent other than OP"), \ + _PE(REPARENT_FAILED, "failed to reparent filter OP"), \ + _PE(BAD_FILTER_ARG, "bad arg in filter tree") #undef _PE #define _PE(__code, __str) PEVENT_ERRNO__ ## __code diff --git a/parse-filter.c b/parse-filter.c index 774c3e4..9b05892 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -784,15 +784,18 @@ enum filter_vals { FILTER_VAL_TRUE, }; -void reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child, - struct filter_arg *arg) +static enum pevent_errno +reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child, + struct filter_arg *arg, char **error_str) { struct filter_arg *other_child; struct filter_arg **ptr; if (parent->type != FILTER_ARG_OP && - arg->type != FILTER_ARG_OP) - die("can not reparent other than OP"); + arg->type != FILTER_ARG_OP) { + show_error(error_str, "can not reparent other than OP"); + return PEVENT_ERRNO__REPARENT_NOT_OP; + } /* Get the sibling */ if (old_child->op.right == arg) { @@ -801,8 +804,10 @@ void reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child, } else if (old_child->op.left == arg) { ptr = &old_child->op.left; other_child = old_child->op.right; - } else - die("Error in reparent op, find other child"); + } else { + show_error(error_str, "Error in reparent op, find other child"); + return PEVENT_ERRNO__REPARENT_FAILED; + } /* Detach arg from old_child */ *ptr = NULL; @@ -813,23 +818,29 @@ void reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child, *parent = *arg; /* Free arg without recussion */ free(arg); - return; + return 0; } if (parent->op.right == old_child) ptr = &parent->op.right; else if (parent->op.left == old_child) ptr = &parent->op.left; - else - die("Error in reparent op"); + else { + show_error(error_str, "Error in reparent op"); + return PEVENT_ERRNO__REPARENT_FAILED; + } + *ptr = arg; free_arg(old_child); + return 0; } -enum filter_vals test_arg(struct filter_arg *parent, struct filter_arg *arg) +/* Returns either filter_vals (success) or pevent_errno (failfure) */ +static int test_arg(struct filter_arg *parent, struct filter_arg *arg, + char **error_str) { - enum filter_vals lval, rval; + int lval, rval; switch (arg->type) { @@ -844,63 +855,68 @@ enum filter_vals test_arg(struct filter_arg *parent, struct filter_arg *arg) return FILTER_VAL_NORM; case FILTER_ARG_EXP: - lval = test_arg(arg, arg->exp.left); + lval = test_arg(arg, arg->exp.left, error_str); if (lval != FILTER_VAL_NORM) return lval; - rval = test_arg(arg, arg->exp.right); + rval = test_arg(arg, arg->exp.right, error_str); if (rval != FILTER_VAL_NORM) return rval; return FILTER_VAL_NORM; case FILTER_ARG_NUM: - lval = test_arg(arg, arg->num.left); + lval = test_arg(arg, arg->num.left, error_str); if (lval != FILTER_VAL_NORM) return lval; - rval = test_arg(arg, arg->num.right); + rval = test_arg(arg, arg->num.right, error_str); if (rval != FILTER_VAL_NORM) return rval; return FILTER_VAL_NORM; case FILTER_ARG_OP: if (arg->op.type != FILTER_OP_NOT) { - lval = test_arg(arg, arg->op.left); + lval = test_arg(arg, arg->op.left, error_str); switch (lval) { case FILTER_VAL_NORM: break; case FILTER_VAL_TRUE: if (arg->op.type == FILTER_OP_OR) return FILTER_VAL_TRUE; - rval = test_arg(arg, arg->op.right); + rval = test_arg(arg, arg->op.right, error_str); if (rval != FILTER_VAL_NORM) return rval; - reparent_op_arg(parent, arg, arg->op.right); - return FILTER_VAL_NORM; + return reparent_op_arg(parent, arg, arg->op.right, + error_str); case FILTER_VAL_FALSE: if (arg->op.type == FILTER_OP_AND) return FILTER_VAL_FALSE; - rval = test_arg(arg, arg->op.right); + rval = test_arg(arg, arg->op.right, error_str); if (rval != FILTER_VAL_NORM) return rval; - reparent_op_arg(parent, arg, arg->op.right); - return FILTER_VAL_NORM; + return reparent_op_arg(parent, arg, arg->op.right, + error_str); + + default: + return lval; } } - rval = test_arg(arg, arg->op.right); + rval = test_arg(arg, arg->op.right, error_str); switch (rval) { case FILTER_VAL_NORM: + default: break; + case FILTER_VAL_TRUE: if (arg->op.type == FILTER_OP_OR) return FILTER_VAL_TRUE; if (arg->op.type == FILTER_OP_NOT) return FILTER_VAL_FALSE; - reparent_op_arg(parent, arg, arg->op.left); - return FILTER_VAL_NORM; + return reparent_op_arg(parent, arg, arg->op.left, + error_str); case FILTER_VAL_FALSE: if (arg->op.type == FILTER_OP_AND) @@ -908,26 +924,27 @@ enum filter_vals test_arg(struct filter_arg *parent, struct filter_arg *arg) if (arg->op.type == FILTER_OP_NOT) return FILTER_VAL_TRUE; - reparent_op_arg(parent, arg, arg->op.left); - return FILTER_VAL_NORM; + return reparent_op_arg(parent, arg, arg->op.left, + error_str); } - return FILTER_VAL_NORM; + return rval; default: - die("bad arg in filter tree"); + show_error(error_str, "bad arg in filter tree"); + return PEVENT_ERRNO__BAD_FILTER_ARG; } return FILTER_VAL_NORM; } /* Remove any unknown event fields */ -static struct filter_arg *collapse_tree(struct filter_arg *arg) +static struct filter_arg *collapse_tree(struct filter_arg *arg, char **error_str) { enum filter_vals ret; - ret = test_arg(arg, arg); + ret = test_arg(arg, arg, error_str); switch (ret) { case FILTER_VAL_NORM: - return arg; + break; case FILTER_VAL_TRUE: case FILTER_VAL_FALSE: @@ -936,7 +953,16 @@ static struct filter_arg *collapse_tree(struct filter_arg *arg) if (arg) { arg->type = FILTER_ARG_BOOLEAN; arg->boolean.value = ret == FILTER_VAL_TRUE; + } else { + show_error(error_str, "Failed to allocate filter arg"); } + break; + + default: + /* test_arg() already set the error_str */ + free_arg(arg); + arg = NULL; + break; } return arg; @@ -1152,9 +1178,9 @@ process_filter(struct event_format *event, struct filter_arg **parg, if (!current_op) current_op = current_exp; - current_op = collapse_tree(current_op); + current_op = collapse_tree(current_op, error_str); if (current_op == NULL) - goto fail_alloc; + goto fail; *parg = current_op; -- cgit v1.2.3 From b77a2bf3882178a23949f625155a3f220d9bf0fa Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 12 Dec 2013 16:36:12 +0900 Subject: tools lib traceevent: Refactor create_arg_item() So that it can return a proper pevent_errno value. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386833777-3790-10-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.h | 3 ++- parse-filter.c | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/event-parse.h b/event-parse.h index 5e4392d..57b66ae 100644 --- a/event-parse.h +++ b/event-parse.h @@ -367,7 +367,8 @@ enum pevent_flag { _PE(ILLEGAL_INTEGER_CMP,"illegal comparison for integer"), \ _PE(REPARENT_NOT_OP, "cannot reparent other than OP"), \ _PE(REPARENT_FAILED, "failed to reparent filter OP"), \ - _PE(BAD_FILTER_ARG, "bad arg in filter tree") + _PE(BAD_FILTER_ARG, "bad arg in filter tree"), \ + _PE(UNEXPECTED_TYPE, "unexpected type (not a value)") #undef _PE #define _PE(__code, __str) PEVENT_ERRNO__ ## __code diff --git a/parse-filter.c b/parse-filter.c index 9b05892..8d71208 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -368,9 +368,9 @@ static void free_events(struct event_list *events) } } -static struct filter_arg * +static enum pevent_errno create_arg_item(struct event_format *event, const char *token, - enum event_type type, char **error_str) + enum event_type type, struct filter_arg **parg, char **error_str) { struct format_field *field; struct filter_arg *arg; @@ -378,7 +378,7 @@ create_arg_item(struct event_format *event, const char *token, arg = allocate_arg(); if (arg == NULL) { show_error(error_str, "failed to allocate filter arg"); - return NULL; + return PEVENT_ERRNO__MEM_ALLOC_FAILED; } switch (type) { @@ -392,7 +392,7 @@ create_arg_item(struct event_format *event, const char *token, if (!arg->value.str) { free_arg(arg); show_error(error_str, "failed to allocate string filter arg"); - return NULL; + return PEVENT_ERRNO__MEM_ALLOC_FAILED; } break; case EVENT_ITEM: @@ -420,11 +420,11 @@ create_arg_item(struct event_format *event, const char *token, break; default: free_arg(arg); - show_error(error_str, "expected a value but found %s", - token); - return NULL; + show_error(error_str, "expected a value but found %s", token); + return PEVENT_ERRNO__UNEXPECTED_TYPE; } - return arg; + *parg = arg; + return 0; } static struct filter_arg * @@ -993,8 +993,8 @@ process_filter(struct event_format *event, struct filter_arg **parg, case EVENT_SQUOTE: case EVENT_DQUOTE: case EVENT_ITEM: - arg = create_arg_item(event, token, type, error_str); - if (!arg) + ret = create_arg_item(event, token, type, &arg, error_str); + if (ret < 0) goto fail; if (!left_item) left_item = arg; -- cgit v1.2.3 From 4382a21256d43e26775818bcad941432a143ec3e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 12 Dec 2013 16:36:13 +0900 Subject: tools lib traceevent: Refactor process_filter() So that it can return a proper pevent_errno value. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386833777-3790-11-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.h | 6 +++++- parse-filter.c | 64 +++++++++++++++++++++++++++++++++------------------------- 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/event-parse.h b/event-parse.h index 57b66ae..da942d5 100644 --- a/event-parse.h +++ b/event-parse.h @@ -368,7 +368,11 @@ enum pevent_flag { _PE(REPARENT_NOT_OP, "cannot reparent other than OP"), \ _PE(REPARENT_FAILED, "failed to reparent filter OP"), \ _PE(BAD_FILTER_ARG, "bad arg in filter tree"), \ - _PE(UNEXPECTED_TYPE, "unexpected type (not a value)") + _PE(UNEXPECTED_TYPE, "unexpected type (not a value)"), \ + _PE(ILLEGAL_TOKEN, "illegal token"), \ + _PE(INVALID_PAREN, "open parenthesis cannot come here"), \ + _PE(UNBALANCED_PAREN, "unbalanced number of parenthesis"), \ + _PE(UNKNOWN_TOKEN, "unknown token") #undef _PE #define _PE(__code, __str) PEVENT_ERRNO__ ## __code diff --git a/parse-filter.c b/parse-filter.c index 8d71208..5aa5012 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -937,9 +937,10 @@ static int test_arg(struct filter_arg *parent, struct filter_arg *arg, } /* Remove any unknown event fields */ -static struct filter_arg *collapse_tree(struct filter_arg *arg, char **error_str) +static int collapse_tree(struct filter_arg *arg, + struct filter_arg **arg_collapsed, char **error_str) { - enum filter_vals ret; + int ret; ret = test_arg(arg, arg, error_str); switch (ret) { @@ -955,6 +956,7 @@ static struct filter_arg *collapse_tree(struct filter_arg *arg, char **error_str arg->boolean.value = ret == FILTER_VAL_TRUE; } else { show_error(error_str, "Failed to allocate filter arg"); + ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; } break; @@ -965,10 +967,11 @@ static struct filter_arg *collapse_tree(struct filter_arg *arg, char **error_str break; } - return arg; + *arg_collapsed = arg; + return ret; } -static int +static enum pevent_errno process_filter(struct event_format *event, struct filter_arg **parg, char **error_str, int not) { @@ -982,7 +985,7 @@ process_filter(struct event_format *event, struct filter_arg **parg, enum filter_op_type btype; enum filter_exp_type etype; enum filter_cmp_type ctype; - int ret; + enum pevent_errno ret; *parg = NULL; @@ -1007,20 +1010,20 @@ process_filter(struct event_format *event, struct filter_arg **parg, if (not) { arg = NULL; if (current_op) - goto fail_print; + goto fail_syntax; free(token); *parg = current_exp; return 0; } } else - goto fail_print; + goto fail_syntax; arg = NULL; break; case EVENT_DELIM: if (*token == ',') { - show_error(error_str, - "Illegal token ','"); + show_error(error_str, "Illegal token ','"); + ret = PEVENT_ERRNO__ILLEGAL_TOKEN; goto fail; } @@ -1028,19 +1031,23 @@ process_filter(struct event_format *event, struct filter_arg **parg, if (left_item) { show_error(error_str, "Open paren can not come after item"); + ret = PEVENT_ERRNO__INVALID_PAREN; goto fail; } if (current_exp) { show_error(error_str, "Open paren can not come after expression"); + ret = PEVENT_ERRNO__INVALID_PAREN; goto fail; } ret = process_filter(event, &arg, error_str, 0); - if (ret != 1) { - if (ret == 0) + if (ret != PEVENT_ERRNO__UNBALANCED_PAREN) { + if (ret == 0) { show_error(error_str, "Unbalanced number of '('"); + ret = PEVENT_ERRNO__UNBALANCED_PAREN; + } goto fail; } ret = 0; @@ -1048,7 +1055,7 @@ process_filter(struct event_format *event, struct filter_arg **parg, /* A not wants just one expression */ if (not) { if (current_op) - goto fail_print; + goto fail_syntax; *parg = arg; return 0; } @@ -1063,19 +1070,19 @@ process_filter(struct event_format *event, struct filter_arg **parg, } else { /* ')' */ if (!current_op && !current_exp) - goto fail_print; + goto fail_syntax; /* Make sure everything is finished at this level */ if (current_exp && !check_op_done(current_exp)) - goto fail_print; + goto fail_syntax; if (current_op && !check_op_done(current_op)) - goto fail_print; + goto fail_syntax; if (current_op) *parg = current_op; else *parg = current_exp; - return 1; + return PEVENT_ERRNO__UNBALANCED_PAREN; } break; @@ -1087,21 +1094,22 @@ process_filter(struct event_format *event, struct filter_arg **parg, case OP_BOOL: /* Logic ops need a left expression */ if (!current_exp && !current_op) - goto fail_print; + goto fail_syntax; /* fall through */ case OP_NOT: /* logic only processes ops and exp */ if (left_item) - goto fail_print; + goto fail_syntax; break; case OP_EXP: case OP_CMP: if (!left_item) - goto fail_print; + goto fail_syntax; break; case OP_NONE: show_error(error_str, "Unknown op token %s", token); + ret = PEVENT_ERRNO__UNKNOWN_TOKEN; goto fail; } @@ -1152,7 +1160,7 @@ process_filter(struct event_format *event, struct filter_arg **parg, ret = add_left(arg, left_item); if (ret < 0) { arg = NULL; - goto fail_print; + goto fail_syntax; } current_exp = arg; break; @@ -1161,25 +1169,25 @@ process_filter(struct event_format *event, struct filter_arg **parg, } arg = NULL; if (ret < 0) - goto fail_print; + goto fail_syntax; break; case EVENT_NONE: break; case EVENT_ERROR: goto fail_alloc; default: - goto fail_print; + goto fail_syntax; } } while (type != EVENT_NONE); if (!current_op && !current_exp) - goto fail_print; + goto fail_syntax; if (!current_op) current_op = current_exp; - current_op = collapse_tree(current_op, error_str); - if (current_op == NULL) + ret = collapse_tree(current_op, parg, error_str); + if (ret < 0) goto fail; *parg = current_op; @@ -1188,15 +1196,17 @@ process_filter(struct event_format *event, struct filter_arg **parg, fail_alloc: show_error(error_str, "failed to allocate filter arg"); + ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; goto fail; - fail_print: + fail_syntax: show_error(error_str, "Syntax error"); + ret = PEVENT_ERRNO__SYNTAX_ERROR; fail: free_arg(current_op); free_arg(current_exp); free_arg(arg); free(token); - return -1; + return ret; } static int -- cgit v1.2.3 From f8dd420f66e9a3024a17fe5df5f7488f241b71b7 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 12 Dec 2013 16:36:14 +0900 Subject: tools lib traceevent: Make pevent_filter_add_filter_str() return pevent_errno Refactor the pevent_filter_add_filter_str() to return a proper error code and get rid of the third error_str argument. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386833777-3790-12-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.h | 8 +++--- parse-filter.c | 78 +++++++++++++++++----------------------------------------- 2 files changed, 27 insertions(+), 59 deletions(-) diff --git a/event-parse.h b/event-parse.h index da942d5..089964e 100644 --- a/event-parse.h +++ b/event-parse.h @@ -372,7 +372,8 @@ enum pevent_flag { _PE(ILLEGAL_TOKEN, "illegal token"), \ _PE(INVALID_PAREN, "open parenthesis cannot come here"), \ _PE(UNBALANCED_PAREN, "unbalanced number of parenthesis"), \ - _PE(UNKNOWN_TOKEN, "unknown token") + _PE(UNKNOWN_TOKEN, "unknown token"), \ + _PE(FILTER_NOT_FOUND, "no filter found") #undef _PE #define _PE(__code, __str) PEVENT_ERRNO__ ## __code @@ -863,9 +864,8 @@ enum filter_trivial_type { FILTER_TRIVIAL_BOTH, }; -int pevent_filter_add_filter_str(struct event_filter *filter, - const char *filter_str, - char **error_str); +enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter, + const char *filter_str); int pevent_filter_match(struct event_filter *filter, diff --git a/parse-filter.c b/parse-filter.c index 5aa5012..78440d7 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -1209,7 +1209,7 @@ process_filter(struct event_format *event, struct filter_arg **parg, return ret; } -static int +static enum pevent_errno process_event(struct event_format *event, const char *filter_str, struct filter_arg **parg, char **error_str) { @@ -1218,21 +1218,15 @@ process_event(struct event_format *event, const char *filter_str, pevent_buffer_init(filter_str, strlen(filter_str)); ret = process_filter(event, parg, error_str, 0); - if (ret == 1) { - show_error(error_str, - "Unbalanced number of ')'"); - return -1; - } if (ret < 0) return ret; /* If parg is NULL, then make it into FALSE */ if (!*parg) { *parg = allocate_arg(); - if (*parg == NULL) { - show_error(error_str, "failed to allocate filter arg"); - return -1; - } + if (*parg == NULL) + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + (*parg)->type = FILTER_ARG_BOOLEAN; (*parg)->boolean.value = FILTER_FALSE; } @@ -1240,13 +1234,13 @@ process_event(struct event_format *event, const char *filter_str, return 0; } -static int filter_event(struct event_filter *filter, - struct event_format *event, - const char *filter_str, char **error_str) +static enum pevent_errno +filter_event(struct event_filter *filter, struct event_format *event, + const char *filter_str, char **error_str) { struct filter_type *filter_type; struct filter_arg *arg; - int ret; + enum pevent_errno ret; if (filter_str) { ret = process_event(event, filter_str, &arg, error_str); @@ -1256,20 +1250,16 @@ static int filter_event(struct event_filter *filter, } else { /* just add a TRUE arg */ arg = allocate_arg(); - if (arg == NULL) { - show_error(error_str, "failed to allocate filter arg"); - return -1; - } + if (arg == NULL) + return PEVENT_ERRNO__MEM_ALLOC_FAILED; + arg->type = FILTER_ARG_BOOLEAN; arg->boolean.value = FILTER_TRUE; } filter_type = add_filter_type(filter, event->id); - if (filter_type == NULL) { - show_error(error_str, "failed to add a new filter: %s", - filter_str ? filter_str : "true"); - return -1; - } + if (filter_type == NULL) + return PEVENT_ERRNO__MEM_ALLOC_FAILED; if (filter_type->filter) free_arg(filter_type->filter); @@ -1282,18 +1272,12 @@ static int filter_event(struct event_filter *filter, * pevent_filter_add_filter_str - add a new filter * @filter: the event filter to add to * @filter_str: the filter string that contains the filter - * @error_str: string containing reason for failed filter - * - * Returns 0 if the filter was successfully added - * -1 if there was an error. * - * On error, if @error_str points to a string pointer, - * it is set to the reason that the filter failed. - * This string must be freed with "free". + * Returns 0 if the filter was successfully added or a + * negative error code. */ -int pevent_filter_add_filter_str(struct event_filter *filter, - const char *filter_str, - char **error_str) +enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter, + const char *filter_str) { struct pevent *pevent = filter->pevent; struct event_list *event; @@ -1304,23 +1288,20 @@ int pevent_filter_add_filter_str(struct event_filter *filter, char *event_name = NULL; char *sys_name = NULL; char *sp; - int rtn = 0; + enum pevent_errno rtn = 0; /* PEVENT_ERRNO__SUCCESS */ int len; int ret; + char *error_str = NULL; /* clear buffer to reset show error */ pevent_buffer_init("", 0); - if (error_str) - *error_str = NULL; - filter_start = strchr(filter_str, ':'); if (filter_start) len = filter_start - filter_str; else len = strlen(filter_str); - do { next_event = strchr(filter_str, ','); if (next_event && @@ -1333,10 +1314,9 @@ int pevent_filter_add_filter_str(struct event_filter *filter, this_event = malloc(len + 1); if (this_event == NULL) { - show_error(error_str, "Memory allocation failure"); /* This can only happen when events is NULL, but still */ free_events(events); - return -1; + return PEVENT_ERRNO__MEM_ALLOC_FAILED; } memcpy(this_event, filter_str, len); this_event[len] = 0; @@ -1350,30 +1330,18 @@ int pevent_filter_add_filter_str(struct event_filter *filter, event_name = strtok_r(NULL, "/", &sp); if (!sys_name) { - show_error(error_str, "No filter found"); /* This can only happen when events is NULL, but still */ free_events(events); free(this_event); - return -1; + return PEVENT_ERRNO__FILTER_NOT_FOUND; } /* Find this event */ ret = find_event(pevent, &events, strim(sys_name), strim(event_name)); if (ret < 0) { - if (ret == PEVENT_ERRNO__MEM_ALLOC_FAILED) - show_error(error_str, - "Memory allocation failure"); - else if (event_name) - show_error(error_str, - "No event found under '%s.%s'", - sys_name, event_name); - else - show_error(error_str, - "No event found under '%s'", - sys_name); free_events(events); free(this_event); - return -1; + return ret; } free(this_event); } while (filter_str); @@ -1385,7 +1353,7 @@ int pevent_filter_add_filter_str(struct event_filter *filter, /* filter starts here */ for (event = events; event; event = event->next) { ret = filter_event(filter, event->event, filter_start, - error_str); + &error_str); /* Failures are returned if a parse error happened */ if (ret < 0) rtn = ret; -- cgit v1.2.3 From 253b70cda28cd84a5461cb7f852f4c46cc27ae90 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 12 Dec 2013 16:36:15 +0900 Subject: tools lib traceevent: Refactor pevent_filter_match() to get rid of die() The test_filter() function is for testing given filter is matched to a given record. However it doesn't handle error cases properly so add a new argument err to save error info during the test and also pass it to internal test functions. The return value of pevent_filter_match() also converted to pevent_errno to indicate an exact error case. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386833777-3790-13-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.h | 21 ++++++--- parse-filter.c | 135 ++++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 99 insertions(+), 57 deletions(-) diff --git a/event-parse.h b/event-parse.h index 089964e..3ad784f 100644 --- a/event-parse.h +++ b/event-parse.h @@ -357,6 +357,8 @@ enum pevent_flag { _PE(READ_PRINT_FAILED, "failed to read event print fmt"), \ _PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace"),\ _PE(INVALID_ARG_TYPE, "invalid argument type"), \ + _PE(INVALID_EXP_TYPE, "invalid expression type"), \ + _PE(INVALID_OP_TYPE, "invalid operator type"), \ _PE(INVALID_EVENT_NAME, "invalid event name"), \ _PE(EVENT_NOT_FOUND, "no event found"), \ _PE(SYNTAX_ERROR, "syntax error"), \ @@ -373,12 +375,16 @@ enum pevent_flag { _PE(INVALID_PAREN, "open parenthesis cannot come here"), \ _PE(UNBALANCED_PAREN, "unbalanced number of parenthesis"), \ _PE(UNKNOWN_TOKEN, "unknown token"), \ - _PE(FILTER_NOT_FOUND, "no filter found") + _PE(FILTER_NOT_FOUND, "no filter found"), \ + _PE(NOT_A_NUMBER, "must have number field"), \ + _PE(NO_FILTER, "no filters exists"), \ + _PE(FILTER_MISS, "record does not match to filter") #undef _PE #define _PE(__code, __str) PEVENT_ERRNO__ ## __code enum pevent_errno { PEVENT_ERRNO__SUCCESS = 0, + PEVENT_ERRNO__FILTER_MATCH = PEVENT_ERRNO__SUCCESS, /* * Choose an arbitrary negative big number not to clash with standard @@ -853,10 +859,11 @@ struct event_filter { struct event_filter *pevent_filter_alloc(struct pevent *pevent); -#define FILTER_NONE -2 -#define FILTER_NOEXIST -1 -#define FILTER_MISS 0 -#define FILTER_MATCH 1 +/* for backward compatibility */ +#define FILTER_NONE PEVENT_ERRNO__FILTER_NOT_FOUND +#define FILTER_NOEXIST PEVENT_ERRNO__NO_FILTER +#define FILTER_MISS PEVENT_ERRNO__FILTER_MISS +#define FILTER_MATCH PEVENT_ERRNO__FILTER_MATCH enum filter_trivial_type { FILTER_TRIVIAL_FALSE, @@ -868,8 +875,8 @@ enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter, const char *filter_str); -int pevent_filter_match(struct event_filter *filter, - struct pevent_record *record); +enum pevent_errno pevent_filter_match(struct event_filter *filter, + struct pevent_record *record); int pevent_event_filtered(struct event_filter *filter, int event_id); diff --git a/parse-filter.c b/parse-filter.c index 78440d7..9303c55 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -1678,8 +1678,8 @@ int pevent_filter_event_has_trivial(struct event_filter *filter, } } -static int test_filter(struct event_format *event, - struct filter_arg *arg, struct pevent_record *record); +static int test_filter(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err); static const char * get_comm(struct event_format *event, struct pevent_record *record) @@ -1725,15 +1725,24 @@ get_value(struct event_format *event, } static unsigned long long -get_arg_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record); +get_arg_value(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err); static unsigned long long -get_exp_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record) +get_exp_value(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err) { unsigned long long lval, rval; - lval = get_arg_value(event, arg->exp.left, record); - rval = get_arg_value(event, arg->exp.right, record); + lval = get_arg_value(event, arg->exp.left, record, err); + rval = get_arg_value(event, arg->exp.right, record, err); + + if (*err) { + /* + * There was an error, no need to process anymore. + */ + return 0; + } switch (arg->exp.type) { case FILTER_EXP_ADD: @@ -1768,39 +1777,51 @@ get_exp_value(struct event_format *event, struct filter_arg *arg, struct pevent_ case FILTER_EXP_NOT: default: - die("error in exp"); + if (!*err) + *err = PEVENT_ERRNO__INVALID_EXP_TYPE; } return 0; } static unsigned long long -get_arg_value(struct event_format *event, struct filter_arg *arg, struct pevent_record *record) +get_arg_value(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err) { switch (arg->type) { case FILTER_ARG_FIELD: return get_value(event, arg->field.field, record); case FILTER_ARG_VALUE: - if (arg->value.type != FILTER_NUMBER) - die("must have number field!"); + if (arg->value.type != FILTER_NUMBER) { + if (!*err) + *err = PEVENT_ERRNO__NOT_A_NUMBER; + } return arg->value.val; case FILTER_ARG_EXP: - return get_exp_value(event, arg, record); + return get_exp_value(event, arg, record, err); default: - die("oops in filter"); + if (!*err) + *err = PEVENT_ERRNO__INVALID_ARG_TYPE; } return 0; } -static int test_num(struct event_format *event, - struct filter_arg *arg, struct pevent_record *record) +static int test_num(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err) { unsigned long long lval, rval; - lval = get_arg_value(event, arg->num.left, record); - rval = get_arg_value(event, arg->num.right, record); + lval = get_arg_value(event, arg->num.left, record, err); + rval = get_arg_value(event, arg->num.right, record, err); + + if (*err) { + /* + * There was an error, no need to process anymore. + */ + return 0; + } switch (arg->num.type) { case FILTER_CMP_EQ: @@ -1822,7 +1843,8 @@ static int test_num(struct event_format *event, return lval <= rval; default: - /* ?? */ + if (!*err) + *err = PEVENT_ERRNO__ILLEGAL_INTEGER_CMP; return 0; } } @@ -1869,8 +1891,8 @@ static const char *get_field_str(struct filter_arg *arg, struct pevent_record *r return val; } -static int test_str(struct event_format *event, - struct filter_arg *arg, struct pevent_record *record) +static int test_str(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err) { const char *val; @@ -1894,48 +1916,57 @@ static int test_str(struct event_format *event, return regexec(&arg->str.reg, val, 0, NULL, 0); default: - /* ?? */ + if (!*err) + *err = PEVENT_ERRNO__ILLEGAL_STRING_CMP; return 0; } } -static int test_op(struct event_format *event, - struct filter_arg *arg, struct pevent_record *record) +static int test_op(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err) { switch (arg->op.type) { case FILTER_OP_AND: - return test_filter(event, arg->op.left, record) && - test_filter(event, arg->op.right, record); + return test_filter(event, arg->op.left, record, err) && + test_filter(event, arg->op.right, record, err); case FILTER_OP_OR: - return test_filter(event, arg->op.left, record) || - test_filter(event, arg->op.right, record); + return test_filter(event, arg->op.left, record, err) || + test_filter(event, arg->op.right, record, err); case FILTER_OP_NOT: - return !test_filter(event, arg->op.right, record); + return !test_filter(event, arg->op.right, record, err); default: - /* ?? */ + if (!*err) + *err = PEVENT_ERRNO__INVALID_OP_TYPE; return 0; } } -static int test_filter(struct event_format *event, - struct filter_arg *arg, struct pevent_record *record) +static int test_filter(struct event_format *event, struct filter_arg *arg, + struct pevent_record *record, enum pevent_errno *err) { + if (*err) { + /* + * There was an error, no need to process anymore. + */ + return 0; + } + switch (arg->type) { case FILTER_ARG_BOOLEAN: /* easy case */ return arg->boolean.value; case FILTER_ARG_OP: - return test_op(event, arg, record); + return test_op(event, arg, record, err); case FILTER_ARG_NUM: - return test_num(event, arg, record); + return test_num(event, arg, record, err); case FILTER_ARG_STR: - return test_str(event, arg, record); + return test_str(event, arg, record, err); case FILTER_ARG_EXP: case FILTER_ARG_VALUE: @@ -1944,11 +1975,11 @@ static int test_filter(struct event_format *event, * Expressions, fields and values evaluate * to true if they return non zero */ - return !!get_arg_value(event, arg, record); + return !!get_arg_value(event, arg, record, err); default: - die("oops!"); - /* ?? */ + if (!*err) + *err = PEVENT_ERRNO__INVALID_ARG_TYPE; return 0; } } @@ -1961,8 +1992,7 @@ static int test_filter(struct event_format *event, * Returns 1 if filter found for @event_id * otherwise 0; */ -int pevent_event_filtered(struct event_filter *filter, - int event_id) +int pevent_event_filtered(struct event_filter *filter, int event_id) { struct filter_type *filter_type; @@ -1979,31 +2009,36 @@ int pevent_event_filtered(struct event_filter *filter, * @filter: filter struct with filter information * @record: the record to test against the filter * - * Returns: - * 1 - filter found for event and @record matches - * 0 - filter found for event and @record does not match - * -1 - no filter found for @record's event - * -2 - if no filters exist + * Returns: match result or error code (prefixed with PEVENT_ERRNO__) + * FILTER_MATCH - filter found for event and @record matches + * FILTER_MISS - filter found for event and @record does not match + * FILTER_NOT_FOUND - no filter found for @record's event + * NO_FILTER - if no filters exist + * otherwise - error occurred during test */ -int pevent_filter_match(struct event_filter *filter, - struct pevent_record *record) +enum pevent_errno pevent_filter_match(struct event_filter *filter, + struct pevent_record *record) { struct pevent *pevent = filter->pevent; struct filter_type *filter_type; int event_id; + int ret; + enum pevent_errno err = 0; if (!filter->filters) - return FILTER_NONE; + return PEVENT_ERRNO__NO_FILTER; event_id = pevent_data_type(pevent, record); filter_type = find_filter_type(filter, event_id); - if (!filter_type) - return FILTER_NOEXIST; + return PEVENT_ERRNO__FILTER_NOT_FOUND; + + ret = test_filter(filter_type->event, filter_type->filter, record, &err); + if (err) + return err; - return test_filter(filter_type->event, filter_type->filter, record) ? - FILTER_MATCH : FILTER_MISS; + return ret ? PEVENT_ERRNO__FILTER_MATCH : PEVENT_ERRNO__FILTER_MISS; } static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) -- cgit v1.2.3 From 1f88f6c5a76706b4be521709df928b86df965be5 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 17 Dec 2013 09:02:36 +0900 Subject: tools lib traceevent: Get rid of die() in some string conversion functions Those functions stringify filter arguments. As caller of those functions handles NULL string properly, it seems that it's enough to return NULL rather than calling die(). Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/878uvkgx9f.fsf@sejong.aot.lge.com Signed-off-by: Arnaldo Carvalho de Melo --- parse-filter.c | 67 +++++++++++++++++----------------------------------------- 1 file changed, 19 insertions(+), 48 deletions(-) diff --git a/parse-filter.c b/parse-filter.c index 9303c55..e2842b9 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -1361,8 +1361,10 @@ enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter, if (ret >= 0 && pevent->test_filters) { char *test; test = pevent_filter_make_string(filter, event->event->id); - printf(" '%s: %s'\n", event->event->name, test); - free(test); + if (test) { + printf(" '%s: %s'\n", event->event->name, test); + free(test); + } } } @@ -2050,7 +2052,6 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) int left_val = -1; int right_val = -1; int val; - int len; switch (arg->op.type) { case FILTER_OP_AND: @@ -2097,11 +2098,7 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) default: break; } - str = malloc_or_die(6); - if (val) - strcpy(str, "TRUE"); - else - strcpy(str, "FALSE"); + asprintf(&str, val ? "TRUE" : "FALSE"); break; } } @@ -2119,10 +2116,7 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) break; } - len = strlen(left) + strlen(right) + strlen(op) + 10; - str = malloc_or_die(len); - snprintf(str, len, "(%s) %s (%s)", - left, op, right); + asprintf(&str, "(%s) %s (%s)", left, op, right); break; case FILTER_OP_NOT: @@ -2138,16 +2132,10 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) right_val = 0; if (right_val >= 0) { /* just return the opposite */ - str = malloc_or_die(6); - if (right_val) - strcpy(str, "FALSE"); - else - strcpy(str, "TRUE"); + asprintf(&str, right_val ? "FALSE" : "TRUE"); break; } - len = strlen(right) + strlen(op) + 3; - str = malloc_or_die(len); - snprintf(str, len, "%s(%s)", op, right); + asprintf(&str, "%s(%s)", op, right); break; default: @@ -2161,11 +2149,9 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) static char *val_to_str(struct event_filter *filter, struct filter_arg *arg) { - char *str; - - str = malloc_or_die(30); + char *str = NULL; - snprintf(str, 30, "%lld", arg->value.val); + asprintf(&str, "%lld", arg->value.val); return str; } @@ -2181,7 +2167,6 @@ static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg) char *rstr; char *op; char *str = NULL; - int len; lstr = arg_to_str(filter, arg->exp.left); rstr = arg_to_str(filter, arg->exp.right); @@ -2220,12 +2205,11 @@ static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg) op = "^"; break; default: - die("oops in exp"); + op = "[ERROR IN EXPRESSION TYPE]"; + break; } - len = strlen(op) + strlen(lstr) + strlen(rstr) + 4; - str = malloc_or_die(len); - snprintf(str, len, "%s %s %s", lstr, op, rstr); + asprintf(&str, "%s %s %s", lstr, op, rstr); out: free(lstr); free(rstr); @@ -2239,7 +2223,6 @@ static char *num_to_str(struct event_filter *filter, struct filter_arg *arg) char *rstr; char *str = NULL; char *op = NULL; - int len; lstr = arg_to_str(filter, arg->num.left); rstr = arg_to_str(filter, arg->num.right); @@ -2270,10 +2253,7 @@ static char *num_to_str(struct event_filter *filter, struct filter_arg *arg) if (!op) op = "<="; - len = strlen(lstr) + strlen(op) + strlen(rstr) + 4; - str = malloc_or_die(len); - sprintf(str, "%s %s %s", lstr, op, rstr); - + asprintf(&str, "%s %s %s", lstr, op, rstr); break; default: @@ -2291,7 +2271,6 @@ static char *str_to_str(struct event_filter *filter, struct filter_arg *arg) { char *str = NULL; char *op = NULL; - int len; switch (arg->str.type) { case FILTER_CMP_MATCH: @@ -2309,12 +2288,8 @@ static char *str_to_str(struct event_filter *filter, struct filter_arg *arg) if (!op) op = "!~"; - len = strlen(arg->str.field->name) + strlen(op) + - strlen(arg->str.val) + 6; - str = malloc_or_die(len); - snprintf(str, len, "%s %s \"%s\"", - arg->str.field->name, - op, arg->str.val); + asprintf(&str, "%s %s \"%s\"", + arg->str.field->name, op, arg->str.val); break; default: @@ -2326,15 +2301,11 @@ static char *str_to_str(struct event_filter *filter, struct filter_arg *arg) static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg) { - char *str; + char *str = NULL; switch (arg->type) { case FILTER_ARG_BOOLEAN: - str = malloc_or_die(6); - if (arg->boolean.value) - strcpy(str, "TRUE"); - else - strcpy(str, "FALSE"); + asprintf(&str, arg->boolean.value ? "TRUE" : "FALSE"); return str; case FILTER_ARG_OP: @@ -2369,7 +2340,7 @@ static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg) * * Returns a string that displays the filter contents. * This string must be freed with free(str). - * NULL is returned if no filter is found. + * NULL is returned if no filter is found or allocation failed. */ char * pevent_filter_make_string(struct event_filter *filter, int event_id) -- cgit v1.2.3 From b8bf84d3cedde6c2ce0e3afe551f7956e9dca2e4 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 12 Dec 2013 16:36:17 +0900 Subject: tools lib traceevent: Introduce pevent_filter_strerror() The pevent_filter_strerror() function is for receiving actual error message from pevent_errno value. To do that, add a static buffer to event_filter for saving internal error message If a failed function saved other information in the static buffer returns the information, otherwise returns generic error message. Signed-off-by: Namhyung Kim Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1386833777-3790-15-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 17 +--------- event-parse.h | 7 ++++- parse-filter.c | 98 ++++++++++++++++++++++++++++++++-------------------------- 3 files changed, 61 insertions(+), 61 deletions(-) diff --git a/event-parse.c b/event-parse.c index 22566c2..2ce565a 100644 --- a/event-parse.c +++ b/event-parse.c @@ -5230,22 +5230,7 @@ int pevent_strerror(struct pevent *pevent __maybe_unused, idx = errnum - __PEVENT_ERRNO__START - 1; msg = pevent_error_str[idx]; - - switch (errnum) { - case PEVENT_ERRNO__MEM_ALLOC_FAILED: - case PEVENT_ERRNO__PARSE_EVENT_FAILED: - case PEVENT_ERRNO__READ_ID_FAILED: - case PEVENT_ERRNO__READ_FORMAT_FAILED: - case PEVENT_ERRNO__READ_PRINT_FAILED: - case PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED: - case PEVENT_ERRNO__INVALID_ARG_TYPE: - snprintf(buf, buflen, "%s", msg); - break; - - default: - /* cannot reach here */ - break; - } + snprintf(buf, buflen, "%s", msg); return 0; } diff --git a/event-parse.h b/event-parse.h index 3ad784f..cf5db90 100644 --- a/event-parse.h +++ b/event-parse.h @@ -851,10 +851,13 @@ struct filter_type { struct filter_arg *filter; }; +#define PEVENT_FILTER_ERROR_BUFSZ 1024 + struct event_filter { struct pevent *pevent; int filters; struct filter_type *event_filters; + char error_buffer[PEVENT_FILTER_ERROR_BUFSZ]; }; struct event_filter *pevent_filter_alloc(struct pevent *pevent); @@ -874,10 +877,12 @@ enum filter_trivial_type { enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter, const char *filter_str); - enum pevent_errno pevent_filter_match(struct event_filter *filter, struct pevent_record *record); +int pevent_filter_strerror(struct event_filter *filter, enum pevent_errno err, + char *buf, size_t buflen); + int pevent_event_filtered(struct event_filter *filter, int event_id); diff --git a/parse-filter.c b/parse-filter.c index e2842b9..b502344 100644 --- a/parse-filter.c +++ b/parse-filter.c @@ -38,55 +38,31 @@ struct event_list { struct event_format *event; }; -#define MAX_ERR_STR_SIZE 256 - -static void show_error(char **error_str, const char *fmt, ...) +static void show_error(char *error_buf, const char *fmt, ...) { unsigned long long index; const char *input; - char *error; va_list ap; int len; int i; - if (!error_str) - return; - input = pevent_get_input_buf(); index = pevent_get_input_buf_ptr(); len = input ? strlen(input) : 0; - error = malloc(MAX_ERR_STR_SIZE + (len*2) + 3); - if (error == NULL) { - /* - * Maybe it's due to len is too long. - * Retry without the input buffer part. - */ - len = 0; - - error = malloc(MAX_ERR_STR_SIZE); - if (error == NULL) { - /* no memory */ - *error_str = NULL; - return; - } - } - if (len) { - strcpy(error, input); - error[len] = '\n'; + strcpy(error_buf, input); + error_buf[len] = '\n'; for (i = 1; i < len && i < index; i++) - error[len+i] = ' '; - error[len + i] = '^'; - error[len + i + 1] = '\n'; + error_buf[len+i] = ' '; + error_buf[len + i] = '^'; + error_buf[len + i + 1] = '\n'; len += i+2; } va_start(ap, fmt); - vsnprintf(error + len, MAX_ERR_STR_SIZE, fmt, ap); + vsnprintf(error_buf + len, PEVENT_FILTER_ERROR_BUFSZ - len, fmt, ap); va_end(ap); - - *error_str = error; } static void free_token(char *token) @@ -370,7 +346,7 @@ static void free_events(struct event_list *events) static enum pevent_errno create_arg_item(struct event_format *event, const char *token, - enum event_type type, struct filter_arg **parg, char **error_str) + enum event_type type, struct filter_arg **parg, char *error_str) { struct format_field *field; struct filter_arg *arg; @@ -474,7 +450,7 @@ create_arg_cmp(enum filter_exp_type etype) } static enum pevent_errno -add_right(struct filter_arg *op, struct filter_arg *arg, char **error_str) +add_right(struct filter_arg *op, struct filter_arg *arg, char *error_str) { struct filter_arg *left; char *str; @@ -786,7 +762,7 @@ enum filter_vals { static enum pevent_errno reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child, - struct filter_arg *arg, char **error_str) + struct filter_arg *arg, char *error_str) { struct filter_arg *other_child; struct filter_arg **ptr; @@ -838,7 +814,7 @@ reparent_op_arg(struct filter_arg *parent, struct filter_arg *old_child, /* Returns either filter_vals (success) or pevent_errno (failfure) */ static int test_arg(struct filter_arg *parent, struct filter_arg *arg, - char **error_str) + char *error_str) { int lval, rval; @@ -938,7 +914,7 @@ static int test_arg(struct filter_arg *parent, struct filter_arg *arg, /* Remove any unknown event fields */ static int collapse_tree(struct filter_arg *arg, - struct filter_arg **arg_collapsed, char **error_str) + struct filter_arg **arg_collapsed, char *error_str) { int ret; @@ -973,7 +949,7 @@ static int collapse_tree(struct filter_arg *arg, static enum pevent_errno process_filter(struct event_format *event, struct filter_arg **parg, - char **error_str, int not) + char *error_str, int not) { enum event_type type; char *token = NULL; @@ -1211,7 +1187,7 @@ process_filter(struct event_format *event, struct filter_arg **parg, static enum pevent_errno process_event(struct event_format *event, const char *filter_str, - struct filter_arg **parg, char **error_str) + struct filter_arg **parg, char *error_str) { int ret; @@ -1236,7 +1212,7 @@ process_event(struct event_format *event, const char *filter_str, static enum pevent_errno filter_event(struct event_filter *filter, struct event_format *event, - const char *filter_str, char **error_str) + const char *filter_str, char *error_str) { struct filter_type *filter_type; struct filter_arg *arg; @@ -1268,13 +1244,21 @@ filter_event(struct event_filter *filter, struct event_format *event, return 0; } +static void filter_init_error_buf(struct event_filter *filter) +{ + /* clear buffer to reset show error */ + pevent_buffer_init("", 0); + filter->error_buffer[0] = '\0'; +} + /** * pevent_filter_add_filter_str - add a new filter * @filter: the event filter to add to * @filter_str: the filter string that contains the filter * * Returns 0 if the filter was successfully added or a - * negative error code. + * negative error code. Use pevent_filter_strerror() to see + * actual error message in case of error. */ enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter, const char *filter_str) @@ -1291,10 +1275,8 @@ enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter, enum pevent_errno rtn = 0; /* PEVENT_ERRNO__SUCCESS */ int len; int ret; - char *error_str = NULL; - /* clear buffer to reset show error */ - pevent_buffer_init("", 0); + filter_init_error_buf(filter); filter_start = strchr(filter_str, ':'); if (filter_start) @@ -1353,7 +1335,7 @@ enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter, /* filter starts here */ for (event = events; event; event = event->next) { ret = filter_event(filter, event->event, filter_start, - &error_str); + filter->error_buffer); /* Failures are returned if a parse error happened */ if (ret < 0) rtn = ret; @@ -1381,6 +1363,32 @@ static void free_filter_type(struct filter_type *filter_type) free_arg(filter_type->filter); } +/** + * pevent_filter_strerror - fill error message in a buffer + * @filter: the event filter contains error + * @err: the error code + * @buf: the buffer to be filled in + * @buflen: the size of the buffer + * + * Returns 0 if message was filled successfully, -1 if error + */ +int pevent_filter_strerror(struct event_filter *filter, enum pevent_errno err, + char *buf, size_t buflen) +{ + if (err <= __PEVENT_ERRNO__START || err >= __PEVENT_ERRNO__END) + return -1; + + if (strlen(filter->error_buffer) > 0) { + size_t len = snprintf(buf, buflen, "%s", filter->error_buffer); + + if (len > buflen) + return -1; + return 0; + } + + return pevent_strerror(filter->pevent, err, buf, buflen); +} + /** * pevent_filter_remove_event - remove a filter for an event * @filter: the event filter to remove from @@ -2027,6 +2035,8 @@ enum pevent_errno pevent_filter_match(struct event_filter *filter, int ret; enum pevent_errno err = 0; + filter_init_error_buf(filter); + if (!filter->filters) return PEVENT_ERRNO__NO_FILTER; -- cgit v1.2.3 From 0885cdf79f31accf1454cb155636c1abd75df020 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 19 Dec 2013 14:42:01 +0100 Subject: tools lib traceevent: Remove print_app_build variable Removing print_app_build variable, because it's not needed. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1387460527-15030-4-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Makefile b/Makefile index 0d9cbb4..022c987 100644 --- a/Makefile +++ b/Makefile @@ -151,7 +151,6 @@ override CFLAGS += $(udis86-flags) -D_GNU_SOURCE ifeq ($(VERBOSE),1) Q = print_compile = - print_app_build = print_fpic_compile = print_shared_lib_compile = print_plugin_obj_compile = @@ -160,7 +159,6 @@ ifeq ($(VERBOSE),1) else Q = @ print_compile = echo ' CC '$(OBJ); - print_app_build = echo ' BUILD '$(OBJ); print_fpic_compile = echo ' CC FPIC '$(OBJ); print_shared_lib_compile = echo ' BUILD SHARED LIB '$(OBJ); print_plugin_obj_compile = echo ' CC FPIC '$(OBJ); @@ -173,10 +171,6 @@ do_fpic_compile = \ ($(print_fpic_compile) \ $(CC) -c $(CFLAGS) $(EXT) -fPIC $< -o $@) -do_app_build = \ - ($(print_app_build) \ - $(CC) $^ -rdynamic -o $@ $(CONFIG_LIBS) $(LIBS)) - do_compile_shared_library = \ ($(print_shared_lib_compile) \ $(CC) --shared $^ -o $@) -- cgit v1.2.3 From d7a08754fb84c89bab7b16ca1250f27ef73a8531 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 19 Dec 2013 14:42:02 +0100 Subject: tools lib traceevent: Use global QUIET_CC build output Using global QUIET_CC build output variable and getting rid of local print_compile. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1387460527-15030-5-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 022c987..e852a8d 100644 --- a/Makefile +++ b/Makefile @@ -67,6 +67,8 @@ PLUGIN_DIR = -DPLUGIN_DIR="$(DESTDIR)/$(plugin_dir)" PLUGIN_DIR_SQ = '$(subst ','\'',$(PLUGIN_DIR))' endif +include $(if $(BUILD_SRC),$(BUILD_SRC)/)../../scripts/Makefile.include + # copy a bit from Linux kbuild ifeq ("$(origin V)", "command line") @@ -150,7 +152,6 @@ override CFLAGS += $(udis86-flags) -D_GNU_SOURCE ifeq ($(VERBOSE),1) Q = - print_compile = print_fpic_compile = print_shared_lib_compile = print_plugin_obj_compile = @@ -158,7 +159,6 @@ ifeq ($(VERBOSE),1) print_install = else Q = @ - print_compile = echo ' CC '$(OBJ); print_fpic_compile = echo ' CC FPIC '$(OBJ); print_shared_lib_compile = echo ' BUILD SHARED LIB '$(OBJ); print_plugin_obj_compile = echo ' CC FPIC '$(OBJ); @@ -188,16 +188,13 @@ do_build_static_lib = \ $(RM) $@; $(AR) rcs $@ $^) -define do_compile - $(print_compile) \ - $(CC) -c $(CFLAGS) $(EXT) $< -o $(obj)/$@; -endef +do_compile = $(QUIET_CC)$(CC) -c $(CFLAGS) $(EXT) $< -o $(obj)/$@; $(obj)/%.o: $(src)/%.c - $(Q)$(call do_compile) + $(call do_compile) %.o: $(src)/%.c - $(Q)$(call do_compile) + $(call do_compile) PEVENT_LIB_OBJS = event-parse.o PEVENT_LIB_OBJS += event-plugin.o -- cgit v1.2.3 From 50c87bfd1ad2571bb424720661cee69286e1e613 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 19 Dec 2013 14:42:03 +0100 Subject: tools lib traceevent: Add global QUIET_CC_FPIC build output Adding global QUIET_CC_FPIC build output variable and getting rid of local print_fpic_compile and print_plugin_obj_compile. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1387460527-15030-6-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index e852a8d..24203cc 100644 --- a/Makefile +++ b/Makefile @@ -152,33 +152,21 @@ override CFLAGS += $(udis86-flags) -D_GNU_SOURCE ifeq ($(VERBOSE),1) Q = - print_fpic_compile = print_shared_lib_compile = - print_plugin_obj_compile = print_plugin_build = print_install = else Q = @ - print_fpic_compile = echo ' CC FPIC '$(OBJ); print_shared_lib_compile = echo ' BUILD SHARED LIB '$(OBJ); - print_plugin_obj_compile = echo ' CC FPIC '$(OBJ); print_plugin_build = echo ' BUILD PLUGIN '$(OBJ); print_static_lib_build = echo ' BUILD STATIC LIB '$(OBJ); print_install = echo ' INSTALL '$1; endif -do_fpic_compile = \ - ($(print_fpic_compile) \ - $(CC) -c $(CFLAGS) $(EXT) -fPIC $< -o $@) - do_compile_shared_library = \ ($(print_shared_lib_compile) \ $(CC) --shared $^ -o $@) -do_compile_plugin_obj = \ - ($(print_plugin_obj_compile) \ - $(CC) -c $(CFLAGS) -fPIC -o $@ $<) - do_plugin_build = \ ($(print_plugin_build) \ $(CC) $(CFLAGS) -shared -nostartfiles -o $@ $<) @@ -236,10 +224,10 @@ libtraceevent.a: $(PEVENT_LIB_OBJS) plugins: $(PLUGINS) $(PEVENT_LIB_OBJS): %.o: $(src)/%.c TRACEEVENT-CFLAGS - $(Q)$(do_fpic_compile) + $(QUIET_CC_FPIC)$(CC) -c $(CFLAGS) $(EXT) -fPIC $< -o $@ $(PLUGIN_OBJS): %.o : $(src)/%.c - $(Q)$(do_compile_plugin_obj) + $(QUIET_CC_FPIC)$(CC) -c $(CFLAGS) -fPIC -o $@ $< $(PLUGINS): %.so: %.o $(Q)$(do_plugin_build) -- cgit v1.2.3 From 048b506461a8f8d87ed727a1015724ef2b335dc6 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 19 Dec 2013 14:42:04 +0100 Subject: tools lib traceevent: Use global QUIET_LINK build output Using global QUIET_LINK build output variable and getting rid of local print_static_lib_build, print_plugin_build and print_shared_lib_compile. We no longer distinguish between shared and static library in the build message. It's differenced by the built file suffix, like: $ make ... LINK libtraceevent.a LINK libtraceevent.so Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1387460527-15030-7-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 24203cc..51be8ab 100644 --- a/Makefile +++ b/Makefile @@ -152,14 +152,9 @@ override CFLAGS += $(udis86-flags) -D_GNU_SOURCE ifeq ($(VERBOSE),1) Q = - print_shared_lib_compile = - print_plugin_build = print_install = else Q = @ - print_shared_lib_compile = echo ' BUILD SHARED LIB '$(OBJ); - print_plugin_build = echo ' BUILD PLUGIN '$(OBJ); - print_static_lib_build = echo ' BUILD STATIC LIB '$(OBJ); print_install = echo ' INSTALL '$1; endif @@ -216,10 +211,10 @@ all: all_cmd all_cmd: $(CMD_TARGETS) libtraceevent.so: $(PEVENT_LIB_OBJS) - $(Q)$(do_compile_shared_library) + $(QUIET_LINK)$(CC) --shared $^ -o $@ libtraceevent.a: $(PEVENT_LIB_OBJS) - $(Q)$(do_build_static_lib) + $(QUIET_LINK)$(RM) $@; $(AR) rcs $@ $^ plugins: $(PLUGINS) @@ -230,7 +225,7 @@ $(PLUGIN_OBJS): %.o : $(src)/%.c $(QUIET_CC_FPIC)$(CC) -c $(CFLAGS) -fPIC -o $@ $< $(PLUGINS): %.so: %.o - $(Q)$(do_plugin_build) + $(QUIET_LINK)$(CC) $(CFLAGS) -shared -nostartfiles -o $@ $< define make_version.h (echo '/* This file is automatically generated. Do not modify. */'; \ -- cgit v1.2.3 From 0d333dcb284ac010565fa0c400d34fff2aa9f62d Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 19 Dec 2013 14:42:05 +0100 Subject: tools lib traceevent: Use global QUIET_INSTALL build output Using global QUIET_INSTALL build output variable and factoring plugins installation so we could have only single install message for plugins: INSTALL trace_plugins Getting rid of local print_install. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1387460527-15030-8-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 51be8ab..8ea4368 100644 --- a/Makefile +++ b/Makefile @@ -152,10 +152,8 @@ override CFLAGS += $(udis86-flags) -D_GNU_SOURCE ifeq ($(VERBOSE),1) Q = - print_install = else Q = @ - print_install = echo ' INSTALL '$1; endif do_compile_shared_library = \ @@ -307,22 +305,25 @@ TAGS: force --regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/' define do_install - $(print_install) \ if [ ! -d '$(DESTDIR_SQ)$2' ]; then \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \ fi; \ $(INSTALL) $1 '$(DESTDIR_SQ)$2' endef -install_lib: all_cmd install_plugins - $(Q)$(call do_install,$(LIB_FILE),$(bindir_SQ)) - -PLUGINS_INSTALL = $(subst .so,.install,$(PLUGINS)) +define do_install_plugins + for plugin in $1; do \ + $(call do_install,$$plugin,$(plugin_dir_SQ)); \ + done +endef -$(PLUGINS_INSTALL): %.install : %.so force - $(Q)$(call do_install,$<,$(plugin_dir_SQ)) +install_lib: all_cmd install_plugins + $(call QUIET_INSTALL, $(LIB_FILE)) \ + $(call do_install,$(LIB_FILE),$(bindir_SQ)) -install_plugins: $(PLUGINS_INSTALL) +install_plugins: $(PLUGINS) + $(call QUIET_INSTALL, trace_plugins) \ + $(call do_install_plugins, $(PLUGINS)) install: install_lib -- cgit v1.2.3 From 17643f7878c4bd3a575661597218b7fb47d618a5 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 19 Dec 2013 14:42:06 +0100 Subject: tools lib traceevent: Use global QUIET_CLEAN build output Using global QUIET_CLEAN build output variable and so we could have only single clean message: CLEAN libtraceevent Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1387460527-15030-9-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 8ea4368..3496c9c 100644 --- a/Makefile +++ b/Makefile @@ -328,8 +328,9 @@ install_plugins: $(PLUGINS) install: install_lib clean: - $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d - $(RM) TRACEEVENT-CFLAGS tags TAGS + $(call QUIET_CLEAN, libtraceevent) \ + $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d \ + $(RM) TRACEEVENT-CFLAGS tags TAGS endif # skip-makefile -- cgit v1.2.3 From 88e4f81f0accfe4d8eb742d0037b7ee1ba9d7d44 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 19 Dec 2013 14:42:07 +0100 Subject: tools lib traceevent: Use global 'O' processing code Using global 'O' processing code because it's already setup due to the scripts/Makefile.include include. Using global variable OUTPUT instead of the local BUILD_OUTPUT. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1387460527-15030-10-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 3496c9c..ca4ab78 100644 --- a/Makefile +++ b/Makefile @@ -83,18 +83,13 @@ ifeq ("$(origin O)", "command line") endif ifeq ($(BUILD_SRC),) -ifneq ($(BUILD_OUTPUT),) +ifneq ($(OUTPUT),) define build_output - $(if $(VERBOSE:1=),@)+$(MAKE) -C $(BUILD_OUTPUT) \ - BUILD_SRC=$(CURDIR) -f $(CURDIR)/Makefile $1 + $(if $(VERBOSE:1=),@)+$(MAKE) -C $(OUTPUT) \ + BUILD_SRC=$(CURDIR)/ -f $(CURDIR)/Makefile $1 endef -saved-output := $(BUILD_OUTPUT) -BUILD_OUTPUT := $(shell cd $(BUILD_OUTPUT) && /bin/pwd) -$(if $(BUILD_OUTPUT),, \ - $(error output directory "$(saved-output)" does not exist)) - all: sub-make $(MAKECMDGOALS): sub-make @@ -106,7 +101,7 @@ sub-make: force # Leave processing to above invocation of make skip-makefile := 1 -endif # BUILD_OUTPUT +endif # OUTPUT endif # BUILD_SRC # We process the rest of the Makefile if this is the final invocation of make -- cgit v1.2.3 From c3a132124aa26fefbaf224ebaa4135eac048d026 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 2 Jan 2014 10:53:04 +0100 Subject: tools lib traceevent: Replace tabs with spaces for all non-commands statements The tabbed indentation in non-commands statements could be sometimes considered as follow up for the rule command in the Makefile. This error is hard to find, so as a precaution replacing tabs with spaces for all non-commands statements. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://marc.info/?t=136484403900003&r=1&w=2 Link: http://lkml.kernel.org/r/20140102095304.GA1196@krava.brq.redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/Makefile b/Makefile index ca4ab78..76fe0ae 100644 --- a/Makefile +++ b/Makefile @@ -86,8 +86,8 @@ ifeq ($(BUILD_SRC),) ifneq ($(OUTPUT),) define build_output - $(if $(VERBOSE:1=),@)+$(MAKE) -C $(OUTPUT) \ - BUILD_SRC=$(CURDIR)/ -f $(CURDIR)/Makefile $1 + $(if $(VERBOSE:1=),@)+$(MAKE) -C $(OUTPUT) \ + BUILD_SRC=$(CURDIR)/ -f $(CURDIR)/Makefile $1 endef all: sub-make @@ -221,23 +221,23 @@ $(PLUGINS): %.so: %.o $(QUIET_LINK)$(CC) $(CFLAGS) -shared -nostartfiles -o $@ $< define make_version.h - (echo '/* This file is automatically generated. Do not modify. */'; \ - echo \#define VERSION_CODE $(shell \ - expr $(VERSION) \* 256 + $(PATCHLEVEL)); \ - echo '#define EXTRAVERSION ' $(EXTRAVERSION); \ - echo '#define VERSION_STRING "'$(VERSION).$(PATCHLEVEL).$(EXTRAVERSION)'"'; \ - echo '#define FILE_VERSION '$(FILE_VERSION); \ - ) > $1 + (echo '/* This file is automatically generated. Do not modify. */'; \ + echo \#define VERSION_CODE $(shell \ + expr $(VERSION) \* 256 + $(PATCHLEVEL)); \ + echo '#define EXTRAVERSION ' $(EXTRAVERSION); \ + echo '#define VERSION_STRING "'$(VERSION).$(PATCHLEVEL).$(EXTRAVERSION)'"'; \ + echo '#define FILE_VERSION '$(FILE_VERSION); \ + ) > $1 endef define update_version.h - ($(call make_version.h, $@.tmp); \ - if [ -r $@ ] && cmp -s $@ $@.tmp; then \ - rm -f $@.tmp; \ - else \ - echo ' UPDATE $@'; \ - mv -f $@.tmp $@; \ - fi); + ($(call make_version.h, $@.tmp); \ + if [ -r $@ ] && cmp -s $@ $@.tmp; then \ + rm -f $@.tmp; \ + else \ + echo ' UPDATE $@'; \ + mv -f $@.tmp $@; \ + fi); endef ep_version.h: force @@ -246,13 +246,13 @@ ep_version.h: force VERSION_FILES = ep_version.h define update_dir - (echo $1 > $@.tmp; \ - if [ -r $@ ] && cmp -s $@ $@.tmp; then \ - rm -f $@.tmp; \ - else \ - echo ' UPDATE $@'; \ - mv -f $@.tmp $@; \ - fi); + (echo $1 > $@.tmp; \ + if [ -r $@ ] && cmp -s $@ $@.tmp; then \ + rm -f $@.tmp; \ + else \ + echo ' UPDATE $@'; \ + mv -f $@.tmp $@; \ + fi); endef ## make deps @@ -262,10 +262,10 @@ all_deps := $(all_objs:%.o=.%.d) # let .d file also depends on the source and header files define check_deps - @set -e; $(RM) $@; \ - $(CC) -MM $(CFLAGS) $< > $@.$$$$; \ - sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ - $(RM) $@.$$$$ + @set -e; $(RM) $@; \ + $(CC) -MM $(CFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ + $(RM) $@.$$$$ endef $(all_deps): .%.d: $(src)/%.c -- cgit v1.2.3 From 5fe6fa5f2b6ce70b68563071089c1f0375edcd5a Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Wed, 1 Jan 2014 17:50:50 +0100 Subject: tools lib traceevent: Shut up plugins make message Getting rid of following build output: $ make O=/tmp/build/perf -C tools/perf/ install-bin ... make[3]: Nothing to be done for `plugins'. make[2]: Nothing to be done for `plugins'. ... which triggers when traceevent library needs to be rebuilt, but we have plugins built already. Adding extra 'plugins' target with nop which is visible and triggers in both Makefile parts (for detached output directory (O=...) the traceevent Makefile spawns sub make for the build itself). Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1388595050-23005-2-git-send-email-jolsa@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 76fe0ae..f778d48 100644 --- a/Makefile +++ b/Makefile @@ -329,9 +329,12 @@ clean: endif # skip-makefile -PHONY += force +PHONY += force plugins force: +plugins: + @echo > /dev/null + # Declare the contents of the .PHONY variable as phony. We keep that # information in a variable so we can use it in if_changed and friends. .PHONY: $(PHONY) -- cgit v1.2.3 From 4cf974acf63e71b08b8396eb9cb44b820deb3308 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 19 Dec 2013 18:17:44 +0900 Subject: tools lib traceevent: Add state member to struct trace_seq The trace_seq->state is for tracking errors during the use of trace_seq APIs and getting rid of die() in it. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/87fvopalbb.fsf@sejong.aot.lge.com Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 2 +- event-parse.h | 7 +++++++ trace-seq.c | 55 ++++++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index f778d48..56d52a3 100644 --- a/Makefile +++ b/Makefile @@ -136,7 +136,7 @@ export Q VERBOSE EVENT_PARSE_VERSION = $(EP_VERSION).$(EP_PATCHLEVEL).$(EP_EXTRAVERSION) -INCLUDES = -I. $(CONFIG_INCLUDES) +INCLUDES = -I. -I $(srctree)/../../include $(CONFIG_INCLUDES) # Set compile option CFLAGS if not set elsewhere CFLAGS ?= -g -Wall diff --git a/event-parse.h b/event-parse.h index cf5db90..3c890cb 100644 --- a/event-parse.h +++ b/event-parse.h @@ -58,6 +58,12 @@ struct pevent_record { #endif }; +enum trace_seq_fail { + TRACE_SEQ__GOOD, + TRACE_SEQ__BUFFER_POISONED, + TRACE_SEQ__MEM_ALLOC_FAILED, +}; + /* * Trace sequences are used to allow a function to call several other functions * to create a string of data to use (up to a max of PAGE_SIZE). @@ -68,6 +74,7 @@ struct trace_seq { unsigned int buffer_size; unsigned int len; unsigned int readpos; + enum trace_seq_fail state; }; void trace_seq_init(struct trace_seq *s); diff --git a/trace-seq.c b/trace-seq.c index d7f2e68..f711213 100644 --- a/trace-seq.c +++ b/trace-seq.c @@ -22,6 +22,7 @@ #include #include +#include #include "event-parse.h" #include "event-utils.h" @@ -32,10 +33,21 @@ #define TRACE_SEQ_POISON ((void *)0xdeadbeef) #define TRACE_SEQ_CHECK(s) \ do { \ - if ((s)->buffer == TRACE_SEQ_POISON) \ - die("Usage of trace_seq after it was destroyed"); \ + if (WARN_ONCE((s)->buffer == TRACE_SEQ_POISON, \ + "Usage of trace_seq after it was destroyed")) \ + (s)->state = TRACE_SEQ__BUFFER_POISONED; \ } while (0) +#define TRACE_SEQ_CHECK_RET_N(s, n) \ +do { \ + TRACE_SEQ_CHECK(s); \ + if ((s)->state != TRACE_SEQ__GOOD) \ + return n; \ +} while (0) + +#define TRACE_SEQ_CHECK_RET(s) TRACE_SEQ_CHECK_RET_N(s, ) +#define TRACE_SEQ_CHECK_RET0(s) TRACE_SEQ_CHECK_RET_N(s, 0) + /** * trace_seq_init - initialize the trace_seq structure * @s: a pointer to the trace_seq structure to initialize @@ -46,6 +58,7 @@ void trace_seq_init(struct trace_seq *s) s->readpos = 0; s->buffer_size = TRACE_SEQ_BUF_SIZE; s->buffer = malloc_or_die(s->buffer_size); + s->state = TRACE_SEQ__GOOD; } /** @@ -71,7 +84,7 @@ void trace_seq_destroy(struct trace_seq *s) { if (!s) return; - TRACE_SEQ_CHECK(s); + TRACE_SEQ_CHECK_RET(s); free(s->buffer); s->buffer = TRACE_SEQ_POISON; } @@ -80,8 +93,9 @@ static void expand_buffer(struct trace_seq *s) { s->buffer_size += TRACE_SEQ_BUF_SIZE; s->buffer = realloc(s->buffer, s->buffer_size); - if (!s->buffer) - die("Can't allocate trace_seq buffer memory"); + if (WARN_ONCE(!s->buffer, + "Can't allocate trace_seq buffer memory")) + s->state = TRACE_SEQ__MEM_ALLOC_FAILED; } /** @@ -105,9 +119,9 @@ trace_seq_printf(struct trace_seq *s, const char *fmt, ...) int len; int ret; - TRACE_SEQ_CHECK(s); - try_again: + TRACE_SEQ_CHECK_RET0(s); + len = (s->buffer_size - 1) - s->len; va_start(ap, fmt); @@ -141,9 +155,9 @@ trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args) int len; int ret; - TRACE_SEQ_CHECK(s); - try_again: + TRACE_SEQ_CHECK_RET0(s); + len = (s->buffer_size - 1) - s->len; ret = vsnprintf(s->buffer + s->len, len, fmt, args); @@ -172,13 +186,15 @@ int trace_seq_puts(struct trace_seq *s, const char *str) { int len; - TRACE_SEQ_CHECK(s); + TRACE_SEQ_CHECK_RET0(s); len = strlen(str); while (len > ((s->buffer_size - 1) - s->len)) expand_buffer(s); + TRACE_SEQ_CHECK_RET0(s); + memcpy(s->buffer + s->len, str, len); s->len += len; @@ -187,11 +203,13 @@ int trace_seq_puts(struct trace_seq *s, const char *str) int trace_seq_putc(struct trace_seq *s, unsigned char c) { - TRACE_SEQ_CHECK(s); + TRACE_SEQ_CHECK_RET0(s); while (s->len >= (s->buffer_size - 1)) expand_buffer(s); + TRACE_SEQ_CHECK_RET0(s); + s->buffer[s->len++] = c; return 1; @@ -199,7 +217,7 @@ int trace_seq_putc(struct trace_seq *s, unsigned char c) void trace_seq_terminate(struct trace_seq *s) { - TRACE_SEQ_CHECK(s); + TRACE_SEQ_CHECK_RET(s); /* There's always one character left on the buffer */ s->buffer[s->len] = 0; @@ -208,5 +226,16 @@ void trace_seq_terminate(struct trace_seq *s) int trace_seq_do_printf(struct trace_seq *s) { TRACE_SEQ_CHECK(s); - return printf("%.*s", s->len, s->buffer); + + switch (s->state) { + case TRACE_SEQ__GOOD: + return printf("%.*s", s->len, s->buffer); + case TRACE_SEQ__BUFFER_POISONED: + puts("Usage of trace_seq after it was destroyed"); + break; + case TRACE_SEQ__MEM_ALLOC_FAILED: + puts("Can't allocate trace_seq buffer memory"); + break; + } + return -1; } -- cgit v1.2.3 From 9283fa322228d08a966eeed0d920145bbf89a7c8 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 15 Jan 2014 10:45:25 +0900 Subject: tools lib traceevent: Check return value of realloc() If realloc() fails, it'll leak the buffer. Also increate buffer size only if the allocation succeeded. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389750340-15965-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- trace-seq.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/trace-seq.c b/trace-seq.c index f711213..e454a2c 100644 --- a/trace-seq.c +++ b/trace-seq.c @@ -91,11 +91,16 @@ void trace_seq_destroy(struct trace_seq *s) static void expand_buffer(struct trace_seq *s) { - s->buffer_size += TRACE_SEQ_BUF_SIZE; - s->buffer = realloc(s->buffer, s->buffer_size); - if (WARN_ONCE(!s->buffer, - "Can't allocate trace_seq buffer memory")) + char *buf; + + buf = realloc(s->buffer, s->buffer_size + TRACE_SEQ_BUF_SIZE); + if (WARN_ONCE(!buf, "Can't allocate trace_seq buffer memory")) { s->state = TRACE_SEQ__MEM_ALLOC_FAILED; + return; + } + + s->buffer = buf; + s->buffer_size += TRACE_SEQ_BUF_SIZE; } /** -- cgit v1.2.3 From e069d1ec1594c1de55d953a59a3bff8807e97a56 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 15 Jan 2014 10:45:26 +0900 Subject: tools lib traceevent: Get rid of malloc_or_die() in trace_seq_init() Use plain malloc() and check its return value. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389750340-15965-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- trace-seq.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/trace-seq.c b/trace-seq.c index e454a2c..ec3bd16 100644 --- a/trace-seq.c +++ b/trace-seq.c @@ -57,8 +57,11 @@ void trace_seq_init(struct trace_seq *s) s->len = 0; s->readpos = 0; s->buffer_size = TRACE_SEQ_BUF_SIZE; - s->buffer = malloc_or_die(s->buffer_size); - s->state = TRACE_SEQ__GOOD; + s->buffer = malloc(s->buffer_size); + if (s->buffer != NULL) + s->state = TRACE_SEQ__GOOD; + else + s->state = TRACE_SEQ__MEM_ALLOC_FAILED; } /** -- cgit v1.2.3 From 6b2f57bad028695e8d6a9d48236deead2c70c22e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 15 Jan 2014 10:45:27 +0900 Subject: tools lib traceevent: Get rid of die() finally!! Now all of its users were gone. :) Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389750340-15965-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-utils.h | 4 ---- parse-utils.c | 44 -------------------------------------------- 2 files changed, 48 deletions(-) diff --git a/event-utils.h b/event-utils.h index e76c9ac..d1dc217 100644 --- a/event-utils.h +++ b/event-utils.h @@ -23,18 +23,14 @@ #include /* Can be overridden */ -void die(const char *fmt, ...); -void *malloc_or_die(unsigned int size); void warning(const char *fmt, ...); void pr_stat(const char *fmt, ...); void vpr_stat(const char *fmt, va_list ap); /* Always available */ -void __die(const char *fmt, ...); void __warning(const char *fmt, ...); void __pr_stat(const char *fmt, ...); -void __vdie(const char *fmt, ...); void __vwarning(const char *fmt, ...); void __vpr_stat(const char *fmt, ...); diff --git a/parse-utils.c b/parse-utils.c index bba701c..eda07fa 100644 --- a/parse-utils.c +++ b/parse-utils.c @@ -25,40 +25,6 @@ #define __weak __attribute__((weak)) -void __vdie(const char *fmt, va_list ap) -{ - int ret = errno; - - if (errno) - perror("trace-cmd"); - else - ret = -1; - - fprintf(stderr, " "); - vfprintf(stderr, fmt, ap); - - fprintf(stderr, "\n"); - exit(ret); -} - -void __die(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - __vdie(fmt, ap); - va_end(ap); -} - -void __weak die(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - __vdie(fmt, ap); - va_end(ap); -} - void __vwarning(const char *fmt, va_list ap) { if (errno) @@ -117,13 +83,3 @@ void __weak pr_stat(const char *fmt, ...) __vpr_stat(fmt, ap); va_end(ap); } - -void __weak *malloc_or_die(unsigned int size) -{ - void *data; - - data = malloc(size); - if (!data) - die("malloc"); - return data; -} -- cgit v1.2.3 From e3547ca7f45ece5939304a704e1a8311c9c9c345 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 15 Jan 2014 10:45:28 +0900 Subject: tools lib traceevent: Make plugin unload function receive pevent The PEVENT_PLUGIN_UNLOADER function might need some cleanup using pevent like unregister some handlers. So pass pevent as argument. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389750340-15965-6-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.h | 7 ++++--- event-plugin.c | 4 ++-- plugin_function.c | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/event-parse.h b/event-parse.h index 3c890cb..a3beca5 100644 --- a/event-parse.h +++ b/event-parse.h @@ -105,7 +105,7 @@ typedef int (*pevent_event_handler_func)(struct trace_seq *s, void *context); typedef int (*pevent_plugin_load_func)(struct pevent *pevent); -typedef int (*pevent_plugin_unload_func)(void); +typedef int (*pevent_plugin_unload_func)(struct pevent *pevent); struct plugin_option { struct plugin_option *next; @@ -130,7 +130,7 @@ struct plugin_option { * PEVENT_PLUGIN_UNLOADER: (optional) * The function called just before unloading * - * int PEVENT_PLUGIN_UNLOADER(void) + * int PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) * * PEVENT_PLUGIN_OPTIONS: (optional) * Plugin options that can be set before loading @@ -411,7 +411,8 @@ enum pevent_errno { struct plugin_list; struct plugin_list *traceevent_load_plugins(struct pevent *pevent); -void traceevent_unload_plugins(struct plugin_list *plugin_list); +void traceevent_unload_plugins(struct plugin_list *plugin_list, + struct pevent *pevent); struct cmdline; struct cmdline_list; diff --git a/event-plugin.c b/event-plugin.c index 125f567..0c8bf67 100644 --- a/event-plugin.c +++ b/event-plugin.c @@ -197,7 +197,7 @@ traceevent_load_plugins(struct pevent *pevent) } void -traceevent_unload_plugins(struct plugin_list *plugin_list) +traceevent_unload_plugins(struct plugin_list *plugin_list, struct pevent *pevent) { pevent_plugin_unload_func func; struct plugin_list *list; @@ -207,7 +207,7 @@ traceevent_unload_plugins(struct plugin_list *plugin_list) plugin_list = list->next; func = dlsym(list->handle, PEVENT_PLUGIN_UNLOADER_NAME); if (func) - func(); + func(pevent); dlclose(list->handle); free(list->name); free(list); diff --git a/plugin_function.c b/plugin_function.c index aad92ad..3946148 100644 --- a/plugin_function.c +++ b/plugin_function.c @@ -144,7 +144,7 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent) return 0; } -void PEVENT_PLUGIN_UNLOADER(void) +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) { int i, x; -- cgit v1.2.3 From b20d5fe877bd33d1c6fe17f803cbba4bb056ba5d Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Wed, 15 Jan 2014 10:44:07 +0000 Subject: tools lib traceevent: fix pointer-integer size mismatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The scsi and cfg80211 plugins cast between unsigned long long and pointers, which is problematic for architectures where unsigned long long is wider than the native pointer size: linux/tools/lib/traceevent/plugin_scsi.c: In function ‘process_scsi_trace_parse_cdb’: linux/tools/lib/traceevent/plugin_scsi.c:408:26: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] scsi_trace_parse_cdb(s, (unsigned char *) args[1], args[2]); linux/tools/lib/traceevent/plugin_cfg80211.c: In function ‘process___le16_to_cpup’: linux/tools/lib/traceevent/plugin_cfg80211.c:11:18: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] uint16_t *val = (uint16_t *) args[0]; This patch adds an intermediate cast to unsigned long, silencing the warning. Signed-off-by: Mark Rutland Acked-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Ingo Molnar Cc: Jiri Olsa Cc: Will Deacon Link: http://lkml.kernel.org/r/1389782648-4417-3-git-send-email-mark.rutland@arm.com Signed-off-by: Arnaldo Carvalho de Melo --- plugin_cfg80211.c | 2 +- plugin_scsi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin_cfg80211.c b/plugin_cfg80211.c index dcab8e8..57e9822 100644 --- a/plugin_cfg80211.c +++ b/plugin_cfg80211.c @@ -8,7 +8,7 @@ static unsigned long long process___le16_to_cpup(struct trace_seq *s, unsigned long long *args) { - uint16_t *val = (uint16_t *) args[0]; + uint16_t *val = (uint16_t *) (unsigned long) args[0]; return val ? (long long) le16toh(*val) : 0; } diff --git a/plugin_scsi.c b/plugin_scsi.c index 6fb8e3e..7ef16cc 100644 --- a/plugin_scsi.c +++ b/plugin_scsi.c @@ -405,7 +405,7 @@ scsi_trace_parse_cdb(struct trace_seq *p, unsigned char *cdb, int len) unsigned long long process_scsi_trace_parse_cdb(struct trace_seq *s, unsigned long long *args) { - scsi_trace_parse_cdb(s, (unsigned char *) args[1], args[2]); + scsi_trace_parse_cdb(s, (unsigned char *) (unsigned long) args[1], args[2]); return 0; } -- cgit v1.2.3 From 1ac3f18aa465c7c2c13c12b7792b30193758c48d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 16 Jan 2014 11:31:07 +0900 Subject: tools lib traceevent: Add pevent_unregister_event_handler() When a plugin is unloaded it needs to unregister its handler from pevent. So add an unregister function to do it. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389839478-5887-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- event-parse.h | 3 ++ 2 files changed, 102 insertions(+), 14 deletions(-) diff --git a/event-parse.c b/event-parse.c index 2ce565a..d1973cb 100644 --- a/event-parse.c +++ b/event-parse.c @@ -5560,6 +5560,29 @@ int pevent_register_print_function(struct pevent *pevent, return ret; } +static struct event_format *pevent_search_event(struct pevent *pevent, int id, + const char *sys_name, + const char *event_name) +{ + struct event_format *event; + + if (id >= 0) { + /* search by id */ + event = pevent_find_event(pevent, id); + if (!event) + return NULL; + if (event_name && (strcmp(event_name, event->name) != 0)) + return NULL; + if (sys_name && (strcmp(sys_name, event->system) != 0)) + return NULL; + } else { + event = pevent_find_event_by_name(pevent, sys_name, event_name); + if (!event) + return NULL; + } + return event; +} + /** * pevent_register_event_handler - register a way to parse an event * @pevent: the handle to the pevent @@ -5584,20 +5607,9 @@ int pevent_register_event_handler(struct pevent *pevent, int id, struct event_format *event; struct event_handler *handle; - if (id >= 0) { - /* search by id */ - event = pevent_find_event(pevent, id); - if (!event) - goto not_found; - if (event_name && (strcmp(event_name, event->name) != 0)) - goto not_found; - if (sys_name && (strcmp(sys_name, event->system) != 0)) - goto not_found; - } else { - event = pevent_find_event_by_name(pevent, sys_name, event_name); - if (!event) - goto not_found; - } + event = pevent_search_event(pevent, id, sys_name, event_name); + if (event == NULL) + goto not_found; pr_stat("overriding event (%d) %s:%s with new print handler", event->id, event->system, event->name); @@ -5637,6 +5649,79 @@ int pevent_register_event_handler(struct pevent *pevent, int id, return -1; } +static int handle_matches(struct event_handler *handler, int id, + const char *sys_name, const char *event_name, + pevent_event_handler_func func, void *context) +{ + if (id >= 0 && id != handler->id) + return 0; + + if (event_name && (strcmp(event_name, handler->event_name) != 0)) + return 0; + + if (sys_name && (strcmp(sys_name, handler->sys_name) != 0)) + return 0; + + if (func != handler->func || context != handler->context) + return 0; + + return 1; +} + +/** + * pevent_unregister_event_handler - unregister an existing event handler + * @pevent: the handle to the pevent + * @id: the id of the event to unregister + * @sys_name: the system name the handler belongs to + * @event_name: the name of the event handler + * @func: the function to call to parse the event information + * @context: the data to be passed to @func + * + * This function removes existing event handler (parser). + * + * If @id is >= 0, then it is used to find the event. + * else @sys_name and @event_name are used. + * + * Returns 0 if handler was removed successfully, -1 if event was not found. + */ +int pevent_unregister_event_handler(struct pevent *pevent, int id, + const char *sys_name, const char *event_name, + pevent_event_handler_func func, void *context) +{ + struct event_format *event; + struct event_handler *handle; + struct event_handler **next; + + event = pevent_search_event(pevent, id, sys_name, event_name); + if (event == NULL) + goto not_found; + + if (event->handler == func && event->context == context) { + pr_stat("removing override handler for event (%d) %s:%s. Going back to default handler.", + event->id, event->system, event->name); + + event->handler = NULL; + event->context = NULL; + return 0; + } + +not_found: + for (next = &pevent->handlers; *next; next = &(*next)->next) { + handle = *next; + if (handle_matches(handle, id, sys_name, event_name, + func, context)) + break; + } + + if (!(*next)) + return -1; + + *next = handle->next; + free_handler(handle); + + return 0; +} + /** * pevent_alloc - create a pevent handle */ diff --git a/event-parse.h b/event-parse.h index a3beca5..c48acfb 100644 --- a/event-parse.h +++ b/event-parse.h @@ -624,6 +624,9 @@ int pevent_print_func_field(struct trace_seq *s, const char *fmt, int pevent_register_event_handler(struct pevent *pevent, int id, const char *sys_name, const char *event_name, pevent_event_handler_func func, void *context); +int pevent_unregister_event_handler(struct pevent *pevent, int id, + const char *sys_name, const char *event_name, + pevent_event_handler_func func, void *context); int pevent_register_print_function(struct pevent *pevent, pevent_func_handler func, enum pevent_func_arg_type ret_type, -- cgit v1.2.3 From 3551bfee0264319b2d5361cf4797686581898cd9 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 16 Jan 2014 11:31:08 +0900 Subject: tools lib traceevent: Add pevent_unregister_print_function() When a plugin unloaded it needs to unregister its print handler from pevent. So add an unregister function to do it. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389839478-5887-3-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- event-parse.c | 23 +++++++++++++++++++++++ event-parse.h | 2 ++ 2 files changed, 25 insertions(+) diff --git a/event-parse.c b/event-parse.c index d1973cb..1587ea3 100644 --- a/event-parse.c +++ b/event-parse.c @@ -5560,6 +5560,29 @@ int pevent_register_print_function(struct pevent *pevent, return ret; } +/** + * pevent_unregister_print_function - unregister a helper function + * @pevent: the handle to the pevent + * @func: the function to process the helper function + * @name: the name of the helper function + * + * This function removes existing print handler for function @name. + * + * Returns 0 if the handler was removed successully, -1 otherwise. + */ +int pevent_unregister_print_function(struct pevent *pevent, + pevent_func_handler func, char *name) +{ + struct pevent_function_handler *func_handle; + + func_handle = find_func_handler(pevent, name); + if (func_handle && func_handle->func == func) { + remove_func_handler(pevent, name); + return 0; + } + return -1; +} + static struct event_format *pevent_search_event(struct pevent *pevent, int id, const char *sys_name, const char *event_name) diff --git a/event-parse.h b/event-parse.h index c48acfb..791c539 100644 --- a/event-parse.h +++ b/event-parse.h @@ -631,6 +631,8 @@ int pevent_register_print_function(struct pevent *pevent, pevent_func_handler func, enum pevent_func_arg_type ret_type, char *name, ...); +int pevent_unregister_print_function(struct pevent *pevent, + pevent_func_handler func, char *name); struct format_field *pevent_find_common_field(struct event_format *event, const char *name); struct format_field *pevent_find_field(struct event_format *event, const char *name); -- cgit v1.2.3 From dbc6b5a2d5babd45758fb72d34766ef383387d9c Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 16 Jan 2014 11:31:09 +0900 Subject: tools lib traceevent: Unregister handler when function plugin is unloaded The function handler should be unregistered when the plugin is unloaded otherwise it'll try to access invalid memory. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389839478-5887-4-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- plugin_function.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugin_function.c b/plugin_function.c index 3946148..80ba4ff 100644 --- a/plugin_function.c +++ b/plugin_function.c @@ -148,6 +148,9 @@ void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) { int i, x; + pevent_unregister_event_handler(pevent, -1, "ftrace", "function", + function_handler, NULL); + for (i = 0; i <= cpus; i++) { for (x = 0; x < fstack[i].size && fstack[i].stack[x]; x++) free(fstack[i].stack[x]); -- cgit v1.2.3 From 5b3d960ea46e0972d79607349abcd865edf405a7 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 16 Jan 2014 11:31:10 +0900 Subject: tools lib traceevent: Unregister handler when hrtimer plugin is unloaded The timer handlers should be unregistered when the plugin is unloaded otherwise they'll try to access invalid memory. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389839478-5887-5-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- plugin_hrtimer.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugin_hrtimer.c b/plugin_hrtimer.c index 0b0ebf3..12bf14c 100644 --- a/plugin_hrtimer.c +++ b/plugin_hrtimer.c @@ -76,3 +76,13 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent) timer_start_handler, NULL); return 0; } + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_event_handler(pevent, -1, + "timer", "hrtimer_expire_entry", + timer_expire_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "timer", "hrtimer_start", + timer_start_handler, NULL); +} -- cgit v1.2.3 From 37176b279049d68eeca12ce7d224d72554c0b268 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 16 Jan 2014 11:31:11 +0900 Subject: tools lib traceevent: Unregister handler when kmem plugin is unloaded The kmem handlers should be unregistered when the plugin is unloaded otherwise they'll try to access invalid memory. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389839478-5887-6-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- plugin_kmem.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/plugin_kmem.c b/plugin_kmem.c index 7115c80..70650ff 100644 --- a/plugin_kmem.c +++ b/plugin_kmem.c @@ -70,3 +70,25 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent) call_site_handler, NULL); return 0; } + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_event_handler(pevent, -1, "kmem", "kfree", + call_site_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kmem", "kmalloc", + call_site_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kmem", "kmalloc_node", + call_site_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kmem", "kmem_cache_alloc", + call_site_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kmem", + "kmem_cache_alloc_node", + call_site_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kmem", "kmem_cache_free", + call_site_handler, NULL); +} -- cgit v1.2.3 From 8a5a7c8a784f1ab3380f2f64193da194356af8c3 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 16 Jan 2014 11:31:12 +0900 Subject: tools lib traceevent: Unregister handler when kvm plugin is unloaded The kvm handlers should be unregistered when the plugin is unloaded otherwise they'll try to access invalid memory. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389839478-5887-7-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- plugin_kvm.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/plugin_kvm.c b/plugin_kvm.c index a0e282c..9e0e8c6 100644 --- a/plugin_kvm.c +++ b/plugin_kvm.c @@ -434,3 +434,32 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent) PEVENT_FUNC_ARG_VOID); return 0; } + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_exit", + kvm_exit_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_emulate_insn", + kvm_emulate_insn_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_get_page", + kvm_mmu_get_page_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_sync_page", + kvm_mmu_print_role, NULL); + + pevent_unregister_event_handler(pevent, -1, + "kvmmmu", "kvm_mmu_unsync_page", + kvm_mmu_print_role, NULL); + + pevent_unregister_event_handler(pevent, -1, "kvmmmu", "kvm_mmu_zap_page", + kvm_mmu_print_role, NULL); + + pevent_unregister_event_handler(pevent, -1, "kvmmmu", + "kvm_mmu_prepare_zap_page", kvm_mmu_print_role, + NULL); + + pevent_unregister_print_function(pevent, process_is_writable_pte, + "is_writable_pte"); +} -- cgit v1.2.3 From 8294e43c74279860e67416f5291b6bfae4ec3c92 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 16 Jan 2014 11:31:13 +0900 Subject: tools lib traceevent: Unregister handler when sched_switch plugin is unloaded The event handlers should be unregistered when the plugin is unloaded otherwise they'll try to access invalid memory. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389839478-5887-8-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- plugin_sched_switch.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/plugin_sched_switch.c b/plugin_sched_switch.c index fea3724..f1ce600 100644 --- a/plugin_sched_switch.c +++ b/plugin_sched_switch.c @@ -146,3 +146,15 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent) sched_wakeup_handler, NULL); return 0; } + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_event_handler(pevent, -1, "sched", "sched_switch", + sched_switch_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "sched", "sched_wakeup", + sched_wakeup_handler, NULL); + + pevent_unregister_event_handler(pevent, -1, "sched", "sched_wakeup_new", + sched_wakeup_handler, NULL); +} -- cgit v1.2.3 From 12049f5fa04e748f6b7675da96035d192bd8c1d9 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 16 Jan 2014 11:31:14 +0900 Subject: tools lib traceevent: Unregister handler when mac80211 plugin is unloaded The event handler should be unregistered when the plugin is unloaded otherwise it'll try to access invalid memory. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389839478-5887-9-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- plugin_mac80211.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugin_mac80211.c b/plugin_mac80211.c index 558a3b9..7e15a0f 100644 --- a/plugin_mac80211.c +++ b/plugin_mac80211.c @@ -93,3 +93,10 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent) drv_bss_info_changed, NULL); return 0; } + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_event_handler(pevent, -1, "mac80211", + "drv_bss_info_changed", + drv_bss_info_changed, NULL); +} -- cgit v1.2.3 From 93f6a9867f38700c83d8d7639cdb97b68a252353 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 16 Jan 2014 11:31:15 +0900 Subject: tools lib traceevent: Unregister handler when cfg80211 plugin is unloaded The function handler should be unregistered when the plugin is unloaded otherwise it'll try to access invalid memory. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389839478-5887-10-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- plugin_cfg80211.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugin_cfg80211.c b/plugin_cfg80211.c index 57e9822..c066b25 100644 --- a/plugin_cfg80211.c +++ b/plugin_cfg80211.c @@ -22,3 +22,9 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent) PEVENT_FUNC_ARG_VOID); return 0; } + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_print_function(pevent, process___le16_to_cpup, + "__le16_to_cpup"); +} -- cgit v1.2.3 From acf9c272a20998434d974a050ef565208f47fcf9 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 16 Jan 2014 11:31:16 +0900 Subject: tools lib traceevent: Unregister handler when jbd2 plugin is is unloaded The function handlers should be unregistered when the plugin unloaded otherwise they'll try to access invalid memory. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389839478-5887-11-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- plugin_jbd2.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugin_jbd2.c b/plugin_jbd2.c index 2f93f81..0db714c 100644 --- a/plugin_jbd2.c +++ b/plugin_jbd2.c @@ -66,3 +66,12 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent) PEVENT_FUNC_ARG_VOID); return 0; } + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_print_function(pevent, process_jbd2_dev_to_name, + "jbd2_dev_to_name"); + + pevent_unregister_print_function(pevent, process_jiffies_to_msecs, + "jiffies_to_msecs"); +} -- cgit v1.2.3 From e6c8d9f38433fd548b759e274b67275b80f7b710 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 16 Jan 2014 11:31:17 +0900 Subject: tools lib traceevent: Unregister handler when scsi plugin is unloaded The function handler should be unregistered when the plugin is unloaded otherwise it'll try to access invalid memory. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389839478-5887-12-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- plugin_scsi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugin_scsi.c b/plugin_scsi.c index 7ef16cc..eda326f 100644 --- a/plugin_scsi.c +++ b/plugin_scsi.c @@ -421,3 +421,9 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent) PEVENT_FUNC_ARG_VOID); return 0; } + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_print_function(pevent, process_scsi_trace_parse_cdb, + "scsi_trace_parse_cdb"); +} -- cgit v1.2.3 From d541027763e22113ac11da04fddfc3b5a7a40390 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 16 Jan 2014 11:31:18 +0900 Subject: tools lib traceevent: Unregister handler when xen plugin is unloaded The function handler should be unregistered when the plugin is unloaded otherwise it'll try to access invalid memory. Signed-off-by: Namhyung Kim Reviewed-by: Jiri Olsa Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1389839478-5887-13-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- plugin_xen.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugin_xen.c b/plugin_xen.c index e779429..3a413ea 100644 --- a/plugin_xen.c +++ b/plugin_xen.c @@ -128,3 +128,9 @@ int PEVENT_PLUGIN_LOADER(struct pevent *pevent) PEVENT_FUNC_ARG_VOID); return 0; } + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ + pevent_unregister_print_function(pevent, process_xen_hypercall_name, + "xen_hypercall_name"); +} -- cgit v1.2.3 From 4d1e2c593db948b071ad915e82d0be60b111575f Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Wed, 22 Jan 2014 10:01:48 -0500 Subject: perf tools: Fix traceevent plugin path definitions The plugindir_SQ definition contains $(prefix) which is not needed as the $(libdir) definition already contains prefix in it. This leads to the path including an extra prefix in it, e.g. /usr/usr/lib64. The -DPLUGIN_DIR defintion includes DESTDIR. This is incorrect, as it sets the plugin search path to include the value of DESTDIR. DESTDIR is a mechanism to install in a non-standard location such as a chroot or an RPM build root. In the RPM case, this leads to the search path being incorrect after the resulting RPM is installed (or in some cases an RPM build failure). Remove both of these unnecessary inclusions. Signed-off-by: Josh Boyer Acked-by: Jiri Olsa Cc: Ingo Molnar Cc: Jiri Olsa Cc: Steven Rostedt Link: http://lkml.kernel.org/r/20140122150147.GK16455@hansolo.jdub.homelinux.org Signed-off-by: Arnaldo Carvalho de Melo --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 56d52a3..005c9cc 100644 --- a/Makefile +++ b/Makefile @@ -63,7 +63,7 @@ endif endif ifeq ($(set_plugin_dir),1) -PLUGIN_DIR = -DPLUGIN_DIR="$(DESTDIR)/$(plugin_dir)" +PLUGIN_DIR = -DPLUGIN_DIR="$(plugin_dir)" PLUGIN_DIR_SQ = '$(subst ','\'',$(PLUGIN_DIR))' endif -- cgit v1.2.3 From b560ee874a8c57bbe8fef099332867410a3de4fd Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 19 Mar 2014 10:22:53 +0900 Subject: tools lib traceevent: Print event name when show warning if possible It's sometimes useful to know where the parse failure was occurred. Add do_warning_event() macro to see the failing event. It now shows the messages like below: $ perf test 5 5: parse events tests : Warning: [kvmmmu:kvm_mmu_get_page] bad op token { Warning: [kvmmmu:kvm_mmu_sync_page] bad op token { Warning: [kvmmmu:kvm_mmu_unsync_page] bad op token { Warning: [kvmmmu:kvm_mmu_prepare_zap_page] bad op token { Warning: [kvmmmu:fast_page_fault] function is_writable_pte not defined Warning: [xen:xen_mmu_ptep_modify_prot_commit] function sizeof not defined Warning: [xen:xen_mmu_ptep_modify_prot_start] function sizeof not defined Warning: [xen:xen_mmu_set_pgd] function sizeof not defined Warning: [xen:xen_mmu_set_pud] function sizeof not defined Warning: [xen:xen_mmu_set_pmd] function sizeof not defined ... Signed-off-by: Namhyung Kim Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ramkumar Ramachandra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1395192174-26273-1-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Jiri Olsa --- event-parse.c | 103 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 62 insertions(+), 41 deletions(-) diff --git a/event-parse.c b/event-parse.c index 1587ea3..33803c0 100644 --- a/event-parse.c +++ b/event-parse.c @@ -50,6 +50,18 @@ static int show_warning = 1; warning(fmt, ##__VA_ARGS__); \ } while (0) +#define do_warning_event(event, fmt, ...) \ + do { \ + if (!show_warning) \ + continue; \ + \ + if (event) \ + warning("[%s:%s] " fmt, event->system, \ + event->name, ##__VA_ARGS__); \ + else \ + warning(fmt, ##__VA_ARGS__); \ + } while (0) + static void init_input_buf(const char *buf, unsigned long long size) { input_buf = buf; @@ -1355,7 +1367,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f } if (!field->type) { - do_warning("%s: no type found", __func__); + do_warning_event(event, "%s: no type found", __func__); goto fail; } field->name = last_token; @@ -1402,7 +1414,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f free_token(token); type = read_token(&token); if (type == EVENT_NONE) { - do_warning("failed to find token"); + do_warning_event(event, "failed to find token"); goto fail; } } @@ -1636,7 +1648,7 @@ process_cond(struct event_format *event, struct print_arg *top, char **tok) right = alloc_arg(); if (!arg || !left || !right) { - do_warning("%s: not enough memory!", __func__); + do_warning_event(event, "%s: not enough memory!", __func__); /* arg will be freed at out_free */ free_arg(left); free_arg(right); @@ -1686,7 +1698,7 @@ process_array(struct event_format *event, struct print_arg *top, char **tok) arg = alloc_arg(); if (!arg) { - do_warning("%s: not enough memory!", __func__); + do_warning_event(event, "%s: not enough memory!", __func__); /* '*tok' is set to top->op.op. No need to free. */ *tok = NULL; return EVENT_ERROR; @@ -1792,7 +1804,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) if (arg->type == PRINT_OP && !arg->op.left) { /* handle single op */ if (token[1]) { - do_warning("bad op token %s", token); + do_warning_event(event, "bad op token %s", token); goto out_free; } switch (token[0]) { @@ -1802,7 +1814,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) case '-': break; default: - do_warning("bad op token %s", token); + do_warning_event(event, "bad op token %s", token); goto out_free; } @@ -1888,7 +1900,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) char *new_atom; if (left->type != PRINT_ATOM) { - do_warning("bad pointer type"); + do_warning_event(event, "bad pointer type"); goto out_free; } new_atom = realloc(left->atom.atom, @@ -1930,7 +1942,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) type = process_array(event, arg, tok); } else { - do_warning("unknown op '%s'", token); + do_warning_event(event, "unknown op '%s'", token); event->flags |= EVENT_FL_FAILED; /* the arg is now the left side */ goto out_free; @@ -1951,7 +1963,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) return type; out_warn_free: - do_warning("%s: not enough memory!", __func__); + do_warning_event(event, "%s: not enough memory!", __func__); out_free: free_token(token); *tok = NULL; @@ -2385,7 +2397,7 @@ process_flags(struct event_format *event, struct print_arg *arg, char **tok) field = alloc_arg(); if (!field) { - do_warning("%s: not enough memory!", __func__); + do_warning_event(event, "%s: not enough memory!", __func__); goto out_free; } @@ -2438,7 +2450,7 @@ process_symbols(struct event_format *event, struct print_arg *arg, char **tok) field = alloc_arg(); if (!field) { - do_warning("%s: not enough memory!", __func__); + do_warning_event(event, "%s: not enough memory!", __func__); goto out_free; } @@ -2477,7 +2489,7 @@ process_hex(struct event_format *event, struct print_arg *arg, char **tok) field = alloc_arg(); if (!field) { - do_warning("%s: not enough memory!", __func__); + do_warning_event(event, "%s: not enough memory!", __func__); goto out_free; } @@ -2492,7 +2504,7 @@ process_hex(struct event_format *event, struct print_arg *arg, char **tok) field = alloc_arg(); if (!field) { - do_warning("%s: not enough memory!", __func__); + do_warning_event(event, "%s: not enough memory!", __func__); *tok = NULL; return EVENT_ERROR; } @@ -2555,7 +2567,7 @@ process_dynamic_array(struct event_format *event, struct print_arg *arg, char ** free_token(token); arg = alloc_arg(); if (!arg) { - do_warning("%s: not enough memory!", __func__); + do_warning_event(event, "%s: not enough memory!", __func__); *tok = NULL; return EVENT_ERROR; } @@ -2614,13 +2626,14 @@ process_paren(struct event_format *event, struct print_arg *arg, char **tok) /* prevous must be an atom */ if (arg->type != PRINT_ATOM) { - do_warning("previous needed to be PRINT_ATOM"); + do_warning_event(event, "previous needed to be PRINT_ATOM"); goto out_free; } item_arg = alloc_arg(); if (!item_arg) { - do_warning("%s: not enough memory!", __func__); + do_warning_event(event, "%s: not enough memory!", + __func__); goto out_free; } @@ -2721,7 +2734,8 @@ process_func_handler(struct event_format *event, struct pevent_function_handler for (i = 0; i < func->nr_args; i++) { farg = alloc_arg(); if (!farg) { - do_warning("%s: not enough memory!", __func__); + do_warning_event(event, "%s: not enough memory!", + __func__); return EVENT_ERROR; } @@ -2792,7 +2806,7 @@ process_function(struct event_format *event, struct print_arg *arg, return process_func_handler(event, func, arg, tok); } - do_warning("function %s not defined", token); + do_warning_event(event, "function %s not defined", token); free_token(token); return EVENT_ERROR; } @@ -2878,7 +2892,7 @@ process_arg_token(struct event_format *event, struct print_arg *arg, case EVENT_ERROR ... EVENT_NEWLINE: default: - do_warning("unexpected type %d", type); + do_warning_event(event, "unexpected type %d", type); return EVENT_ERROR; } *tok = token; @@ -2901,7 +2915,8 @@ static int event_read_print_args(struct event_format *event, struct print_arg ** arg = alloc_arg(); if (!arg) { - do_warning("%s: not enough memory!", __func__); + do_warning_event(event, "%s: not enough memory!", + __func__); return -1; } @@ -3481,11 +3496,12 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg return val; out_warning_op: - do_warning("%s: unknown op '%s'", __func__, arg->op.op); + do_warning_event(event, "%s: unknown op '%s'", __func__, arg->op.op); return 0; out_warning_field: - do_warning("%s: field %s not found", __func__, arg->field.name); + do_warning_event(event, "%s: field %s not found", + __func__, arg->field.name); return 0; } @@ -3591,7 +3607,8 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, } str = malloc(len + 1); if (!str) { - do_warning("%s: not enough memory!", __func__); + do_warning_event(event, "%s: not enough memory!", + __func__); return; } memcpy(str, data + field->offset, len); @@ -3697,7 +3714,8 @@ static void print_str_arg(struct trace_seq *s, void *data, int size, return; out_warning_field: - do_warning("%s: field %s not found", __func__, arg->field.name); + do_warning_event(event, "%s: field %s not found", + __func__, arg->field.name); } static unsigned long long @@ -3742,14 +3760,16 @@ process_defined_func(struct trace_seq *s, void *data, int size, trace_seq_terminate(&str); string = malloc(sizeof(*string)); if (!string) { - do_warning("%s(%d): malloc str", __func__, __LINE__); + do_warning_event(event, "%s(%d): malloc str", + __func__, __LINE__); goto out_free; } string->next = strings; string->str = strdup(str.buffer); if (!string->str) { free(string); - do_warning("%s(%d): malloc str", __func__, __LINE__); + do_warning_event(event, "%s(%d): malloc str", + __func__, __LINE__); goto out_free; } args[i] = (uintptr_t)string->str; @@ -3761,7 +3781,7 @@ process_defined_func(struct trace_seq *s, void *data, int size, * Something went totally wrong, this is not * an input error, something in this code broke. */ - do_warning("Unexpected end of arguments\n"); + do_warning_event(event, "Unexpected end of arguments\n"); goto out_free; } farg = farg->next; @@ -3811,12 +3831,12 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc if (!field) { field = pevent_find_field(event, "buf"); if (!field) { - do_warning("can't find buffer field for binary printk"); + do_warning_event(event, "can't find buffer field for binary printk"); return NULL; } ip_field = pevent_find_field(event, "ip"); if (!ip_field) { - do_warning("can't find ip field for binary printk"); + do_warning_event(event, "can't find ip field for binary printk"); return NULL; } pevent->bprint_buf_field = field; @@ -3830,7 +3850,8 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc */ args = alloc_arg(); if (!args) { - do_warning("%s(%d): not enough memory!", __func__, __LINE__); + do_warning_event(event, "%s(%d): not enough memory!", + __func__, __LINE__); return NULL; } arg = args; @@ -3896,7 +3917,7 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc bptr += vsize; arg = alloc_arg(); if (!arg) { - do_warning("%s(%d): not enough memory!", + do_warning_event(event, "%s(%d): not enough memory!", __func__, __LINE__); goto out_free; } @@ -3919,7 +3940,7 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc case 's': arg = alloc_arg(); if (!arg) { - do_warning("%s(%d): not enough memory!", + do_warning_event(event, "%s(%d): not enough memory!", __func__, __LINE__); goto out_free; } @@ -3959,7 +3980,7 @@ get_bprint_format(void *data, int size __maybe_unused, if (!field) { field = pevent_find_field(event, "fmt"); if (!field) { - do_warning("can't find format field for binary printk"); + do_warning_event(event, "can't find format field for binary printk"); return NULL; } pevent->bprint_fmt_field = field; @@ -4003,8 +4024,8 @@ static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size, arg->field.field = pevent_find_any_field(event, arg->field.name); if (!arg->field.field) { - do_warning("%s: field %s not found", - __func__, arg->field.name); + do_warning_event(event, "%s: field %s not found", + __func__, arg->field.name); return; } } @@ -4176,7 +4197,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event case '*': /* The argument is the length. */ if (!arg) { - do_warning("no argument match"); + do_warning_event(event, "no argument match"); event->flags |= EVENT_FL_FAILED; goto out_failed; } @@ -4213,7 +4234,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event case 'X': case 'u': if (!arg) { - do_warning("no argument match"); + do_warning_event(event, "no argument match"); event->flags |= EVENT_FL_FAILED; goto out_failed; } @@ -4223,7 +4244,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event /* should never happen */ if (len > 31) { - do_warning("bad format!"); + do_warning_event(event, "bad format!"); event->flags |= EVENT_FL_FAILED; len = 31; } @@ -4290,13 +4311,13 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event trace_seq_printf(s, format, (long long)val); break; default: - do_warning("bad count (%d)", ls); + do_warning_event(event, "bad count (%d)", ls); event->flags |= EVENT_FL_FAILED; } break; case 's': if (!arg) { - do_warning("no matching argument"); + do_warning_event(event, "no matching argument"); event->flags |= EVENT_FL_FAILED; goto out_failed; } @@ -4306,7 +4327,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event /* should never happen */ if (len > 31) { - do_warning("bad format!"); + do_warning_event(event, "bad format!"); event->flags |= EVENT_FL_FAILED; len = 31; } -- cgit v1.2.3 From 99f2ccf983accc93457631beaa035955c01c51b3 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 19 Mar 2014 10:22:54 +0900 Subject: tools lib traceevent: Do not call warning() directly The patch 3a3ffa2e82205 ("tools lib traceevent: Report better error message on bad function args") added the error message but it seems there's no reason to call warning() directly. So change it to do_warning_event() to provide event information too. Signed-off-by: Namhyung Kim Acked-by: Steven Rostedt Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt Link: http://lkml.kernel.org/r/1395192174-26273-2-git-send-email-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Jiri Olsa --- event-parse.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/event-parse.c b/event-parse.c index 33803c0..baec7d8 100644 --- a/event-parse.c +++ b/event-parse.c @@ -2742,14 +2742,16 @@ process_func_handler(struct event_format *event, struct pevent_function_handler type = process_arg(event, farg, &token); if (i < (func->nr_args - 1)) { if (type != EVENT_DELIM || strcmp(token, ",") != 0) { - warning("Error: function '%s()' expects %d arguments but event %s only uses %d", + do_warning_event(event, + "Error: function '%s()' expects %d arguments but event %s only uses %d", func->name, func->nr_args, event->name, i + 1); goto err; } } else { if (type != EVENT_DELIM || strcmp(token, ")") != 0) { - warning("Error: function '%s()' only expects %d arguments but event %s has more", + do_warning_event(event, + "Error: function '%s()' only expects %d arguments but event %s has more", func->name, func->nr_args, event->name); goto err; } -- cgit v1.2.3 From 5dee5b0228eb7fcbfb483df2d16fdce0a0522d13 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 21 Apr 2014 22:23:46 -0400 Subject: tools lib traceevent: Fix backward compatibility macros for pevent filter enums The return value for pevent_filter_match() is suppose to return FILTER_NONE if the event doesn't have a filter, and FILTER_NOEXIST if there is no filter at all. But the change 41e12e580a7 "tools lib traceevent: Refactor pevent_filter_match() to get rid of die()" replaced the return value with PEVENT_ERRNO__* values and added "backward compatibility" macros that used the old names. Unfortunately, the NOEXIST and NONE macros were swapped, and this broke users that use the old return names. Signed-off-by: Steven Rostedt Link: http://lkml.kernel.org/r/20140421222346.0351ced4@gandalf.local.home Signed-off-by: Jiri Olsa --- event-parse.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/event-parse.h b/event-parse.h index 791c539..feab942 100644 --- a/event-parse.h +++ b/event-parse.h @@ -876,8 +876,8 @@ struct event_filter { struct event_filter *pevent_filter_alloc(struct pevent *pevent); /* for backward compatibility */ -#define FILTER_NONE PEVENT_ERRNO__FILTER_NOT_FOUND -#define FILTER_NOEXIST PEVENT_ERRNO__NO_FILTER +#define FILTER_NONE PEVENT_ERRNO__NO_FILTER +#define FILTER_NOEXIST PEVENT_ERRNO__FILTER_NOT_FOUND #define FILTER_MISS PEVENT_ERRNO__FILTER_MISS #define FILTER_MATCH PEVENT_ERRNO__FILTER_MATCH -- cgit v1.2.3 From 56461efb68a917fac5a143ac0fdde5ecb772c948 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 22 Apr 2014 19:23:30 -0400 Subject: tools lib traceevent: Fix memory leak in pretty_print() Commit 12e55569a244 "tools lib traceevent: Use helper trace-seq in print functions like kernel does" added a extra trace_seq helper to process string arguments like the kernel does it. But the difference between the kernel and the userspace library is that the kernel's trace_seq structure has a static allocated buffer. The userspace one has a dynamically allocated one. It requires a trace_seq_destroy(), otherwise it produces a nasty memory leak. Cc: stable@vger.kernel.org # 3.14+ Signed-off-by: Steven Rostedt Link: http://lkml.kernel.org/r/20140422192330.6bb09bf8@gandalf.local.home Signed-off-by: Jiri Olsa --- event-parse.c | 1 + 1 file changed, 1 insertion(+) diff --git a/event-parse.c b/event-parse.c index baec7d8..b83184f 100644 --- a/event-parse.c +++ b/event-parse.c @@ -4344,6 +4344,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event format, len_arg, arg); trace_seq_terminate(&p); trace_seq_puts(s, p.buffer); + trace_seq_destroy(&p); arg = arg->next; break; default: -- cgit v1.2.3 From e41e33d386ce4a206aa01e8eefb1a7f8616a14f6 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Mon, 2 Jun 2014 23:20:13 -0400 Subject: tools lib traceevent: Add flag to not load event plugins Add a flag to pevent that will let the callers be able to set it and keep the system, and perhaps even normal plugins from being loaded. This is useful when plugins might hide certain information and seeing the raw events shows what may be going on. Signed-off-by: Steven Rostedt Acked-by: Namhyung Kim Link: http://lkml.kernel.org/r/20140603032223.678098063@goodmis.org Signed-off-by: Jiri Olsa --- event-parse.h | 2 ++ event-plugin.c | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/event-parse.h b/event-parse.h index feab942..a68ec3d 100644 --- a/event-parse.h +++ b/event-parse.h @@ -354,6 +354,8 @@ enum pevent_func_arg_type { enum pevent_flag { PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */ + PEVENT_DISABLE_SYS_PLUGINS = 1 << 1, + PEVENT_DISABLE_PLUGINS = 1 << 2, }; #define PEVENT_ERRORS \ diff --git a/event-plugin.c b/event-plugin.c index 0c8bf67..317466b 100644 --- a/event-plugin.c +++ b/event-plugin.c @@ -148,12 +148,17 @@ load_plugins(struct pevent *pevent, const char *suffix, char *path; char *envdir; + if (pevent->flags & PEVENT_DISABLE_PLUGINS) + return; + /* * If a system plugin directory was defined, * check that first. */ #ifdef PLUGIN_DIR - load_plugins_dir(pevent, suffix, PLUGIN_DIR, load_plugin, data); + if (!(pevent->flags & PEVENT_DISABLE_SYS_PLUGINS)) + load_plugins_dir(pevent, suffix, PLUGIN_DIR, + load_plugin, data); #endif /* -- cgit v1.2.3 From 2687f37d5dd8f85580ea9fd91a5ecd20440ff37b Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 3 Jun 2014 18:41:54 -0400 Subject: tools lib traceevent: Add options to plugins The traceevent plugins allows developers to have their events print out information that is more advanced than what can be achieved by the trace event format files. As these plugins are used on the userspace side of the tracing tools, it is only logical that the tools should be able to produce different types of output for the events. The types of events still need to be defined by the plugins thus we need a way to pass information from the tool to the plugin to specify what type of information to be shown. Not only does the information need to be passed by the tool to plugin, but the plugin also requires a way to notify the tool of what options it can provide. This builds the plugin option infrastructure that is taken from trace-cmd that is used to allow plugins to produce different output based on the options specified by the tool. Signed-off-by: Steven Rostedt Acked-by: Namhyung Kim Link: http://lkml.kernel.org/r/20140603184154.0a4c031c@gandalf.local.home Signed-off-by: Jiri Olsa --- event-parse.h | 16 ++++- event-plugin.c | 196 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+), 3 deletions(-) diff --git a/event-parse.h b/event-parse.h index a68ec3d..56e0e6c 100644 --- a/event-parse.h +++ b/event-parse.h @@ -107,8 +107,8 @@ typedef int (*pevent_event_handler_func)(struct trace_seq *s, typedef int (*pevent_plugin_load_func)(struct pevent *pevent); typedef int (*pevent_plugin_unload_func)(struct pevent *pevent); -struct plugin_option { - struct plugin_option *next; +struct pevent_plugin_option { + struct pevent_plugin_option *next; void *handle; char *file; char *name; @@ -135,7 +135,7 @@ struct plugin_option { * PEVENT_PLUGIN_OPTIONS: (optional) * Plugin options that can be set before loading * - * struct plugin_option PEVENT_PLUGIN_OPTIONS[] = { + * struct pevent_plugin_option PEVENT_PLUGIN_OPTIONS[] = { * { * .name = "option-name", * .plugin_alias = "overide-file-name", (optional) @@ -412,9 +412,19 @@ enum pevent_errno { struct plugin_list; +#define INVALID_PLUGIN_LIST_OPTION ((char **)((unsigned long)-1)) + struct plugin_list *traceevent_load_plugins(struct pevent *pevent); void traceevent_unload_plugins(struct plugin_list *plugin_list, struct pevent *pevent); +char **traceevent_plugin_list_options(void); +void traceevent_plugin_free_options_list(char **list); +int traceevent_plugin_add_options(const char *name, + struct pevent_plugin_option *options); +void traceevent_plugin_remove_options(struct pevent_plugin_option *options); +void traceevent_print_plugins(struct trace_seq *s, + const char *prefix, const char *suffix, + const struct plugin_list *list); struct cmdline; struct cmdline_list; diff --git a/event-plugin.c b/event-plugin.c index 317466b..136162c 100644 --- a/event-plugin.c +++ b/event-plugin.c @@ -18,6 +18,7 @@ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#include #include #include #include @@ -30,12 +31,207 @@ #define LOCAL_PLUGIN_DIR ".traceevent/plugins" +static struct registered_plugin_options { + struct registered_plugin_options *next; + struct pevent_plugin_option *options; +} *registered_options; + +static struct trace_plugin_options { + struct trace_plugin_options *next; + char *plugin; + char *option; + char *value; +} *trace_plugin_options; + struct plugin_list { struct plugin_list *next; char *name; void *handle; }; +/** + * traceevent_plugin_list_options - get list of plugin options + * + * Returns an array of char strings that list the currently registered + * plugin options in the format of :