diff options
Diffstat (limited to 'tools/lib/traceevent')
| -rw-r--r-- | tools/lib/traceevent/Makefile | 191 | ||||
| -rw-r--r-- | tools/lib/traceevent/event-parse.c | 244 | ||||
| -rw-r--r-- | tools/lib/traceevent/event-parse.h | 86 | ||||
| -rw-r--r-- | tools/lib/traceevent/event-plugin.c | 215 | ||||
| -rw-r--r-- | tools/lib/traceevent/event-utils.h | 4 | ||||
| -rw-r--r-- | tools/lib/traceevent/parse-filter.c | 673 | ||||
| -rw-r--r-- | tools/lib/traceevent/parse-utils.c | 44 | ||||
| -rw-r--r-- | tools/lib/traceevent/plugin_cfg80211.c | 30 | ||||
| -rw-r--r-- | tools/lib/traceevent/plugin_function.c | 163 | ||||
| -rw-r--r-- | tools/lib/traceevent/plugin_hrtimer.c | 88 | ||||
| -rw-r--r-- | tools/lib/traceevent/plugin_jbd2.c | 77 | ||||
| -rw-r--r-- | tools/lib/traceevent/plugin_kmem.c | 94 | ||||
| -rw-r--r-- | tools/lib/traceevent/plugin_kvm.c | 465 | ||||
| -rw-r--r-- | tools/lib/traceevent/plugin_mac80211.c | 102 | ||||
| -rw-r--r-- | tools/lib/traceevent/plugin_sched_switch.c | 160 | ||||
| -rw-r--r-- | tools/lib/traceevent/plugin_scsi.c | 429 | ||||
| -rw-r--r-- | tools/lib/traceevent/plugin_xen.c | 136 | ||||
| -rw-r--r-- | tools/lib/traceevent/trace-seq.c | 67 | 
18 files changed, 2777 insertions, 491 deletions
diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile index fc150209859..56d52a33a3d 100644 --- a/tools/lib/traceevent/Makefile +++ b/tools/lib/traceevent/Makefile @@ -43,6 +43,32 @@ 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 + +include $(if $(BUILD_SRC),$(BUILD_SRC)/)../../scripts/Makefile.include +  # copy a bit from Linux kbuild  ifeq ("$(origin V)", "command line") @@ -57,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 @@ -80,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 @@ -96,6 +117,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 @@ -114,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 @@ -125,41 +147,14 @@ 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 = -  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 '  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 =					\ -	($(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 $@ $<) @@ -169,23 +164,37 @@ 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 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) +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 +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) + +ALL_OBJS = $(PEVENT_LIB_OBJS) $(PLUGIN_OBJS) -CMD_TARGETS = $(LIB_FILE) +CMD_TARGETS = $(LIB_FILE) $(PLUGINS)  TARGETS = $(CMD_TARGETS) @@ -195,32 +204,40 @@ 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)  $(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 +	$(QUIET_CC_FPIC)$(CC) -c $(CFLAGS) -fPIC -o $@ $< + +$(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 @@ -229,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 @@ -245,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 @@ -283,27 +300,41 @@ 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 -	$(Q)$(call do_install,$(LIB_FILE),$(bindir_SQ)) +define do_install_plugins +	for plugin in $1; do				\ +	  $(call do_install,$$plugin,$(plugin_dir_SQ));	\ +	done +endef + +install_lib: all_cmd install_plugins +	$(call QUIET_INSTALL, $(LIB_FILE)) \ +		$(call do_install,$(LIB_FILE),$(bindir_SQ)) + +install_plugins: $(PLUGINS) +	$(call QUIET_INSTALL, trace_plugins) \ +		$(call do_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 -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) diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 217c82ee366..1587ea392ad 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/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 @@ -4099,6 +4107,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 +4315,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: @@ -5116,8 +5129,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 @@ -5129,10 +5172,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);  }  /** @@ -5153,25 +5198,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 @@ -5203,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;  } @@ -5549,6 +5561,52 @@ int pevent_register_print_function(struct pevent *pevent,  }  /** + * 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) +{ +	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   * @id: the id of the event to register @@ -5572,20 +5630,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); @@ -5625,6 +5672,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/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index 8d73d2594f6..791c539374c 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h @@ -23,6 +23,7 @@  #include <stdbool.h>  #include <stdarg.h>  #include <regex.h> +#include <string.h>  #ifndef __maybe_unused  #define __maybe_unused __attribute__((unused)) @@ -57,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). @@ -67,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); @@ -97,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; @@ -122,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 @@ -355,12 +363,35 @@ 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_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"),			      \ +	_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"), 	      \ +	_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(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(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 @@ -377,6 +408,12 @@ 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 pevent *pevent); +  struct cmdline;  struct cmdline_list;  struct func_map; @@ -522,6 +559,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, @@ -547,7 +593,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); @@ -576,10 +624,15 @@ 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,  				   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); @@ -811,18 +864,22 @@ 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); -#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, @@ -830,20 +887,21 @@ 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); +enum pevent_errno pevent_filter_match(struct event_filter *filter, +				      struct pevent_record *record); -int 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);  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/tools/lib/traceevent/event-plugin.c b/tools/lib/traceevent/event-plugin.c new file mode 100644 index 00000000000..0c8bf6780e4 --- /dev/null +++ b/tools/lib/traceevent/event-plugin.c @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * 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 <http://www.gnu.org/licenses> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <string.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <dirent.h> +#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(strlen(path) + strlen(file) + 2); +	if (!plugin) { +		warning("could not allocate plugin memory\n"); +		return; +	} + +	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(sizeof(*list)); +	if (!list) { +		warning("could not allocate plugin memory\n"); +		goto out_free; +	} + +	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(strlen(home) + strlen(LOCAL_PLUGIN_DIR) + 2); +	if (!path) { +		warning("could not allocate plugin memory\n"); +		return; +	} + +	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, struct pevent *pevent) +{ +	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(pevent); +		dlclose(list->handle); +		free(list->name); +		free(list); +	} +} diff --git a/tools/lib/traceevent/event-utils.h b/tools/lib/traceevent/event-utils.h index e76c9acb92c..d1dc2170e40 100644 --- a/tools/lib/traceevent/event-utils.h +++ b/tools/lib/traceevent/event-utils.h @@ -23,18 +23,14 @@  #include <ctype.h>  /* 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/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c index 2500e75583f..b50234402fc 100644 --- a/tools/lib/traceevent/parse-filter.c +++ b/tools/lib/traceevent/parse-filter.c @@ -38,41 +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_or_die(MAX_ERR_STR_SIZE + (len*2) + 3); -  	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) @@ -95,7 +85,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 */ @@ -147,11 +141,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) @@ -182,7 +178,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); @@ -192,12 +191,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) @@ -242,15 +236,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, @@ -265,7 +263,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)  { @@ -273,6 +271,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; @@ -283,23 +282,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;  		}  	} @@ -307,7 +314,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; +			}  		}  	} @@ -316,7 +326,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 PEVENT_ERRNO__MEM_ALLOC_FAILED;  	return 0;  } @@ -332,14 +344,18 @@ 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;  	arg = allocate_arg(); +	if (arg == NULL) { +		show_error(error_str, "failed to allocate filter arg"); +		return PEVENT_ERRNO__MEM_ALLOC_FAILED; +	}  	switch (type) { @@ -349,8 +365,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 PEVENT_ERRNO__MEM_ALLOC_FAILED; +		}  		break;  	case EVENT_ITEM:  		/* if it is a number, then convert it */ @@ -377,11 +396,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 * @@ -390,6 +409,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; @@ -402,6 +424,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; @@ -414,6 +439,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; @@ -421,8 +449,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; @@ -453,9 +481,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;  		}  		/* @@ -502,7 +529,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 */ @@ -521,25 +548,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; @@ -557,7 +590,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; @@ -577,9 +610,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 * @@ -592,7 +624,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: @@ -611,11 +643,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;  } @@ -728,15 +760,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) { @@ -745,8 +780,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; @@ -757,23 +794,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) { @@ -788,63 +831,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) @@ -852,41 +900,56 @@ 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 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); +	ret = test_arg(arg, arg, error_str);  	switch (ret) {  	case FILTER_VAL_NORM: -		return arg; +		break;  	case FILTER_VAL_TRUE:  	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; +		} else { +			show_error(error_str, "Failed to allocate filter arg"); +			ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; +		} +		break; + +	default: +		/* test_arg() already set the error_str */ +		free_arg(arg); +		arg = NULL; +		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) +	       char *error_str, int not)  {  	enum event_type type;  	char *token = NULL; @@ -898,7 +961,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; @@ -909,8 +972,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; @@ -923,20 +986,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;  			} @@ -944,19 +1007,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; @@ -964,7 +1031,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;  				} @@ -979,19 +1046,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; @@ -1003,21 +1070,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;  			} @@ -1025,6 +1093,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 @@ -1035,6 +1105,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) @@ -1054,6 +1126,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); @@ -1062,7 +1136,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; @@ -1071,57 +1145,64 @@ 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); +	ret = collapse_tree(current_op, parg, error_str); +	if (ret < 0) +		goto fail;  	*parg = current_op;  	return 0; - fail_print: + fail_alloc: +	show_error(error_str, "failed to allocate filter arg"); +	ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; +	goto fail; + 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 +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;  	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) +			return PEVENT_ERRNO__MEM_ALLOC_FAILED; +  		(*parg)->type = FILTER_ARG_BOOLEAN;  		(*parg)->boolean.value = FILTER_FALSE;  	} @@ -1129,13 +1210,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); @@ -1145,11 +1226,17 @@ static int filter_event(struct event_filter *filter,  	} else {  		/* just add a TRUE arg */  		arg = allocate_arg(); +		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) +		return PEVENT_ERRNO__MEM_ALLOC_FAILED; +  	if (filter_type->filter)  		free_arg(filter_type->filter);  	filter_type->filter = arg; @@ -1157,22 +1244,24 @@ static int filter_event(struct event_filter *filter,  	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 - * @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.  Use pevent_filter_strerror() to see + * actual error message in case of error.   */ -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; @@ -1183,15 +1272,11 @@ 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; -	/* clear buffer to reset show error */ -	pevent_buffer_init("", 0); - -	if (error_str) -		*error_str = NULL; +	filter_init_error_buf(filter);  	filter_start = strchr(filter_str, ':');  	if (filter_start) @@ -1199,7 +1284,6 @@ int pevent_filter_add_filter_str(struct event_filter *filter,  	else  		len = strlen(filter_str); -  	do {  		next_event = strchr(filter_str, ',');  		if (next_event && @@ -1210,7 +1294,12 @@ 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) { +			/* This can only happen when events is NULL, but still */ +			free_events(events); +			return PEVENT_ERRNO__MEM_ALLOC_FAILED; +		}  		memcpy(this_event, filter_str, len);  		this_event[len] = 0; @@ -1223,27 +1312,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 (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); @@ -1255,7 +1335,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); +				   filter->error_buffer);  		/* Failures are returned if a parse error happened */  		if (ret < 0)  			rtn = ret; @@ -1263,8 +1343,10 @@ int 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); +			}  		}  	} @@ -1282,6 +1364,32 @@ static void free_filter_type(struct filter_type *filter_type)  }  /** + * 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   * @event_id: the event to remove a filter for @@ -1374,6 +1482,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; @@ -1381,6 +1492,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); @@ -1482,8 +1596,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; @@ -1492,13 +1608,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; @@ -1513,19 +1631,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;  }  /** @@ -1565,8 +1688,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) @@ -1612,15 +1735,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: @@ -1655,39 +1787,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: @@ -1709,7 +1853,8 @@ static int test_num(struct event_format *event,  		return lval <= rval;  	default: -		/* ?? */ +		if (!*err) +			*err = PEVENT_ERRNO__ILLEGAL_INTEGER_CMP;  		return 0;  	}  } @@ -1756,8 +1901,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; @@ -1781,48 +1926,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: @@ -1831,11 +1985,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;  	}  } @@ -1848,8 +2002,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; @@ -1866,31 +2019,38 @@ 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; + +	filter_init_error_buf(filter);  	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) @@ -1902,7 +2062,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: @@ -1949,11 +2108,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;  			}  		} @@ -1971,10 +2126,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: @@ -1990,16 +2142,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: @@ -2013,11 +2159,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;  } @@ -2033,7 +2177,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); @@ -2072,12 +2215,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); @@ -2091,7 +2233,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); @@ -2122,10 +2263,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: @@ -2143,7 +2281,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: @@ -2161,12 +2298,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: @@ -2178,15 +2311,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: @@ -2221,7 +2350,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) diff --git a/tools/lib/traceevent/parse-utils.c b/tools/lib/traceevent/parse-utils.c index bba701cf10e..eda07fa31dc 100644 --- a/tools/lib/traceevent/parse-utils.c +++ b/tools/lib/traceevent/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; -} diff --git a/tools/lib/traceevent/plugin_cfg80211.c b/tools/lib/traceevent/plugin_cfg80211.c new file mode 100644 index 00000000000..c066b25905f --- /dev/null +++ b/tools/lib/traceevent/plugin_cfg80211.c @@ -0,0 +1,30 @@ +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <endian.h> +#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 *) (unsigned long) 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; +} + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ +	pevent_unregister_print_function(pevent, process___le16_to_cpup, +					 "__le16_to_cpup"); +} diff --git a/tools/lib/traceevent/plugin_function.c b/tools/lib/traceevent/plugin_function.c new file mode 100644 index 00000000000..80ba4ff1fe8 --- /dev/null +++ b/tools/lib/traceevent/plugin_function.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * 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 <http://www.gnu.org/licenses> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "event-parse.h" +#include "event-utils.h" + +static struct func_stack { +	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 { +		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; +	} + +	stack->stack[pos] = strdup(child); +} + +static int add_and_get_index(const char *parent, const char *child, int cpu) +{ +	int i; + +	if (cpu < 0) +		return 0; + +	if (cpu > cpus) { +		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++) +			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 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 = add_and_get_index(parent, func, record->cpu); + +	trace_seq_printf(s, "%*s", index*3, ""); + +	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(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]); +		free(fstack[i].stack); +	} + +	free(fstack); +	fstack = NULL; +	cpus = -1; +} diff --git a/tools/lib/traceevent/plugin_hrtimer.c b/tools/lib/traceevent/plugin_hrtimer.c new file mode 100644 index 00000000000..12bf14cc115 --- /dev/null +++ b/tools/lib/traceevent/plugin_hrtimer.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> + * Copyright (C) 2009 Johannes Berg <johannes@sipsolutions.net> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * 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 <http://www.gnu.org/licenses> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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) +{ +	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); + +	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); + +	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; +} + +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); +} diff --git a/tools/lib/traceevent/plugin_jbd2.c b/tools/lib/traceevent/plugin_jbd2.c new file mode 100644 index 00000000000..0db714c721b --- /dev/null +++ b/tools/lib/traceevent/plugin_jbd2.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * 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 <http://www.gnu.org/licenses> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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)) + +static 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; +} + +static 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; +} + +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"); +} diff --git a/tools/lib/traceevent/plugin_kmem.c b/tools/lib/traceevent/plugin_kmem.c new file mode 100644 index 00000000000..70650ff48d7 --- /dev/null +++ b/tools/lib/traceevent/plugin_kmem.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * 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 <http://www.gnu.org/licenses> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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; +} + +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); +} diff --git a/tools/lib/traceevent/plugin_kvm.c b/tools/lib/traceevent/plugin_kvm.c new file mode 100644 index 00000000000..9e0e8c61b43 --- /dev/null +++ b/tools/lib/traceevent/plugin_kvm.c @@ -0,0 +1,465 @@ +/* + * Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * 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 <http://www.gnu.org/licenses> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> + +#include "event-parse.h" + +#ifdef HAVE_UDIS86 + +#include <udis86.h> + +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); +} + +#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(); + +	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); + +	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; +} + +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"); +} diff --git a/tools/lib/traceevent/plugin_mac80211.c b/tools/lib/traceevent/plugin_mac80211.c new file mode 100644 index 00000000000..7e15a0f1c2f --- /dev/null +++ b/tools/lib/traceevent/plugin_mac80211.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2009 Johannes Berg <johannes@sipsolutions.net> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * 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 <http://www.gnu.org/licenses> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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; +} + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ +	pevent_unregister_event_handler(pevent, -1, "mac80211", +					"drv_bss_info_changed", +					drv_bss_info_changed, NULL); +} diff --git a/tools/lib/traceevent/plugin_sched_switch.c b/tools/lib/traceevent/plugin_sched_switch.c new file mode 100644 index 00000000000..f1ce6006525 --- /dev/null +++ b/tools/lib/traceevent/plugin_sched_switch.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * 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 <http://www.gnu.org/licenses> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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; +} + +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); +} diff --git a/tools/lib/traceevent/plugin_scsi.c b/tools/lib/traceevent/plugin_scsi.c new file mode 100644 index 00000000000..eda326fc862 --- /dev/null +++ b/tools/lib/traceevent/plugin_scsi.c @@ -0,0 +1,429 @@ +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#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 *) (unsigned long) 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; +} + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ +	pevent_unregister_print_function(pevent, process_scsi_trace_parse_cdb, +					 "scsi_trace_parse_cdb"); +} diff --git a/tools/lib/traceevent/plugin_xen.c b/tools/lib/traceevent/plugin_xen.c new file mode 100644 index 00000000000..3a413eaada6 --- /dev/null +++ b/tools/lib/traceevent/plugin_xen.c @@ -0,0 +1,136 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#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; +} + +void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent) +{ +	pevent_unregister_print_function(pevent, process_xen_hypercall_name, +					 "xen_hypercall_name"); +} diff --git a/tools/lib/traceevent/trace-seq.c b/tools/lib/traceevent/trace-seq.c index d7f2e68bc5b..ec3bd16a548 100644 --- a/tools/lib/traceevent/trace-seq.c +++ b/tools/lib/traceevent/trace-seq.c @@ -22,6 +22,7 @@  #include <string.h>  #include <stdarg.h> +#include <asm/bug.h>  #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 @@ -45,7 +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->buffer = malloc(s->buffer_size); +	if (s->buffer != NULL) +		s->state = TRACE_SEQ__GOOD; +	else +		s->state = TRACE_SEQ__MEM_ALLOC_FAILED;  }  /** @@ -71,17 +87,23 @@ 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;  }  static void expand_buffer(struct trace_seq *s)  { +	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; -	s->buffer = realloc(s->buffer, s->buffer_size); -	if (!s->buffer) -		die("Can't allocate trace_seq buffer memory");  }  /** @@ -105,9 +127,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 +163,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 +194,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 +211,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 +225,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 +234,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;  }  | 
