mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 17:18:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
340
tools/lib/traceevent/Makefile
Normal file
340
tools/lib/traceevent/Makefile
Normal file
|
@ -0,0 +1,340 @@
|
|||
# trace-cmd version
|
||||
EP_VERSION = 1
|
||||
EP_PATCHLEVEL = 1
|
||||
EP_EXTRAVERSION = 0
|
||||
|
||||
# file format version
|
||||
FILE_VERSION = 6
|
||||
|
||||
MAKEFLAGS += --no-print-directory
|
||||
|
||||
|
||||
# Makefiles suck: This macro sets a default value of $(2) for the
|
||||
# variable named by $(1), unless the variable has been set by
|
||||
# environment or command line. This is necessary for CC and AR
|
||||
# because make sets default values, so the simpler ?= approach
|
||||
# won't work as expected.
|
||||
define allow-override
|
||||
$(if $(or $(findstring environment,$(origin $(1))),\
|
||||
$(findstring command line,$(origin $(1)))),,\
|
||||
$(eval $(1) = $(2)))
|
||||
endef
|
||||
|
||||
# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix.
|
||||
$(call allow-override,CC,$(CROSS_COMPILE)gcc)
|
||||
$(call allow-override,AR,$(CROSS_COMPILE)ar)
|
||||
|
||||
EXT = -std=gnu99
|
||||
INSTALL = install
|
||||
|
||||
# Use DESTDIR for installing into a different root directory.
|
||||
# This is useful for building a package. The program will be
|
||||
# installed in this directory as if it was the root directory.
|
||||
# Then the build tool can move it later.
|
||||
DESTDIR ?=
|
||||
DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))'
|
||||
|
||||
prefix ?= /usr/local
|
||||
bindir_relative = bin
|
||||
bindir = $(prefix)/$(bindir_relative)
|
||||
man_dir = $(prefix)/share/man
|
||||
man_dir_SQ = '$(subst ','\'',$(man_dir))'
|
||||
|
||||
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="$(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")
|
||||
VERBOSE = $(V)
|
||||
endif
|
||||
ifndef VERBOSE
|
||||
VERBOSE = 0
|
||||
endif
|
||||
|
||||
ifeq ("$(origin O)", "command line")
|
||||
BUILD_OUTPUT := $(O)
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_SRC),)
|
||||
ifneq ($(OUTPUT),)
|
||||
|
||||
define build_output
|
||||
$(if $(VERBOSE:1=),@)+$(MAKE) -C $(OUTPUT) \
|
||||
BUILD_SRC=$(CURDIR)/ -f $(CURDIR)/Makefile $1
|
||||
endef
|
||||
|
||||
all: sub-make
|
||||
|
||||
$(MAKECMDGOALS): sub-make
|
||||
|
||||
sub-make: force
|
||||
$(call build_output, $(MAKECMDGOALS))
|
||||
|
||||
|
||||
# Leave processing to above invocation of make
|
||||
skip-makefile := 1
|
||||
|
||||
endif # OUTPUT
|
||||
endif # BUILD_SRC
|
||||
|
||||
# We process the rest of the Makefile if this is the final invocation of make
|
||||
ifeq ($(skip-makefile),)
|
||||
|
||||
srctree := $(if $(BUILD_SRC),$(BUILD_SRC),$(CURDIR))
|
||||
objtree := $(CURDIR)
|
||||
src := $(srctree)
|
||||
obj := $(objtree)
|
||||
|
||||
export prefix bindir src obj
|
||||
|
||||
# Shell quotes
|
||||
bindir_SQ = $(subst ','\'',$(bindir))
|
||||
bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
|
||||
plugin_dir_SQ = $(subst ','\'',$(plugin_dir))
|
||||
|
||||
LIB_FILE = libtraceevent.a libtraceevent.so
|
||||
|
||||
CONFIG_INCLUDES =
|
||||
CONFIG_LIBS =
|
||||
CONFIG_FLAGS =
|
||||
|
||||
VERSION = $(EP_VERSION)
|
||||
PATCHLEVEL = $(EP_PATCHLEVEL)
|
||||
EXTRAVERSION = $(EP_EXTRAVERSION)
|
||||
|
||||
OBJ = $@
|
||||
N =
|
||||
|
||||
export Q VERBOSE
|
||||
|
||||
EVENT_PARSE_VERSION = $(EP_VERSION).$(EP_PATCHLEVEL).$(EP_EXTRAVERSION)
|
||||
|
||||
INCLUDES = -I. -I $(srctree)/../../include $(CONFIG_INCLUDES)
|
||||
|
||||
# Set compile option CFLAGS if not set elsewhere
|
||||
CFLAGS ?= -g -Wall
|
||||
|
||||
# Append required CFLAGS
|
||||
override CFLAGS += $(CONFIG_FLAGS) $(INCLUDES) $(PLUGIN_DIR_SQ)
|
||||
override CFLAGS += $(udis86-flags) -D_GNU_SOURCE
|
||||
|
||||
ifeq ($(VERBOSE),1)
|
||||
Q =
|
||||
else
|
||||
Q = @
|
||||
endif
|
||||
|
||||
do_compile_shared_library = \
|
||||
($(print_shared_lib_compile) \
|
||||
$(CC) --shared $^ -o $@)
|
||||
|
||||
do_plugin_build = \
|
||||
($(print_plugin_build) \
|
||||
$(CC) $(CFLAGS) -shared -nostartfiles -o $@ $<)
|
||||
|
||||
do_build_static_lib = \
|
||||
($(print_static_lib_build) \
|
||||
$(RM) $@; $(AR) rcs $@ $^)
|
||||
|
||||
|
||||
do_compile = $(QUIET_CC)$(CC) -c $(CFLAGS) $(EXT) $< -o $(obj)/$@;
|
||||
|
||||
$(obj)/%.o: $(src)/%.c
|
||||
$(call do_compile)
|
||||
|
||||
%.o: $(src)/%.c
|
||||
$(call do_compile)
|
||||
|
||||
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
|
||||
|
||||
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) $(PLUGINS)
|
||||
|
||||
TARGETS = $(CMD_TARGETS)
|
||||
|
||||
|
||||
all: all_cmd
|
||||
|
||||
all_cmd: $(CMD_TARGETS)
|
||||
|
||||
libtraceevent.so: $(PEVENT_LIB_OBJS)
|
||||
$(QUIET_LINK)$(CC) --shared $^ -o $@
|
||||
|
||||
libtraceevent.a: $(PEVENT_LIB_OBJS)
|
||||
$(QUIET_LINK)$(RM) $@; $(AR) rcs $@ $^
|
||||
|
||||
plugins: $(PLUGINS)
|
||||
|
||||
$(PEVENT_LIB_OBJS): %.o: $(src)/%.c TRACEEVENT-CFLAGS
|
||||
$(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
|
||||
endef
|
||||
|
||||
define update_version.h
|
||||
($(call make_version.h, $@.tmp); \
|
||||
if [ -r $@ ] && cmp -s $@ $@.tmp; then \
|
||||
rm -f $@.tmp; \
|
||||
else \
|
||||
echo ' UPDATE $@'; \
|
||||
mv -f $@.tmp $@; \
|
||||
fi);
|
||||
endef
|
||||
|
||||
ep_version.h: force
|
||||
$(Q)$(N)$(call update_version.h)
|
||||
|
||||
VERSION_FILES = ep_version.h
|
||||
|
||||
define update_dir
|
||||
(echo $1 > $@.tmp; \
|
||||
if [ -r $@ ] && cmp -s $@ $@.tmp; then \
|
||||
rm -f $@.tmp; \
|
||||
else \
|
||||
echo ' UPDATE $@'; \
|
||||
mv -f $@.tmp $@; \
|
||||
fi);
|
||||
endef
|
||||
|
||||
## make deps
|
||||
|
||||
all_objs := $(sort $(ALL_OBJS))
|
||||
all_deps := $(all_objs:%.o=.%.d)
|
||||
|
||||
# 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) $@.$$$$
|
||||
endef
|
||||
|
||||
$(all_deps): .%.d: $(src)/%.c
|
||||
$(Q)$(call check_deps)
|
||||
|
||||
$(all_objs) : %.o : .%.d
|
||||
|
||||
dep_includes := $(wildcard $(all_deps))
|
||||
|
||||
ifneq ($(dep_includes),)
|
||||
include $(dep_includes)
|
||||
endif
|
||||
|
||||
### Detect environment changes
|
||||
TRACK_CFLAGS = $(subst ','\'',$(CFLAGS)):$(ARCH):$(CROSS_COMPILE)
|
||||
|
||||
TRACEEVENT-CFLAGS: force
|
||||
@FLAGS='$(TRACK_CFLAGS)'; \
|
||||
if test x"$$FLAGS" != x"`cat TRACEEVENT-CFLAGS 2>/dev/null`" ; then \
|
||||
echo 1>&2 " FLAGS: * new build flags or cross compiler"; \
|
||||
echo "$$FLAGS" >TRACEEVENT-CFLAGS; \
|
||||
fi
|
||||
|
||||
tags: force
|
||||
$(RM) tags
|
||||
find . -name '*.[ch]' | xargs ctags --extra=+f --c-kinds=+px \
|
||||
--regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/'
|
||||
|
||||
TAGS: force
|
||||
$(RM) TAGS
|
||||
find . -name '*.[ch]' | xargs etags \
|
||||
--regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/'
|
||||
|
||||
define do_install
|
||||
if [ ! -d '$(DESTDIR_SQ)$2' ]; then \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \
|
||||
fi; \
|
||||
$(INSTALL) $1 '$(DESTDIR_SQ)$2'
|
||||
endef
|
||||
|
||||
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:
|
||||
$(call QUIET_CLEAN, libtraceevent) \
|
||||
$(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d \
|
||||
$(RM) TRACEEVENT-CFLAGS tags TAGS
|
||||
|
||||
endif # skip-makefile
|
||||
|
||||
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)
|
6025
tools/lib/traceevent/event-parse.c
Normal file
6025
tools/lib/traceevent/event-parse.c
Normal file
File diff suppressed because it is too large
Load diff
944
tools/lib/traceevent/event-parse.h
Normal file
944
tools/lib/traceevent/event-parse.h
Normal file
|
@ -0,0 +1,944 @@
|
|||
/*
|
||||
* 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>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#ifndef _PARSE_EVENTS_H
|
||||
#define _PARSE_EVENTS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <regex.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef __maybe_unused
|
||||
#define __maybe_unused __attribute__((unused))
|
||||
#endif
|
||||
|
||||
/* ----------------------- trace_seq ----------------------- */
|
||||
|
||||
|
||||
#ifndef TRACE_SEQ_BUF_SIZE
|
||||
#define TRACE_SEQ_BUF_SIZE 4096
|
||||
#endif
|
||||
|
||||
#ifndef DEBUG_RECORD
|
||||
#define DEBUG_RECORD 0
|
||||
#endif
|
||||
|
||||
struct pevent_record {
|
||||
unsigned long long ts;
|
||||
unsigned long long offset;
|
||||
long long missed_events; /* buffer dropped events before */
|
||||
int record_size; /* size of binary record */
|
||||
int size; /* size of data */
|
||||
void *data;
|
||||
int cpu;
|
||||
int ref_count;
|
||||
int locked; /* Do not free, even if ref_count is zero */
|
||||
void *priv;
|
||||
#if DEBUG_RECORD
|
||||
struct pevent_record *prev;
|
||||
struct pevent_record *next;
|
||||
long alloc_addr;
|
||||
#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).
|
||||
*/
|
||||
|
||||
struct trace_seq {
|
||||
char *buffer;
|
||||
unsigned int buffer_size;
|
||||
unsigned int len;
|
||||
unsigned int readpos;
|
||||
enum trace_seq_fail state;
|
||||
};
|
||||
|
||||
void trace_seq_init(struct trace_seq *s);
|
||||
void trace_seq_reset(struct trace_seq *s);
|
||||
void trace_seq_destroy(struct trace_seq *s);
|
||||
|
||||
extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
|
||||
__attribute__ ((format (printf, 2, 3)));
|
||||
extern int trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
|
||||
__attribute__ ((format (printf, 2, 0)));
|
||||
|
||||
extern int trace_seq_puts(struct trace_seq *s, const char *str);
|
||||
extern int trace_seq_putc(struct trace_seq *s, unsigned char c);
|
||||
|
||||
extern void trace_seq_terminate(struct trace_seq *s);
|
||||
|
||||
extern int trace_seq_do_printf(struct trace_seq *s);
|
||||
|
||||
|
||||
/* ----------------------- pevent ----------------------- */
|
||||
|
||||
struct pevent;
|
||||
struct event_format;
|
||||
|
||||
typedef int (*pevent_event_handler_func)(struct trace_seq *s,
|
||||
struct pevent_record *record,
|
||||
struct event_format *event,
|
||||
void *context);
|
||||
|
||||
typedef int (*pevent_plugin_load_func)(struct pevent *pevent);
|
||||
typedef int (*pevent_plugin_unload_func)(struct pevent *pevent);
|
||||
|
||||
struct pevent_plugin_option {
|
||||
struct pevent_plugin_option *next;
|
||||
void *handle;
|
||||
char *file;
|
||||
char *name;
|
||||
char *plugin_alias;
|
||||
char *description;
|
||||
char *value;
|
||||
void *priv;
|
||||
int set;
|
||||
};
|
||||
|
||||
/*
|
||||
* Plugin hooks that can be called:
|
||||
*
|
||||
* PEVENT_PLUGIN_LOADER: (required)
|
||||
* The function name to initialized the plugin.
|
||||
*
|
||||
* int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
|
||||
*
|
||||
* PEVENT_PLUGIN_UNLOADER: (optional)
|
||||
* The function called just before unloading
|
||||
*
|
||||
* int PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
|
||||
*
|
||||
* PEVENT_PLUGIN_OPTIONS: (optional)
|
||||
* Plugin options that can be set before loading
|
||||
*
|
||||
* struct pevent_plugin_option PEVENT_PLUGIN_OPTIONS[] = {
|
||||
* {
|
||||
* .name = "option-name",
|
||||
* .plugin_alias = "overide-file-name", (optional)
|
||||
* .description = "description of option to show users",
|
||||
* },
|
||||
* {
|
||||
* .name = NULL,
|
||||
* },
|
||||
* };
|
||||
*
|
||||
* Array must end with .name = NULL;
|
||||
*
|
||||
*
|
||||
* .plugin_alias is used to give a shorter name to access
|
||||
* the vairable. Useful if a plugin handles more than one event.
|
||||
*
|
||||
* PEVENT_PLUGIN_ALIAS: (optional)
|
||||
* The name to use for finding options (uses filename if not defined)
|
||||
*/
|
||||
#define PEVENT_PLUGIN_LOADER pevent_plugin_loader
|
||||
#define PEVENT_PLUGIN_UNLOADER pevent_plugin_unloader
|
||||
#define PEVENT_PLUGIN_OPTIONS pevent_plugin_options
|
||||
#define PEVENT_PLUGIN_ALIAS pevent_plugin_alias
|
||||
#define _MAKE_STR(x) #x
|
||||
#define MAKE_STR(x) _MAKE_STR(x)
|
||||
#define PEVENT_PLUGIN_LOADER_NAME MAKE_STR(PEVENT_PLUGIN_LOADER)
|
||||
#define PEVENT_PLUGIN_UNLOADER_NAME MAKE_STR(PEVENT_PLUGIN_UNLOADER)
|
||||
#define PEVENT_PLUGIN_OPTIONS_NAME MAKE_STR(PEVENT_PLUGIN_OPTIONS)
|
||||
#define PEVENT_PLUGIN_ALIAS_NAME MAKE_STR(PEVENT_PLUGIN_ALIAS)
|
||||
|
||||
#define NSECS_PER_SEC 1000000000ULL
|
||||
#define NSECS_PER_USEC 1000ULL
|
||||
|
||||
enum format_flags {
|
||||
FIELD_IS_ARRAY = 1,
|
||||
FIELD_IS_POINTER = 2,
|
||||
FIELD_IS_SIGNED = 4,
|
||||
FIELD_IS_STRING = 8,
|
||||
FIELD_IS_DYNAMIC = 16,
|
||||
FIELD_IS_LONG = 32,
|
||||
FIELD_IS_FLAG = 64,
|
||||
FIELD_IS_SYMBOLIC = 128,
|
||||
};
|
||||
|
||||
struct format_field {
|
||||
struct format_field *next;
|
||||
struct event_format *event;
|
||||
char *type;
|
||||
char *name;
|
||||
int offset;
|
||||
int size;
|
||||
unsigned int arraylen;
|
||||
unsigned int elementsize;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
struct format {
|
||||
int nr_common;
|
||||
int nr_fields;
|
||||
struct format_field *common_fields;
|
||||
struct format_field *fields;
|
||||
};
|
||||
|
||||
struct print_arg_atom {
|
||||
char *atom;
|
||||
};
|
||||
|
||||
struct print_arg_string {
|
||||
char *string;
|
||||
int offset;
|
||||
};
|
||||
|
||||
struct print_arg_bitmask {
|
||||
char *bitmask;
|
||||
int offset;
|
||||
};
|
||||
|
||||
struct print_arg_field {
|
||||
char *name;
|
||||
struct format_field *field;
|
||||
};
|
||||
|
||||
struct print_flag_sym {
|
||||
struct print_flag_sym *next;
|
||||
char *value;
|
||||
char *str;
|
||||
};
|
||||
|
||||
struct print_arg_typecast {
|
||||
char *type;
|
||||
struct print_arg *item;
|
||||
};
|
||||
|
||||
struct print_arg_flags {
|
||||
struct print_arg *field;
|
||||
char *delim;
|
||||
struct print_flag_sym *flags;
|
||||
};
|
||||
|
||||
struct print_arg_symbol {
|
||||
struct print_arg *field;
|
||||
struct print_flag_sym *symbols;
|
||||
};
|
||||
|
||||
struct print_arg_hex {
|
||||
struct print_arg *field;
|
||||
struct print_arg *size;
|
||||
};
|
||||
|
||||
struct print_arg_dynarray {
|
||||
struct format_field *field;
|
||||
struct print_arg *index;
|
||||
};
|
||||
|
||||
struct print_arg;
|
||||
|
||||
struct print_arg_op {
|
||||
char *op;
|
||||
int prio;
|
||||
struct print_arg *left;
|
||||
struct print_arg *right;
|
||||
};
|
||||
|
||||
struct pevent_function_handler;
|
||||
|
||||
struct print_arg_func {
|
||||
struct pevent_function_handler *func;
|
||||
struct print_arg *args;
|
||||
};
|
||||
|
||||
enum print_arg_type {
|
||||
PRINT_NULL,
|
||||
PRINT_ATOM,
|
||||
PRINT_FIELD,
|
||||
PRINT_FLAGS,
|
||||
PRINT_SYMBOL,
|
||||
PRINT_HEX,
|
||||
PRINT_TYPE,
|
||||
PRINT_STRING,
|
||||
PRINT_BSTRING,
|
||||
PRINT_DYNAMIC_ARRAY,
|
||||
PRINT_OP,
|
||||
PRINT_FUNC,
|
||||
PRINT_BITMASK,
|
||||
};
|
||||
|
||||
struct print_arg {
|
||||
struct print_arg *next;
|
||||
enum print_arg_type type;
|
||||
union {
|
||||
struct print_arg_atom atom;
|
||||
struct print_arg_field field;
|
||||
struct print_arg_typecast typecast;
|
||||
struct print_arg_flags flags;
|
||||
struct print_arg_symbol symbol;
|
||||
struct print_arg_hex hex;
|
||||
struct print_arg_func func;
|
||||
struct print_arg_string string;
|
||||
struct print_arg_bitmask bitmask;
|
||||
struct print_arg_op op;
|
||||
struct print_arg_dynarray dynarray;
|
||||
};
|
||||
};
|
||||
|
||||
struct print_fmt {
|
||||
char *format;
|
||||
struct print_arg *args;
|
||||
};
|
||||
|
||||
struct event_format {
|
||||
struct pevent *pevent;
|
||||
char *name;
|
||||
int id;
|
||||
int flags;
|
||||
struct format format;
|
||||
struct print_fmt print_fmt;
|
||||
char *system;
|
||||
pevent_event_handler_func handler;
|
||||
void *context;
|
||||
};
|
||||
|
||||
enum {
|
||||
EVENT_FL_ISFTRACE = 0x01,
|
||||
EVENT_FL_ISPRINT = 0x02,
|
||||
EVENT_FL_ISBPRINT = 0x04,
|
||||
EVENT_FL_ISFUNCENT = 0x10,
|
||||
EVENT_FL_ISFUNCRET = 0x20,
|
||||
EVENT_FL_NOHANDLE = 0x40,
|
||||
EVENT_FL_PRINTRAW = 0x80,
|
||||
|
||||
EVENT_FL_FAILED = 0x80000000
|
||||
};
|
||||
|
||||
enum event_sort_type {
|
||||
EVENT_SORT_ID,
|
||||
EVENT_SORT_NAME,
|
||||
EVENT_SORT_SYSTEM,
|
||||
};
|
||||
|
||||
enum event_type {
|
||||
EVENT_ERROR,
|
||||
EVENT_NONE,
|
||||
EVENT_SPACE,
|
||||
EVENT_NEWLINE,
|
||||
EVENT_OP,
|
||||
EVENT_DELIM,
|
||||
EVENT_ITEM,
|
||||
EVENT_DQUOTE,
|
||||
EVENT_SQUOTE,
|
||||
};
|
||||
|
||||
typedef unsigned long long (*pevent_func_handler)(struct trace_seq *s,
|
||||
unsigned long long *args);
|
||||
|
||||
enum pevent_func_arg_type {
|
||||
PEVENT_FUNC_ARG_VOID,
|
||||
PEVENT_FUNC_ARG_INT,
|
||||
PEVENT_FUNC_ARG_LONG,
|
||||
PEVENT_FUNC_ARG_STRING,
|
||||
PEVENT_FUNC_ARG_PTR,
|
||||
PEVENT_FUNC_ARG_MAX_TYPES
|
||||
};
|
||||
|
||||
enum pevent_flag {
|
||||
PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */
|
||||
PEVENT_DISABLE_SYS_PLUGINS = 1 << 1,
|
||||
PEVENT_DISABLE_PLUGINS = 1 << 2,
|
||||
};
|
||||
|
||||
#define PEVENT_ERRORS \
|
||||
_PE(MEM_ALLOC_FAILED, "failed to allocate memory"), \
|
||||
_PE(PARSE_EVENT_FAILED, "failed to parse event"), \
|
||||
_PE(READ_ID_FAILED, "failed to read event id"), \
|
||||
_PE(READ_FORMAT_FAILED, "failed to read event format"), \
|
||||
_PE(READ_PRINT_FAILED, "failed to read event print fmt"), \
|
||||
_PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace"),\
|
||||
_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
|
||||
* errno since SUS requires the errno has distinct positive values.
|
||||
* See 'Issue 6' in the link below.
|
||||
*
|
||||
* http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
|
||||
*/
|
||||
__PEVENT_ERRNO__START = -100000,
|
||||
|
||||
PEVENT_ERRORS,
|
||||
|
||||
__PEVENT_ERRNO__END,
|
||||
};
|
||||
#undef _PE
|
||||
|
||||
struct plugin_list;
|
||||
|
||||
#define INVALID_PLUGIN_LIST_OPTION ((char **)((unsigned long)-1))
|
||||
|
||||
struct plugin_list *traceevent_load_plugins(struct pevent *pevent);
|
||||
void traceevent_unload_plugins(struct plugin_list *plugin_list,
|
||||
struct pevent *pevent);
|
||||
char **traceevent_plugin_list_options(void);
|
||||
void traceevent_plugin_free_options_list(char **list);
|
||||
int traceevent_plugin_add_options(const char *name,
|
||||
struct pevent_plugin_option *options);
|
||||
void traceevent_plugin_remove_options(struct pevent_plugin_option *options);
|
||||
void traceevent_print_plugins(struct trace_seq *s,
|
||||
const char *prefix, const char *suffix,
|
||||
const struct plugin_list *list);
|
||||
|
||||
struct cmdline;
|
||||
struct cmdline_list;
|
||||
struct func_map;
|
||||
struct func_list;
|
||||
struct event_handler;
|
||||
|
||||
struct pevent {
|
||||
int ref_count;
|
||||
|
||||
int header_page_ts_offset;
|
||||
int header_page_ts_size;
|
||||
int header_page_size_offset;
|
||||
int header_page_size_size;
|
||||
int header_page_data_offset;
|
||||
int header_page_data_size;
|
||||
int header_page_overwrite;
|
||||
|
||||
int file_bigendian;
|
||||
int host_bigendian;
|
||||
|
||||
int latency_format;
|
||||
|
||||
int old_format;
|
||||
|
||||
int cpus;
|
||||
int long_size;
|
||||
int page_size;
|
||||
|
||||
struct cmdline *cmdlines;
|
||||
struct cmdline_list *cmdlist;
|
||||
int cmdline_count;
|
||||
|
||||
struct func_map *func_map;
|
||||
struct func_list *funclist;
|
||||
unsigned int func_count;
|
||||
|
||||
struct printk_map *printk_map;
|
||||
struct printk_list *printklist;
|
||||
unsigned int printk_count;
|
||||
|
||||
|
||||
struct event_format **events;
|
||||
int nr_events;
|
||||
struct event_format **sort_events;
|
||||
enum event_sort_type last_type;
|
||||
|
||||
int type_offset;
|
||||
int type_size;
|
||||
|
||||
int pid_offset;
|
||||
int pid_size;
|
||||
|
||||
int pc_offset;
|
||||
int pc_size;
|
||||
|
||||
int flags_offset;
|
||||
int flags_size;
|
||||
|
||||
int ld_offset;
|
||||
int ld_size;
|
||||
|
||||
int print_raw;
|
||||
|
||||
int test_filters;
|
||||
|
||||
int flags;
|
||||
|
||||
struct format_field *bprint_ip_field;
|
||||
struct format_field *bprint_fmt_field;
|
||||
struct format_field *bprint_buf_field;
|
||||
|
||||
struct event_handler *handlers;
|
||||
struct pevent_function_handler *func_handlers;
|
||||
|
||||
/* cache */
|
||||
struct event_format *last_event;
|
||||
|
||||
char *trace_clock;
|
||||
};
|
||||
|
||||
static inline void pevent_set_flag(struct pevent *pevent, int flag)
|
||||
{
|
||||
pevent->flags |= flag;
|
||||
}
|
||||
|
||||
static inline unsigned short
|
||||
__data2host2(struct pevent *pevent, unsigned short data)
|
||||
{
|
||||
unsigned short swap;
|
||||
|
||||
if (pevent->host_bigendian == pevent->file_bigendian)
|
||||
return data;
|
||||
|
||||
swap = ((data & 0xffULL) << 8) |
|
||||
((data & (0xffULL << 8)) >> 8);
|
||||
|
||||
return swap;
|
||||
}
|
||||
|
||||
static inline unsigned int
|
||||
__data2host4(struct pevent *pevent, unsigned int data)
|
||||
{
|
||||
unsigned int swap;
|
||||
|
||||
if (pevent->host_bigendian == pevent->file_bigendian)
|
||||
return data;
|
||||
|
||||
swap = ((data & 0xffULL) << 24) |
|
||||
((data & (0xffULL << 8)) << 8) |
|
||||
((data & (0xffULL << 16)) >> 8) |
|
||||
((data & (0xffULL << 24)) >> 24);
|
||||
|
||||
return swap;
|
||||
}
|
||||
|
||||
static inline unsigned long long
|
||||
__data2host8(struct pevent *pevent, unsigned long long data)
|
||||
{
|
||||
unsigned long long swap;
|
||||
|
||||
if (pevent->host_bigendian == pevent->file_bigendian)
|
||||
return data;
|
||||
|
||||
swap = ((data & 0xffULL) << 56) |
|
||||
((data & (0xffULL << 8)) << 40) |
|
||||
((data & (0xffULL << 16)) << 24) |
|
||||
((data & (0xffULL << 24)) << 8) |
|
||||
((data & (0xffULL << 32)) >> 8) |
|
||||
((data & (0xffULL << 40)) >> 24) |
|
||||
((data & (0xffULL << 48)) >> 40) |
|
||||
((data & (0xffULL << 56)) >> 56);
|
||||
|
||||
return swap;
|
||||
}
|
||||
|
||||
#define data2host2(pevent, ptr) __data2host2(pevent, *(unsigned short *)(ptr))
|
||||
#define data2host4(pevent, ptr) __data2host4(pevent, *(unsigned int *)(ptr))
|
||||
#define data2host8(pevent, ptr) \
|
||||
({ \
|
||||
unsigned long long __val; \
|
||||
\
|
||||
memcpy(&__val, (ptr), sizeof(unsigned long long)); \
|
||||
__data2host8(pevent, __val); \
|
||||
})
|
||||
|
||||
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,
|
||||
TRACE_FLAG_IRQS_NOSUPPORT = 0x02,
|
||||
TRACE_FLAG_NEED_RESCHED = 0x04,
|
||||
TRACE_FLAG_HARDIRQ = 0x08,
|
||||
TRACE_FLAG_SOFTIRQ = 0x10,
|
||||
};
|
||||
|
||||
int pevent_register_comm(struct pevent *pevent, const char *comm, int pid);
|
||||
void pevent_register_trace_clock(struct pevent *pevent, char *trace_clock);
|
||||
int pevent_register_function(struct pevent *pevent, char *name,
|
||||
unsigned long long addr, char *mod);
|
||||
int pevent_register_print_string(struct pevent *pevent, const char *fmt,
|
||||
unsigned long long addr);
|
||||
int pevent_pid_is_registered(struct pevent *pevent, int pid);
|
||||
|
||||
void pevent_print_event(struct pevent *pevent, struct trace_seq *s,
|
||||
struct pevent_record *record, bool use_trace_clock);
|
||||
|
||||
int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size,
|
||||
int long_size);
|
||||
|
||||
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 pevent *pevent,
|
||||
struct event_format **eventp,
|
||||
const char *buf,
|
||||
unsigned long size, const char *sys);
|
||||
void pevent_free_format(struct event_format *event);
|
||||
|
||||
void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event,
|
||||
const char *name, struct pevent_record *record,
|
||||
int *len, int err);
|
||||
|
||||
int pevent_get_field_val(struct trace_seq *s, struct event_format *event,
|
||||
const char *name, struct pevent_record *record,
|
||||
unsigned long long *val, int err);
|
||||
int pevent_get_common_field_val(struct trace_seq *s, struct event_format *event,
|
||||
const char *name, struct pevent_record *record,
|
||||
unsigned long long *val, int err);
|
||||
int pevent_get_any_field_val(struct trace_seq *s, struct event_format *event,
|
||||
const char *name, struct pevent_record *record,
|
||||
unsigned long long *val, int err);
|
||||
|
||||
int pevent_print_num_field(struct trace_seq *s, const char *fmt,
|
||||
struct event_format *event, const char *name,
|
||||
struct pevent_record *record, int err);
|
||||
|
||||
int pevent_print_func_field(struct trace_seq *s, const char *fmt,
|
||||
struct event_format *event, const char *name,
|
||||
struct pevent_record *record, int err);
|
||||
|
||||
int pevent_register_event_handler(struct pevent *pevent, int id,
|
||||
const char *sys_name, const char *event_name,
|
||||
pevent_event_handler_func func, void *context);
|
||||
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);
|
||||
struct format_field *pevent_find_any_field(struct event_format *event, const char *name);
|
||||
|
||||
const char *pevent_find_function(struct pevent *pevent, unsigned long long addr);
|
||||
unsigned long long
|
||||
pevent_find_function_address(struct pevent *pevent, unsigned long long addr);
|
||||
unsigned long long pevent_read_number(struct pevent *pevent, const void *ptr, int size);
|
||||
int pevent_read_number_field(struct format_field *field, const void *data,
|
||||
unsigned long long *value);
|
||||
|
||||
struct event_format *pevent_find_event(struct pevent *pevent, int id);
|
||||
|
||||
struct event_format *
|
||||
pevent_find_event_by_name(struct pevent *pevent, const char *sys, const char *name);
|
||||
|
||||
void pevent_data_lat_fmt(struct pevent *pevent,
|
||||
struct trace_seq *s, struct pevent_record *record);
|
||||
int pevent_data_type(struct pevent *pevent, struct pevent_record *rec);
|
||||
struct event_format *pevent_data_event_from_type(struct pevent *pevent, int type);
|
||||
int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec);
|
||||
const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid);
|
||||
void pevent_event_info(struct trace_seq *s, struct event_format *event,
|
||||
struct pevent_record *record);
|
||||
int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum,
|
||||
char *buf, size_t buflen);
|
||||
|
||||
struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type);
|
||||
struct format_field **pevent_event_common_fields(struct event_format *event);
|
||||
struct format_field **pevent_event_fields(struct event_format *event);
|
||||
|
||||
static inline int pevent_get_cpus(struct pevent *pevent)
|
||||
{
|
||||
return pevent->cpus;
|
||||
}
|
||||
|
||||
static inline void pevent_set_cpus(struct pevent *pevent, int cpus)
|
||||
{
|
||||
pevent->cpus = cpus;
|
||||
}
|
||||
|
||||
static inline int pevent_get_long_size(struct pevent *pevent)
|
||||
{
|
||||
return pevent->long_size;
|
||||
}
|
||||
|
||||
static inline void pevent_set_long_size(struct pevent *pevent, int long_size)
|
||||
{
|
||||
pevent->long_size = long_size;
|
||||
}
|
||||
|
||||
static inline int pevent_get_page_size(struct pevent *pevent)
|
||||
{
|
||||
return pevent->page_size;
|
||||
}
|
||||
|
||||
static inline void pevent_set_page_size(struct pevent *pevent, int _page_size)
|
||||
{
|
||||
pevent->page_size = _page_size;
|
||||
}
|
||||
|
||||
static inline int pevent_is_file_bigendian(struct pevent *pevent)
|
||||
{
|
||||
return pevent->file_bigendian;
|
||||
}
|
||||
|
||||
static inline void pevent_set_file_bigendian(struct pevent *pevent, int endian)
|
||||
{
|
||||
pevent->file_bigendian = endian;
|
||||
}
|
||||
|
||||
static inline int pevent_is_host_bigendian(struct pevent *pevent)
|
||||
{
|
||||
return pevent->host_bigendian;
|
||||
}
|
||||
|
||||
static inline void pevent_set_host_bigendian(struct pevent *pevent, int endian)
|
||||
{
|
||||
pevent->host_bigendian = endian;
|
||||
}
|
||||
|
||||
static inline int pevent_is_latency_format(struct pevent *pevent)
|
||||
{
|
||||
return pevent->latency_format;
|
||||
}
|
||||
|
||||
static inline void pevent_set_latency_format(struct pevent *pevent, int lat)
|
||||
{
|
||||
pevent->latency_format = lat;
|
||||
}
|
||||
|
||||
struct pevent *pevent_alloc(void);
|
||||
void pevent_free(struct pevent *pevent);
|
||||
void pevent_ref(struct pevent *pevent);
|
||||
void pevent_unref(struct pevent *pevent);
|
||||
|
||||
/* access to the internal parser */
|
||||
void pevent_buffer_init(const char *buf, unsigned long long size);
|
||||
enum event_type pevent_read_token(char **tok);
|
||||
void pevent_free_token(char *token);
|
||||
int pevent_peek_char(void);
|
||||
const char *pevent_get_input_buf(void);
|
||||
unsigned long long pevent_get_input_buf_ptr(void);
|
||||
|
||||
/* for debugging */
|
||||
void pevent_print_funcs(struct pevent *pevent);
|
||||
void pevent_print_printk(struct pevent *pevent);
|
||||
|
||||
/* ----------------------- filtering ----------------------- */
|
||||
|
||||
enum filter_boolean_type {
|
||||
FILTER_FALSE,
|
||||
FILTER_TRUE,
|
||||
};
|
||||
|
||||
enum filter_op_type {
|
||||
FILTER_OP_AND = 1,
|
||||
FILTER_OP_OR,
|
||||
FILTER_OP_NOT,
|
||||
};
|
||||
|
||||
enum filter_cmp_type {
|
||||
FILTER_CMP_NONE,
|
||||
FILTER_CMP_EQ,
|
||||
FILTER_CMP_NE,
|
||||
FILTER_CMP_GT,
|
||||
FILTER_CMP_LT,
|
||||
FILTER_CMP_GE,
|
||||
FILTER_CMP_LE,
|
||||
FILTER_CMP_MATCH,
|
||||
FILTER_CMP_NOT_MATCH,
|
||||
FILTER_CMP_REGEX,
|
||||
FILTER_CMP_NOT_REGEX,
|
||||
};
|
||||
|
||||
enum filter_exp_type {
|
||||
FILTER_EXP_NONE,
|
||||
FILTER_EXP_ADD,
|
||||
FILTER_EXP_SUB,
|
||||
FILTER_EXP_MUL,
|
||||
FILTER_EXP_DIV,
|
||||
FILTER_EXP_MOD,
|
||||
FILTER_EXP_RSHIFT,
|
||||
FILTER_EXP_LSHIFT,
|
||||
FILTER_EXP_AND,
|
||||
FILTER_EXP_OR,
|
||||
FILTER_EXP_XOR,
|
||||
FILTER_EXP_NOT,
|
||||
};
|
||||
|
||||
enum filter_arg_type {
|
||||
FILTER_ARG_NONE,
|
||||
FILTER_ARG_BOOLEAN,
|
||||
FILTER_ARG_VALUE,
|
||||
FILTER_ARG_FIELD,
|
||||
FILTER_ARG_EXP,
|
||||
FILTER_ARG_OP,
|
||||
FILTER_ARG_NUM,
|
||||
FILTER_ARG_STR,
|
||||
};
|
||||
|
||||
enum filter_value_type {
|
||||
FILTER_NUMBER,
|
||||
FILTER_STRING,
|
||||
FILTER_CHAR
|
||||
};
|
||||
|
||||
struct fliter_arg;
|
||||
|
||||
struct filter_arg_boolean {
|
||||
enum filter_boolean_type value;
|
||||
};
|
||||
|
||||
struct filter_arg_field {
|
||||
struct format_field *field;
|
||||
};
|
||||
|
||||
struct filter_arg_value {
|
||||
enum filter_value_type type;
|
||||
union {
|
||||
char *str;
|
||||
unsigned long long val;
|
||||
};
|
||||
};
|
||||
|
||||
struct filter_arg_op {
|
||||
enum filter_op_type type;
|
||||
struct filter_arg *left;
|
||||
struct filter_arg *right;
|
||||
};
|
||||
|
||||
struct filter_arg_exp {
|
||||
enum filter_exp_type type;
|
||||
struct filter_arg *left;
|
||||
struct filter_arg *right;
|
||||
};
|
||||
|
||||
struct filter_arg_num {
|
||||
enum filter_cmp_type type;
|
||||
struct filter_arg *left;
|
||||
struct filter_arg *right;
|
||||
};
|
||||
|
||||
struct filter_arg_str {
|
||||
enum filter_cmp_type type;
|
||||
struct format_field *field;
|
||||
char *val;
|
||||
char *buffer;
|
||||
regex_t reg;
|
||||
};
|
||||
|
||||
struct filter_arg {
|
||||
enum filter_arg_type type;
|
||||
union {
|
||||
struct filter_arg_boolean boolean;
|
||||
struct filter_arg_field field;
|
||||
struct filter_arg_value value;
|
||||
struct filter_arg_op op;
|
||||
struct filter_arg_exp exp;
|
||||
struct filter_arg_num num;
|
||||
struct filter_arg_str str;
|
||||
};
|
||||
};
|
||||
|
||||
struct filter_type {
|
||||
int event_id;
|
||||
struct event_format *event;
|
||||
struct filter_arg *filter;
|
||||
};
|
||||
|
||||
#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);
|
||||
|
||||
/* for backward compatibility */
|
||||
#define FILTER_NONE PEVENT_ERRNO__NO_FILTER
|
||||
#define FILTER_NOEXIST PEVENT_ERRNO__FILTER_NOT_FOUND
|
||||
#define FILTER_MISS PEVENT_ERRNO__FILTER_MISS
|
||||
#define FILTER_MATCH PEVENT_ERRNO__FILTER_MATCH
|
||||
|
||||
enum filter_trivial_type {
|
||||
FILTER_TRIVIAL_FALSE,
|
||||
FILTER_TRIVIAL_TRUE,
|
||||
FILTER_TRIVIAL_BOTH,
|
||||
};
|
||||
|
||||
enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter,
|
||||
const char *filter_str);
|
||||
|
||||
enum pevent_errno pevent_filter_match(struct event_filter *filter,
|
||||
struct pevent_record *record);
|
||||
|
||||
int pevent_filter_strerror(struct event_filter *filter, enum pevent_errno err,
|
||||
char *buf, size_t buflen);
|
||||
|
||||
int pevent_event_filtered(struct event_filter *filter,
|
||||
int event_id);
|
||||
|
||||
void pevent_filter_reset(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);
|
||||
|
||||
char *pevent_filter_make_string(struct event_filter *filter, int event_id);
|
||||
|
||||
int pevent_filter_remove_event(struct event_filter *filter,
|
||||
int event_id);
|
||||
|
||||
int pevent_filter_event_has_trivial(struct event_filter *filter,
|
||||
int event_id,
|
||||
enum filter_trivial_type type);
|
||||
|
||||
int pevent_filter_copy(struct event_filter *dest, struct event_filter *source);
|
||||
|
||||
int pevent_update_trivial(struct event_filter *dest, struct event_filter *source,
|
||||
enum filter_trivial_type type);
|
||||
|
||||
int pevent_filter_compare(struct event_filter *filter1, struct event_filter *filter2);
|
||||
|
||||
#endif /* _PARSE_EVENTS_H */
|
416
tools/lib/traceevent/event-plugin.c
Normal file
416
tools/lib/traceevent/event-plugin.c
Normal file
|
@ -0,0 +1,416 @@
|
|||
/*
|
||||
* 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 <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"
|
||||
|
||||
static struct registered_plugin_options {
|
||||
struct registered_plugin_options *next;
|
||||
struct pevent_plugin_option *options;
|
||||
} *registered_options;
|
||||
|
||||
static struct trace_plugin_options {
|
||||
struct trace_plugin_options *next;
|
||||
char *plugin;
|
||||
char *option;
|
||||
char *value;
|
||||
} *trace_plugin_options;
|
||||
|
||||
struct plugin_list {
|
||||
struct plugin_list *next;
|
||||
char *name;
|
||||
void *handle;
|
||||
};
|
||||
|
||||
/**
|
||||
* traceevent_plugin_list_options - get list of plugin options
|
||||
*
|
||||
* Returns an array of char strings that list the currently registered
|
||||
* plugin options in the format of <plugin>:<option>. This list can be
|
||||
* used by toggling the option.
|
||||
*
|
||||
* Returns NULL if there's no options registered. On error it returns
|
||||
* INVALID_PLUGIN_LIST_OPTION
|
||||
*
|
||||
* Must be freed with traceevent_plugin_free_options_list().
|
||||
*/
|
||||
char **traceevent_plugin_list_options(void)
|
||||
{
|
||||
struct registered_plugin_options *reg;
|
||||
struct pevent_plugin_option *op;
|
||||
char **list = NULL;
|
||||
char *name;
|
||||
int count = 0;
|
||||
|
||||
for (reg = registered_options; reg; reg = reg->next) {
|
||||
for (op = reg->options; op->name; op++) {
|
||||
char *alias = op->plugin_alias ? op->plugin_alias : op->file;
|
||||
char **temp = list;
|
||||
|
||||
name = malloc(strlen(op->name) + strlen(alias) + 2);
|
||||
if (!name)
|
||||
goto err;
|
||||
|
||||
sprintf(name, "%s:%s", alias, op->name);
|
||||
list = realloc(list, count + 2);
|
||||
if (!list) {
|
||||
list = temp;
|
||||
free(name);
|
||||
goto err;
|
||||
}
|
||||
list[count++] = name;
|
||||
list[count] = NULL;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
|
||||
err:
|
||||
while (--count >= 0)
|
||||
free(list[count]);
|
||||
free(list);
|
||||
|
||||
return INVALID_PLUGIN_LIST_OPTION;
|
||||
}
|
||||
|
||||
void traceevent_plugin_free_options_list(char **list)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!list)
|
||||
return;
|
||||
|
||||
if (list == INVALID_PLUGIN_LIST_OPTION)
|
||||
return;
|
||||
|
||||
for (i = 0; list[i]; i++)
|
||||
free(list[i]);
|
||||
|
||||
free(list);
|
||||
}
|
||||
|
||||
static int
|
||||
update_option(const char *file, struct pevent_plugin_option *option)
|
||||
{
|
||||
struct trace_plugin_options *op;
|
||||
char *plugin;
|
||||
|
||||
if (option->plugin_alias) {
|
||||
plugin = strdup(option->plugin_alias);
|
||||
if (!plugin)
|
||||
return -1;
|
||||
} else {
|
||||
char *p;
|
||||
plugin = strdup(file);
|
||||
if (!plugin)
|
||||
return -1;
|
||||
p = strstr(plugin, ".");
|
||||
if (p)
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
/* first look for named options */
|
||||
for (op = trace_plugin_options; op; op = op->next) {
|
||||
if (!op->plugin)
|
||||
continue;
|
||||
if (strcmp(op->plugin, plugin) != 0)
|
||||
continue;
|
||||
if (strcmp(op->option, option->name) != 0)
|
||||
continue;
|
||||
|
||||
option->value = op->value;
|
||||
option->set ^= 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* first look for unnamed options */
|
||||
for (op = trace_plugin_options; op; op = op->next) {
|
||||
if (op->plugin)
|
||||
continue;
|
||||
if (strcmp(op->option, option->name) != 0)
|
||||
continue;
|
||||
|
||||
option->value = op->value;
|
||||
option->set ^= 1;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
free(plugin);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* traceevent_plugin_add_options - Add a set of options by a plugin
|
||||
* @name: The name of the plugin adding the options
|
||||
* @options: The set of options being loaded
|
||||
*
|
||||
* Sets the options with the values that have been added by user.
|
||||
*/
|
||||
int traceevent_plugin_add_options(const char *name,
|
||||
struct pevent_plugin_option *options)
|
||||
{
|
||||
struct registered_plugin_options *reg;
|
||||
|
||||
reg = malloc(sizeof(*reg));
|
||||
if (!reg)
|
||||
return -1;
|
||||
reg->next = registered_options;
|
||||
reg->options = options;
|
||||
registered_options = reg;
|
||||
|
||||
while (options->name) {
|
||||
update_option(name, options);
|
||||
options++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* traceevent_plugin_remove_options - remove plugin options that were registered
|
||||
* @options: Options to removed that were registered with traceevent_plugin_add_options
|
||||
*/
|
||||
void traceevent_plugin_remove_options(struct pevent_plugin_option *options)
|
||||
{
|
||||
struct registered_plugin_options **last;
|
||||
struct registered_plugin_options *reg;
|
||||
|
||||
for (last = ®istered_options; *last; last = &(*last)->next) {
|
||||
if ((*last)->options == options) {
|
||||
reg = *last;
|
||||
*last = reg->next;
|
||||
free(reg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* traceevent_print_plugins - print out the list of plugins loaded
|
||||
* @s: the trace_seq descripter to write to
|
||||
* @prefix: The prefix string to add before listing the option name
|
||||
* @suffix: The suffix string ot append after the option name
|
||||
* @list: The list of plugins (usually returned by traceevent_load_plugins()
|
||||
*
|
||||
* Writes to the trace_seq @s the list of plugins (files) that is
|
||||
* returned by traceevent_load_plugins(). Use @prefix and @suffix for formating:
|
||||
* @prefix = " ", @suffix = "\n".
|
||||
*/
|
||||
void traceevent_print_plugins(struct trace_seq *s,
|
||||
const char *prefix, const char *suffix,
|
||||
const struct plugin_list *list)
|
||||
{
|
||||
while (list) {
|
||||
trace_seq_printf(s, "%s%s%s", prefix, list->name, suffix);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
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 (pevent->flags & PEVENT_DISABLE_PLUGINS)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If a system plugin directory was defined,
|
||||
* check that first.
|
||||
*/
|
||||
#ifdef PLUGIN_DIR
|
||||
if (!(pevent->flags & PEVENT_DISABLE_SYS_PLUGINS))
|
||||
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);
|
||||
}
|
||||
}
|
81
tools/lib/traceevent/event-utils.h
Normal file
81
tools/lib/traceevent/event-utils.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#ifndef __UTIL_H
|
||||
#define __UTIL_H
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/* Can be overridden */
|
||||
void warning(const char *fmt, ...);
|
||||
void pr_stat(const char *fmt, ...);
|
||||
void vpr_stat(const char *fmt, va_list ap);
|
||||
|
||||
/* Always available */
|
||||
void __warning(const char *fmt, ...);
|
||||
void __pr_stat(const char *fmt, ...);
|
||||
|
||||
void __vwarning(const char *fmt, ...);
|
||||
void __vpr_stat(const char *fmt, ...);
|
||||
|
||||
#define min(x, y) ({ \
|
||||
typeof(x) _min1 = (x); \
|
||||
typeof(y) _min2 = (y); \
|
||||
(void) (&_min1 == &_min2); \
|
||||
_min1 < _min2 ? _min1 : _min2; })
|
||||
|
||||
static inline char *strim(char *string)
|
||||
{
|
||||
char *ret;
|
||||
|
||||
if (!string)
|
||||
return NULL;
|
||||
while (*string) {
|
||||
if (!isspace(*string))
|
||||
break;
|
||||
string++;
|
||||
}
|
||||
ret = string;
|
||||
|
||||
string = ret + strlen(ret) - 1;
|
||||
while (string > ret) {
|
||||
if (!isspace(*string))
|
||||
break;
|
||||
string--;
|
||||
}
|
||||
string[1] = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int has_text(const char *text)
|
||||
{
|
||||
if (!text)
|
||||
return 0;
|
||||
|
||||
while (*text) {
|
||||
if (!isspace(*text))
|
||||
return 1;
|
||||
text++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
731
tools/lib/traceevent/kbuffer-parse.c
Normal file
731
tools/lib/traceevent/kbuffer-parse.c
Normal file
|
@ -0,0 +1,731 @@
|
|||
/*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "kbuffer.h"
|
||||
|
||||
#define MISSING_EVENTS (1 << 31)
|
||||
#define MISSING_STORED (1 << 30)
|
||||
|
||||
#define COMMIT_MASK ((1 << 27) - 1)
|
||||
|
||||
enum {
|
||||
KBUFFER_FL_HOST_BIG_ENDIAN = (1<<0),
|
||||
KBUFFER_FL_BIG_ENDIAN = (1<<1),
|
||||
KBUFFER_FL_LONG_8 = (1<<2),
|
||||
KBUFFER_FL_OLD_FORMAT = (1<<3),
|
||||
};
|
||||
|
||||
#define ENDIAN_MASK (KBUFFER_FL_HOST_BIG_ENDIAN | KBUFFER_FL_BIG_ENDIAN)
|
||||
|
||||
/** kbuffer
|
||||
* @timestamp - timestamp of current event
|
||||
* @lost_events - # of lost events between this subbuffer and previous
|
||||
* @flags - special flags of the kbuffer
|
||||
* @subbuffer - pointer to the sub-buffer page
|
||||
* @data - pointer to the start of data on the sub-buffer page
|
||||
* @index - index from @data to the @curr event data
|
||||
* @curr - offset from @data to the start of current event
|
||||
* (includes metadata)
|
||||
* @next - offset from @data to the start of next event
|
||||
* @size - The size of data on @data
|
||||
* @start - The offset from @subbuffer where @data lives
|
||||
*
|
||||
* @read_4 - Function to read 4 raw bytes (may swap)
|
||||
* @read_8 - Function to read 8 raw bytes (may swap)
|
||||
* @read_long - Function to read a long word (4 or 8 bytes with needed swap)
|
||||
*/
|
||||
struct kbuffer {
|
||||
unsigned long long timestamp;
|
||||
long long lost_events;
|
||||
unsigned long flags;
|
||||
void *subbuffer;
|
||||
void *data;
|
||||
unsigned int index;
|
||||
unsigned int curr;
|
||||
unsigned int next;
|
||||
unsigned int size;
|
||||
unsigned int start;
|
||||
|
||||
unsigned int (*read_4)(void *ptr);
|
||||
unsigned long long (*read_8)(void *ptr);
|
||||
unsigned long long (*read_long)(struct kbuffer *kbuf, void *ptr);
|
||||
int (*next_event)(struct kbuffer *kbuf);
|
||||
};
|
||||
|
||||
static void *zmalloc(size_t size)
|
||||
{
|
||||
return calloc(1, size);
|
||||
}
|
||||
|
||||
static int host_is_bigendian(void)
|
||||
{
|
||||
unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 };
|
||||
unsigned int *ptr;
|
||||
|
||||
ptr = (unsigned int *)str;
|
||||
return *ptr == 0x01020304;
|
||||
}
|
||||
|
||||
static int do_swap(struct kbuffer *kbuf)
|
||||
{
|
||||
return ((kbuf->flags & KBUFFER_FL_HOST_BIG_ENDIAN) + kbuf->flags) &
|
||||
ENDIAN_MASK;
|
||||
}
|
||||
|
||||
static unsigned long long __read_8(void *ptr)
|
||||
{
|
||||
unsigned long long data = *(unsigned long long *)ptr;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static unsigned long long __read_8_sw(void *ptr)
|
||||
{
|
||||
unsigned long long data = *(unsigned long long *)ptr;
|
||||
unsigned long long swap;
|
||||
|
||||
swap = ((data & 0xffULL) << 56) |
|
||||
((data & (0xffULL << 8)) << 40) |
|
||||
((data & (0xffULL << 16)) << 24) |
|
||||
((data & (0xffULL << 24)) << 8) |
|
||||
((data & (0xffULL << 32)) >> 8) |
|
||||
((data & (0xffULL << 40)) >> 24) |
|
||||
((data & (0xffULL << 48)) >> 40) |
|
||||
((data & (0xffULL << 56)) >> 56);
|
||||
|
||||
return swap;
|
||||
}
|
||||
|
||||
static unsigned int __read_4(void *ptr)
|
||||
{
|
||||
unsigned int data = *(unsigned int *)ptr;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static unsigned int __read_4_sw(void *ptr)
|
||||
{
|
||||
unsigned int data = *(unsigned int *)ptr;
|
||||
unsigned int swap;
|
||||
|
||||
swap = ((data & 0xffULL) << 24) |
|
||||
((data & (0xffULL << 8)) << 8) |
|
||||
((data & (0xffULL << 16)) >> 8) |
|
||||
((data & (0xffULL << 24)) >> 24);
|
||||
|
||||
return swap;
|
||||
}
|
||||
|
||||
static unsigned long long read_8(struct kbuffer *kbuf, void *ptr)
|
||||
{
|
||||
return kbuf->read_8(ptr);
|
||||
}
|
||||
|
||||
static unsigned int read_4(struct kbuffer *kbuf, void *ptr)
|
||||
{
|
||||
return kbuf->read_4(ptr);
|
||||
}
|
||||
|
||||
static unsigned long long __read_long_8(struct kbuffer *kbuf, void *ptr)
|
||||
{
|
||||
return kbuf->read_8(ptr);
|
||||
}
|
||||
|
||||
static unsigned long long __read_long_4(struct kbuffer *kbuf, void *ptr)
|
||||
{
|
||||
return kbuf->read_4(ptr);
|
||||
}
|
||||
|
||||
static unsigned long long read_long(struct kbuffer *kbuf, void *ptr)
|
||||
{
|
||||
return kbuf->read_long(kbuf, ptr);
|
||||
}
|
||||
|
||||
static int calc_index(struct kbuffer *kbuf, void *ptr)
|
||||
{
|
||||
return (unsigned long)ptr - (unsigned long)kbuf->data;
|
||||
}
|
||||
|
||||
static int __next_event(struct kbuffer *kbuf);
|
||||
|
||||
/**
|
||||
* kbuffer_alloc - allocat a new kbuffer
|
||||
* @size; enum to denote size of word
|
||||
* @endian: enum to denote endianness
|
||||
*
|
||||
* Allocates and returns a new kbuffer.
|
||||
*/
|
||||
struct kbuffer *
|
||||
kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian)
|
||||
{
|
||||
struct kbuffer *kbuf;
|
||||
int flags = 0;
|
||||
|
||||
switch (size) {
|
||||
case KBUFFER_LSIZE_4:
|
||||
break;
|
||||
case KBUFFER_LSIZE_8:
|
||||
flags |= KBUFFER_FL_LONG_8;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (endian) {
|
||||
case KBUFFER_ENDIAN_LITTLE:
|
||||
break;
|
||||
case KBUFFER_ENDIAN_BIG:
|
||||
flags |= KBUFFER_FL_BIG_ENDIAN;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
kbuf = zmalloc(sizeof(*kbuf));
|
||||
if (!kbuf)
|
||||
return NULL;
|
||||
|
||||
kbuf->flags = flags;
|
||||
|
||||
if (host_is_bigendian())
|
||||
kbuf->flags |= KBUFFER_FL_HOST_BIG_ENDIAN;
|
||||
|
||||
if (do_swap(kbuf)) {
|
||||
kbuf->read_8 = __read_8_sw;
|
||||
kbuf->read_4 = __read_4_sw;
|
||||
} else {
|
||||
kbuf->read_8 = __read_8;
|
||||
kbuf->read_4 = __read_4;
|
||||
}
|
||||
|
||||
if (kbuf->flags & KBUFFER_FL_LONG_8)
|
||||
kbuf->read_long = __read_long_8;
|
||||
else
|
||||
kbuf->read_long = __read_long_4;
|
||||
|
||||
/* May be changed by kbuffer_set_old_format() */
|
||||
kbuf->next_event = __next_event;
|
||||
|
||||
return kbuf;
|
||||
}
|
||||
|
||||
/** kbuffer_free - free an allocated kbuffer
|
||||
* @kbuf: The kbuffer to free
|
||||
*
|
||||
* Can take NULL as a parameter.
|
||||
*/
|
||||
void kbuffer_free(struct kbuffer *kbuf)
|
||||
{
|
||||
free(kbuf);
|
||||
}
|
||||
|
||||
static unsigned int type4host(struct kbuffer *kbuf,
|
||||
unsigned int type_len_ts)
|
||||
{
|
||||
if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN)
|
||||
return (type_len_ts >> 29) & 3;
|
||||
else
|
||||
return type_len_ts & 3;
|
||||
}
|
||||
|
||||
static unsigned int len4host(struct kbuffer *kbuf,
|
||||
unsigned int type_len_ts)
|
||||
{
|
||||
if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN)
|
||||
return (type_len_ts >> 27) & 7;
|
||||
else
|
||||
return (type_len_ts >> 2) & 7;
|
||||
}
|
||||
|
||||
static unsigned int type_len4host(struct kbuffer *kbuf,
|
||||
unsigned int type_len_ts)
|
||||
{
|
||||
if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN)
|
||||
return (type_len_ts >> 27) & ((1 << 5) - 1);
|
||||
else
|
||||
return type_len_ts & ((1 << 5) - 1);
|
||||
}
|
||||
|
||||
static unsigned int ts4host(struct kbuffer *kbuf,
|
||||
unsigned int type_len_ts)
|
||||
{
|
||||
if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN)
|
||||
return type_len_ts & ((1 << 27) - 1);
|
||||
else
|
||||
return type_len_ts >> 5;
|
||||
}
|
||||
|
||||
/*
|
||||
* Linux 2.6.30 and earlier (not much ealier) had a different
|
||||
* ring buffer format. It should be obsolete, but we handle it anyway.
|
||||
*/
|
||||
enum old_ring_buffer_type {
|
||||
OLD_RINGBUF_TYPE_PADDING,
|
||||
OLD_RINGBUF_TYPE_TIME_EXTEND,
|
||||
OLD_RINGBUF_TYPE_TIME_STAMP,
|
||||
OLD_RINGBUF_TYPE_DATA,
|
||||
};
|
||||
|
||||
static unsigned int old_update_pointers(struct kbuffer *kbuf)
|
||||
{
|
||||
unsigned long long extend;
|
||||
unsigned int type_len_ts;
|
||||
unsigned int type;
|
||||
unsigned int len;
|
||||
unsigned int delta;
|
||||
unsigned int length;
|
||||
void *ptr = kbuf->data + kbuf->curr;
|
||||
|
||||
type_len_ts = read_4(kbuf, ptr);
|
||||
ptr += 4;
|
||||
|
||||
type = type4host(kbuf, type_len_ts);
|
||||
len = len4host(kbuf, type_len_ts);
|
||||
delta = ts4host(kbuf, type_len_ts);
|
||||
|
||||
switch (type) {
|
||||
case OLD_RINGBUF_TYPE_PADDING:
|
||||
kbuf->next = kbuf->size;
|
||||
return 0;
|
||||
|
||||
case OLD_RINGBUF_TYPE_TIME_EXTEND:
|
||||
extend = read_4(kbuf, ptr);
|
||||
extend <<= TS_SHIFT;
|
||||
extend += delta;
|
||||
delta = extend;
|
||||
ptr += 4;
|
||||
break;
|
||||
|
||||
case OLD_RINGBUF_TYPE_TIME_STAMP:
|
||||
/* should never happen! */
|
||||
kbuf->curr = kbuf->size;
|
||||
kbuf->next = kbuf->size;
|
||||
kbuf->index = kbuf->size;
|
||||
return -1;
|
||||
default:
|
||||
if (len)
|
||||
length = len * 4;
|
||||
else {
|
||||
length = read_4(kbuf, ptr);
|
||||
length -= 4;
|
||||
ptr += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
kbuf->timestamp += delta;
|
||||
kbuf->index = calc_index(kbuf, ptr);
|
||||
kbuf->next = kbuf->index + length;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static int __old_next_event(struct kbuffer *kbuf)
|
||||
{
|
||||
int type;
|
||||
|
||||
do {
|
||||
kbuf->curr = kbuf->next;
|
||||
if (kbuf->next >= kbuf->size)
|
||||
return -1;
|
||||
type = old_update_pointers(kbuf);
|
||||
} while (type == OLD_RINGBUF_TYPE_TIME_EXTEND || type == OLD_RINGBUF_TYPE_PADDING);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
translate_data(struct kbuffer *kbuf, void *data, void **rptr,
|
||||
unsigned long long *delta, int *length)
|
||||
{
|
||||
unsigned long long extend;
|
||||
unsigned int type_len_ts;
|
||||
unsigned int type_len;
|
||||
|
||||
type_len_ts = read_4(kbuf, data);
|
||||
data += 4;
|
||||
|
||||
type_len = type_len4host(kbuf, type_len_ts);
|
||||
*delta = ts4host(kbuf, type_len_ts);
|
||||
|
||||
switch (type_len) {
|
||||
case KBUFFER_TYPE_PADDING:
|
||||
*length = read_4(kbuf, data);
|
||||
break;
|
||||
|
||||
case KBUFFER_TYPE_TIME_EXTEND:
|
||||
extend = read_4(kbuf, data);
|
||||
data += 4;
|
||||
extend <<= TS_SHIFT;
|
||||
extend += *delta;
|
||||
*delta = extend;
|
||||
*length = 0;
|
||||
break;
|
||||
|
||||
case KBUFFER_TYPE_TIME_STAMP:
|
||||
data += 12;
|
||||
*length = 0;
|
||||
break;
|
||||
case 0:
|
||||
*length = read_4(kbuf, data) - 4;
|
||||
*length = (*length + 3) & ~3;
|
||||
data += 4;
|
||||
break;
|
||||
default:
|
||||
*length = type_len * 4;
|
||||
break;
|
||||
}
|
||||
|
||||
*rptr = data;
|
||||
|
||||
return type_len;
|
||||
}
|
||||
|
||||
static unsigned int update_pointers(struct kbuffer *kbuf)
|
||||
{
|
||||
unsigned long long delta;
|
||||
unsigned int type_len;
|
||||
int length;
|
||||
void *ptr = kbuf->data + kbuf->curr;
|
||||
|
||||
type_len = translate_data(kbuf, ptr, &ptr, &delta, &length);
|
||||
|
||||
kbuf->timestamp += delta;
|
||||
kbuf->index = calc_index(kbuf, ptr);
|
||||
kbuf->next = kbuf->index + length;
|
||||
|
||||
return type_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_translate_data - read raw data to get a record
|
||||
* @swap: Set to 1 if bytes in words need to be swapped when read
|
||||
* @data: The raw data to read
|
||||
* @size: Address to store the size of the event data.
|
||||
*
|
||||
* Returns a pointer to the event data. To determine the entire
|
||||
* record size (record metadata + data) just add the difference between
|
||||
* @data and the returned value to @size.
|
||||
*/
|
||||
void *kbuffer_translate_data(int swap, void *data, unsigned int *size)
|
||||
{
|
||||
unsigned long long delta;
|
||||
struct kbuffer kbuf;
|
||||
int type_len;
|
||||
int length;
|
||||
void *ptr;
|
||||
|
||||
if (swap) {
|
||||
kbuf.read_8 = __read_8_sw;
|
||||
kbuf.read_4 = __read_4_sw;
|
||||
kbuf.flags = host_is_bigendian() ? 0 : KBUFFER_FL_BIG_ENDIAN;
|
||||
} else {
|
||||
kbuf.read_8 = __read_8;
|
||||
kbuf.read_4 = __read_4;
|
||||
kbuf.flags = host_is_bigendian() ? KBUFFER_FL_BIG_ENDIAN: 0;
|
||||
}
|
||||
|
||||
type_len = translate_data(&kbuf, data, &ptr, &delta, &length);
|
||||
switch (type_len) {
|
||||
case KBUFFER_TYPE_PADDING:
|
||||
case KBUFFER_TYPE_TIME_EXTEND:
|
||||
case KBUFFER_TYPE_TIME_STAMP:
|
||||
return NULL;
|
||||
};
|
||||
|
||||
*size = length;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static int __next_event(struct kbuffer *kbuf)
|
||||
{
|
||||
int type;
|
||||
|
||||
do {
|
||||
kbuf->curr = kbuf->next;
|
||||
if (kbuf->next >= kbuf->size)
|
||||
return -1;
|
||||
type = update_pointers(kbuf);
|
||||
} while (type == KBUFFER_TYPE_TIME_EXTEND || type == KBUFFER_TYPE_PADDING);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int next_event(struct kbuffer *kbuf)
|
||||
{
|
||||
return kbuf->next_event(kbuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_next_event - increment the current pointer
|
||||
* @kbuf: The kbuffer to read
|
||||
* @ts: Address to store the next record's timestamp (may be NULL to ignore)
|
||||
*
|
||||
* Increments the pointers into the subbuffer of the kbuffer to point to the
|
||||
* next event so that the next kbuffer_read_event() will return a
|
||||
* new event.
|
||||
*
|
||||
* Returns the data of the next event if a new event exists on the subbuffer,
|
||||
* NULL otherwise.
|
||||
*/
|
||||
void *kbuffer_next_event(struct kbuffer *kbuf, unsigned long long *ts)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!kbuf || !kbuf->subbuffer)
|
||||
return NULL;
|
||||
|
||||
ret = next_event(kbuf);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
if (ts)
|
||||
*ts = kbuf->timestamp;
|
||||
|
||||
return kbuf->data + kbuf->index;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_load_subbuffer - load a new subbuffer into the kbuffer
|
||||
* @kbuf: The kbuffer to load
|
||||
* @subbuffer: The subbuffer to load into @kbuf.
|
||||
*
|
||||
* Load a new subbuffer (page) into @kbuf. This will reset all
|
||||
* the pointers and update the @kbuf timestamp. The next read will
|
||||
* return the first event on @subbuffer.
|
||||
*
|
||||
* Returns 0 on succes, -1 otherwise.
|
||||
*/
|
||||
int kbuffer_load_subbuffer(struct kbuffer *kbuf, void *subbuffer)
|
||||
{
|
||||
unsigned long long flags;
|
||||
void *ptr = subbuffer;
|
||||
|
||||
if (!kbuf || !subbuffer)
|
||||
return -1;
|
||||
|
||||
kbuf->subbuffer = subbuffer;
|
||||
|
||||
kbuf->timestamp = read_8(kbuf, ptr);
|
||||
ptr += 8;
|
||||
|
||||
kbuf->curr = 0;
|
||||
|
||||
if (kbuf->flags & KBUFFER_FL_LONG_8)
|
||||
kbuf->start = 16;
|
||||
else
|
||||
kbuf->start = 12;
|
||||
|
||||
kbuf->data = subbuffer + kbuf->start;
|
||||
|
||||
flags = read_long(kbuf, ptr);
|
||||
kbuf->size = (unsigned int)flags & COMMIT_MASK;
|
||||
|
||||
if (flags & MISSING_EVENTS) {
|
||||
if (flags & MISSING_STORED) {
|
||||
ptr = kbuf->data + kbuf->size;
|
||||
kbuf->lost_events = read_long(kbuf, ptr);
|
||||
} else
|
||||
kbuf->lost_events = -1;
|
||||
} else
|
||||
kbuf->lost_events = 0;
|
||||
|
||||
kbuf->index = 0;
|
||||
kbuf->next = 0;
|
||||
|
||||
next_event(kbuf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_read_event - read the next event in the kbuffer subbuffer
|
||||
* @kbuf: The kbuffer to read from
|
||||
* @ts: The address to store the timestamp of the event (may be NULL to ignore)
|
||||
*
|
||||
* Returns a pointer to the data part of the current event.
|
||||
* NULL if no event is left on the subbuffer.
|
||||
*/
|
||||
void *kbuffer_read_event(struct kbuffer *kbuf, unsigned long long *ts)
|
||||
{
|
||||
if (!kbuf || !kbuf->subbuffer)
|
||||
return NULL;
|
||||
|
||||
if (kbuf->curr >= kbuf->size)
|
||||
return NULL;
|
||||
|
||||
if (ts)
|
||||
*ts = kbuf->timestamp;
|
||||
return kbuf->data + kbuf->index;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_timestamp - Return the timestamp of the current event
|
||||
* @kbuf: The kbuffer to read from
|
||||
*
|
||||
* Returns the timestamp of the current (next) event.
|
||||
*/
|
||||
unsigned long long kbuffer_timestamp(struct kbuffer *kbuf)
|
||||
{
|
||||
return kbuf->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_read_at_offset - read the event that is at offset
|
||||
* @kbuf: The kbuffer to read from
|
||||
* @offset: The offset into the subbuffer
|
||||
* @ts: The address to store the timestamp of the event (may be NULL to ignore)
|
||||
*
|
||||
* The @offset must be an index from the @kbuf subbuffer beginning.
|
||||
* If @offset is bigger than the stored subbuffer, NULL will be returned.
|
||||
*
|
||||
* Returns the data of the record that is at @offset. Note, @offset does
|
||||
* not need to be the start of the record, the offset just needs to be
|
||||
* in the record (or beginning of it).
|
||||
*
|
||||
* Note, the kbuf timestamp and pointers are updated to the
|
||||
* returned record. That is, kbuffer_read_event() will return the same
|
||||
* data and timestamp, and kbuffer_next_event() will increment from
|
||||
* this record.
|
||||
*/
|
||||
void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset,
|
||||
unsigned long long *ts)
|
||||
{
|
||||
void *data;
|
||||
|
||||
if (offset < kbuf->start)
|
||||
offset = 0;
|
||||
else
|
||||
offset -= kbuf->start;
|
||||
|
||||
/* Reset the buffer */
|
||||
kbuffer_load_subbuffer(kbuf, kbuf->subbuffer);
|
||||
|
||||
while (kbuf->curr < offset) {
|
||||
data = kbuffer_next_event(kbuf, ts);
|
||||
if (!data)
|
||||
break;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_subbuffer_size - the size of the loaded subbuffer
|
||||
* @kbuf: The kbuffer to read from
|
||||
*
|
||||
* Returns the size of the subbuffer. Note, this size is
|
||||
* where the last event resides. The stored subbuffer may actually be
|
||||
* bigger due to padding and such.
|
||||
*/
|
||||
int kbuffer_subbuffer_size(struct kbuffer *kbuf)
|
||||
{
|
||||
return kbuf->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_curr_index - Return the index of the record
|
||||
* @kbuf: The kbuffer to read from
|
||||
*
|
||||
* Returns the index from the start of the data part of
|
||||
* the subbuffer to the current location. Note this is not
|
||||
* from the start of the subbuffer. An index of zero will
|
||||
* point to the first record. Use kbuffer_curr_offset() for
|
||||
* the actually offset (that can be used by kbuffer_read_at_offset())
|
||||
*/
|
||||
int kbuffer_curr_index(struct kbuffer *kbuf)
|
||||
{
|
||||
return kbuf->curr;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_curr_offset - Return the offset of the record
|
||||
* @kbuf: The kbuffer to read from
|
||||
*
|
||||
* Returns the offset from the start of the subbuffer to the
|
||||
* current location.
|
||||
*/
|
||||
int kbuffer_curr_offset(struct kbuffer *kbuf)
|
||||
{
|
||||
return kbuf->curr + kbuf->start;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_event_size - return the size of the event data
|
||||
* @kbuf: The kbuffer to read
|
||||
*
|
||||
* Returns the size of the event data (the payload not counting
|
||||
* the meta data of the record) of the current event.
|
||||
*/
|
||||
int kbuffer_event_size(struct kbuffer *kbuf)
|
||||
{
|
||||
return kbuf->next - kbuf->index;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_curr_size - return the size of the entire record
|
||||
* @kbuf: The kbuffer to read
|
||||
*
|
||||
* Returns the size of the entire record (meta data and payload)
|
||||
* of the current event.
|
||||
*/
|
||||
int kbuffer_curr_size(struct kbuffer *kbuf)
|
||||
{
|
||||
return kbuf->next - kbuf->curr;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_missed_events - return the # of missed events from last event.
|
||||
* @kbuf: The kbuffer to read from
|
||||
*
|
||||
* Returns the # of missed events (if recorded) before the current
|
||||
* event. Note, only events on the beginning of a subbuffer can
|
||||
* have missed events, all other events within the buffer will be
|
||||
* zero.
|
||||
*/
|
||||
int kbuffer_missed_events(struct kbuffer *kbuf)
|
||||
{
|
||||
/* Only the first event can have missed events */
|
||||
if (kbuf->curr)
|
||||
return 0;
|
||||
|
||||
return kbuf->lost_events;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_set_old_forma - set the kbuffer to use the old format parsing
|
||||
* @kbuf: The kbuffer to set
|
||||
*
|
||||
* This is obsolete (or should be). The first kernels to use the
|
||||
* new ring buffer had a slightly different ring buffer format
|
||||
* (2.6.30 and earlier). It is still somewhat supported by kbuffer,
|
||||
* but should not be counted on in the future.
|
||||
*/
|
||||
void kbuffer_set_old_format(struct kbuffer *kbuf)
|
||||
{
|
||||
kbuf->flags |= KBUFFER_FL_OLD_FORMAT;
|
||||
|
||||
kbuf->next_event = __old_next_event;
|
||||
}
|
67
tools/lib/traceevent/kbuffer.h
Normal file
67
tools/lib/traceevent/kbuffer.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (C) 2012 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#ifndef _KBUFFER_H
|
||||
#define _KBUFFER_H
|
||||
|
||||
#ifndef TS_SHIFT
|
||||
#define TS_SHIFT 27
|
||||
#endif
|
||||
|
||||
enum kbuffer_endian {
|
||||
KBUFFER_ENDIAN_BIG,
|
||||
KBUFFER_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
enum kbuffer_long_size {
|
||||
KBUFFER_LSIZE_4,
|
||||
KBUFFER_LSIZE_8,
|
||||
};
|
||||
|
||||
enum {
|
||||
KBUFFER_TYPE_PADDING = 29,
|
||||
KBUFFER_TYPE_TIME_EXTEND = 30,
|
||||
KBUFFER_TYPE_TIME_STAMP = 31,
|
||||
};
|
||||
|
||||
struct kbuffer;
|
||||
|
||||
struct kbuffer *kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian);
|
||||
void kbuffer_free(struct kbuffer *kbuf);
|
||||
int kbuffer_load_subbuffer(struct kbuffer *kbuf, void *subbuffer);
|
||||
void *kbuffer_read_event(struct kbuffer *kbuf, unsigned long long *ts);
|
||||
void *kbuffer_next_event(struct kbuffer *kbuf, unsigned long long *ts);
|
||||
unsigned long long kbuffer_timestamp(struct kbuffer *kbuf);
|
||||
|
||||
void *kbuffer_translate_data(int swap, void *data, unsigned int *size);
|
||||
|
||||
void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset, unsigned long long *ts);
|
||||
|
||||
int kbuffer_curr_index(struct kbuffer *kbuf);
|
||||
|
||||
int kbuffer_curr_offset(struct kbuffer *kbuf);
|
||||
int kbuffer_curr_size(struct kbuffer *kbuf);
|
||||
int kbuffer_event_size(struct kbuffer *kbuf);
|
||||
int kbuffer_missed_events(struct kbuffer *kbuf);
|
||||
int kbuffer_subbuffer_size(struct kbuffer *kbuf);
|
||||
|
||||
void kbuffer_set_old_format(struct kbuffer *kbuf);
|
||||
|
||||
#endif /* _K_BUFFER_H */
|
2432
tools/lib/traceevent/parse-filter.c
Normal file
2432
tools/lib/traceevent/parse-filter.c
Normal file
File diff suppressed because it is too large
Load diff
85
tools/lib/traceevent/parse-utils.c
Normal file
85
tools/lib/traceevent/parse-utils.c
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* 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 <stdarg.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define __weak __attribute__((weak))
|
||||
|
||||
void __vwarning(const char *fmt, va_list ap)
|
||||
{
|
||||
if (errno)
|
||||
perror("trace-cmd");
|
||||
errno = 0;
|
||||
|
||||
fprintf(stderr, " ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
void __warning(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
__vwarning(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void __weak warning(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
__vwarning(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void __vpr_stat(const char *fmt, va_list ap)
|
||||
{
|
||||
vprintf(fmt, ap);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void __pr_stat(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
__vpr_stat(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void __weak vpr_stat(const char *fmt, va_list ap)
|
||||
{
|
||||
__vpr_stat(fmt, ap);
|
||||
}
|
||||
|
||||
void __weak pr_stat(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
__vpr_stat(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
29
tools/lib/traceevent/plugin_cfg80211.c
Normal file
29
tools/lib/traceevent/plugin_cfg80211.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
#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");
|
||||
}
|
194
tools/lib/traceevent/plugin_function.c
Normal file
194
tools/lib/traceevent/plugin_function.c
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
struct pevent_plugin_option plugin_options[] =
|
||||
{
|
||||
{
|
||||
.name = "parent",
|
||||
.plugin_alias = "ftrace",
|
||||
.description =
|
||||
"Print parent of functions for function events",
|
||||
},
|
||||
{
|
||||
.name = "indent",
|
||||
.plugin_alias = "ftrace",
|
||||
.description =
|
||||
"Try to show function call indents, based on parents",
|
||||
.set = 1,
|
||||
},
|
||||
{
|
||||
.name = NULL,
|
||||
}
|
||||
};
|
||||
|
||||
static struct pevent_plugin_option *ftrace_parent = &plugin_options[0];
|
||||
static struct pevent_plugin_option *ftrace_indent = &plugin_options[1];
|
||||
|
||||
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);
|
||||
|
||||
if (parent && ftrace_indent->set)
|
||||
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);
|
||||
|
||||
if (ftrace_parent->set) {
|
||||
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);
|
||||
|
||||
traceevent_plugin_add_options("ftrace", plugin_options);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
traceevent_plugin_remove_options(plugin_options);
|
||||
|
||||
free(fstack);
|
||||
fstack = NULL;
|
||||
cpus = -1;
|
||||
}
|
88
tools/lib/traceevent/plugin_hrtimer.c
Normal file
88
tools/lib/traceevent/plugin_hrtimer.c
Normal file
|
@ -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);
|
||||
}
|
75
tools/lib/traceevent/plugin_jbd2.c
Normal file
75
tools/lib/traceevent/plugin_jbd2.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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");
|
||||
}
|
94
tools/lib/traceevent/plugin_kmem.c
Normal file
94
tools/lib/traceevent/plugin_kmem.c
Normal file
|
@ -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);
|
||||
}
|
513
tools/lib/traceevent/plugin_kvm.c
Normal file
513
tools/lib/traceevent/plugin_kvm.c
Normal file
|
@ -0,0 +1,513 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
return strings[i].str;
|
||||
}
|
||||
|
||||
static int print_exit_reason(struct trace_seq *s, struct pevent_record *record,
|
||||
struct event_format *event, const char *field)
|
||||
{
|
||||
unsigned long long isa;
|
||||
unsigned long long val;
|
||||
const char *reason;
|
||||
|
||||
if (pevent_get_field_val(s, event, field, record, &val, 1) < 0)
|
||||
return -1;
|
||||
|
||||
if (pevent_get_field_val(s, event, "isa", record, &isa, 0) < 0)
|
||||
isa = 1;
|
||||
|
||||
reason = find_exit_reason(isa, val);
|
||||
if (reason)
|
||||
trace_seq_printf(s, "reason %s", reason);
|
||||
else
|
||||
trace_seq_printf(s, "reason UNKNOWN (%llu)", val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvm_exit_handler(struct trace_seq *s, struct pevent_record *record,
|
||||
struct event_format *event, void *context)
|
||||
{
|
||||
unsigned long long info1 = 0, info2 = 0;
|
||||
|
||||
if (print_exit_reason(s, record, event, "exit_reason") < 0)
|
||||
return -1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static int kvm_nested_vmexit_inject_handler(struct trace_seq *s, struct pevent_record *record,
|
||||
struct event_format *event, void *context)
|
||||
{
|
||||
if (print_exit_reason(s, record, event, "exit_code") < 0)
|
||||
return -1;
|
||||
|
||||
pevent_print_num_field(s, " info1 %llx", event, "exit_info1", record, 1);
|
||||
pevent_print_num_field(s, " info2 %llx", event, "exit_info2", record, 1);
|
||||
pevent_print_num_field(s, " int_info %llx", event, "exit_int_info", record, 1);
|
||||
pevent_print_num_field(s, " int_info_err %llx", event, "exit_int_info_err", record, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kvm_nested_vmexit_handler(struct trace_seq *s, struct pevent_record *record,
|
||||
struct event_format *event, void *context)
|
||||
{
|
||||
pevent_print_num_field(s, "rip %llx ", event, "rip", record, 1);
|
||||
|
||||
return kvm_nested_vmexit_inject_handler(s, record, event, context);
|
||||
}
|
||||
|
||||
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, "kvm", "kvm_nested_vmexit",
|
||||
kvm_nested_vmexit_handler, NULL);
|
||||
|
||||
pevent_register_event_handler(pevent, -1, "kvm", "kvm_nested_vmexit_inject",
|
||||
kvm_nested_vmexit_inject_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, "kvm", "kvm_nested_vmexit",
|
||||
kvm_nested_vmexit_handler, NULL);
|
||||
|
||||
pevent_unregister_event_handler(pevent, -1, "kvm", "kvm_nested_vmexit_inject",
|
||||
kvm_nested_vmexit_inject_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");
|
||||
}
|
102
tools/lib/traceevent/plugin_mac80211.c
Normal file
102
tools/lib/traceevent/plugin_mac80211.c
Normal file
|
@ -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);
|
||||
}
|
160
tools/lib/traceevent/plugin_sched_switch.c
Normal file
160
tools/lib/traceevent/plugin_sched_switch.c
Normal file
|
@ -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);
|
||||
}
|
429
tools/lib/traceevent/plugin_scsi.c
Normal file
429
tools/lib/traceevent/plugin_scsi.c
Normal file
|
@ -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");
|
||||
}
|
136
tools/lib/traceevent/plugin_xen.c
Normal file
136
tools/lib/traceevent/plugin_xen.c
Normal file
|
@ -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");
|
||||
}
|
249
tools/lib/traceevent/trace-seq.c
Normal file
249
tools/lib/traceevent/trace-seq.c
Normal file
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* 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 <stdarg.h>
|
||||
|
||||
#include <asm/bug.h>
|
||||
#include "event-parse.h"
|
||||
#include "event-utils.h"
|
||||
|
||||
/*
|
||||
* The TRACE_SEQ_POISON is to catch the use of using
|
||||
* a trace_seq structure after it was destroyed.
|
||||
*/
|
||||
#define TRACE_SEQ_POISON ((void *)0xdeadbeef)
|
||||
#define TRACE_SEQ_CHECK(s) \
|
||||
do { \
|
||||
if (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
|
||||
*/
|
||||
void trace_seq_init(struct trace_seq *s)
|
||||
{
|
||||
s->len = 0;
|
||||
s->readpos = 0;
|
||||
s->buffer_size = TRACE_SEQ_BUF_SIZE;
|
||||
s->buffer = malloc(s->buffer_size);
|
||||
if (s->buffer != NULL)
|
||||
s->state = TRACE_SEQ__GOOD;
|
||||
else
|
||||
s->state = TRACE_SEQ__MEM_ALLOC_FAILED;
|
||||
}
|
||||
|
||||
/**
|
||||
* trace_seq_reset - re-initialize the trace_seq structure
|
||||
* @s: a pointer to the trace_seq structure to reset
|
||||
*/
|
||||
void trace_seq_reset(struct trace_seq *s)
|
||||
{
|
||||
if (!s)
|
||||
return;
|
||||
TRACE_SEQ_CHECK(s);
|
||||
s->len = 0;
|
||||
s->readpos = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* trace_seq_destroy - free up memory of a trace_seq
|
||||
* @s: a pointer to the trace_seq to free the buffer
|
||||
*
|
||||
* Only frees the buffer, not the trace_seq struct itself.
|
||||
*/
|
||||
void trace_seq_destroy(struct trace_seq *s)
|
||||
{
|
||||
if (!s)
|
||||
return;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* trace_seq_printf - sequence printing of trace information
|
||||
* @s: trace sequence descriptor
|
||||
* @fmt: printf format string
|
||||
*
|
||||
* It returns 0 if the trace oversizes the buffer's free
|
||||
* space, 1 otherwise.
|
||||
*
|
||||
* The tracer may use either sequence operations or its own
|
||||
* copy to user routines. To simplify formating of a trace
|
||||
* trace_seq_printf is used to store strings into a special
|
||||
* buffer (@s). Then the output may be either used by
|
||||
* the sequencer or pulled into another buffer.
|
||||
*/
|
||||
int
|
||||
trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int len;
|
||||
int ret;
|
||||
|
||||
try_again:
|
||||
TRACE_SEQ_CHECK_RET0(s);
|
||||
|
||||
len = (s->buffer_size - 1) - s->len;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = vsnprintf(s->buffer + s->len, len, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (ret >= len) {
|
||||
expand_buffer(s);
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
s->len += ret;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* trace_seq_vprintf - sequence printing of trace information
|
||||
* @s: trace sequence descriptor
|
||||
* @fmt: printf format string
|
||||
*
|
||||
* The tracer may use either sequence operations or its own
|
||||
* copy to user routines. To simplify formating of a trace
|
||||
* trace_seq_printf is used to store strings into a special
|
||||
* buffer (@s). Then the output may be either used by
|
||||
* the sequencer or pulled into another buffer.
|
||||
*/
|
||||
int
|
||||
trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
|
||||
{
|
||||
int len;
|
||||
int ret;
|
||||
|
||||
try_again:
|
||||
TRACE_SEQ_CHECK_RET0(s);
|
||||
|
||||
len = (s->buffer_size - 1) - s->len;
|
||||
|
||||
ret = vsnprintf(s->buffer + s->len, len, fmt, args);
|
||||
|
||||
if (ret >= len) {
|
||||
expand_buffer(s);
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
s->len += ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* trace_seq_puts - trace sequence printing of simple string
|
||||
* @s: trace sequence descriptor
|
||||
* @str: simple string to record
|
||||
*
|
||||
* The tracer may use either the sequence operations or its own
|
||||
* copy to user routines. This function records a simple string
|
||||
* into a special buffer (@s) for later retrieval by a sequencer
|
||||
* or other mechanism.
|
||||
*/
|
||||
int trace_seq_puts(struct trace_seq *s, const char *str)
|
||||
{
|
||||
int len;
|
||||
|
||||
TRACE_SEQ_CHECK_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;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int trace_seq_putc(struct trace_seq *s, unsigned char c)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
void trace_seq_terminate(struct trace_seq *s)
|
||||
{
|
||||
TRACE_SEQ_CHECK_RET(s);
|
||||
|
||||
/* There's always one character left on the buffer */
|
||||
s->buffer[s->len] = 0;
|
||||
}
|
||||
|
||||
int trace_seq_do_printf(struct trace_seq *s)
|
||||
{
|
||||
TRACE_SEQ_CHECK(s);
|
||||
|
||||
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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue