diff options
Diffstat (limited to 'tools/perf')
79 files changed, 4040 insertions, 4943 deletions
diff --git a/tools/perf/Documentation/perf-evlist.txt b/tools/perf/Documentation/perf-evlist.txt index 0507ec7bad7..15217345c2f 100644 --- a/tools/perf/Documentation/perf-evlist.txt +++ b/tools/perf/Documentation/perf-evlist.txt @@ -20,6 +20,14 @@ OPTIONS --input=:: Input file name. (default: perf.data unless stdin is a fifo) +-F:: +--freq=:: + Show just the sample frequency used for each event. + +-v:: +--verbose=:: + Show all fields. + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-list[1], diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt index 2780d9ce48b..b715cb71592 100644 --- a/tools/perf/Documentation/perf-probe.txt +++ b/tools/perf/Documentation/perf-probe.txt @@ -77,7 +77,8 @@ OPTIONS -F:: --funcs:: - Show available functions in given module or kernel. + Show available functions in given module or kernel. With -x/--exec, + can also list functions in a user space executable / shared library. --filter=FILTER:: (Only for --vars and --funcs) Set filter. FILTER is a combination of glob @@ -98,6 +99,15 @@ OPTIONS --max-probes:: Set the maximum number of probe points for an event. Default is 128. +-x:: +--exec=PATH:: + Specify path to the executable or shared library file for user + space tracing. Can also be used with --funcs option. + +In absence of -m/-x options, perf probe checks if the first argument after +the options is an absolute path name. If its an absolute path, perf probe +uses it as a target module/target user space binary to probe. + PROBE SYNTAX ------------ Probe points are defined by following syntax. @@ -182,6 +192,13 @@ Delete all probes on schedule(). ./perf probe --del='schedule*' +Add probes at zfree() function on /bin/zsh + + ./perf probe -x /bin/zsh zfree or ./perf probe /bin/zsh zfree + +Add probes at malloc() function on libc + + ./perf probe -x /lib/libc.so.6 malloc or ./perf probe /lib/libc.so.6 malloc SEE ALSO -------- diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index a1386b2fff0..b38a1f9ad46 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -168,7 +168,7 @@ following filters are defined: - any: any type of branches - any_call: any function call or system call - any_ret: any function return or system call return - - any_ind: any indirect branch + - ind_call: any indirect branch - u: only when the branch target is at the user level - k: only when the branch target is in the kernel - hv: only when the target is at the hypervisor level diff --git a/tools/perf/Documentation/perfconfig.example b/tools/perf/Documentation/perfconfig.example index d1448668f4d..42c6fd2ae85 100644 --- a/tools/perf/Documentation/perfconfig.example +++ b/tools/perf/Documentation/perfconfig.example @@ -6,6 +6,7 @@ normal = black, lightgray selected = lightgray, magenta code = blue, lightgray + addr = magenta, lightgray [tui] diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 9bf3fc75934..1d3d513beb9 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -1,18 +1,10 @@ -ifeq ("$(origin O)", "command line") - OUTPUT := $(O)/ -endif +include ../scripts/Makefile.include # The default target of this Makefile is... all: include config/utilities.mak -ifneq ($(OUTPUT),) -# check that the output directory actually exists -OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd) -$(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist)) -endif - # Define V to have a more verbose compile. # # Define O to save output files in a separate directory. @@ -84,31 +76,6 @@ ifneq ($(WERROR),0) CFLAGS_WERROR := -Werror endif -# -# Include saner warnings here, which can catch bugs: -# - -EXTRA_WARNINGS := -Wformat -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-security -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-y2k -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wshadow -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Winit-self -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wpacked -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wredundant-decls -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-aliasing=3 -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-default -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-enum -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wno-system-headers -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wundef -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wwrite-strings -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wbad-function-cast -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-declarations -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-prototypes -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wnested-externs -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wold-style-definition -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-prototypes -EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wdeclaration-after-statement - ifeq ("$(origin DEBUG)", "command line") PERF_DEBUG = $(DEBUG) endif @@ -116,7 +83,13 @@ ifndef PERF_DEBUG CFLAGS_OPTIMIZE = -O6 endif -CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) +ifdef PARSER_DEBUG + PARSER_DEBUG_BISON := -t + PARSER_DEBUG_FLEX := -d + PARSER_DEBUG_CFLAGS := -DPARSER_DEBUG +endif + +CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) $(PARSER_DEBUG_CFLAGS) EXTLIBS = -lpthread -lrt -lelf -lm ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE ALL_LDFLAGS = $(LDFLAGS) @@ -182,7 +155,7 @@ endif ### --- END CONFIGURATION SECTION --- -BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)/util -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE +BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)/util -I$(TRACE_EVENT_DIR) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE BASIC_LDFLAGS = # Guard against environment variables @@ -211,6 +184,17 @@ $(OUTPUT)python/perf.so: $(PYRF_OBJS) $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) +TRACE_EVENT_DIR = ../lib/traceevent/ + +ifeq ("$(origin O)", "command line") + TE_PATH=$(OUTPUT)/ +else + TE_PATH=$(TRACE_EVENT_DIR)/ +endif + +LIBTRACEEVENT = $(TE_PATH)libtraceevent.a +TE_LIB := -L$(TE_PATH) -ltraceevent + # # Single 'perf' binary right now: # @@ -238,10 +222,10 @@ FLEX = flex BISON= bison $(OUTPUT)util/parse-events-flex.c: util/parse-events.l - $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c + $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c: util/parse-events.y - $(QUIET_BISON)$(BISON) -v util/parse-events.y -d -o $(OUTPUT)util/parse-events-bison.c + $(QUIET_BISON)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $(OUTPUT)util/parse-events-bison.c $(OUTPUT)util/pmu-flex.c: util/pmu.l $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c @@ -333,6 +317,8 @@ LIB_H += util/cpumap.h LIB_H += util/top.h LIB_H += $(ARCH_INCLUDE) LIB_H += util/cgroup.h +LIB_H += $(TRACE_EVENT_DIR)event-parse.h +LIB_H += util/target.h LIB_OBJS += $(OUTPUT)util/abspath.o LIB_OBJS += $(OUTPUT)util/alias.o @@ -352,6 +338,7 @@ LIB_OBJS += $(OUTPUT)util/help.o LIB_OBJS += $(OUTPUT)util/levenshtein.o LIB_OBJS += $(OUTPUT)util/parse-options.o LIB_OBJS += $(OUTPUT)util/parse-events.o +LIB_OBJS += $(OUTPUT)util/parse-events-test.o LIB_OBJS += $(OUTPUT)util/path.o LIB_OBJS += $(OUTPUT)util/rbtree.o LIB_OBJS += $(OUTPUT)util/bitmap.o @@ -394,6 +381,7 @@ LIB_OBJS += $(OUTPUT)util/util.o LIB_OBJS += $(OUTPUT)util/xyarray.o LIB_OBJS += $(OUTPUT)util/cpumap.o LIB_OBJS += $(OUTPUT)util/cgroup.o +LIB_OBJS += $(OUTPUT)util/target.o BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o @@ -429,7 +417,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o BUILTIN_OBJS += $(OUTPUT)builtin-test.o BUILTIN_OBJS += $(OUTPUT)builtin-inject.o -PERFLIBS = $(LIB_FILE) +PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT) # Files needed for the python binding, perf.so # pyrf is just an internal name needed for all those wrappers. @@ -506,22 +494,23 @@ else # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h BASIC_CFLAGS += -I/usr/include/slang EXTLIBS += -lnewt -lslang - LIB_OBJS += $(OUTPUT)util/ui/setup.o - LIB_OBJS += $(OUTPUT)util/ui/browser.o - LIB_OBJS += $(OUTPUT)util/ui/browsers/annotate.o - LIB_OBJS += $(OUTPUT)util/ui/browsers/hists.o - LIB_OBJS += $(OUTPUT)util/ui/browsers/map.o - LIB_OBJS += $(OUTPUT)util/ui/helpline.o - LIB_OBJS += $(OUTPUT)util/ui/progress.o - LIB_OBJS += $(OUTPUT)util/ui/util.o - LIB_H += util/ui/browser.h - LIB_H += util/ui/browsers/map.h - LIB_H += util/ui/helpline.h - LIB_H += util/ui/keysyms.h - LIB_H += util/ui/libslang.h - LIB_H += util/ui/progress.h - LIB_H += util/ui/util.h - LIB_H += util/ui/ui.h + LIB_OBJS += $(OUTPUT)ui/setup.o + LIB_OBJS += $(OUTPUT)ui/browser.o + LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o + LIB_OBJS += $(OUTPUT)ui/browsers/hists.o + LIB_OBJS += $(OUTPUT)ui/browsers/map.o + LIB_OBJS += $(OUTPUT)ui/helpline.o + LIB_OBJS += $(OUTPUT)ui/progress.o + LIB_OBJS += $(OUTPUT)ui/util.o + LIB_OBJS += $(OUTPUT)ui/tui/setup.o + LIB_H += ui/browser.h + LIB_H += ui/browsers/map.h + LIB_H += ui/helpline.h + LIB_H += ui/keysyms.h + LIB_H += ui/libslang.h + LIB_H += ui/progress.h + LIB_H += ui/util.h + LIB_H += ui/ui.h endif endif @@ -535,7 +524,12 @@ else else BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0) EXTLIBS += $(shell pkg-config --libs gtk+-2.0) - LIB_OBJS += $(OUTPUT)util/gtk/browser.o + LIB_OBJS += $(OUTPUT)ui/gtk/browser.o + LIB_OBJS += $(OUTPUT)ui/gtk/setup.o + # Make sure that it'd be included only once. + ifneq ($(findstring -DNO_NEWT_SUPPORT,$(BASIC_CFLAGS)),) + LIB_OBJS += $(OUTPUT)ui/setup.o + endif endif endif @@ -678,18 +672,6 @@ else endif endif -ifneq ($(findstring $(MAKEFLAGS),s),s) -ifndef V - QUIET_CC = @echo ' ' CC $@; - QUIET_AR = @echo ' ' AR $@; - QUIET_LINK = @echo ' ' LINK $@; - QUIET_MKDIR = @echo ' ' MKDIR $@; - QUIET_GEN = @echo ' ' GEN $@; - QUIET_FLEX = @echo ' ' FLEX $@; - QUIET_BISON = @echo ' ' BISON $@; -endif -endif - ifdef ASCIIDOC8 export ASCIIDOC8 endif @@ -774,10 +756,10 @@ $(OUTPUT)perf.o perf.spec \ # over the general rule for .o $(OUTPUT)util/%-flex.o: $(OUTPUT)util/%-flex.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Iutil/ -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $< + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Iutil/ -w $< $(OUTPUT)util/%-bison.o: $(OUTPUT)util/%-bison.c $(OUTPUT)PERF-CFLAGS - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -Iutil/ -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $< + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -Iutil/ -w $< $(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< @@ -800,16 +782,16 @@ $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< -$(OUTPUT)util/ui/browser.o: util/ui/browser.c $(OUTPUT)PERF-CFLAGS +$(OUTPUT)ui/browser.o: ui/browser.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< -$(OUTPUT)util/ui/browsers/annotate.o: util/ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS +$(OUTPUT)ui/browsers/annotate.o: ui/browsers/annotate.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< -$(OUTPUT)util/ui/browsers/hists.o: util/ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS +$(OUTPUT)ui/browsers/hists.o: ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< -$(OUTPUT)util/ui/browsers/map.o: util/ui/browsers/map.c $(OUTPUT)PERF-CFLAGS +$(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS @@ -844,6 +826,10 @@ $(sort $(dir $(DIRECTORY_DEPS))): $(LIB_FILE): $(LIB_OBJS) $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS) +# libtraceevent.a +$(LIBTRACEEVENT): + $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) $(COMMAND_O) libtraceevent.a + help: @echo 'Perf make targets:' @echo ' doc - make *all* documentation (see below)' @@ -990,6 +976,6 @@ clean: $(RM) $(OUTPUT)util/*-{bison,flex}* $(python-clean) -.PHONY: all install clean strip +.PHONY: all install clean strip $(LIBTRACEEVENT) .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell .PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 52480467e9f..6b2bcfbde15 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -84,7 +84,11 @@ static int perf_session__list_build_ids(void) if (filename__fprintf_build_id(session->filename, stdout)) goto out; - if (with_hits) + /* + * in pipe-mode, the only way to get the buildids is to parse + * the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID + */ + if (with_hits || session->fd_pipe) perf_session__process_events(session, &build_id__mark_dso_hit_ops); perf_session__fprintf_dsos_buildid(session, stdout, with_hits); diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index 26760322c4f..e52d77ec708 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c @@ -15,9 +15,40 @@ #include "util/parse-options.h" #include "util/session.h" -static const char *input_name; +struct perf_attr_details { + bool freq; + bool verbose; +}; + +static int comma_printf(bool *first, const char *fmt, ...) +{ + va_list args; + int ret = 0; + + if (!*first) { + ret += printf(","); + } else { + ret += printf(":"); + *first = false; + } + + va_start(args, fmt); + ret += vprintf(fmt, args); + va_end(args); + return ret; +} + +static int __if_print(bool *first, const char *field, u64 value) +{ + if (value == 0) + return 0; + + return comma_printf(first, " %s: %" PRIu64, field, value); +} + +#define if_print(field) __if_print(&first, #field, pos->attr.field) -static int __cmd_evlist(void) +static int __cmd_evlist(const char *input_name, struct perf_attr_details *details) { struct perf_session *session; struct perf_evsel *pos; @@ -26,8 +57,52 @@ static int __cmd_evlist(void) if (session == NULL) return -ENOMEM; - list_for_each_entry(pos, &session->evlist->entries, node) - printf("%s\n", event_name(pos)); + list_for_each_entry(pos, &session->evlist->entries, node) { + bool first = true; + + printf("%s", event_name(pos)); + + if (details->verbose || details->freq) { + comma_printf(&first, " sample_freq=%" PRIu64, + (u64)pos->attr.sample_freq); + } + + if (details->verbose) { + if_print(type); + if_print(config); + if_print(config1); + if_print(config2); + if_print(size); + if_print(sample_type); + if_print(read_format); + if_print(disabled); + if_print(inherit); + if_print(pinned); + if_print(exclusive); + if_print(exclude_user); + if_print(exclude_kernel); + if_print(exclude_hv); + if_print(exclude_idle); + if_print(mmap); + if_print(comm); + if_print(freq); + if_print(inherit_stat); + if_print(enable_on_exec); + if_print(task); + if_print(watermark); + if_print(precise_ip); + if_print(mmap_data); + if_print(sample_id_all); + if_print(exclude_host); + if_print(exclude_guest); + if_print(__reserved_1); + if_print(wakeup_events); + if_print(bp_type); + if_print(branch_sample_type); + } + + putchar('\n'); + } perf_session__delete(session); return 0; @@ -38,17 +113,23 @@ static const char * const evlist_usage[] = { NULL }; -static const struct option options[] = { - OPT_STRING('i', "input", &input_name, "file", - "input file name"), - OPT_END() -}; - int cmd_evlist(int argc, const char **argv, const char *prefix __used) { + struct perf_attr_details details = { .verbose = false, }; + const char *input_name; + const struct option options[] = { + OPT_STRING('i', "input", &input_name, "file", + "Input file name"), + OPT_BOOLEAN('F', "freq", &details.freq, + "Show the sample frequency"), + OPT_BOOLEAN('v', "verbose", &details.verbose, + "Show all event attr details"), + OPT_END() + }; + argc = parse_options(argc, argv, options, evlist_usage, 0); if (argc) usage_with_options(evlist_usage, options); - return __cmd_evlist(); + return __cmd_evlist(input_name, &details); } diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 09c106193e6..3beab489afc 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -60,6 +60,11 @@ static int perf_event__repipe_tracing_data_synth(union perf_event *event, static int perf_event__repipe_attr(union perf_event *event, struct perf_evlist **pevlist __used) { + int ret; + ret = perf_event__process_attr(event, pevlist); + if (ret) + return ret; + return perf_event__repipe_synth(NULL, event, NULL); } diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 39104c0beea..547af48deb4 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -192,7 +192,7 @@ static void insert_caller_stat(unsigned long call_site, } static void process_alloc_event(void *data, - struct event *event, + struct event_format *event, int cpu, u64 timestamp __used, struct thread *thread __used, @@ -253,7 +253,7 @@ static struct alloc_stat *search_alloc_stat(unsigned long ptr, } static void process_free_event(void *data, - struct event *event, + struct event_format *event, int cpu, u64 timestamp __used, struct thread *thread __used) @@ -281,7 +281,7 @@ static void process_free_event(void *data, static void process_raw_event(union perf_event *raw_event __used, void *data, int cpu, u64 timestamp, struct thread *thread) { - struct event *event; + struct event_format *event; int type; type = trace_parse_common_type(data); diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 12c81483899..fd53319de20 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -356,25 +356,25 @@ struct trace_release_event { struct trace_lock_handler { void (*acquire_event)(struct trace_acquire_event *, - struct event *, + struct event_format *, int cpu, u64 timestamp, struct thread *thread); void (*acquired_event)(struct trace_acquired_event *, - struct event *, + struct event_format *, int cpu, u64 timestamp, struct thread *thread); void (*contended_event)(struct trace_contended_event *, - struct event *, + struct event_format *, int cpu, u64 timestamp, struct thread *thread); void (*release_event)(struct trace_release_event *, - struct event *, + struct event_format *, int cpu, u64 timestamp, struct thread *thread); @@ -416,7 +416,7 @@ enum acquire_flags { static void report_lock_acquire_event(struct trace_acquire_event *acquire_event, - struct event *__event __used, + struct event_format *__event __used, int cpu __used, u64 timestamp __used, struct thread *thread __used) @@ -480,7 +480,7 @@ end: static void report_lock_acquired_event(struct trace_acquired_event *acquired_event, - struct event *__event __used, + struct event_format *__event __used, int cpu __used, u64 timestamp __used, struct thread *thread __used) @@ -536,7 +536,7 @@ end: static void report_lock_contended_event(struct trace_contended_event *contended_event, - struct event *__event __used, + struct event_format *__event __used, int cpu __used, u64 timestamp __used, struct thread *thread __used) @@ -583,7 +583,7 @@ end: static void report_lock_release_event(struct trace_release_event *release_event, - struct event *__event __used, + struct event_format *__event __used, int cpu __used, u64 timestamp __used, struct thread *thread __used) @@ -647,7 +647,7 @@ static struct trace_lock_handler *trace_handler; static void process_lock_acquire_event(void *data, - struct event *event __used, + struct event_format *event __used, int cpu __used, u64 timestamp __used, struct thread *thread __used) @@ -666,7 +666,7 @@ process_lock_acquire_event(void *data, static void process_lock_acquired_event(void *data, - struct event *event __used, + struct event_format *event __used, int cpu __used, u64 timestamp __used, struct thread *thread __used) @@ -684,7 +684,7 @@ process_lock_acquired_event(void *data, static void process_lock_contended_event(void *data, - struct event *event __used, + struct event_format *event __used, int cpu __used, u64 timestamp __used, struct thread *thread __used) @@ -702,7 +702,7 @@ process_lock_contended_event(void *data, static void process_lock_release_event(void *data, - struct event *event __used, + struct event_format *event __used, int cpu __used, u64 timestamp __used, struct thread *thread __used) @@ -721,7 +721,7 @@ process_lock_release_event(void *data, static void process_raw_event(void *data, int cpu, u64 timestamp, struct thread *thread) { - struct event *event; + struct event_format *event; int type; type = trace_parse_common_type(data); diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 4935c09dd5b..e215ae61b2a 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -54,6 +54,7 @@ static struct { bool show_ext_vars; bool show_funcs; bool mod_events; + bool uprobes; int nevents; struct perf_probe_event events[MAX_PROBES]; struct strlist *dellist; @@ -75,6 +76,8 @@ static int parse_probe_event(const char *str) return -1; } + pev->uprobes = params.uprobes; + /* Parse a perf-probe command into event */ ret = parse_perf_probe_command(str, pev); pr_debug("%d arguments\n", pev->nargs); @@ -82,21 +85,58 @@ static int parse_probe_event(const char *str) return ret; } +static int set_target(const char *ptr) +{ + int found = 0; + const char *buf; + + /* + * The first argument after options can be an absolute path + * to an executable / library or kernel module. + * + * TODO: Support relative path, and $PATH, $LD_LIBRARY_PATH, + * short module name. + */ + if (!params.target && ptr && *ptr == '/') { + params.target = ptr; + found = 1; + buf = ptr + (strlen(ptr) - 3); + + if (strcmp(buf, ".ko")) + params.uprobes = true; + + } + + return found; +} + static int parse_probe_event_argv(int argc, const char **argv) { - int i, len, ret; + int i, len, ret, found_target; char *buf; + found_target = set_target(argv[0]); + if (found_target && argc == 1) + return 0; + /* Bind up rest arguments */ len = 0; - for (i = 0; i < argc; i++) + for (i = 0; i < argc; i++) { + if (i == 0 && found_target) + continue; + len += strlen(argv[i]) + 1; + } buf = zalloc(len + 1); if (buf == NULL) return -ENOMEM; len = 0; - for (i = 0; i < argc; i++) + for (i = 0; i < argc; i++) { + if (i == 0 && found_target) + continue; + len += sprintf(&buf[len], "%s ", argv[i]); + } params.mod_events = true; ret = parse_probe_event(buf); free(buf); @@ -125,6 +165,28 @@ static int opt_del_probe_event(const struct option *opt __used, return 0; } +static int opt_set_target(const struct option *opt, const char *str, + int unset __used) +{ + int ret = -ENOENT; + + if (str && !params.target) { + if (!strcmp(opt->long_name, "exec")) + params.uprobes = true; +#ifdef DWARF_SUPPORT + else if (!strcmp(opt->long_name, "module")) + params.uprobes = false; +#endif + else + return ret; + + params.target = str; + ret = 0; + } + + return ret; +} + #ifdef DWARF_SUPPORT static int opt_show_lines(const struct option *opt __used, const char *str, int unset __used) @@ -246,9 +308,9 @@ static const struct option options[] = { "file", "vmlinux pathname"), OPT_STRING('s', "source", &symbol_conf.source_prefix, "directory", "path to kernel source"), - OPT_STRING('m', "module", ¶ms.target, - "modname|path", - "target module name (for online) or path (for offline)"), + OPT_CALLBACK('m', "module", NULL, "modname|path", + "target module name (for online) or path (for offline)", + opt_set_target), #endif OPT__DRY_RUN(&probe_event_dry_run), OPT_INTEGER('\0', "max-probes", ¶ms.max_probe_points, @@ -260,6 +322,8 @@ static const struct option options[] = { "\t\t\t(default: \"" DEFAULT_VAR_FILTER "\" for --vars,\n" "\t\t\t \"" DEFAULT_FUNC_FILTER "\" for --funcs)", opt_set_filter), + OPT_CALLBACK('x', "exec", NULL, "executable|path", + "target executable name or path", opt_set_target), OPT_END() }; @@ -310,6 +374,10 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) pr_err(" Error: Don't use --list with --funcs.\n"); usage_with_options(probe_usage, options); } + if (params.uprobes) { + pr_warning(" Error: Don't use --list with --exec.\n"); + usage_with_options(probe_usage, options); + } ret = show_perf_probe_events(); if (ret < 0) pr_err(" Error: Failed to show event list. (%d)\n", @@ -333,8 +401,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) if (!params.filter) params.filter = strfilter__new(DEFAULT_FUNC_FILTER, NULL); - ret = show_available_funcs(params.target, - params.filter); + ret = show_available_funcs(params.target, params.filter, + params.uprobes); strfilter__delete(params.filter); if (ret < 0) pr_err(" Error: Failed to show functions." @@ -343,7 +411,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) } #ifdef DWARF_SUPPORT - if (params.show_lines) { + if (params.show_lines && !params.uprobes) { if (params.mod_events) { pr_err(" Error: Don't use --line with" " --add/--del.\n"); diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index be4e1eee782..e5cb08427e1 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -44,7 +44,6 @@ struct perf_record { struct perf_evlist *evlist; struct perf_session *session; const char *progname; - const char *uid_str; int output; unsigned int page_size; int realtime_prio; @@ -218,7 +217,7 @@ try_again: if (err == EPERM || err == EACCES) { ui__error_paranoid(); exit(EXIT_FAILURE); - } else if (err == ENODEV && opts->cpu_list) { + } else if (err == ENODEV && opts->target.cpu_list) { die("No such device - did you specify" " an out-of-range profile CPU?\n"); } else if (err == EINVAL) { @@ -243,9 +242,13 @@ try_again: /* * If it's cycles then fall back to hrtimer * based cpu-clock-tick sw counter, which - * is always available even if no PMU support: + * is always available even if no PMU support. + * + * PPC returns ENXIO until 2.6.37 (behavior changed + * with commit b0a873e). */ - if (attr->type == PERF_TYPE_HARDWARE + if ((err == ENOENT || err == ENXIO) + && attr->type == PERF_TYPE_HARDWARE && attr->config == PERF_COUNT_HW_CPU_CYCLES) { if (verbose) @@ -253,6 +256,10 @@ try_again: "trying to fall back to cpu-clock-ticks\n"); attr->type = PERF_TYPE_SOFTWARE; attr->config = PERF_COUNT_SW_CPU_CLOCK; + if (pos->name) { + free(pos->name); + pos->name = NULL; + } goto try_again; } @@ -389,7 +396,7 @@ static void perf_record__mmap_read_all(struct perf_record *rec) perf_record__mmap_read(rec, &rec->evlist->mmap[i]); } - if (perf_header__has_feat(&rec->session->header, HEADER_TRACE_INFO)) + if (perf_header__has_feat(&rec->session->header, HEADER_TRACING_DATA)) write_output(rec, &finished_round_event, sizeof(finished_round_event)); } @@ -471,7 +478,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) perf_header__clear_feat(&session->header, HEADER_BUILD_ID); if (!have_tracepoints(&evsel_list->entries)) - perf_header__clear_feat(&session->header, HEADER_TRACE_INFO); + perf_header__clear_feat(&session->header, HEADER_TRACING_DATA); if (!rec->opts.branch_stack) perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); @@ -578,7 +585,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) perf_session__process_machines(session, tool, perf_event__synthesize_guest_os); - if (!opts->system_wide) + if (!opts->target.system_wide) perf_event__synthesize_thread_map(tool, evsel_list->threads, process_synthesized_event, machine); @@ -746,7 +753,10 @@ static struct perf_record record = { .mmap_pages = UINT_MAX, .user_freq = UINT_MAX, .user_interval = ULLONG_MAX, - .freq = 1000, + .freq = 4000, + .target = { + .uses_mmap = true, + }, }, .write_mode = WRITE_FORCE, .file_new = true, @@ -765,9 +775,9 @@ const struct option record_options[] = { parse_events_option), OPT_CALLBACK(0, "filter", &record.evlist, "filter", "event filter", parse_filter), - OPT_STRING('p', "pid", &record.opts.target_pid, "pid", + OPT_STRING('p', "pid", &record.opts.target.pid, "pid", "record events on existing process id"), - OPT_STRING('t', "tid", &record.opts.target_tid, "tid", + OPT_STRING('t', "tid", &record.opts.target.tid, "tid", "record events on existing thread id"), OPT_INTEGER('r', "realtime", &record.realtime_prio, "collect data with this RT SCHED_FIFO priority"), @@ -775,11 +785,11 @@ const struct option record_options[] = { "collect data without buffering"), OPT_BOOLEAN('R', "raw-samples", &record.opts.raw_samples, "collect raw sample records from all opened counters"), - OPT_BOOLEAN('a', "all-cpus", &record.opts.system_wide, + OPT_BOOLEAN('a', "all-cpus", &record.opts.target.system_wide, "system-wide collection from all CPUs"), OPT_BOOLEAN('A', "append", &record.append_file, "append to the output file to do incremental profiling"), - OPT_STRING('C', "cpu", &record.opts.cpu_list, "cpu", + OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu", "list of cpus to monitor"), OPT_BOOLEAN('f', "force", &record.force, "overwrite existing data file (deprecated)"), @@ -813,7 +823,8 @@ const struct option record_options[] = { OPT_CALLBACK('G', "cgroup", &record.evlist, "name", "monitor event in cgroup name only", parse_cgroups), - OPT_STRING('u', "uid", &record.uid_str, "user", "user to profile"), + OPT_STRING('u', "uid", &record.opts.target.uid_str, "user", + "user to profile"), OPT_CALLBACK_NOOPT('b', "branch-any", &record.opts.branch_stack, "branch any", "sample any taken branches", @@ -831,6 +842,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) struct perf_evsel *pos; struct perf_evlist *evsel_list; struct perf_record *rec = &record; + char errbuf[BUFSIZ]; perf_header__set_cmdline(argc, argv); @@ -842,8 +854,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) argc = parse_options(argc, argv, record_options, record_usage, PARSE_OPT_STOP_AT_NON_OPTION); - if (!argc && !rec->opts.target_pid && !rec->opts.target_tid && - !rec->opts.system_wide && !rec->opts.cpu_list && !rec->uid_str) + if (!argc && perf_target__none(&rec->opts.target)) usage_with_options(record_usage, record_options); if (rec->force && rec->append_file) { @@ -856,7 +867,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) rec->write_mode = WRITE_FORCE; } - if (nr_cgroups && !rec->opts.system_wide) { + if (nr_cgroups && !rec->opts.target.system_wide) { fprintf(stderr, "cgroup monitoring only available in" " system-wide mode\n"); usage_with_options(record_usage, record_options); @@ -883,17 +894,25 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) goto out_symbol_exit; } - rec->opts.uid = parse_target_uid(rec->uid_str, rec->opts.target_tid, - rec->opts.target_pid); - if (rec->uid_str != NULL && rec->opts.uid == UINT_MAX - 1) - goto out_free_fd; + err = perf_target__validate(&rec->opts.target); + if (err) { + perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); + ui__warning("%s", errbuf); + } + + err = perf_target__parse_uid(&rec->opts.target); + if (err) { + int saved_errno = errno; - if (rec->opts.target_pid) - rec->opts.target_tid = rec->opts.target_pid; + perf_target__strerror(&rec->opts.target, err, errbuf, BUFSIZ); + ui__warning("%s", errbuf); + + err = -saved_errno; + goto out_free_fd; + } - if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid, - rec->opts.target_tid, rec->opts.uid, - rec->opts.cpu_list) < 0) + err = -ENOMEM; + if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0) usage_with_options(record_usage, record_options); list_for_each_entry(pos, &evsel_list->entries, node) { diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index cdae9b2db1c..d58e41445d0 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -296,12 +296,15 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self, { size_t ret; char unit; - unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; + unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE]; + u64 nr_events = self->stats.total_period; - nr_events = convert_unit(nr_events, &unit); - ret = fprintf(fp, "# Events: %lu%c", nr_events, unit); + nr_samples = convert_unit(nr_samples, &unit); + ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit); if (evname != NULL) - ret += fprintf(fp, " %s", evname); + ret += fprintf(fp, " of event '%s'", evname); + + ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events); return ret + fprintf(fp, "\n#\n"); } @@ -680,14 +683,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) } - if (strcmp(report.input_name, "-") != 0) { - if (report.use_gtk) - perf_gtk_setup_browser(argc, argv, true); - else - setup_browser(true); - } else { + if (strcmp(report.input_name, "-") != 0) + setup_browser(true); + else use_browser = 0; - } /* * Only in the newt browser we are doing integrated annotation, diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 1cad3af4bf4..b125e07eb39 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -728,34 +728,34 @@ struct trace_migrate_task_event { struct trace_sched_handler { void (*switch_event)(struct trace_switch_event *, struct machine *, - struct event *, + struct event_format *, int cpu, u64 timestamp, struct thread *thread); void (*runtime_event)(struct trace_runtime_event *, struct machine *, - struct event *, + struct event_format *, int cpu, u64 timestamp, struct thread *thread); void (*wakeup_event)(struct trace_wakeup_event *, struct machine *, - struct event *, + struct event_format *, int cpu, u64 timestamp, struct thread *thread); void (*fork_event)(struct trace_fork_event *, - struct event *, + struct event_format *, int cpu, u64 timestamp, struct thread *thread); void (*migrate_task_event)(struct trace_migrate_task_event *, struct machine *machine, - struct event *, + struct event_format *, int cpu, u64 timestamp, struct thread *thread); @@ -765,7 +765,7 @@ struct trace_sched_handler { static void replay_wakeup_event(struct trace_wakeup_event *wakeup_event, struct machine *machine __used, - struct event *event, + struct event_format *event, int cpu __used, u64 timestamp __used, struct thread *thread __used) @@ -792,7 +792,7 @@ static u64 cpu_last_switched[MAX_CPUS]; static void replay_switch_event(struct trace_switch_event *switch_event, struct machine *machine __used, - struct event *event, + struct event_format *event, int cpu, u64 timestamp, struct thread *thread __used) @@ -835,7 +835,7 @@ replay_switch_event(struct trace_switch_event *switch_event, static void replay_fork_event(struct trace_fork_event *fork_event, - struct event *event, + struct event_format *event, int cpu __used, u64 timestamp __used, struct thread *thread __used) @@ -944,7 +944,7 @@ static void thread_atoms_insert(struct thread *thread) static void latency_fork_event(struct trace_fork_event *fork_event __used, - struct event *event __used, + struct event_format *event __used, int cpu __used, u64 timestamp __used, struct thread *thread __used) @@ -1026,7 +1026,7 @@ add_sched_in_event(struct work_atoms *atoms, u64 timestamp) static void latency_switch_event(struct trace_switch_event *switch_event, struct machine *machine, - struct event *event __used, + struct event_format *event __used, int cpu, u64 timestamp, struct thread *thread __used) @@ -1079,7 +1079,7 @@ latency_switch_event(struct trace_switch_event *switch_event, static void latency_runtime_event(struct trace_runtime_event *runtime_event, struct machine *machine, - struct event *event __used, + struct event_format *event __used, int cpu, u64 timestamp, struct thread *this_thread __used) @@ -1102,7 +1102,7 @@ latency_runtime_event(struct trace_runtime_event *runtime_event, static void latency_wakeup_event(struct trace_wakeup_event *wakeup_event, struct machine *machine, - struct event *__event __used, + struct event_format *__event __used, int cpu __used, u64 timestamp, struct thread *thread __used) @@ -1150,7 +1150,7 @@ latency_wakeup_event(struct trace_wakeup_event *wakeup_event, static void latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event, struct machine *machine, - struct event *__event __used, + struct event_format *__event __used, int cpu __used, u64 timestamp, struct thread *thread __used) @@ -1361,7 +1361,7 @@ static struct trace_sched_handler *trace_handler; static void process_sched_wakeup_event(struct perf_tool *tool __used, - struct event *event, + struct event_format *event, struct perf_sample *sample, struct machine *machine, struct thread *thread) @@ -1398,7 +1398,7 @@ static char next_shortname2 = '0'; static void map_switch_event(struct trace_switch_event *switch_event, struct machine *machine, - struct event *event __used, + struct event_format *event __used, int this_cpu, u64 timestamp, struct thread *thread __used) @@ -1476,7 +1476,7 @@ map_switch_event(struct trace_switch_event *switch_event, static void process_sched_switch_event(struct perf_tool *tool __used, - struct event *event, + struct event_format *event, struct perf_sample *sample, struct machine *machine, struct thread *thread) @@ -1512,7 +1512,7 @@ process_sched_switch_event(struct perf_tool *tool __used, static void process_sched_runtime_event(struct perf_tool *tool __used, - struct event *event, + struct event_format *event, struct perf_sample *sample, struct machine *machine, struct thread *thread) @@ -1532,7 +1532,7 @@ process_sched_runtime_event(struct perf_tool *tool __used, static void process_sched_fork_event(struct perf_tool *tool __used, - struct event *event, + struct event_format *event, struct perf_sample *sample, struct machine *machine __used, struct thread *thread) @@ -1554,7 +1554,7 @@ process_sched_fork_event(struct perf_tool *tool __used, static void process_sched_exit_event(struct perf_tool *tool __used, - struct event *event, + struct event_format *event, struct perf_sample *sample __used, struct machine *machine __used, struct thread *thread __used) @@ -1565,7 +1565,7 @@ process_sched_exit_event(struct perf_tool *tool __used, static void process_sched_migrate_task_event(struct perf_tool *tool __used, - struct event *event, + struct event_format *event, struct perf_sample *sample, struct machine *machine, struct thread *thread) @@ -1586,7 +1586,7 @@ process_sched_migrate_task_event(struct perf_tool *tool __used, sample->time, thread); } -typedef void (*tracepoint_handler)(struct perf_tool *tool, struct event *event, +typedef void (*tracepoint_handler)(struct perf_tool *tool, struct event_format *event, struct perf_sample *sample, struct machine *machine, struct thread *thread); diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index d4ce733b9eb..8e395a538eb 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -261,7 +261,7 @@ static void print_sample_start(struct perf_sample *sample, struct perf_event_attr *attr) { int type; - struct event *event; + struct event_format *event; const char *evname = NULL; unsigned long secs; unsigned long usecs; diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index c941bb640f4..62ae30d34fa 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -173,24 +173,23 @@ static struct perf_event_attr very_very_detailed_attrs[] = { -struct perf_evlist *evsel_list; +static struct perf_evlist *evsel_list; -static bool system_wide = false; -static int run_idx = 0; +static struct perf_target target = { + .uid = UINT_MAX, +}; +static int run_idx = 0; static int run_count = 1; static bool no_inherit = false; static bool scale = true; static bool no_aggr = false; -static const char *target_pid; -static const char *target_tid; static pid_t child_pid = -1; static bool null_run = false; static int detailed_run = 0; static bool sync_run = false; static bool big_num = true; static int big_num_opt = -1; -static const char *cpu_list; static const char *csv_sep = NULL; static bool csv_output = false; static bool group = false; @@ -265,24 +264,26 @@ static double stddev_stats(struct stats *stats) return sqrt(variance_mean); } -struct stats runtime_nsecs_stats[MAX_NR_CPUS]; -struct stats runtime_cycles_stats[MAX_NR_CPUS]; -struct stats runtime_stalled_cycles_front_stats[MAX_NR_CPUS]; -struct stats runtime_stalled_cycles_back_stats[MAX_NR_CPUS]; -struct stats runtime_branches_stats[MAX_NR_CPUS]; -struct stats runtime_cacherefs_stats[MAX_NR_CPUS]; -struct stats runtime_l1_dcache_stats[MAX_NR_CPUS]; -struct stats runtime_l1_icache_stats[MAX_NR_CPUS]; -struct stats runtime_ll_cache_stats[MAX_NR_CPUS]; -struct stats runtime_itlb_cache_stats[MAX_NR_CPUS]; -struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; -struct stats walltime_nsecs_stats; +static struct stats runtime_nsecs_stats[MAX_NR_CPUS]; +static struct stats runtime_cycles_stats[MAX_NR_CPUS]; +static struct stats runtime_stalled_cycles_front_stats[MAX_NR_CPUS]; +static struct stats runtime_stalled_cycles_back_stats[MAX_NR_CPUS]; +static struct stats runtime_branches_stats[MAX_NR_CPUS]; +static struct stats runtime_cacherefs_stats[MAX_NR_CPUS]; +static struct stats runtime_l1_dcache_stats[MAX_NR_CPUS]; +static struct stats runtime_l1_icache_stats[MAX_NR_CPUS]; +static struct stats runtime_ll_cache_stats[MAX_NR_CPUS]; +static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS]; +static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; +static struct stats walltime_nsecs_stats; static int create_perf_stat_counter(struct perf_evsel *evsel, struct perf_evsel *first) { struct perf_event_attr *attr = &evsel->attr; struct xyarray *group_fd = NULL; + bool exclude_guest_missing = false; + int ret; if (group && evsel != first) group_fd = first->fd; @@ -293,16 +294,39 @@ static int create_perf_stat_counter(struct perf_evsel *evsel, attr->inherit = !no_inherit; - if (system_wide) - return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, - group, group_fd); - if (!target_pid && !target_tid && (!group || evsel == first)) { +retry: + if (exclude_guest_missing) + evsel->attr.exclude_guest = evsel->attr.exclude_host = 0; + + if (perf_target__has_cpu(&target)) { + ret = perf_evsel__open_per_cpu(evsel, evsel_list->cpus, + group, group_fd); + if (ret) + goto check_ret; + return 0; + } + + if (!perf_target__has_task(&target) && (!group || evsel == first)) { attr->disabled = 1; attr->enable_on_exec = 1; } - return perf_evsel__open_per_thread(evsel, evsel_list->threads, - group, group_fd); + ret = perf_evsel__open_per_thread(evsel, evsel_list->threads, + group, group_fd); + if (!ret) + return 0; + /* fall through */ +check_ret: + if (ret && errno == EINVAL) { + if (!exclude_guest_missing && + (evsel->attr.exclude_guest || evsel->attr.exclude_host)) { + pr_debug("Old kernel, cannot exclude " + "guest or host samples.\n"); + exclude_guest_missing = true; + goto retry; + } + } + return ret; } /* @@ -446,7 +470,7 @@ static int run_perf_stat(int argc __used, const char **argv) exit(-1); } - if (!target_tid && !target_pid && !system_wide) + if (perf_target__none(&target)) evsel_list->threads->map[0] = child_pid; /* @@ -463,8 +487,13 @@ static int run_perf_stat(int argc __used, const char **argv) list_for_each_entry(counter, &evsel_list->entries, node) { if (create_perf_stat_counter(counter, first) < 0) { + /* + * PPC returns ENXIO for HW counters until 2.6.37 + * (behavior changed with commit b0a873e). + */ if (errno == EINVAL || errno == ENOSYS || - errno == ENOENT || errno == EOPNOTSUPP) { + errno == ENOENT || errno == EOPNOTSUPP || + errno == ENXIO) { if (verbose) ui__warning("%s event is not supported by the kernel.\n", event_name(counter)); @@ -476,7 +505,7 @@ static int run_perf_stat(int argc __used, const char **argv) error("You may not have permission to collect %sstats.\n" "\t Consider tweaking" " /proc/sys/kernel/perf_event_paranoid or running as root.", - system_wide ? "system-wide " : ""); + target.system_wide ? "system-wide " : ""); } else { error("open_counter returned with %d (%s). " "/bin/dmesg may provide additional information.\n", @@ -968,14 +997,14 @@ static void print_stat(int argc, const char **argv) if (!csv_output) { fprintf(output, "\n"); fprintf(output, " Performance counter stats for "); - if (!target_pid && !target_tid) { + if (!perf_target__has_task(&target)) { fprintf(output, "\'%s", argv[0]); for (i = 1; i < argc; i++) fprintf(output, " %s", argv[i]); - } else if (target_pid) - fprintf(output, "process id \'%s", target_pid); + } else if (target.pid) + fprintf(output, "process id \'%s", target.pid); else - fprintf(output, "thread id \'%s", target_tid); + fprintf(output, "thread id \'%s", target.tid); fprintf(output, "\'"); if (run_count > 1) @@ -1049,11 +1078,11 @@ static const struct option options[] = { "event filter", parse_filter), OPT_BOOLEAN('i', "no-inherit", &no_inherit, "child tasks do not inherit counters"), - OPT_STRING('p', "pid", &target_pid, "pid", + OPT_STRING('p', "pid", &target.pid, "pid", "stat events on existing process id"), - OPT_STRING('t', "tid", &target_tid, "tid", + OPT_STRING('t', "tid", &target.tid, "tid", "stat events on existing thread id"), - OPT_BOOLEAN('a', "all-cpus", &system_wide, + OPT_BOOLEAN('a', "all-cpus", &target.system_wide, "system-wide collection from all CPUs"), OPT_BOOLEAN('g', "group", &group, "put the counters into a counter group"), @@ -1072,7 +1101,7 @@ static const struct option options[] = { OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL, "print large numbers with thousands\' separators", stat__set_big_num), - OPT_STRING('C', "cpu", &cpu_list, "cpu", + OPT_STRING('C', "cpu", &target.cpu_list, "cpu", "list of cpus to monitor in system-wide"), OPT_BOOLEAN('A', "no-aggr", &no_aggr, "disable CPU count aggregation"), @@ -1190,13 +1219,13 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) } else if (big_num_opt == 0) /* User passed --no-big-num */ big_num = false; - if (!argc && !target_pid && !target_tid) + if (!argc && !perf_target__has_task(&target)) usage_with_options(stat_usage, options); if (run_count <= 0) usage_with_options(stat_usage, options); /* no_aggr, cgroup are for system-wide only */ - if ((no_aggr || nr_cgroups) && !system_wide) { + if ((no_aggr || nr_cgroups) && !perf_target__has_cpu(&target)) { fprintf(stderr, "both cgroup and no-aggregation " "modes only available in system-wide mode\n"); @@ -1206,23 +1235,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) if (add_default_attributes()) goto out; - if (target_pid) - target_tid = target_pid; + perf_target__validate(&target); - evsel_list->threads = thread_map__new_str(target_pid, - target_tid, UINT_MAX); - if (evsel_list->threads == NULL) { - pr_err("Problems finding threads of monitor\n"); - usage_with_options(stat_usage, options); - } - - if (system_wide) - evsel_list->cpus = cpu_map__new(cpu_list); - else - evsel_list->cpus = cpu_map__dummy_new(); + if (perf_evlist__create_maps(evsel_list, &target) < 0) { + if (perf_target__has_task(&target)) + pr_err("Problems finding threads of monitor\n"); + if (perf_target__has_cpu(&target)) + perror("failed to parse CPUs map"); - if (evsel_list->cpus == NULL) { - perror("failed to parse CPUs map"); usage_with_options(stat_usage, options); return -1; } diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 223ffdcc0fd..5a8727c0875 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c @@ -604,556 +604,6 @@ out_free_threads: #undef nsyscalls } -#define TEST_ASSERT_VAL(text, cond) \ -do { \ - if (!(cond)) { \ - pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ - return -1; \ - } \ -} while (0) - -static int test__checkevent_tracepoint(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); - TEST_ASSERT_VAL("wrong sample_type", - (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) == - evsel->attr.sample_type); - TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period); - return 0; -} - -static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel; - - TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); - - list_for_each_entry(evsel, &evlist->entries, node) { - TEST_ASSERT_VAL("wrong type", - PERF_TYPE_TRACEPOINT == evsel->attr.type); - TEST_ASSERT_VAL("wrong sample_type", - (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) - == evsel->attr.sample_type); - TEST_ASSERT_VAL("wrong sample_period", - 1 == evsel->attr.sample_period); - } - return 0; -} - -static int test__checkevent_raw(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); - TEST_ASSERT_VAL("wrong config", 0x1a == evsel->attr.config); - return 0; -} - -static int test__checkevent_numeric(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); - TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type); - TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); - return 0; -} - -static int test__checkevent_symbolic_name(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config); - return 0; -} - -static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); - TEST_ASSERT_VAL("wrong period", - 100000 == evsel->attr.sample_period); - TEST_ASSERT_VAL("wrong config1", - 0 == evsel->attr.config1); - TEST_ASSERT_VAL("wrong config2", - 1 == evsel->attr.config2); - return 0; -} - -static int test__checkevent_symbolic_alias(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type); - TEST_ASSERT_VAL("wrong config", - PERF_COUNT_SW_PAGE_FAULTS == evsel->attr.config); - return 0; -} - -static int test__checkevent_genhw(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->attr.type); - TEST_ASSERT_VAL("wrong config", (1 << 16) == evsel->attr.config); - return 0; -} - -static int test__checkevent_breakpoint(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); - TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); - TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R | HW_BREAKPOINT_W) == - evsel->attr.bp_type); - TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_4 == - evsel->attr.bp_len); - return 0; -} - -static int test__checkevent_breakpoint_x(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); - TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); - TEST_ASSERT_VAL("wrong bp_type", - HW_BREAKPOINT_X == evsel->attr.bp_type); - TEST_ASSERT_VAL("wrong bp_len", sizeof(long) == evsel->attr.bp_len); - return 0; -} - -static int test__checkevent_breakpoint_r(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); - TEST_ASSERT_VAL("wrong type", - PERF_TYPE_BREAKPOINT == evsel->attr.type); - TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); - TEST_ASSERT_VAL("wrong bp_type", - HW_BREAKPOINT_R == evsel->attr.bp_type); - TEST_ASSERT_VAL("wrong bp_len", - HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len); - return 0; -} - -static int test__checkevent_breakpoint_w(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); - TEST_ASSERT_VAL("wrong type", - PERF_TYPE_BREAKPOINT == evsel->attr.type); - TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); - TEST_ASSERT_VAL("wrong bp_type", - HW_BREAKPOINT_W == evsel->attr.bp_type); - TEST_ASSERT_VAL("wrong bp_len", - HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len); - return 0; -} - -static int test__checkevent_tracepoint_modifier(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); - - return test__checkevent_tracepoint(evlist); -} - -static int -test__checkevent_tracepoint_multi_modifier(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel; - - TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); - - list_for_each_entry(evsel, &evlist->entries, node) { - TEST_ASSERT_VAL("wrong exclude_user", - !evsel->attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", - evsel->attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); - } - - return test__checkevent_tracepoint_multi(evlist); -} - -static int test__checkevent_raw_modifier(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); - - return test__checkevent_raw(evlist); -} - -static int test__checkevent_numeric_modifier(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); - - return test__checkevent_numeric(evlist); -} - -static int test__checkevent_symbolic_name_modifier(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); - - return test__checkevent_symbolic_name(evlist); -} - -static int test__checkevent_exclude_host_modifier(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); - - return test__checkevent_symbolic_name(evlist); -} - -static int test__checkevent_exclude_guest_modifier(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); - TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); - - return test__checkevent_symbolic_name(evlist); -} - -static int test__checkevent_symbolic_alias_modifier(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); - - return test__checkevent_symbolic_alias(evlist); -} - -static int test__checkevent_genhw_modifier(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); - - return test__checkevent_genhw(evlist); -} - -static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); - - return test__checkevent_breakpoint(evlist); -} - -static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); - - return test__checkevent_breakpoint_x(evlist); -} - -static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); - - return test__checkevent_breakpoint_r(evlist); -} - -static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); - - return test__checkevent_breakpoint_w(evlist); -} - -static int test__checkevent_pmu(struct perf_evlist *evlist) -{ - - struct perf_evsel *evsel = list_entry(evlist->entries.next, - struct perf_evsel, node); - - TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); - TEST_ASSERT_VAL("wrong config", 10 == evsel->attr.config); - TEST_ASSERT_VAL("wrong config1", 1 == evsel->attr.config1); - TEST_ASSERT_VAL("wrong config2", 3 == evsel->attr.config2); - TEST_ASSERT_VAL("wrong period", 1000 == evsel->attr.sample_period); - - return 0; -} - -static int test__checkevent_list(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel; - - TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); - - /* r1 */ - evsel = list_entry(evlist->entries.next, struct perf_evsel, node); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); - TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); - TEST_ASSERT_VAL("wrong config1", 0 == evsel->attr.config1); - TEST_ASSERT_VAL("wrong config2", 0 == evsel->attr.config2); - TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); - - /* syscalls:sys_enter_open:k */ - evsel = list_entry(evsel->node.next, struct perf_evsel, node); - TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); - TEST_ASSERT_VAL("wrong sample_type", - (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) == - evsel->attr.sample_type); - TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period); - TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); - - /* 1:1:hp */ - evsel = list_entry(evsel->node.next, struct perf_evsel, node); - TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type); - TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); - TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); - TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); - TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); - TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); - - return 0; -} - -static struct test__event_st { - const char *name; - __u32 type; - int (*check)(struct perf_evlist *evlist); -} test__events[] = { - { - .name = "syscalls:sys_enter_open", - .check = test__checkevent_tracepoint, - }, - { - .name = "syscalls:*", - .check = test__checkevent_tracepoint_multi, - }, - { - .name = "r1a", - .check = test__checkevent_raw, - }, - { - .name = "1:1", - .check = test__checkevent_numeric, - }, - { - .name = "instructions", - .check = test__checkevent_symbolic_name, - }, - { - .name = "cycles/period=100000,config2/", - .check = test__checkevent_symbolic_name_config, - }, - { - .name = "faults", - .check = test__checkevent_symbolic_alias, - }, - { - .name = "L1-dcache-load-miss", - .check = test__checkevent_genhw, - }, - { - .name = "mem:0", - .check = test__checkevent_breakpoint, - }, - { - .name = "mem:0:x", - .check = test__checkevent_breakpoint_x, - }, - { - .name = "mem:0:r", - .check = test__checkevent_breakpoint_r, - }, - { - .name = "mem:0:w", - .check = test__checkevent_breakpoint_w, - }, - { - .name = "syscalls:sys_enter_open:k", - .check = test__checkevent_tracepoint_modifier, - }, - { - .name = "syscalls:*:u", - .check = test__checkevent_tracepoint_multi_modifier, - }, - { - .name = "r1a:kp", - .check = test__checkevent_raw_modifier, - }, - { - .name = "1:1:hp", - .check = test__checkevent_numeric_modifier, - }, - { - .name = "instructions:h", - .check = test__checkevent_symbolic_name_modifier, - }, - { - .name = "faults:u", - .check = test__checkevent_symbolic_alias_modifier, - }, - { - .name = "L1-dcache-load-miss:kp", - .check = test__checkevent_genhw_modifier, - }, - { - .name = "mem:0:u", - .check = test__checkevent_breakpoint_modifier, - }, - { - .name = "mem:0:x:k", - .check = test__checkevent_breakpoint_x_modifier, - }, - { - .name = "mem:0:r:hp", - .check = test__checkevent_breakpoint_r_modifier, - }, - { - .name = "mem:0:w:up", - .check = test__checkevent_breakpoint_w_modifier, - }, - { - .name = "cpu/config=10,config1,config2=3,period=1000/u", - .check = test__checkevent_pmu, - }, - { - .name = "r1,syscalls:sys_enter_open:k,1:1:hp", - .check = test__checkevent_list, - }, - { - .name = "instructions:G", - .check = test__checkevent_exclude_host_modifier, - }, - { - .name = "instructions:H", - .check = test__checkevent_exclude_guest_modifier, - }, -}; - -#define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st)) - -static int test__parse_events(void) -{ - struct perf_evlist *evlist; - u_int i; - int ret = 0; - - for (i = 0; i < TEST__EVENTS_CNT; i++) { - struct test__event_st *e = &test__events[i]; - - evlist = perf_evlist__new(NULL, NULL); - if (evlist == NULL) - break; - - ret = parse_events(evlist, e->name, 0); - if (ret) { - pr_debug("failed to parse event '%s', err %d\n", - e->name, ret); - break; - } - - ret = e->check(evlist); - perf_evlist__delete(evlist); - if (ret) - break; - } - - return ret; -} - static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t **maskp, size_t *sizep) { @@ -1195,6 +645,10 @@ realloc: static int test__PERF_RECORD(void) { struct perf_record_opts opts = { + .target = { + .uid = UINT_MAX, + .uses_mmap = true, + }, .no_delay = true, .freq = 10, .mmap_pages = 256, @@ -1237,8 +691,7 @@ static int test__PERF_RECORD(void) * perf_evlist__prepare_workload we'll fill in the only thread * we're monitoring, the one forked there. */ - err = perf_evlist__create_maps(evlist, opts.target_pid, - opts.target_tid, UINT_MAX, opts.cpu_list); + err = perf_evlist__create_maps(evlist, &opts.target); if (err < 0) { pr_debug("Not enough memory to create thread/cpu maps\n"); goto out_delete_evlist; @@ -1579,8 +1032,6 @@ static int __test__rdpmc(void) sa.sa_sigaction = segfault_handler; sigaction(SIGSEGV, &sa, NULL); - fprintf(stderr, "\n\n"); - fd = sys_perf_event_open(&attr, 0, -1, -1, 0); if (fd < 0) { die("Error: sys_perf_event_open() syscall returned " @@ -1605,7 +1056,7 @@ static int __test__rdpmc(void) loops *= 10; delta = now - stamp; - fprintf(stderr, "%14d: %14Lu\n", n, (long long)delta); + pr_debug("%14d: %14Lu\n", n, (long long)delta); delta_sum += delta; } @@ -1613,7 +1064,7 @@ static int __test__rdpmc(void) munmap(addr, page_size); close(fd); - fprintf(stderr, " "); + pr_debug(" "); if (!delta_sum) return -1; @@ -1674,7 +1125,7 @@ static struct test { }, { .desc = "parse events tests", - .func = test__parse_events, + .func = parse_events__test, }, #if defined(__x86_64__) || defined(__i386__) { diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 8ef59f8262b..6031dce0429 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -588,7 +588,7 @@ static void *display_thread_tui(void *arg) * via --uid. */ list_for_each_entry(pos, &top->evlist->entries, node) - pos->hists.uid_filter_str = top->uid_str; + pos->hists.uid_filter_str = top->target.uid_str; perf_evlist__tui_browse_hists(top->evlist, help, perf_top__sort_new_samples, @@ -900,6 +900,9 @@ static void perf_top__start_counters(struct perf_top *top) attr->read_format |= PERF_FORMAT_ID; } + if (perf_target__has_cpu(&top->target)) + attr->sample_type |= PERF_SAMPLE_CPU; + if (symbol_conf.use_callchain) attr->sample_type |= PERF_SAMPLE_CALLCHAIN; @@ -948,6 +951,10 @@ try_again: attr->type = PERF_TYPE_SOFTWARE; attr->config = PERF_COUNT_SW_CPU_CLOCK; + if (counter->name) { + free(counter->name); + counter->name = strdup(event_name(counter)); + } goto try_again; } @@ -1016,7 +1023,7 @@ static int __cmd_top(struct perf_top *top) if (ret) goto out_delete; - if (top->target_tid || top->uid != UINT_MAX) + if (perf_target__has_task(&top->target)) perf_event__synthesize_thread_map(&top->tool, top->evlist->threads, perf_event__process, &top->session->host_machine); @@ -1150,14 +1157,17 @@ static const char * const top_usage[] = { int cmd_top(int argc, const char **argv, const char *prefix __used) { struct perf_evsel *pos; - int status = -ENOMEM; + int status; + char errbuf[BUFSIZ]; struct perf_top top = { .count_filter = 5, .delay_secs = 2, - .uid = UINT_MAX, - .freq = 1000, /* 1 KHz */ + .freq = 4000, /* 4 KHz */ .mmap_pages = 128, .sym_pcnt_filter = 5, + .target = { + .uses_mmap = true, + }, }; char callchain_default_opt[] = "fractal,0.5,callee"; const struct option options[] = { @@ -1166,13 +1176,13 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) parse_events_option), OPT_INTEGER('c', "count", &top.default_interval, "event period to sample"), - OPT_STRING('p', "pid", &top.target_pid, "pid", + OPT_STRING('p', "pid", &top.target.pid, "pid", "profile events on existing process id"), - OPT_STRING('t', "tid", &top.target_tid, "tid", + OPT_STRING('t', "tid", &top.target.tid, "tid", "profile events on existing thread id"), - OPT_BOOLEAN('a', "all-cpus", &top.system_wide, + OPT_BOOLEAN('a', "all-cpus", &top.target.system_wide, "system-wide collection from all CPUs"), - OPT_STRING('C', "cpu", &top.cpu_list, "cpu", + OPT_STRING('C', "cpu", &top.target.cpu_list, "cpu", "list of cpus to monitor"), OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), @@ -1227,7 +1237,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) "Display raw encoding of assembly instructions (default)"), OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", "Specify disassembler style (e.g. -M intel for intel syntax)"), - OPT_STRING('u', "uid", &top.uid_str, "user", "user to profile"), + OPT_STRING('u', "uid", &top.target.uid_str, "user", "user to profile"), OPT_END() }; @@ -1253,22 +1263,27 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) setup_browser(false); - top.uid = parse_target_uid(top.uid_str, top.target_tid, top.target_pid); - if (top.uid_str != NULL && top.uid == UINT_MAX - 1) - goto out_delete_evlist; + status = perf_target__validate(&top.target); + if (status) { + perf_target__strerror(&top.target, status, errbuf, BUFSIZ); + ui__warning("%s", errbuf); + } - /* CPU and PID are mutually exclusive */ - if (top.target_tid && top.cpu_list) { - printf("WARNING: PID switch overriding CPU\n"); - sleep(1); - top.cpu_list = NULL; + status = perf_target__parse_uid(&top.target); + if (status) { + int saved_errno = errno; + + perf_target__strerror(&top.target, status, errbuf, BUFSIZ); + ui__warning("%s", errbuf); + + status = -saved_errno; + goto out_delete_evlist; } - if (top.target_pid) - top.target_tid = top.target_pid; + if (perf_target__none(&top.target)) + top.target.system_wide = true; - if (perf_evlist__create_maps(top.evlist, top.target_pid, - top.target_tid, top.uid, top.cpu_list) < 0) + if (perf_evlist__create_maps(top.evlist, &top.target) < 0) usage_with_options(top_usage, options); if (!top.evlist->nr_entries && diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 89e3355ab17..14f1034f14f 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -207,10 +207,10 @@ extern const char perf_version_string[]; void pthread__unblock_sigwinch(void); +#include "util/target.h" + struct perf_record_opts { - const char *target_pid; - const char *target_tid; - uid_t uid; + struct perf_target target; bool call_graph; bool group; bool inherit_stat; @@ -223,7 +223,6 @@ struct perf_record_opts { bool sample_time; bool sample_id_all_missing; bool exclude_guest_missing; - bool system_wide; bool period; unsigned int freq; unsigned int mmap_pages; @@ -231,7 +230,6 @@ struct perf_record_opts { int branch_stack; u64 default_interval; u64 user_interval; - const char *cpu_list; }; #endif diff --git a/tools/perf/util/ui/browser.c b/tools/perf/ui/browser.c index 556829124b0..cde4d0f0ddb 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/ui/browser.c @@ -27,9 +27,12 @@ static int ui_browser__percent_color(struct ui_browser *browser, return HE_COLORSET_NORMAL; } -void ui_browser__set_color(struct ui_browser *self __used, int color) +int ui_browser__set_color(struct ui_browser *browser, int color) { + int ret = browser->current_color; + browser->current_color = color; SLsmg_set_color(color); + return ret; } void ui_browser__set_percent_color(struct ui_browser *self, @@ -503,6 +506,12 @@ static struct ui_browser__colorset { .bg = "default", }, { + .colorset = HE_COLORSET_ADDR, + .name = "addr", + .fg = "magenta", + .bg = "default", + }, + { .name = NULL, } }; @@ -584,6 +593,111 @@ unsigned int ui_browser__argv_refresh(struct ui_browser *browser) return row; } +void __ui_browser__vline(struct ui_browser *browser, unsigned int column, + u16 start, u16 end) +{ + SLsmg_set_char_set(1); + ui_browser__gotorc(browser, start, column); + SLsmg_draw_vline(end - start + 1); + SLsmg_set_char_set(0); +} + +void ui_browser__write_graph(struct ui_browser *browser __used, int graph) +{ + SLsmg_set_char_set(1); + SLsmg_write_char(graph); + SLsmg_set_char_set(0); +} + +static void __ui_browser__line_arrow_up(struct ui_browser *browser, + unsigned int column, + u64 start, u64 end) +{ + unsigned int row, end_row; + + SLsmg_set_char_set(1); + + if (start < browser->top_idx + browser->height) { + row = start - browser->top_idx; + ui_browser__gotorc(browser, row, column); + SLsmg_write_char(SLSMG_LLCORN_CHAR); + ui_browser__gotorc(browser, row, column + 1); + SLsmg_draw_hline(2); + + if (row-- == 0) + goto out; + } else + row = browser->height - 1; + + if (end > browser->top_idx) + end_row = end - browser->top_idx; + else + end_row = 0; + + ui_browser__gotorc(browser, end_row, column); + SLsmg_draw_vline(row - end_row + 1); + + ui_browser__gotorc(browser, end_row, column); + if (end >= browser->top_idx) { + SLsmg_write_char(SLSMG_ULCORN_CHAR); + ui_browser__gotorc(browser, end_row, column + 1); + SLsmg_write_char(SLSMG_HLINE_CHAR); + ui_browser__gotorc(browser, end_row, column + 2); + SLsmg_write_char(SLSMG_RARROW_CHAR); + } +out: + SLsmg_set_char_set(0); +} + +static void __ui_browser__line_arrow_down(struct ui_browser *browser, + unsigned int column, + u64 start, u64 end) +{ + unsigned int row, end_row; + + SLsmg_set_char_set(1); + + if (start >= browser->top_idx) { + row = start - browser->top_idx; + ui_browser__gotorc(browser, row, column); + SLsmg_write_char(SLSMG_ULCORN_CHAR); + ui_browser__gotorc(browser, row, column + 1); + SLsmg_draw_hline(2); + + if (row++ == 0) + goto out; + } else + row = 0; + + if (end >= browser->top_idx + browser->height) + end_row = browser->height - 1; + else + end_row = end - browser->top_idx;; + + ui_browser__gotorc(browser, row, column); + SLsmg_draw_vline(end_row - row + 1); + + ui_browser__gotorc(browser, end_row, column); + if (end < browser->top_idx + browser->height) { + SLsmg_write_char(SLSMG_LLCORN_CHAR); + ui_browser__gotorc(browser, end_row, column + 1); + SLsmg_write_char(SLSMG_HLINE_CHAR); + ui_browser__gotorc(browser, end_row, column + 2); + SLsmg_write_char(SLSMG_RARROW_CHAR); + } +out: + SLsmg_set_char_set(0); +} + +void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column, + u64 start, u64 end) +{ + if (start > end) + __ui_browser__line_arrow_up(browser, column, start, end); + else + __ui_browser__line_arrow_down(browser, column, start, end); +} + void ui_browser__init(void) { int i = 0; diff --git a/tools/perf/util/ui/browser.h b/tools/perf/ui/browser.h index 6ee82f60fea..dd96d822990 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/ui/browser.h @@ -10,11 +10,13 @@ #define HE_COLORSET_NORMAL 52 #define HE_COLORSET_SELECTED 53 #define HE_COLORSET_CODE 54 +#define HE_COLORSET_ADDR 55 struct ui_browser { u64 index, top_idx; void *top, *entries; u16 y, x, width, height; + int current_color; void *priv; const char *title; char *helpline; @@ -27,7 +29,7 @@ struct ui_browser { bool use_navkeypressed; }; -void ui_browser__set_color(struct ui_browser *self, int color); +int ui_browser__set_color(struct ui_browser *browser, int color); void ui_browser__set_percent_color(struct ui_browser *self, double percent, bool current); bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row); @@ -35,6 +37,9 @@ void ui_browser__refresh_dimensions(struct ui_browser *self); void ui_browser__reset_index(struct ui_browser *self); void ui_browser__gotorc(struct ui_browser *self, int y, int x); +void ui_browser__write_graph(struct ui_browser *browser, int graph); +void __ui_browser__line_arrow(struct ui_browser *browser, unsigned int column, + u64 start, u64 end); void __ui_browser__show_title(struct ui_browser *browser, const char *title); void ui_browser__show_title(struct ui_browser *browser, const char *title); int ui_browser__show(struct ui_browser *self, const char *title, @@ -44,6 +49,8 @@ int ui_browser__refresh(struct ui_browser *self); int ui_browser__run(struct ui_browser *browser, int delay_secs); void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries); void ui_browser__handle_resize(struct ui_browser *browser); +void __ui_browser__vline(struct ui_browser *browser, unsigned int column, + u16 start, u16 end); int ui_browser__warning(struct ui_browser *browser, int timeout, const char *format, ...); diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c new file mode 100644 index 00000000000..6e0ef79be16 --- /dev/null +++ b/tools/perf/ui/browsers/annotate.c @@ -0,0 +1,867 @@ +#include "../../util/util.h" +#include "../browser.h" +#include "../helpline.h" +#include "../libslang.h" +#include "../ui.h" +#include "../util.h" +#include "../../util/annotate.h" +#include "../../util/hist.h" +#include "../../util/sort.h" +#include "../../util/symbol.h" +#include <pthread.h> +#include <newt.h> + +struct browser_disasm_line { + struct rb_node rb_node; + double percent; + u32 idx; + int idx_asm; + int jump_sources; +}; + +struct annotate_browser { + struct ui_browser b; + struct rb_root entries; + struct rb_node *curr_hot; + struct disasm_line *selection; + struct disasm_line **offsets; + u64 start; + int nr_asm_entries; + int nr_entries; + int max_jump_sources; + int nr_jumps; + bool hide_src_code; + bool use_offset; + bool jump_arrows; + bool show_nr_jumps; + bool searching_backwards; + u8 addr_width; + u8 jumps_width; + u8 target_width; + u8 min_addr_width; + u8 max_addr_width; + char search_bf[128]; +}; + +static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl) +{ + return (struct browser_disasm_line *)(dl + 1); +} + +static bool disasm_line__filter(struct ui_browser *browser, void *entry) +{ + struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); + + if (ab->hide_src_code) { + struct disasm_line *dl = list_entry(entry, struct disasm_line, node); + return dl->offset == -1; + } + + return false; +} + +static int annotate_browser__jumps_percent_color(struct annotate_browser *browser, + int nr, bool current) +{ + if (current && (!browser->b.use_navkeypressed || browser->b.navkeypressed)) + return HE_COLORSET_SELECTED; + if (nr == browser->max_jump_sources) + return HE_COLORSET_TOP; + if (nr > 1) + return HE_COLORSET_MEDIUM; + return HE_COLORSET_NORMAL; +} + +static int annotate_browser__set_jumps_percent_color(struct annotate_browser *browser, + int nr, bool current) +{ + int color = annotate_browser__jumps_percent_color(browser, nr, current); + return ui_browser__set_color(&browser->b, color); +} + +static void annotate_browser__write(struct ui_browser *self, void *entry, int row) +{ + struct annotate_browser *ab = container_of(self, struct annotate_browser, b); + struct disasm_line *dl = list_entry(entry, struct disasm_line, node); + struct browser_disasm_line *bdl = disasm_line__browser(dl); + bool current_entry = ui_browser__is_current_entry(self, row); + bool change_color = (!ab->hide_src_code && + (!current_entry || (self->use_navkeypressed && + !self->navkeypressed))); + int width = self->width, printed; + char bf[256]; + + if (dl->offset != -1 && bdl->percent != 0.0) { + ui_browser__set_percent_color(self, bdl->percent, current_entry); + slsmg_printf("%6.2f ", bdl->percent); + } else { + ui_browser__set_percent_color(self, 0, current_entry); + slsmg_write_nstring(" ", 7); + } + + SLsmg_write_char(' '); + + /* The scroll bar isn't being used */ + if (!self->navkeypressed) + width += 1; + + if (!*dl->line) + slsmg_write_nstring(" ", width - 7); + else if (dl->offset == -1) { + printed = scnprintf(bf, sizeof(bf), "%*s ", + ab->addr_width, " "); + slsmg_write_nstring(bf, printed); + slsmg_write_nstring(dl->line, width - printed - 6); + } else { + u64 addr = dl->offset; + int color = -1; + + if (!ab->use_offset) + addr += ab->start; + + if (!ab->use_offset) { + printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr); + } else { + if (bdl->jump_sources) { + if (ab->show_nr_jumps) { + int prev; + printed = scnprintf(bf, sizeof(bf), "%*d ", + ab->jumps_width, + bdl->jump_sources); + prev = annotate_browser__set_jumps_percent_color(ab, bdl->jump_sources, + current_entry); + slsmg_write_nstring(bf, printed); + ui_browser__set_color(self, prev); + } + + printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ", + ab->target_width, addr); + } else { + printed = scnprintf(bf, sizeof(bf), "%*s ", + ab->addr_width, " "); + } + } + + if (change_color) + color = ui_browser__set_color(self, HE_COLORSET_ADDR); + slsmg_write_nstring(bf, printed); + if (change_color) + ui_browser__set_color(self, color); + if (dl->ins && dl->ins->ops->scnprintf) { + if (ins__is_jump(dl->ins)) { + bool fwd = dl->ops.target.offset > (u64)dl->offset; + + ui_browser__write_graph(self, fwd ? SLSMG_DARROW_CHAR : + SLSMG_UARROW_CHAR); + SLsmg_write_char(' '); + } else if (ins__is_call(dl->ins)) { + ui_browser__write_graph(self, SLSMG_RARROW_CHAR); + SLsmg_write_char(' '); + } else { + slsmg_write_nstring(" ", 2); + } + } else { + if (strcmp(dl->name, "retq")) { + slsmg_write_nstring(" ", 2); + } else { + ui_browser__write_graph(self, SLSMG_LARROW_CHAR); + SLsmg_write_char(' '); + } + } + + disasm_line__scnprintf(dl, bf, sizeof(bf), !ab->use_offset); + slsmg_write_nstring(bf, width - 10 - printed); + } + + if (current_entry) + ab->selection = dl; +} + +static void annotate_browser__draw_current_jump(struct ui_browser *browser) +{ + struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); + struct disasm_line *cursor = ab->selection, *target; + struct browser_disasm_line *btarget, *bcursor; + unsigned int from, to; + + if (!cursor->ins || !ins__is_jump(cursor->ins) || + !disasm_line__has_offset(cursor)) + return; + + target = ab->offsets[cursor->ops.target.offset]; + if (!target) + return; + + bcursor = disasm_line__browser(cursor); + btarget = disasm_line__browser(target); + + if (ab->hide_src_code) { + from = bcursor->idx_asm; + to = btarget->idx_asm; + } else { + from = (u64)bcursor->idx; + to = (u64)btarget->idx; + } + + ui_browser__set_color(browser, HE_COLORSET_CODE); + __ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to); +} + +static unsigned int annotate_browser__refresh(struct ui_browser *browser) +{ + struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); + int ret = ui_browser__list_head_refresh(browser); + + if (ab->jump_arrows) + annotate_browser__draw_current_jump(browser); + + ui_browser__set_color(browser, HE_COLORSET_NORMAL); + __ui_browser__vline(browser, 7, 0, browser->height - 1); + return ret; +} + +static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx) +{ + double percent = 0.0; + + if (dl->offset != -1) { + int len = sym->end - sym->start; + unsigned int hits = 0; + struct annotation *notes = symbol__annotation(sym); + struct source_line *src_line = notes->src->lines; + struct sym_hist *h = annotation__histogram(notes, evidx); + s64 offset = dl->offset; + struct disasm_line *next; + + next = disasm__get_next_ip_line(¬es->src->source, dl); + while (offset < (s64)len && + (next == NULL || offset < next->offset)) { + if (src_line) { + percent += src_line[offset].percent; + } else + hits += h->addr[offset]; + + ++offset; + } + /* + * If the percentage wasn't already calculated in + * symbol__get_source_line, do it now: + */ + if (src_line == NULL && h->sum) + percent = 100.0 * hits / h->sum; + } + + return percent; +} + +static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl) +{ + struct rb_node **p = &root->rb_node; + struct rb_node *parent = NULL; + struct browser_disasm_line *l; + + while (*p != NULL) { + parent = *p; + l = rb_entry(parent, struct browser_disasm_line, rb_node); + if (bdl->percent < l->percent) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&bdl->rb_node, parent, p); + rb_insert_color(&bdl->rb_node, root); +} + +static void annotate_browser__set_top(struct annotate_browser *self, + struct disasm_line *pos, u32 idx) +{ + unsigned back; + + ui_browser__refresh_dimensions(&self->b); + back = self->b.height / 2; + self->b.top_idx = self->b.index = idx; + + while (self->b.top_idx != 0 && back != 0) { + pos = list_entry(pos->node.prev, struct disasm_line, node); + + if (disasm_line__filter(&self->b, &pos->node)) + continue; + + --self->b.top_idx; + --back; + } + + self->b.top = pos; + self->b.navkeypressed = true; +} + +static void annotate_browser__set_rb_top(struct annotate_browser *browser, + struct rb_node *nd) +{ + struct browser_disasm_line *bpos; + struct disasm_line *pos; + + bpos = rb_entry(nd, struct browser_disasm_line, rb_node); + pos = ((struct disasm_line *)bpos) - 1; + annotate_browser__set_top(browser, pos, bpos->idx); + browser->curr_hot = nd; +} + +static void annotate_browser__calc_percent(struct annotate_browser *browser, + int evidx) +{ + struct map_symbol *ms = browser->b.priv; + struct symbol *sym = ms->sym; + struct annotation *notes = symbol__annotation(sym); + struct disasm_line *pos; + + browser->entries = RB_ROOT; + + pthread_mutex_lock(¬es->lock); + + list_for_each_entry(pos, ¬es->src->source, node) { + struct browser_disasm_line *bpos = disasm_line__browser(pos); + bpos->percent = disasm_line__calc_percent(pos, sym, evidx); + if (bpos->percent < 0.01) { + RB_CLEAR_NODE(&bpos->rb_node); + continue; + } + disasm_rb_tree__insert(&browser->entries, bpos); + } + pthread_mutex_unlock(¬es->lock); + + browser->curr_hot = rb_last(&browser->entries); +} + +static bool annotate_browser__toggle_source(struct annotate_browser *browser) +{ + struct disasm_line *dl; + struct browser_disasm_line *bdl; + off_t offset = browser->b.index - browser->b.top_idx; + + browser->b.seek(&browser->b, offset, SEEK_CUR); + dl = list_entry(browser->b.top, struct disasm_line, node); + bdl = disasm_line__browser(dl); + + if (browser->hide_src_code) { + if (bdl->idx_asm < offset) + offset = bdl->idx; + + browser->b.nr_entries = browser->nr_entries; + browser->hide_src_code = false; + browser->b.seek(&browser->b, -offset, SEEK_CUR); + browser->b.top_idx = bdl->idx - offset; + browser->b.index = bdl->idx; + } else { + if (bdl->idx_asm < 0) { + ui_helpline__puts("Only available for assembly lines."); + browser->b.seek(&browser->b, -offset, SEEK_CUR); + return false; + } + + if (bdl->idx_asm < offset) + offset = bdl->idx_asm; + + browser->b.nr_entries = browser->nr_asm_entries; + browser->hide_src_code = true; + browser->b.seek(&browser->b, -offset, SEEK_CUR); + browser->b.top_idx = bdl->idx_asm - offset; + browser->b.index = bdl->idx_asm; + } + + return true; +} + +static bool annotate_browser__callq(struct annotate_browser *browser, + int evidx, void (*timer)(void *arg), + void *arg, int delay_secs) +{ + struct map_symbol *ms = browser->b.priv; + struct disasm_line *dl = browser->selection; + struct symbol *sym = ms->sym; + struct annotation *notes; + struct symbol *target; + u64 ip; + + if (!ins__is_call(dl->ins)) + return false; + + ip = ms->map->map_ip(ms->map, dl->ops.target.addr); + target = map__find_symbol(ms->map, ip, NULL); + if (target == NULL) { + ui_helpline__puts("The called function was not found."); + return true; + } + + notes = symbol__annotation(target); + pthread_mutex_lock(¬es->lock); + + if (notes->src == NULL && symbol__alloc_hist(target) < 0) { + pthread_mutex_unlock(¬es->lock); + ui__warning("Not enough memory for annotating '%s' symbol!\n", + target->name); + return true; + } + + pthread_mutex_unlock(¬es->lock); + symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs); + ui_browser__show_title(&browser->b, sym->name); + return true; +} + +static +struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser, + s64 offset, s64 *idx) +{ + struct map_symbol *ms = browser->b.priv; + struct symbol *sym = ms->sym; + struct annotation *notes = symbol__annotation(sym); + struct disasm_line *pos; + + *idx = 0; + list_for_each_entry(pos, ¬es->src->source, node) { + if (pos->offset == offset) + return pos; + if (!disasm_line__filter(&browser->b, &pos->node)) + ++*idx; + } + + return NULL; +} + +static bool annotate_browser__jump(struct annotate_browser *browser) +{ + struct disasm_line *dl = browser->selection; + s64 idx; + + if (!ins__is_jump(dl->ins)) + return false; + + dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx); + if (dl == NULL) { + ui_helpline__puts("Invallid jump offset"); + return true; + } + + annotate_browser__set_top(browser, dl, idx); + + return true; +} + +static +struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser, + char *s, s64 *idx) +{ + struct map_symbol *ms = browser->b.priv; + struct symbol *sym = ms->sym; + struct annotation *notes = symbol__annotation(sym); + struct disasm_line *pos = browser->selection; + + *idx = browser->b.index; + list_for_each_entry_continue(pos, ¬es->src->source, node) { + if (disasm_line__filter(&browser->b, &pos->node)) + continue; + + ++*idx; + + if (pos->line && strstr(pos->line, s) != NULL) + return pos; + } + + return NULL; +} + +static bool __annotate_browser__search(struct annotate_browser *browser) +{ + struct disasm_line *dl; + s64 idx; + + dl = annotate_browser__find_string(browser, browser->search_bf, &idx); + if (dl == NULL) { + ui_helpline__puts("String not found!"); + return false; + } + + annotate_browser__set_top(browser, dl, idx); + browser->searching_backwards = false; + return true; +} + +static +struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser, + char *s, s64 *idx) +{ + struct map_symbol *ms = browser->b.priv; + struct symbol *sym = ms->sym; + struct annotation *notes = symbol__annotation(sym); + struct disasm_line *pos = browser->selection; + + *idx = browser->b.index; + list_for_each_entry_continue_reverse(pos, ¬es->src->source, node) { + if (disasm_line__filter(&browser->b, &pos->node)) + continue; + + --*idx; + + if (pos->line && strstr(pos->line, s) != NULL) + return pos; + } + + return NULL; +} + +static bool __annotate_browser__search_reverse(struct annotate_browser *browser) +{ + struct disasm_line *dl; + s64 idx; + + dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx); + if (dl == NULL) { + ui_helpline__puts("String not found!"); + return false; + } + + annotate_browser__set_top(browser, dl, idx); + browser->searching_backwards = true; + return true; +} + +static bool annotate_browser__search_window(struct annotate_browser *browser, + int delay_secs) +{ + if (ui_browser__input_window("Search", "String: ", browser->search_bf, + "ENTER: OK, ESC: Cancel", + delay_secs * 2) != K_ENTER || + !*browser->search_bf) + return false; + + return true; +} + +static bool annotate_browser__search(struct annotate_browser *browser, int delay_secs) +{ + if (annotate_browser__search_window(browser, delay_secs)) + return __annotate_browser__search(browser); + + return false; +} + +static bool annotate_browser__continue_search(struct annotate_browser *browser, + int delay_secs) +{ + if (!*browser->search_bf) + return annotate_browser__search(browser, delay_secs); + + return __annotate_browser__search(browser); +} + +static bool annotate_browser__search_reverse(struct annotate_browser *browser, + int delay_secs) +{ + if (annotate_browser__search_window(browser, delay_secs)) + return __annotate_browser__search_reverse(browser); + + return false; +} + +static +bool annotate_browser__continue_search_reverse(struct annotate_browser *browser, + int delay_secs) +{ + if (!*browser->search_bf) + return annotate_browser__search_reverse(browser, delay_secs); + + return __annotate_browser__search_reverse(browser); +} + +static int annotate_browser__run(struct annotate_browser *self, int evidx, + void(*timer)(void *arg), + void *arg, int delay_secs) +{ + struct rb_node *nd = NULL; + struct map_symbol *ms = self->b.priv; + struct symbol *sym = ms->sym; + const char *help = "Press 'h' for help on key bindings"; + int key; + + if (ui_browser__show(&self->b, sym->name, help) < 0) + return -1; + + annotate_browser__calc_percent(self, evidx); + + if (self->curr_hot) { + annotate_browser__set_rb_top(self, self->curr_hot); + self->b.navkeypressed = false; + } + + nd = self->curr_hot; + + while (1) { + key = ui_browser__run(&self->b, delay_secs); + + if (delay_secs != 0) { + annotate_browser__calc_percent(self, evidx); + /* + * Current line focus got out of the list of most active + * lines, NULL it so that if TAB|UNTAB is pressed, we + * move to curr_hot (current hottest line). + */ + if (nd != NULL && RB_EMPTY_NODE(nd)) + nd = NULL; + } + + switch (key) { + case K_TIMER: + if (timer != NULL) + timer(arg); + + if (delay_secs != 0) + symbol__annotate_decay_histogram(sym, evidx); + continue; + case K_TAB: + if (nd != NULL) { + nd = rb_prev(nd); + if (nd == NULL) + nd = rb_last(&self->entries); + } else + nd = self->curr_hot; + break; + case K_UNTAB: + if (nd != NULL) + nd = rb_next(nd); + if (nd == NULL) + nd = rb_first(&self->entries); + else + nd = self->curr_hot; + break; + case K_F1: + case 'h': + ui_browser__help_window(&self->b, + "UP/DOWN/PGUP\n" + "PGDN/SPACE Navigate\n" + "q/ESC/CTRL+C Exit\n\n" + "-> Go to target\n" + "<- Exit\n" + "h Cycle thru hottest instructions\n" + "j Toggle showing jump to target arrows\n" + "J Toggle showing number of jump sources on targets\n" + "n Search next string\n" + "o Toggle disassembler output/simplified view\n" + "s Toggle source code view\n" + "/ Search string\n" + "? Search previous string\n"); + continue; + case 'H': + nd = self->curr_hot; + break; + case 's': + if (annotate_browser__toggle_source(self)) + ui_helpline__puts(help); + continue; + case 'o': + self->use_offset = !self->use_offset; + if (self->use_offset) + self->target_width = self->min_addr_width; + else + self->target_width = self->max_addr_width; +update_addr_width: + self->addr_width = self->target_width; + if (self->show_nr_jumps) + self->addr_width += self->jumps_width + 1; + continue; + case 'j': + self->jump_arrows = !self->jump_arrows; + continue; + case 'J': + self->show_nr_jumps = !self->show_nr_jumps; + goto update_addr_width; + case '/': + if (annotate_browser__search(self, delay_secs)) { +show_help: + ui_helpline__puts(help); + } + continue; + case 'n': + if (self->searching_backwards ? + annotate_browser__continue_search_reverse(self, delay_secs) : + annotate_browser__continue_search(self, delay_secs)) + goto show_help; + continue; + case '?': + if (annotate_browser__search_reverse(self, delay_secs)) + goto show_help; + continue; + case K_ENTER: + case K_RIGHT: + if (self->selection == NULL) + ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); + else if (self->selection->offset == -1) + ui_helpline__puts("Actions are only available for assembly lines."); + else if (!self->selection->ins) { + if (strcmp(self->selection->name, "retq")) + goto show_sup_ins; + goto out; + } else if (!(annotate_browser__jump(self) || + annotate_browser__callq(self, evidx, timer, arg, delay_secs))) { +show_sup_ins: + ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions."); + } + continue; + case K_LEFT: + case K_ESC: + case 'q': + case CTRL('c'): + goto out; + default: + continue; + } + + if (nd != NULL) + annotate_browser__set_rb_top(self, nd); + } +out: + ui_browser__hide(&self->b); + return key; +} + +int hist_entry__tui_annotate(struct hist_entry *he, int evidx, + void(*timer)(void *arg), void *arg, int delay_secs) +{ + return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, + timer, arg, delay_secs); +} + +static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, + size_t size) +{ + u64 offset; + + for (offset = 0; offset < size; ++offset) { + struct disasm_line *dl = browser->offsets[offset], *dlt; + struct browser_disasm_line *bdlt; + + if (!dl || !dl->ins || !ins__is_jump(dl->ins) || + !disasm_line__has_offset(dl)) + continue; + + if (dl->ops.target.offset >= size) { + ui__error("jump to after symbol!\n" + "size: %zx, jump target: %" PRIx64, + size, dl->ops.target.offset); + continue; + } + + dlt = browser->offsets[dl->ops.target.offset]; + /* + * FIXME: Oops, no jump target? Buggy disassembler? Or do we + * have to adjust to the previous offset? + */ + if (dlt == NULL) + continue; + + bdlt = disasm_line__browser(dlt); + if (++bdlt->jump_sources > browser->max_jump_sources) + browser->max_jump_sources = bdlt->jump_sources; + + ++browser->nr_jumps; + } + +} + +static inline int width_jumps(int n) +{ + if (n >= 100) + return 5; + if (n / 10) + return 2; + return 1; +} + +int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, + void(*timer)(void *arg), void *arg, + int delay_secs) +{ + struct disasm_line *pos, *n; + struct annotation *notes; + const size_t size = symbol__size(sym); + struct map_symbol ms = { + .map = map, + .sym = sym, + }; + struct annotate_browser browser = { + .b = { + .refresh = annotate_browser__refresh, + .seek = ui_browser__list_head_seek, + .write = annotate_browser__write, + .filter = disasm_line__filter, + .priv = &ms, + .use_navkeypressed = true, + }, + .use_offset = true, + .jump_arrows = true, + }; + int ret = -1; + + if (sym == NULL) + return -1; + + if (map->dso->annotate_warned) + return -1; + + browser.offsets = zalloc(size * sizeof(struct disasm_line *)); + if (browser.offsets == NULL) { + ui__error("Not enough memory!"); + return -1; + } + + if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) { + ui__error("%s", ui_helpline__last_msg); + goto out_free_offsets; + } + + ui_helpline__push("Press <- or ESC to exit"); + + notes = symbol__annotation(sym); + browser.start = map__rip_2objdump(map, sym->start); + + list_for_each_entry(pos, ¬es->src->source, node) { + struct browser_disasm_line *bpos; + size_t line_len = strlen(pos->line); + + if (browser.b.width < line_len) + browser.b.width = line_len; + bpos = disasm_line__browser(pos); + bpos->idx = browser.nr_entries++; + if (pos->offset != -1) { + bpos->idx_asm = browser.nr_asm_entries++; + /* + * FIXME: short term bandaid to cope with assembly + * routines that comes with labels in the same column + * as the address in objdump, sigh. + * + * E.g. copy_user_generic_unrolled + */ + if (pos->offset < (s64)size) + browser.offsets[pos->offset] = pos; + } else + bpos->idx_asm = -1; + } + + annotate_browser__mark_jump_targets(&browser, size); + + browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size); + browser.max_addr_width = hex_width(sym->end); + browser.jumps_width = width_jumps(browser.max_jump_sources); + browser.b.nr_entries = browser.nr_entries; + browser.b.entries = ¬es->src->source, + browser.b.width += 18; /* Percentage */ + ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs); + list_for_each_entry_safe(pos, n, ¬es->src->source, node) { + list_del(&pos->node); + disasm_line__free(pos); + } + +out_free_offsets: + free(browser.offsets); + return ret; +} diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 2f83e5dc996..a372a4b0263 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -5,12 +5,12 @@ #include <newt.h> #include <linux/rbtree.h> -#include "../../evsel.h" -#include "../../evlist.h" -#include "../../hist.h" -#include "../../pstack.h" -#include "../../sort.h" -#include "../../util.h" +#include "../../util/evsel.h" +#include "../../util/evlist.h" +#include "../../util/hist.h" +#include "../../util/pstack.h" +#include "../../util/sort.h" +#include "../../util/util.h" #include "../browser.h" #include "../helpline.h" @@ -840,10 +840,14 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, int printed; const struct dso *dso = self->dso_filter; const struct thread *thread = self->thread_filter; - unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; + unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE]; + u64 nr_events = self->stats.total_period; + + nr_samples = convert_unit(nr_samples, &unit); + printed = scnprintf(bf, size, + "Samples: %lu%c of event '%s', Event count (approx.): %lu", + nr_samples, unit, ev_name, nr_events); - nr_events = convert_unit(nr_events, &unit); - printed = scnprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name); if (self->uid_filter_str) printed += snprintf(bf + printed, size - printed, @@ -937,7 +941,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, goto zoom_dso; case 't': goto zoom_thread; - case 's': + case '/': if (ui_browser__input_window("Symbol to show", "Please enter the name of symbol you want to see", buf, "ENTER: OK, ESC: Cancel", @@ -965,7 +969,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, "E Expand all callchains\n" "d Zoom into current DSO\n" "t Zoom into current Thread\n" - "s Filter symbol by name"); + "/ Filter symbol by name"); continue; case K_ENTER: case K_RIGHT: diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/ui/browsers/map.c index eca6575abfd..98851d55a53 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/ui/browsers/map.c @@ -5,9 +5,9 @@ #include <sys/ttydefaults.h> #include <string.h> #include <linux/bitops.h> -#include "../../util.h" -#include "../../debug.h" -#include "../../symbol.h" +#include "../../util/util.h" +#include "../../util/debug.h" +#include "../../util/symbol.h" #include "../browser.h" #include "../helpline.h" #include "map.h" diff --git a/tools/perf/util/ui/browsers/map.h b/tools/perf/ui/browsers/map.h index df8581a43e1..df8581a43e1 100644 --- a/tools/perf/util/ui/browsers/map.h +++ b/tools/perf/ui/browsers/map.h diff --git a/tools/perf/util/gtk/browser.c b/tools/perf/ui/gtk/browser.c index 258352a2356..0656c381a89 100644 --- a/tools/perf/util/gtk/browser.c +++ b/tools/perf/ui/gtk/browser.c @@ -9,24 +9,13 @@ #define MAX_COLUMNS 32 -void perf_gtk_setup_browser(int argc, const char *argv[], - bool fallback_to_pager __used) -{ - gtk_init(&argc, (char ***)&argv); -} - -void perf_gtk_exit_browser(bool wait_for_ok __used) -{ - gtk_main_quit(); -} - -static void perf_gtk_signal(int sig) +static void perf_gtk__signal(int sig) { psignal(sig, "perf"); gtk_main_quit(); } -static void perf_gtk_resize_window(GtkWidget *window) +static void perf_gtk__resize_window(GtkWidget *window) { GdkRectangle rect; GdkScreen *screen; @@ -46,7 +35,7 @@ static void perf_gtk_resize_window(GtkWidget *window) gtk_window_resize(GTK_WINDOW(window), width, height); } -static void perf_gtk_show_hists(GtkWidget *window, struct hists *hists) +static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists) { GType col_types[MAX_COLUMNS]; GtkCellRenderer *renderer; @@ -142,11 +131,11 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, GtkWidget *notebook; GtkWidget *window; - signal(SIGSEGV, perf_gtk_signal); - signal(SIGFPE, perf_gtk_signal); - signal(SIGINT, perf_gtk_signal); - signal(SIGQUIT, perf_gtk_signal); - signal(SIGTERM, perf_gtk_signal); + signal(SIGSEGV, perf_gtk__signal); + signal(SIGFPE, perf_gtk__signal); + signal(SIGINT, perf_gtk__signal); + signal(SIGQUIT, perf_gtk__signal); + signal(SIGTERM, perf_gtk__signal); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); @@ -168,7 +157,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - perf_gtk_show_hists(scrolled_window, hists); + perf_gtk__show_hists(scrolled_window, hists); tab_label = gtk_label_new(evname); @@ -179,7 +168,7 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, gtk_widget_show_all(window); - perf_gtk_resize_window(window); + perf_gtk__resize_window(window); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); diff --git a/tools/perf/util/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h index 75177ee0403..75177ee0403 100644 --- a/tools/perf/util/gtk/gtk.h +++ b/tools/perf/ui/gtk/gtk.h diff --git a/tools/perf/ui/gtk/setup.c b/tools/perf/ui/gtk/setup.c new file mode 100644 index 00000000000..82952995776 --- /dev/null +++ b/tools/perf/ui/gtk/setup.c @@ -0,0 +1,12 @@ +#include "gtk.h" +#include "../../util/cache.h" + +int perf_gtk__init(void) +{ + return gtk_init_check(NULL, NULL) ? 0 : -1; +} + +void perf_gtk__exit(bool wait_for_ok __used) +{ + gtk_main_quit(); +} diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/ui/helpline.c index 2f950c2641c..2f950c2641c 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/ui/helpline.c diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/ui/helpline.h index 7bab6b34e35..7bab6b34e35 100644 --- a/tools/perf/util/ui/helpline.h +++ b/tools/perf/ui/helpline.h diff --git a/tools/perf/util/ui/keysyms.h b/tools/perf/ui/keysyms.h index 809eca5707f..809eca5707f 100644 --- a/tools/perf/util/ui/keysyms.h +++ b/tools/perf/ui/keysyms.h diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/ui/libslang.h index 4d54b6450f5..4d54b6450f5 100644 --- a/tools/perf/util/ui/libslang.h +++ b/tools/perf/ui/libslang.h diff --git a/tools/perf/util/ui/progress.c b/tools/perf/ui/progress.c index 13aa64e50e1..13aa64e50e1 100644 --- a/tools/perf/util/ui/progress.c +++ b/tools/perf/ui/progress.c diff --git a/tools/perf/util/ui/progress.h b/tools/perf/ui/progress.h index d9c205b59aa..d9c205b59aa 100644 --- a/tools/perf/util/ui/progress.h +++ b/tools/perf/ui/progress.h diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c new file mode 100644 index 00000000000..9f5f888f73e --- /dev/null +++ b/tools/perf/ui/setup.c @@ -0,0 +1,45 @@ +#include "../cache.h" +#include "../debug.h" + + +void setup_browser(bool fallback_to_pager) +{ + if (!isatty(1) || dump_trace) + use_browser = 0; + + /* default to TUI */ + if (use_browser < 0) + use_browser = 1; + + switch (use_browser) { + case 2: + if (perf_gtk__init() == 0) + break; + /* fall through */ + case 1: + use_browser = 1; + if (ui__init() == 0) + break; + /* fall through */ + default: + if (fallback_to_pager) + setup_pager(); + break; + } +} + +void exit_browser(bool wait_for_ok) +{ + switch (use_browser) { + case 2: + perf_gtk__exit(wait_for_ok); + break; + + case 1: + ui__exit(wait_for_ok); + break; + + default: + break; + } +} diff --git a/tools/perf/util/ui/setup.c b/tools/perf/ui/tui/setup.c index 85a69faa09a..d33e943ac43 100644 --- a/tools/perf/util/ui/setup.c +++ b/tools/perf/ui/tui/setup.c @@ -2,14 +2,14 @@ #include <signal.h> #include <stdbool.h> -#include "../cache.h" -#include "../debug.h" -#include "browser.h" -#include "helpline.h" -#include "ui.h" -#include "util.h" -#include "libslang.h" -#include "keysyms.h" +#include "../../util/cache.h" +#include "../../util/debug.h" +#include "../browser.h" +#include "../helpline.h" +#include "../ui.h" +#include "../util.h" +#include "../libslang.h" +#include "../keysyms.h" pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; @@ -93,45 +93,26 @@ static void newt_suspend(void *d __used) newtResume(); } -static int ui__init(void) -{ - int err = SLkp_init(); - - if (err < 0) - goto out; - - SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB); -out: - return err; -} - -static void ui__exit(void) -{ - SLtt_set_cursor_visibility(1); - SLsmg_refresh(); - SLsmg_reset_smg(); - SLang_reset_tty(); -} - static void ui__signal(int sig) { - ui__exit(); + ui__exit(false); psignal(sig, "perf"); exit(0); } -void setup_browser(bool fallback_to_pager) +int ui__init(void) { - if (!isatty(1) || !use_browser || dump_trace) { - use_browser = 0; - if (fallback_to_pager) - setup_pager(); - return; - } + int err; - use_browser = 1; newtInit(); - ui__init(); + err = SLkp_init(); + if (err < 0) { + pr_err("TUI initialization failed.\n"); + goto out; + } + + SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB); + newtSetSuspendCallback(newt_suspend, NULL); ui_helpline__init(); ui_browser__init(); @@ -141,15 +122,19 @@ void setup_browser(bool fallback_to_pager) signal(SIGINT, ui__signal); signal(SIGQUIT, ui__signal); signal(SIGTERM, ui__signal); +out: + return err; } -void exit_browser(bool wait_for_ok) +void ui__exit(bool wait_for_ok) { - if (use_browser > 0) { - if (wait_for_ok) - ui__question_window("Fatal Error", - ui_helpline__last_msg, - "Press any key...", 0); - ui__exit(); - } + if (wait_for_ok) + ui__question_window("Fatal Error", + ui_helpline__last_msg, + "Press any key...", 0); + + SLtt_set_cursor_visibility(1); + SLsmg_refresh(); + SLsmg_reset_smg(); + SLang_reset_tty(); } diff --git a/tools/perf/util/ui/ui.h b/tools/perf/ui/ui.h index 7b67045479f..7b67045479f 100644 --- a/tools/perf/util/ui/ui.h +++ b/tools/perf/ui/ui.h diff --git a/tools/perf/util/ui/util.c b/tools/perf/ui/util.c index ad4374a16bb..ad4374a16bb 100644 --- a/tools/perf/util/ui/util.c +++ b/tools/perf/ui/util.c diff --git a/tools/perf/util/ui/util.h b/tools/perf/ui/util.h index 2d1738bd71c..2d1738bd71c 100644 --- a/tools/perf/util/ui/util.h +++ b/tools/perf/ui/util.h diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 08c6d138a65..8069dfb5ba7 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -18,6 +18,403 @@ const char *disassembler_style; +static struct ins *ins__find(const char *name); +static int disasm_line__parse(char *line, char **namep, char **rawp); + +static void ins__delete(struct ins_operands *ops) +{ + free(ops->source.raw); + free(ops->source.name); + free(ops->target.raw); + free(ops->target.name); +} + +static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops) +{ + return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->raw); +} + +int ins__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops) +{ + if (ins->ops->scnprintf) + return ins->ops->scnprintf(ins, bf, size, ops); + + return ins__raw_scnprintf(ins, bf, size, ops); +} + +static int call__parse(struct ins_operands *ops) +{ + char *endptr, *tok, *name; + + ops->target.addr = strtoull(ops->raw, &endptr, 16); + + name = strchr(endptr, '<'); + if (name == NULL) + goto indirect_call; + + name++; + + tok = strchr(name, '>'); + if (tok == NULL) + return -1; + + *tok = '\0'; + ops->target.name = strdup(name); + *tok = '>'; + + return ops->target.name == NULL ? -1 : 0; + +indirect_call: + tok = strchr(endptr, '('); + if (tok != NULL) { + ops->target.addr = 0; + return 0; + } + + tok = strchr(endptr, '*'); + if (tok == NULL) + return -1; + + ops->target.addr = strtoull(tok + 1, NULL, 16); + return 0; +} + +static int call__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops) +{ + if (ops->target.name) + return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->target.name); + + if (ops->target.addr == 0) + return ins__raw_scnprintf(ins, bf, size, ops); + + return scnprintf(bf, size, "%-6.6s *%" PRIx64, ins->name, ops->target.addr); +} + +static struct ins_ops call_ops = { + .parse = call__parse, + .scnprintf = call__scnprintf, +}; + +bool ins__is_call(const struct ins *ins) +{ + return ins->ops == &call_ops; +} + +static int jump__parse(struct ins_operands *ops) +{ + const char *s = strchr(ops->raw, '+'); + + ops->target.addr = strtoll(ops->raw, NULL, 16); + + if (s++ != NULL) + ops->target.offset = strtoll(s, NULL, 16); + else + ops->target.offset = UINT64_MAX; + + return 0; +} + +static int jump__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops) +{ + return scnprintf(bf, size, "%-6.6s %" PRIx64, ins->name, ops->target.offset); +} + +static struct ins_ops jump_ops = { + .parse = jump__parse, + .scnprintf = jump__scnprintf, +}; + +bool ins__is_jump(const struct ins *ins) +{ + return ins->ops == &jump_ops; +} + +static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep) +{ + char *endptr, *name, *t; + + if (strstr(raw, "(%rip)") == NULL) + return 0; + + *addrp = strtoull(comment, &endptr, 16); + name = strchr(endptr, '<'); + if (name == NULL) + return -1; + + name++; + + t = strchr(name, '>'); + if (t == NULL) + return 0; + + *t = '\0'; + *namep = strdup(name); + *t = '>'; + + return 0; +} + +static int lock__parse(struct ins_operands *ops) +{ + char *name; + + ops->locked.ops = zalloc(sizeof(*ops->locked.ops)); + if (ops->locked.ops == NULL) + return 0; + + if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0) + goto out_free_ops; + + ops->locked.ins = ins__find(name); + if (ops->locked.ins == NULL) + goto out_free_ops; + + if (!ops->locked.ins->ops) + return 0; + + if (ops->locked.ins->ops->parse) + ops->locked.ins->ops->parse(ops->locked.ops); + + return 0; + +out_free_ops: + free(ops->locked.ops); + ops->locked.ops = NULL; + return 0; +} + +static int lock__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops) +{ + int printed; + + if (ops->locked.ins == NULL) + return ins__raw_scnprintf(ins, bf, size, ops); + + printed = scnprintf(bf, size, "%-6.6s ", ins->name); + return printed + ins__scnprintf(ops->locked.ins, bf + printed, + size - printed, ops->locked.ops); +} + +static void lock__delete(struct ins_operands *ops) +{ + free(ops->locked.ops); + free(ops->target.raw); + free(ops->target.name); +} + +static struct ins_ops lock_ops = { + .free = lock__delete, + .parse = lock__parse, + .scnprintf = lock__scnprintf, +}; + +static int mov__parse(struct ins_operands *ops) +{ + char *s = strchr(ops->raw, ','), *target, *comment, prev; + + if (s == NULL) + return -1; + + *s = '\0'; + ops->source.raw = strdup(ops->raw); + *s = ','; + + if (ops->source.raw == NULL) + return -1; + + target = ++s; + + while (s[0] != '\0' && !isspace(s[0])) + ++s; + prev = *s; + *s = '\0'; + + ops->target.raw = strdup(target); + *s = prev; + + if (ops->target.raw == NULL) + goto out_free_source; + + comment = strchr(s, '#'); + if (comment == NULL) + return 0; + + while (comment[0] != '\0' && isspace(comment[0])) + ++comment; + + comment__symbol(ops->source.raw, comment, &ops->source.addr, &ops->source.name); + comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name); + + return 0; + +out_free_source: + free(ops->source.raw); + ops->source.raw = NULL; + return -1; +} + +static int mov__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops) +{ + return scnprintf(bf, size, "%-6.6s %s,%s", ins->name, + ops->source.name ?: ops->source.raw, + ops->target.name ?: ops->target.raw); +} + +static struct ins_ops mov_ops = { + .parse = mov__parse, + .scnprintf = mov__scnprintf, +}; + +static int dec__parse(struct ins_operands *ops) +{ + char *target, *comment, *s, prev; + + target = s = ops->raw; + + while (s[0] != '\0' && !isspace(s[0])) + ++s; + prev = *s; + *s = '\0'; + + ops->target.raw = strdup(target); + *s = prev; + + if (ops->target.raw == NULL) + return -1; + + comment = strchr(s, '#'); + if (comment == NULL) + return 0; + + while (comment[0] != '\0' && isspace(comment[0])) + ++comment; + + comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name); + + return 0; +} + +static int dec__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops) +{ + return scnprintf(bf, size, "%-6.6s %s", ins->name, + ops->target.name ?: ops->target.raw); +} + +static struct ins_ops dec_ops = { + .parse = dec__parse, + .scnprintf = dec__scnprintf, +}; + +static int nop__scnprintf(struct ins *ins __used, char *bf, size_t size, + struct ins_operands *ops __used) +{ + return scnprintf(bf, size, "%-6.6s", "nop"); +} + +static struct ins_ops nop_ops = { + .scnprintf = nop__scnprintf, +}; + +/* + * Must be sorted by name! + */ +static struct ins instructions[] = { + { .name = "add", .ops = &mov_ops, }, + { .name = "addl", .ops = &mov_ops, }, + { .name = "addq", .ops = &mov_ops, }, + { .name = "addw", .ops = &mov_ops, }, + { .name = "and", .ops = &mov_ops, }, + { .name = "bts", .ops = &mov_ops, }, + { .name = "call", .ops = &call_ops, }, + { .name = "callq", .ops = &call_ops, }, + { .name = "cmp", .ops = &mov_ops, }, + { .name = "cmpb", .ops = &mov_ops, }, + { .name = "cmpl", .ops = &mov_ops, }, + { .name = "cmpq", .ops = &mov_ops, }, + { .name = "cmpw", .ops = &mov_ops, }, + { .name = "cmpxch", .ops = &mov_ops, }, + { .name = "dec", .ops = &dec_ops, }, + { .name = "decl", .ops = &dec_ops, }, + { .name = "imul", .ops = &mov_ops, }, + { .name = "inc", .ops = &dec_ops, }, + { .name = "incl", .ops = &dec_ops, }, + { .name = "ja", .ops = &jump_ops, }, + { .name = "jae", .ops = &jump_ops, }, + { .name = "jb", .ops = &jump_ops, }, + { .name = "jbe", .ops = &jump_ops, }, + { .name = "jc", .ops = &jump_ops, }, + { .name = "jcxz", .ops = &jump_ops, }, + { .name = "je", .ops = &jump_ops, }, + { .name = "jecxz", .ops = &jump_ops, }, + { .name = "jg", .ops = &jump_ops, }, + { .name = "jge", .ops = &jump_ops, }, + { .name = "jl", .ops = &jump_ops, }, + { .name = "jle", .ops = &jump_ops, }, + { .name = "jmp", .ops = &jump_ops, }, + { .name = "jmpq", .ops = &jump_ops, }, + { .name = "jna", .ops = &jump_ops, }, + { .name = "jnae", .ops = &jump_ops, }, + { .name = "jnb", .ops = &jump_ops, }, + { .name = "jnbe", .ops = &jump_ops, }, + { .name = "jnc", .ops = &jump_ops, }, + { .name = "jne", .ops = &jump_ops, }, + { .name = "jng", .ops = &jump_ops, }, + { .name = "jnge", .ops = &jump_ops, }, + { .name = "jnl", .ops = &jump_ops, }, + { .name = "jnle", .ops = &jump_ops, }, + { .name = "jno", .ops = &jump_ops, }, + { .name = "jnp", .ops = &jump_ops, }, + { .name = "jns", .ops = &jump_ops, }, + { .name = "jnz", .ops = &jump_ops, }, + { .name = "jo", .ops = &jump_ops, }, + { .name = "jp", .ops = &jump_ops, }, + { .name = "jpe", .ops = &jump_ops, }, + { .name = "jpo", .ops = &jump_ops, }, + { .name = "jrcxz", .ops = &jump_ops, }, + { .name = "js", .ops = &jump_ops, }, + { .name = "jz", .ops = &jump_ops, }, + { .name = "lea", .ops = &mov_ops, }, + { .name = "lock", .ops = &lock_ops, }, + { .name = "mov", .ops = &mov_ops, }, + { .name = "movb", .ops = &mov_ops, }, + { .name = "movdqa",.ops = &mov_ops, }, + { .name = "movl", .ops = &mov_ops, }, + { .name = "movq", .ops = &mov_ops, }, + { .name = "movslq", .ops = &mov_ops, }, + { .name = "movzbl", .ops = &mov_ops, }, + { .name = "movzwl", .ops = &mov_ops, }, + { .name = "nop", .ops = &nop_ops, }, + { .name = "nopl", .ops = &nop_ops, }, + { .name = "nopw", .ops = &nop_ops, }, + { .name = "or", .ops = &mov_ops, }, + { .name = "orl", .ops = &mov_ops, }, + { .name = "test", .ops = &mov_ops, }, + { .name = "testb", .ops = &mov_ops, }, + { .name = "testl", .ops = &mov_ops, }, + { .name = "xadd", .ops = &mov_ops, }, +}; + +static int ins__cmp(const void *name, const void *insp) +{ + const struct ins *ins = insp; + + return strcmp(name, ins->name); +} + +static struct ins *ins__find(const char *name) +{ + const int nmemb = ARRAY_SIZE(instructions); + + return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__cmp); +} + int symbol__annotate_init(struct map *map __used, struct symbol *sym) { struct annotation *notes = symbol__annotation(sym); @@ -28,7 +425,7 @@ int symbol__annotate_init(struct map *map __used, struct symbol *sym) int symbol__alloc_hist(struct symbol *sym) { struct annotation *notes = symbol__annotation(sym); - const size_t size = sym->end - sym->start + 1; + const size_t size = symbol__size(sym); size_t sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64)); notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist); @@ -78,31 +475,110 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map, return 0; } -static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize) +static void disasm_line__init_ins(struct disasm_line *dl) +{ + dl->ins = ins__find(dl->name); + + if (dl->ins == NULL) + return; + + if (!dl->ins->ops) + return; + + if (dl->ins->ops->parse) + dl->ins->ops->parse(&dl->ops); +} + +static int disasm_line__parse(char *line, char **namep, char **rawp) +{ + char *name = line, tmp; + + while (isspace(name[0])) + ++name; + + if (name[0] == '\0') + return -1; + + *rawp = name + 1; + + while ((*rawp)[0] != '\0' && !isspace((*rawp)[0])) + ++*rawp; + + tmp = (*rawp)[0]; + (*rawp)[0] = '\0'; + *namep = strdup(name); + + if (*namep == NULL) + goto out_free_name; + + (*rawp)[0] = tmp; + + if ((*rawp)[0] != '\0') { + (*rawp)++; + while (isspace((*rawp)[0])) + ++(*rawp); + } + + return 0; + +out_free_name: + free(*namep); + *namep = NULL; + return -1; +} + +static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privsize) { - struct objdump_line *self = malloc(sizeof(*self) + privsize); + struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); - if (self != NULL) { - self->offset = offset; - self->line = line; + if (dl != NULL) { + dl->offset = offset; + dl->line = strdup(line); + if (dl->line == NULL) + goto out_delete; + + if (offset != -1) { + if (disasm_line__parse(dl->line, &dl->name, &dl->ops.raw) < 0) + goto out_free_line; + + disasm_line__init_ins(dl); + } } - return self; + return dl; + +out_free_line: + free(dl->line); +out_delete: + free(dl); + return NULL; +} + +void disasm_line__free(struct disasm_line *dl) +{ + free(dl->line); + free(dl->name); + if (dl->ins && dl->ins->ops->free) + dl->ins->ops->free(&dl->ops); + else + ins__delete(&dl->ops); + free(dl); } -void objdump_line__free(struct objdump_line *self) +int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw) { - free(self->line); - free(self); + if (raw || !dl->ins) + return scnprintf(bf, size, "%-6.6s %s", dl->name, dl->ops.raw); + + return ins__scnprintf(dl->ins, bf, size, &dl->ops); } -static void objdump__add_line(struct list_head *head, struct objdump_line *line) +static void disasm__add(struct list_head *head, struct disasm_line *line) { list_add_tail(&line->node, head); } -struct objdump_line *objdump__get_next_ip_line(struct list_head *head, - struct objdump_line *pos) +struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos) { list_for_each_entry_continue(pos, head, node) if (pos->offset >= 0) @@ -111,15 +587,14 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head, return NULL; } -static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, - int evidx, u64 len, int min_pcnt, - int printed, int max_lines, - struct objdump_line *queue) +static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, + int evidx, u64 len, int min_pcnt, int printed, + int max_lines, struct disasm_line *queue) { static const char *prev_line; static const char *prev_color; - if (oline->offset != -1) { + if (dl->offset != -1) { const char *path = NULL; unsigned int hits = 0; double percent = 0.0; @@ -127,10 +602,11 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, struct annotation *notes = symbol__annotation(sym); struct source_line *src_line = notes->src->lines; struct sym_hist *h = annotation__histogram(notes, evidx); - s64 offset = oline->offset; - struct objdump_line *next; + s64 offset = dl->offset; + const u64 addr = start + offset; + struct disasm_line *next; - next = objdump__get_next_ip_line(¬es->src->source, oline); + next = disasm__get_next_ip_line(¬es->src->source, dl); while (offset < (s64)len && (next == NULL || offset < next->offset)) { @@ -155,9 +631,9 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, if (queue != NULL) { list_for_each_entry_from(queue, ¬es->src->source, node) { - if (queue == oline) + if (queue == dl) break; - objdump_line__print(queue, sym, evidx, len, + disasm_line__print(queue, sym, start, evidx, len, 0, 0, 1, NULL); } } @@ -180,17 +656,18 @@ static int objdump_line__print(struct objdump_line *oline, struct symbol *sym, color_fprintf(stdout, color, " %7.2f", percent); printf(" : "); - color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", oline->line); + color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr); + color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line); } else if (max_lines && printed >= max_lines) return 1; else { if (queue) return -1; - if (!*oline->line) + if (!*dl->line) printf(" :\n"); else - printf(" : %s\n", oline->line); + printf(" : %s\n", dl->line); } return 0; @@ -200,8 +677,8 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, FILE *file, size_t privsize) { struct annotation *notes = symbol__annotation(sym); - struct objdump_line *objdump_line; - char *line = NULL, *tmp, *tmp2, *c; + struct disasm_line *dl; + char *line = NULL, *parsed_line, *tmp, *tmp2, *c; size_t line_len; s64 line_ip, offset = -1; @@ -219,6 +696,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, *c = 0; line_ip = -1; + parsed_line = line; /* * Strip leading spaces: @@ -246,14 +724,17 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, offset = line_ip - start; if (offset < 0 || (u64)line_ip > end) offset = -1; + else + parsed_line = tmp2 + 1; } - objdump_line = objdump_line__new(offset, line, privsize); - if (objdump_line == NULL) { - free(line); + dl = disasm_line__new(offset, parsed_line, privsize); + free(line); + + if (dl == NULL) return -1; - } - objdump__add_line(¬es->src->source, objdump_line); + + disasm__add(¬es->src->source, dl); return 0; } @@ -476,7 +957,7 @@ static void symbol__annotate_hits(struct symbol *sym, int evidx) { struct annotation *notes = symbol__annotation(sym); struct sym_hist *h = annotation__histogram(notes, evidx); - u64 len = sym->end - sym->start, offset; + u64 len = symbol__size(sym), offset; for (offset = 0; offset < len; ++offset) if (h->addr[offset] != 0) @@ -492,7 +973,8 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, struct dso *dso = map->dso; const char *filename = dso->long_name, *d_filename; struct annotation *notes = symbol__annotation(sym); - struct objdump_line *pos, *queue = NULL; + struct disasm_line *pos, *queue = NULL; + u64 start = map__rip_2objdump(map, sym->start); int printed = 2, queue_len = 0; int more = 0; u64 len; @@ -502,7 +984,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, else d_filename = basename(filename); - len = sym->end - sym->start; + len = symbol__size(sym); printf(" Percent | Source code & Disassembly of %s\n", d_filename); printf("------------------------------------------------\n"); @@ -516,8 +998,9 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, queue_len = 0; } - switch (objdump_line__print(pos, sym, evidx, len, min_pcnt, - printed, max_lines, queue)) { + switch (disasm_line__print(pos, sym, start, evidx, len, + min_pcnt, printed, max_lines, + queue)) { case 0: ++printed; if (context) { @@ -561,7 +1044,7 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx) { struct annotation *notes = symbol__annotation(sym); struct sym_hist *h = annotation__histogram(notes, evidx); - int len = sym->end - sym->start, offset; + int len = symbol__size(sym), offset; h->sum = 0; for (offset = 0; offset < len; ++offset) { @@ -570,14 +1053,42 @@ void symbol__annotate_decay_histogram(struct symbol *sym, int evidx) } } -void objdump_line_list__purge(struct list_head *head) +void disasm__purge(struct list_head *head) { - struct objdump_line *pos, *n; + struct disasm_line *pos, *n; list_for_each_entry_safe(pos, n, head, node) { list_del(&pos->node); - objdump_line__free(pos); + disasm_line__free(pos); + } +} + +static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp) +{ + size_t printed; + + if (dl->offset == -1) + return fprintf(fp, "%s\n", dl->line); + + printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->name); + + if (dl->ops.raw[0] != '\0') { + printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ", + dl->ops.raw); } + + return printed + fprintf(fp, "\n"); +} + +size_t disasm__fprintf(struct list_head *head, FILE *fp) +{ + struct disasm_line *pos; + size_t printed = 0; + + list_for_each_entry(pos, head, node) + printed += disasm_line__fprintf(pos, fp); + + return printed; } int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, @@ -592,7 +1103,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, if (symbol__annotate(sym, map, 0) < 0) return -1; - len = sym->end - sym->start; + len = symbol__size(sym); if (print_lines) { symbol__get_source_line(sym, map, evidx, &source_line, @@ -605,7 +1116,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, if (print_lines) symbol__free_source_line(sym, len); - objdump_line_list__purge(&symbol__annotation(sym)->src->source); + disasm__purge(&symbol__annotation(sym)->src->source); return 0; } diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index efa5dc82bfa..78a5692dd71 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -2,20 +2,69 @@ #define __PERF_ANNOTATE_H #include <stdbool.h> +#include <stdint.h> #include "types.h" #include "symbol.h" #include <linux/list.h> #include <linux/rbtree.h> -struct objdump_line { - struct list_head node; - s64 offset; - char *line; +struct ins; + +struct ins_operands { + char *raw; + struct { + char *raw; + char *name; + u64 addr; + u64 offset; + } target; + union { + struct { + char *raw; + char *name; + u64 addr; + } source; + struct { + struct ins *ins; + struct ins_operands *ops; + } locked; + }; }; -void objdump_line__free(struct objdump_line *self); -struct objdump_line *objdump__get_next_ip_line(struct list_head *head, - struct objdump_line *pos); +struct ins_ops { + void (*free)(struct ins_operands *ops); + int (*parse)(struct ins_operands *ops); + int (*scnprintf)(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops); +}; + +struct ins { + const char *name; + struct ins_ops *ops; +}; + +bool ins__is_jump(const struct ins *ins); +bool ins__is_call(const struct ins *ins); +int ins__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops); + +struct disasm_line { + struct list_head node; + s64 offset; + char *line; + char *name; + struct ins *ins; + struct ins_operands ops; +}; + +static inline bool disasm_line__has_offset(const struct disasm_line *dl) +{ + return dl->ops.target.offset != UINT64_MAX; +} + +void disasm_line__free(struct disasm_line *dl); +struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos); +int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw); +size_t disasm__fprintf(struct list_head *head, FILE *fp); struct sym_hist { u64 sum; @@ -32,7 +81,7 @@ struct source_line { * * @histogram: Array of addr hit histograms per event being monitored * @lines: If 'print_lines' is specified, per source code line percentages - * @source: source parsed from objdump -dS + * @source: source parsed from a disassembler like objdump -dS * * lines is allocated, percentages calculated and all sorted by percentage * when the annotation is about to be presented, so the percentages are for @@ -82,7 +131,7 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx, int context); void symbol__annotate_zero_histogram(struct symbol *sym, int evidx); void symbol__annotate_decay_histogram(struct symbol *sym, int evidx); -void objdump_line_list__purge(struct list_head *head); +void disasm__purge(struct list_head *head); int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, bool print_lines, bool full_paths, int min_pcnt, diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index dff9c7a725f..fd9a5944b62 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -65,6 +65,8 @@ struct perf_tool build_id__mark_dso_hit_ops = { .mmap = perf_event__process_mmap, .fork = perf_event__process_task, .exit = perf_event__exit_del_thread, + .attr = perf_event__process_attr, + .build_id = perf_event__process_build_id, }; char *dso__build_id_filename(struct dso *self, char *bf, size_t size) diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 8dd224df3e5..cff18c617d1 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -33,7 +33,7 @@ extern int pager_use_color; extern int use_browser; -#ifdef NO_NEWT_SUPPORT +#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT) static inline void setup_browser(bool fallback_to_pager) { if (fallback_to_pager) @@ -43,19 +43,29 @@ static inline void exit_browser(bool wait_for_ok __used) {} #else void setup_browser(bool fallback_to_pager); void exit_browser(bool wait_for_ok); + +#ifdef NO_NEWT_SUPPORT +static inline int ui__init(void) +{ + return -1; +} +static inline void ui__exit(bool wait_for_ok __used) {} +#else +int ui__init(void); +void ui__exit(bool wait_for_ok); #endif #ifdef NO_GTK2_SUPPORT -static inline void perf_gtk_setup_browser(int argc __used, const char *argv[] __used, bool fallback_to_pager) +static inline int perf_gtk__init(void) { - if (fallback_to_pager) - setup_pager(); + return -1; } -static inline void perf_gtk_exit_browser(bool wait_for_ok __used) {} +static inline void perf_gtk__exit(bool wait_for_ok __used) {} #else -void perf_gtk_setup_browser(int argc, const char *argv[], bool fallback_to_pager); -void perf_gtk_exit_browser(bool wait_for_ok); +int perf_gtk__init(void); +void perf_gtk__exit(bool wait_for_ok); #endif +#endif /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */ char *alias_lookup(const char *alias); int split_cmdline(char *cmdline, const char ***argv); diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 26817daa296..efb1fce259a 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -11,6 +11,7 @@ #include "event.h" #include "debug.h" #include "util.h" +#include "target.h" int verbose; bool dump_trace = false, quiet = false; diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index f2ce88d04f5..6bebe7f0a20 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -26,7 +26,7 @@ static inline void ui_progress__update(u64 curr __used, u64 total __used, #else extern char ui_helpline__last_msg[]; int ui_helpline__show_help(const char *format, va_list ap); -#include "ui/progress.h" +#include "../ui/progress.h" int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); #endif diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 1986d8051bd..4ac5f5ae4ce 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -11,6 +11,7 @@ #include <poll.h> #include "cpumap.h" #include "thread_map.h" +#include "target.h" #include "evlist.h" #include "evsel.h" #include <unistd.h> @@ -599,18 +600,21 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, return perf_evlist__mmap_per_cpu(evlist, prot, mask); } -int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid, - const char *target_tid, uid_t uid, const char *cpu_list) +int perf_evlist__create_maps(struct perf_evlist *evlist, + struct perf_target *target) { - evlist->threads = thread_map__new_str(target_pid, target_tid, uid); + evlist->threads = thread_map__new_str(target->pid, target->tid, + target->uid); if (evlist->threads == NULL) return -1; - if (uid != UINT_MAX || (cpu_list == NULL && target_tid)) + if (perf_target__has_task(target)) + evlist->cpus = cpu_map__dummy_new(); + else if (!perf_target__has_cpu(target) && !target->uses_mmap) evlist->cpus = cpu_map__dummy_new(); else - evlist->cpus = cpu_map__new(cpu_list); + evlist->cpus = cpu_map__new(target->cpu_list); if (evlist->cpus == NULL) goto out_delete_threads; @@ -827,7 +831,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, exit(-1); } - if (!opts->system_wide && !opts->target_tid && !opts->target_pid) + if (perf_target__none(&opts->target)) evlist->threads->map[0] = evlist->workload.pid; close(child_ready_pipe[1]); diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 21f1c9e57f1..58abb63ac13 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -106,8 +106,8 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist, evlist->threads = threads; } -int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid, - const char *tid, uid_t uid, const char *cpu_list); +int perf_evlist__create_maps(struct perf_evlist *evlist, + struct perf_target *target); void perf_evlist__delete_maps(struct perf_evlist *evlist); int perf_evlist__set_filters(struct perf_evlist *evlist); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 8c13dbcb84b..57e4ce57bbc 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -14,6 +14,7 @@ #include "util.h" #include "cpumap.h" #include "thread_map.h" +#include "target.h" #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) #define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0)) @@ -69,6 +70,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, struct perf_event_attr *attr = &evsel->attr; int track = !evsel->idx; /* only the first counter needs these */ + attr->disabled = 1; attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; attr->inherit = !opts->no_inherit; attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | @@ -106,15 +108,15 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, if (opts->call_graph) attr->sample_type |= PERF_SAMPLE_CALLCHAIN; - if (opts->system_wide) + if (perf_target__has_cpu(&opts->target)) attr->sample_type |= PERF_SAMPLE_CPU; if (opts->period) attr->sample_type |= PERF_SAMPLE_PERIOD; if (!opts->sample_id_all_missing && - (opts->sample_time || opts->system_wide || - !opts->no_inherit || opts->cpu_list)) + (opts->sample_time || !opts->no_inherit || + perf_target__has_cpu(&opts->target))) attr->sample_type |= PERF_SAMPLE_TIME; if (opts->raw_samples) { @@ -135,9 +137,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, attr->mmap = track; attr->comm = track; - if (!opts->target_pid && !opts->target_tid && !opts->system_wide && + if (perf_target__none(&opts->target) && (!opts->group || evsel == first)) { - attr->disabled = 1; attr->enable_on_exec = 1; } } @@ -461,10 +462,7 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, * used for cross-endian analysis. See git commit 65014ab3 * for why this goofiness is needed. */ - union { - u64 val64; - u32 val32[2]; - } u; + union u64_swap u; memset(data, 0, sizeof(*data)); data->cpu = data->pid = data->tid = -1; @@ -607,10 +605,7 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, * used for cross-endian analysis. See git commit 65014ab3 * for why this goofiness is needed. */ - union { - u64 val64; - u32 val32[2]; - } u; + union u64_swap u; array = event->sample.array; diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 4c7c2d73251..2dd5edf161b 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -31,21 +31,16 @@ static const char **header_argv; int perf_header__push_event(u64 id, const char *name) { + struct perf_trace_event_type *nevents; + if (strlen(name) > MAX_EVENT_NAME) pr_warning("Event %s will be truncated\n", name); - if (!events) { - events = malloc(sizeof(struct perf_trace_event_type)); - if (events == NULL) - return -ENOMEM; - } else { - struct perf_trace_event_type *nevents; + nevents = realloc(events, (event_count + 1) * sizeof(*events)); + if (nevents == NULL) + return -ENOMEM; + events = nevents; - nevents = realloc(events, (event_count + 1) * sizeof(*events)); - if (nevents == NULL) - return -ENOMEM; - events = nevents; - } memset(&events[event_count], 0, sizeof(struct perf_trace_event_type)); events[event_count].event_id = id; strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1); @@ -296,7 +291,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, if (mkdir_p(filename, 0755)) goto out_free; - snprintf(filename + len, sizeof(filename) - len, "/%s", sbuild_id); + snprintf(filename + len, size - len, "/%s", sbuild_id); if (access(filename, F_OK)) { if (is_kallsyms) { @@ -442,7 +437,7 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with return ret; } -static int write_trace_info(int fd, struct perf_header *h __used, +static int write_tracing_data(int fd, struct perf_header *h __used, struct perf_evlist *evlist) { return read_tracing_data(fd, &evlist->entries); @@ -1477,7 +1472,7 @@ out: return err; } -static int process_trace_info(struct perf_file_section *section __unused, +static int process_tracing_data(struct perf_file_section *section __unused, struct perf_header *ph __unused, int feat __unused, int fd) { @@ -1513,11 +1508,11 @@ struct feature_ops { .full_only = true } /* feature_ops not implemented: */ -#define print_trace_info NULL -#define print_build_id NULL +#define print_tracing_data NULL +#define print_build_id NULL static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { - FEAT_OPP(HEADER_TRACE_INFO, trace_info), + FEAT_OPP(HEADER_TRACING_DATA, tracing_data), FEAT_OPP(HEADER_BUILD_ID, build_id), FEAT_OPA(HEADER_HOSTNAME, hostname), FEAT_OPA(HEADER_OSRELEASE, osrelease), diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 21a6be09c12..2d42b3e1826 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -12,7 +12,7 @@ enum { HEADER_RESERVED = 0, /* always cleared */ HEADER_FIRST_FEATURE = 1, - HEADER_TRACE_INFO = 1, + HEADER_TRACING_DATA = 1, HEADER_BUILD_ID, HEADER_HOSTNAME, diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 9f6d630d531..1293b5ebea4 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -599,7 +599,7 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, if (chain->ms.sym) ret += fprintf(fp, "%s\n", chain->ms.sym->name); else - ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); + ret += fprintf(fp, "0x%0" PRIx64 "\n", chain->ip); return ret; } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 2cae9df40e0..cfc64e293f9 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -138,7 +138,7 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self __used, #define K_LEFT -1 #define K_RIGHT -2 #else -#include "ui/keysyms.h" +#include "../ui/keysyms.h" int hist_entry__tui_annotate(struct hist_entry *he, int evidx, void(*timer)(void *arg), void *arg, int delay_secs); diff --git a/tools/perf/util/parse-events-test.c b/tools/perf/util/parse-events-test.c new file mode 100644 index 00000000000..76b98e2a587 --- /dev/null +++ b/tools/perf/util/parse-events-test.c @@ -0,0 +1,625 @@ + +#include "parse-events.h" +#include "evsel.h" +#include "evlist.h" +#include "sysfs.h" +#include "../../../include/linux/hw_breakpoint.h" + +#define TEST_ASSERT_VAL(text, cond) \ +do { \ + if (!(cond)) { \ + pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ + return -1; \ + } \ +} while (0) + +static int test__checkevent_tracepoint(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); + TEST_ASSERT_VAL("wrong sample_type", + (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) == + evsel->attr.sample_type); + TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period); + return 0; +} + +static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + + TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); + + list_for_each_entry(evsel, &evlist->entries, node) { + TEST_ASSERT_VAL("wrong type", + PERF_TYPE_TRACEPOINT == evsel->attr.type); + TEST_ASSERT_VAL("wrong sample_type", + (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) + == evsel->attr.sample_type); + TEST_ASSERT_VAL("wrong sample_period", + 1 == evsel->attr.sample_period); + } + return 0; +} + +static int test__checkevent_raw(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", 0x1a == evsel->attr.config); + return 0; +} + +static int test__checkevent_numeric(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); + return 0; +} + +static int test__checkevent_symbolic_name(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", + PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config); + return 0; +} + +static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", + PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); + TEST_ASSERT_VAL("wrong period", + 100000 == evsel->attr.sample_period); + TEST_ASSERT_VAL("wrong config1", + 0 == evsel->attr.config1); + TEST_ASSERT_VAL("wrong config2", + 1 == evsel->attr.config2); + return 0; +} + +static int test__checkevent_symbolic_alias(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", + PERF_COUNT_SW_PAGE_FAULTS == evsel->attr.config); + return 0; +} + +static int test__checkevent_genhw(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", (1 << 16) == evsel->attr.config); + return 0; +} + +static int test__checkevent_breakpoint(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); + TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R | HW_BREAKPOINT_W) == + evsel->attr.bp_type); + TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_4 == + evsel->attr.bp_len); + return 0; +} + +static int test__checkevent_breakpoint_x(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); + TEST_ASSERT_VAL("wrong bp_type", + HW_BREAKPOINT_X == evsel->attr.bp_type); + TEST_ASSERT_VAL("wrong bp_len", sizeof(long) == evsel->attr.bp_len); + return 0; +} + +static int test__checkevent_breakpoint_r(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", + PERF_TYPE_BREAKPOINT == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); + TEST_ASSERT_VAL("wrong bp_type", + HW_BREAKPOINT_R == evsel->attr.bp_type); + TEST_ASSERT_VAL("wrong bp_len", + HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len); + return 0; +} + +static int test__checkevent_breakpoint_w(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", + PERF_TYPE_BREAKPOINT == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config); + TEST_ASSERT_VAL("wrong bp_type", + HW_BREAKPOINT_W == evsel->attr.bp_type); + TEST_ASSERT_VAL("wrong bp_len", + HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len); + return 0; +} + +static int test__checkevent_tracepoint_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); + + return test__checkevent_tracepoint(evlist); +} + +static int +test__checkevent_tracepoint_multi_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + + TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); + + list_for_each_entry(evsel, &evlist->entries, node) { + TEST_ASSERT_VAL("wrong exclude_user", + !evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", + evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); + } + + return test__checkevent_tracepoint_multi(evlist); +} + +static int test__checkevent_raw_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); + + return test__checkevent_raw(evlist); +} + +static int test__checkevent_numeric_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); + + return test__checkevent_numeric(evlist); +} + +static int test__checkevent_symbolic_name_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); + + return test__checkevent_symbolic_name(evlist); +} + +static int test__checkevent_exclude_host_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); + + return test__checkevent_symbolic_name(evlist); +} + +static int test__checkevent_exclude_guest_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); + TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); + + return test__checkevent_symbolic_name(evlist); +} + +static int test__checkevent_symbolic_alias_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); + + return test__checkevent_symbolic_alias(evlist); +} + +static int test__checkevent_genhw_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); + + return test__checkevent_genhw(evlist); +} + +static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); + + return test__checkevent_breakpoint(evlist); +} + +static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); + + return test__checkevent_breakpoint_x(evlist); +} + +static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); + + return test__checkevent_breakpoint_r(evlist); +} + +static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); + + return test__checkevent_breakpoint_w(evlist); +} + +static int test__checkevent_pmu(struct perf_evlist *evlist) +{ + + struct perf_evsel *evsel = list_entry(evlist->entries.next, + struct perf_evsel, node); + + TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", 10 == evsel->attr.config); + TEST_ASSERT_VAL("wrong config1", 1 == evsel->attr.config1); + TEST_ASSERT_VAL("wrong config2", 3 == evsel->attr.config2); + TEST_ASSERT_VAL("wrong period", 1000 == evsel->attr.sample_period); + + return 0; +} + +static int test__checkevent_list(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + + TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); + + /* r1 */ + evsel = list_entry(evlist->entries.next, struct perf_evsel, node); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); + TEST_ASSERT_VAL("wrong config1", 0 == evsel->attr.config1); + TEST_ASSERT_VAL("wrong config2", 0 == evsel->attr.config2); + TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); + + /* syscalls:sys_enter_open:k */ + evsel = list_entry(evsel->node.next, struct perf_evsel, node); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); + TEST_ASSERT_VAL("wrong sample_type", + (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) == + evsel->attr.sample_type); + TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period); + TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); + + /* 1:1:hp */ + evsel = list_entry(evsel->node.next, struct perf_evsel, node); + TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); + TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); + TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); + TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); + TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); + + return 0; +} + +static int test__checkevent_pmu_name(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + + /* cpu/config=1,name=krava1/u */ + evsel = list_entry(evlist->entries.next, struct perf_evsel, node); + TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); + TEST_ASSERT_VAL("wrong name", !strcmp(evsel->name, "krava")); + + /* cpu/config=2/" */ + evsel = list_entry(evsel->node.next, struct perf_evsel, node); + TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); + TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); + TEST_ASSERT_VAL("wrong config", 2 == evsel->attr.config); + TEST_ASSERT_VAL("wrong name", !strcmp(evsel->name, "raw 0x2")); + + return 0; +} + +struct test__event_st { + const char *name; + __u32 type; + int (*check)(struct perf_evlist *evlist); +}; + +static struct test__event_st test__events[] = { + [0] = { + .name = "syscalls:sys_enter_open", + .check = test__checkevent_tracepoint, + }, + [1] = { + .name = "syscalls:*", + .check = test__checkevent_tracepoint_multi, + }, + [2] = { + .name = "r1a", + .check = test__checkevent_raw, + }, + [3] = { + .name = "1:1", + .check = test__checkevent_numeric, + }, + [4] = { + .name = "instructions", + .check = test__checkevent_symbolic_name, + }, + [5] = { + .name = "cycles/period=100000,config2/", + .check = test__checkevent_symbolic_name_config, + }, + [6] = { + .name = "faults", + .check = test__checkevent_symbolic_alias, + }, + [7] = { + .name = "L1-dcache-load-miss", + .check = test__checkevent_genhw, + }, + [8] = { + .name = "mem:0", + .check = test__checkevent_breakpoint, + }, + [9] = { + .name = "mem:0:x", + .check = test__checkevent_breakpoint_x, + }, + [10] = { + .name = "mem:0:r", + .check = test__checkevent_breakpoint_r, + }, + [11] = { + .name = "mem:0:w", + .check = test__checkevent_breakpoint_w, + }, + [12] = { + .name = "syscalls:sys_enter_open:k", + .check = test__checkevent_tracepoint_modifier, + }, + [13] = { + .name = "syscalls:*:u", + .check = test__checkevent_tracepoint_multi_modifier, + }, + [14] = { + .name = "r1a:kp", + .check = test__checkevent_raw_modifier, + }, + [15] = { + .name = "1:1:hp", + .check = test__checkevent_numeric_modifier, + }, + [16] = { + .name = "instructions:h", + .check = test__checkevent_symbolic_name_modifier, + }, + [17] = { + .name = "faults:u", + .check = test__checkevent_symbolic_alias_modifier, + }, + [18] = { + .name = "L1-dcache-load-miss:kp", + .check = test__checkevent_genhw_modifier, + }, + [19] = { + .name = "mem:0:u", + .check = test__checkevent_breakpoint_modifier, + }, + [20] = { + .name = "mem:0:x:k", + .check = test__checkevent_breakpoint_x_modifier, + }, + [21] = { + .name = "mem:0:r:hp", + .check = test__checkevent_breakpoint_r_modifier, + }, + [22] = { + .name = "mem:0:w:up", + .check = test__checkevent_breakpoint_w_modifier, + }, + [23] = { + .name = "r1,syscalls:sys_enter_open:k,1:1:hp", + .check = test__checkevent_list, + }, + [24] = { + .name = "instructions:G", + .check = test__checkevent_exclude_host_modifier, + }, + [25] = { + .name = "instructions:H", + .check = test__checkevent_exclude_guest_modifier, + }, +}; + +#define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st)) + +static struct test__event_st test__events_pmu[] = { + [0] = { + .name = "cpu/config=10,config1,config2=3,period=1000/u", + .check = test__checkevent_pmu, + }, + [1] = { + .name = "cpu/config=1,name=krava/u,cpu/config=2/u", + .check = test__checkevent_pmu_name, + }, +}; + +#define TEST__EVENTS_PMU_CNT (sizeof(test__events_pmu) / \ + sizeof(struct test__event_st)) + +static int test(struct test__event_st *e) +{ + struct perf_evlist *evlist; + int ret; + + evlist = perf_evlist__new(NULL, NULL); + if (evlist == NULL) + return -ENOMEM; + + ret = parse_events(evlist, e->name, 0); + if (ret) { + pr_debug("failed to parse event '%s', err %d\n", + e->name, ret); + return ret; + } + + ret = e->check(evlist); + perf_evlist__delete(evlist); + + return ret; +} + +static int test_events(struct test__event_st *events, unsigned cnt) +{ + int ret = 0; + unsigned i; + + for (i = 0; i < cnt; i++) { + struct test__event_st *e = &events[i]; + + pr_debug("running test %d '%s'\n", i, e->name); + ret = test(e); + if (ret) + break; + } + + return ret; +} + +static int test_pmu(void) +{ + struct stat st; + char path[PATH_MAX]; + int ret; + + snprintf(path, PATH_MAX, "%s/bus/event_source/devices/cpu/format/", + sysfs_find_mountpoint()); + + ret = stat(path, &st); + if (ret) + pr_debug("ommiting PMU cpu tests\n"); + return !ret; +} + +int parse_events__test(void) +{ + int ret; + + ret = test_events(test__events, TEST__EVENTS_CNT); + if (!ret && test_pmu()) + ret = test_events(test__events_pmu, TEST__EVENTS_PMU_CNT); + + return ret; +} diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 5b3a0ef4e23..fac7d59309b 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -23,8 +23,10 @@ struct event_symbol { const char *alias; }; -int parse_events_parse(struct list_head *list, struct list_head *list_tmp, - int *idx); +#ifdef PARSER_DEBUG +extern int parse_events_debug; +#endif +int parse_events_parse(struct list_head *list, int *idx); #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x @@ -355,20 +357,30 @@ const char *__event_name(int type, u64 config) return "unknown"; } -static int add_event(struct list_head *list, int *idx, +static int add_event(struct list_head **_list, int *idx, struct perf_event_attr *attr, char *name) { struct perf_evsel *evsel; + struct list_head *list = *_list; + + if (!list) { + list = malloc(sizeof(*list)); + if (!list) + return -ENOMEM; + INIT_LIST_HEAD(list); + } event_attr_init(attr); evsel = perf_evsel__new(attr, (*idx)++); - if (!evsel) + if (!evsel) { + free(list); return -ENOMEM; - - list_add_tail(&evsel->node, list); + } evsel->name = strdup(name); + list_add_tail(&evsel->node, list); + *_list = list; return 0; } @@ -390,7 +402,7 @@ static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size) return -1; } -int parse_events_add_cache(struct list_head *list, int *idx, +int parse_events_add_cache(struct list_head **list, int *idx, char *type, char *op_result1, char *op_result2) { struct perf_event_attr attr; @@ -451,7 +463,7 @@ int parse_events_add_cache(struct list_head *list, int *idx, return add_event(list, idx, &attr, name); } -static int add_tracepoint(struct list_head *list, int *idx, +static int add_tracepoint(struct list_head **list, int *idx, char *sys_name, char *evt_name) { struct perf_event_attr attr; @@ -488,7 +500,7 @@ static int add_tracepoint(struct list_head *list, int *idx, return add_event(list, idx, &attr, name); } -static int add_tracepoint_multi(struct list_head *list, int *idx, +static int add_tracepoint_multi(struct list_head **list, int *idx, char *sys_name, char *evt_name) { char evt_path[MAXPATHLEN]; @@ -519,7 +531,7 @@ static int add_tracepoint_multi(struct list_head *list, int *idx, return ret; } -int parse_events_add_tracepoint(struct list_head *list, int *idx, +int parse_events_add_tracepoint(struct list_head **list, int *idx, char *sys, char *event) { int ret; @@ -563,7 +575,7 @@ parse_breakpoint_type(const char *type, struct perf_event_attr *attr) return 0; } -int parse_events_add_breakpoint(struct list_head *list, int *idx, +int parse_events_add_breakpoint(struct list_head **list, int *idx, void *ptr, char *type) { struct perf_event_attr attr; @@ -593,17 +605,27 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx, static int config_term(struct perf_event_attr *attr, struct parse_events__term *term) { - switch (term->type) { +#define CHECK_TYPE_VAL(type) \ +do { \ + if (PARSE_EVENTS__TERM_TYPE_ ## type != term->type_val) \ + return -EINVAL; \ +} while (0) + + switch (term->type_term) { case PARSE_EVENTS__TERM_TYPE_CONFIG: + CHECK_TYPE_VAL(NUM); attr->config = term->val.num; break; case PARSE_EVENTS__TERM_TYPE_CONFIG1: + CHECK_TYPE_VAL(NUM); attr->config1 = term->val.num; break; case PARSE_EVENTS__TERM_TYPE_CONFIG2: + CHECK_TYPE_VAL(NUM); attr->config2 = term->val.num; break; case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: + CHECK_TYPE_VAL(NUM); attr->sample_period = term->val.num; break; case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: @@ -612,10 +634,15 @@ static int config_term(struct perf_event_attr *attr, * attr->branch_sample_type = term->val.num; */ break; + case PARSE_EVENTS__TERM_TYPE_NAME: + CHECK_TYPE_VAL(STR); + break; default: return -EINVAL; } + return 0; +#undef CHECK_TYPE_VAL } static int config_attr(struct perf_event_attr *attr, @@ -630,7 +657,7 @@ static int config_attr(struct perf_event_attr *attr, return 0; } -int parse_events_add_numeric(struct list_head *list, int *idx, +int parse_events_add_numeric(struct list_head **list, int *idx, unsigned long type, unsigned long config, struct list_head *head_config) { @@ -648,7 +675,24 @@ int parse_events_add_numeric(struct list_head *list, int *idx, (char *) __event_name(type, config)); } -int parse_events_add_pmu(struct list_head *list, int *idx, +static int parse_events__is_name_term(struct parse_events__term *term) +{ + return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME; +} + +static char *pmu_event_name(struct perf_event_attr *attr, + struct list_head *head_terms) +{ + struct parse_events__term *term; + + list_for_each_entry(term, head_terms, list) + if (parse_events__is_name_term(term)) + return term->val.str; + + return (char *) __event_name(PERF_TYPE_RAW, attr->config); +} + +int parse_events_add_pmu(struct list_head **list, int *idx, char *name, struct list_head *head_config) { struct perf_event_attr attr; @@ -669,7 +713,8 @@ int parse_events_add_pmu(struct list_head *list, int *idx, if (perf_pmu__config(pmu, &attr, head_config)) return -EINVAL; - return add_event(list, idx, &attr, (char *) "pmu"); + return add_event(list, idx, &attr, + pmu_event_name(&attr, head_config)); } void parse_events_update_lists(struct list_head *list_event, @@ -681,7 +726,7 @@ void parse_events_update_lists(struct list_head *list_event, * list, for next event definition. */ list_splice_tail(list_event, list_all); - INIT_LIST_HEAD(list_event); + free(list_event); } int parse_events_modifier(struct list_head *list, char *str) @@ -756,10 +801,14 @@ int parse_events(struct perf_evlist *evlist, const char *str, int unset __used) buffer = parse_events__scan_string(str); - ret = parse_events_parse(&list, &list_tmp, &idx); +#ifdef PARSER_DEBUG + parse_events_debug = 1; +#endif + ret = parse_events_parse(&list, &idx); parse_events__flush_buffer(buffer); parse_events__delete_buffer(buffer); + parse_events_lex_destroy(); if (!ret) { int entries = idx - evlist->nr_entries; @@ -1015,11 +1064,12 @@ void print_events(const char *event_glob) int parse_events__is_hardcoded_term(struct parse_events__term *term) { - return term->type <= PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX; + return term->type_term != PARSE_EVENTS__TERM_TYPE_USER; } -int parse_events__new_term(struct parse_events__term **_term, int type, - char *config, char *str, long num) +static int new_term(struct parse_events__term **_term, int type_val, + int type_term, char *config, + char *str, long num) { struct parse_events__term *term; @@ -1028,15 +1078,11 @@ int parse_events__new_term(struct parse_events__term **_term, int type, return -ENOMEM; INIT_LIST_HEAD(&term->list); - term->type = type; + term->type_val = type_val; + term->type_term = type_term; term->config = config; - switch (type) { - case PARSE_EVENTS__TERM_TYPE_CONFIG: - case PARSE_EVENTS__TERM_TYPE_CONFIG1: - case PARSE_EVENTS__TERM_TYPE_CONFIG2: - case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: - case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: + switch (type_val) { case PARSE_EVENTS__TERM_TYPE_NUM: term->val.num = num; break; @@ -1051,6 +1097,20 @@ int parse_events__new_term(struct parse_events__term **_term, int type, return 0; } +int parse_events__term_num(struct parse_events__term **term, + int type_term, char *config, long num) +{ + return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, + config, NULL, num); +} + +int parse_events__term_str(struct parse_events__term **term, + int type_term, char *config, char *str) +{ + return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term, + config, str, 0); +} + void parse_events__free_terms(struct list_head *terms) { struct parse_events__term *term, *h; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index ca069f89338..8cac57ab4ee 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -4,7 +4,11 @@ * Parse symbolic events/counts passed in as options: */ +#include <linux/list.h> +#include <stdbool.h> +#include "types.h" #include "../../../include/linux/perf_event.h" +#include "types.h" struct list_head; struct perf_evsel; @@ -34,16 +38,18 @@ extern int parse_filter(const struct option *opt, const char *str, int unset); #define EVENTS_HELP_MAX (128*1024) enum { + PARSE_EVENTS__TERM_TYPE_NUM, + PARSE_EVENTS__TERM_TYPE_STR, +}; + +enum { + PARSE_EVENTS__TERM_TYPE_USER, PARSE_EVENTS__TERM_TYPE_CONFIG, PARSE_EVENTS__TERM_TYPE_CONFIG1, PARSE_EVENTS__TERM_TYPE_CONFIG2, + PARSE_EVENTS__TERM_TYPE_NAME, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, - PARSE_EVENTS__TERM_TYPE_NUM, - PARSE_EVENTS__TERM_TYPE_STR, - - PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX = - PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, }; struct parse_events__term { @@ -52,35 +58,34 @@ struct parse_events__term { char *str; long num; } val; - int type; - + int type_val; + int type_term; struct list_head list; }; int parse_events__is_hardcoded_term(struct parse_events__term *term); -int parse_events__new_term(struct parse_events__term **term, int type, - char *config, char *str, long num); +int parse_events__term_num(struct parse_events__term **_term, + int type_term, char *config, long num); +int parse_events__term_str(struct parse_events__term **_term, + int type_term, char *config, char *str); void parse_events__free_terms(struct list_head *terms); -int parse_events_modifier(struct list_head *list __used, char *str __used); -int parse_events_add_tracepoint(struct list_head *list, int *idx, +int parse_events_modifier(struct list_head *list, char *str); +int parse_events_add_tracepoint(struct list_head **list, int *idx, char *sys, char *event); -int parse_events_add_raw(struct perf_evlist *evlist, unsigned long config, - unsigned long config1, unsigned long config2, - char *mod); -int parse_events_add_numeric(struct list_head *list, int *idx, +int parse_events_add_numeric(struct list_head **list, int *idx, unsigned long type, unsigned long config, struct list_head *head_config); -int parse_events_add_cache(struct list_head *list, int *idx, +int parse_events_add_cache(struct list_head **list, int *idx, char *type, char *op_result1, char *op_result2); -int parse_events_add_breakpoint(struct list_head *list, int *idx, +int parse_events_add_breakpoint(struct list_head **list, int *idx, void *ptr, char *type); -int parse_events_add_pmu(struct list_head *list, int *idx, +int parse_events_add_pmu(struct list_head **list, int *idx, char *pmu , struct list_head *head_config); void parse_events_update_lists(struct list_head *list_event, struct list_head *list_all); void parse_events_error(struct list_head *list_all, - struct list_head *list_event, int *idx, char const *msg); +int parse_events__test(void); void print_events(const char *event_glob); void print_events_type(u8 type); diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 1fcf1bbc545..618a8e78839 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -1,5 +1,6 @@ %option prefix="parse_events_" +%option stack %{ #include <errno.h> @@ -50,6 +51,8 @@ static int term(int type) %} +%x mem + num_dec [0-9]+ num_hex 0x[a-fA-F0-9]+ num_raw_hex [a-fA-F0-9]+ @@ -102,16 +105,16 @@ misses|miss { return str(PE_NAME_CACHE_OP_RESULT); } config { return term(PARSE_EVENTS__TERM_TYPE_CONFIG); } config1 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG1); } config2 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG2); } +name { return term(PARSE_EVENTS__TERM_TYPE_NAME); } period { return term(PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } branch_type { return term(PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } -mem: { return PE_PREFIX_MEM; } +mem: { BEGIN(mem); return PE_PREFIX_MEM; } r{num_raw_hex} { return raw(); } {num_dec} { return value(10); } {num_hex} { return value(16); } {modifier_event} { return str(PE_MODIFIER_EVENT); } -{modifier_bp} { return str(PE_MODIFIER_BP); } {name} { return str(PE_NAME); } "/" { return '/'; } - { return '-'; } @@ -119,6 +122,25 @@ r{num_raw_hex} { return raw(); } : { return ':'; } = { return '='; } +<mem>{ +{modifier_bp} { return str(PE_MODIFIER_BP); } +: { return ':'; } +{num_dec} { return value(10); } +{num_hex} { return value(16); } + /* + * We need to separate 'mem:' scanner part, in order to get specific + * modifier bits parsed out. Otherwise we would need to handle PE_NAME + * and we'd need to parse it manually. During the escape from <mem> + * state we need to put the escaping char back, so we dont miss it. + */ +. { unput(*parse_events_text); BEGIN(INITIAL); } + /* + * We destroy the scanner after reaching EOF, + * but anyway just to be sure get back to INIT state. + */ +<<EOF>> { BEGIN(INITIAL); } +} + %% int parse_events_wrap(void) diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index d9637da7333..362cc59332a 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -1,7 +1,6 @@ %name-prefix "parse_events_" %parse-param {struct list_head *list_all} -%parse-param {struct list_head *list_event} %parse-param {int *idx} %{ @@ -41,6 +40,14 @@ do { \ %type <str> PE_MODIFIER_BP %type <head> event_config %type <term> event_term +%type <head> event_pmu +%type <head> event_legacy_symbol +%type <head> event_legacy_cache +%type <head> event_legacy_mem +%type <head> event_legacy_tracepoint +%type <head> event_legacy_numeric +%type <head> event_legacy_raw +%type <head> event_def %union { @@ -62,13 +69,13 @@ event_def PE_MODIFIER_EVENT * (there could be more events added for multiple tracepoint * definitions via '*?'. */ - ABORT_ON(parse_events_modifier(list_event, $2)); - parse_events_update_lists(list_event, list_all); + ABORT_ON(parse_events_modifier($1, $2)); + parse_events_update_lists($1, list_all); } | event_def { - parse_events_update_lists(list_event, list_all); + parse_events_update_lists($1, list_all); } event_def: event_pmu | @@ -82,71 +89,102 @@ event_def: event_pmu | event_pmu: PE_NAME '/' event_config '/' { - ABORT_ON(parse_events_add_pmu(list_event, idx, $1, $3)); + struct list_head *list = NULL; + + ABORT_ON(parse_events_add_pmu(&list, idx, $1, $3)); parse_events__free_terms($3); + $$ = list; } event_legacy_symbol: PE_VALUE_SYM '/' event_config '/' { + struct list_head *list = NULL; int type = $1 >> 16; int config = $1 & 255; - ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, $3)); + ABORT_ON(parse_events_add_numeric(&list, idx, type, config, $3)); parse_events__free_terms($3); + $$ = list; } | PE_VALUE_SYM sep_slash_dc { + struct list_head *list = NULL; int type = $1 >> 16; int config = $1 & 255; - ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, NULL)); + ABORT_ON(parse_events_add_numeric(&list, idx, type, config, NULL)); + $$ = list; } event_legacy_cache: PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT { - ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, $5)); + struct list_head *list = NULL; + + ABORT_ON(parse_events_add_cache(&list, idx, $1, $3, $5)); + $$ = list; } | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT { - ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, NULL)); + struct list_head *list = NULL; + + ABORT_ON(parse_events_add_cache(&list, idx, $1, $3, NULL)); + $$ = list; } | PE_NAME_CACHE_TYPE { - ABORT_ON(parse_events_add_cache(list_event, idx, $1, NULL, NULL)); + struct list_head *list = NULL; + + ABORT_ON(parse_events_add_cache(&list, idx, $1, NULL, NULL)); + $$ = list; } event_legacy_mem: PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc { - ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, $4)); + struct list_head *list = NULL; + + ABORT_ON(parse_events_add_breakpoint(&list, idx, (void *) $2, $4)); + $$ = list; } | PE_PREFIX_MEM PE_VALUE sep_dc { - ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, NULL)); + struct list_head *list = NULL; + + ABORT_ON(parse_events_add_breakpoint(&list, idx, (void *) $2, NULL)); + $$ = list; } event_legacy_tracepoint: PE_NAME ':' PE_NAME { - ABORT_ON(parse_events_add_tracepoint(list_event, idx, $1, $3)); + struct list_head *list = NULL; + + ABORT_ON(parse_events_add_tracepoint(&list, idx, $1, $3)); + $$ = list; } event_legacy_numeric: PE_VALUE ':' PE_VALUE { - ABORT_ON(parse_events_add_numeric(list_event, idx, $1, $3, NULL)); + struct list_head *list = NULL; + + ABORT_ON(parse_events_add_numeric(&list, idx, $1, $3, NULL)); + $$ = list; } event_legacy_raw: PE_RAW { - ABORT_ON(parse_events_add_numeric(list_event, idx, PERF_TYPE_RAW, $1, NULL)); + struct list_head *list = NULL; + + ABORT_ON(parse_events_add_numeric(&list, idx, PERF_TYPE_RAW, $1, NULL)); + $$ = list; } event_config: @@ -176,8 +214,8 @@ PE_NAME '=' PE_NAME { struct parse_events__term *term; - ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_STR, - $1, $3, 0)); + ABORT_ON(parse_events__term_str(&term, PARSE_EVENTS__TERM_TYPE_USER, + $1, $3)); $$ = term; } | @@ -185,8 +223,8 @@ PE_NAME '=' PE_VALUE { struct parse_events__term *term; - ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM, - $1, NULL, $3)); + ABORT_ON(parse_events__term_num(&term, PARSE_EVENTS__TERM_TYPE_USER, + $1, $3)); $$ = term; } | @@ -194,8 +232,16 @@ PE_NAME { struct parse_events__term *term; - ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM, - $1, NULL, 1)); + ABORT_ON(parse_events__term_num(&term, PARSE_EVENTS__TERM_TYPE_USER, + $1, 1)); + $$ = term; +} +| +PE_TERM '=' PE_NAME +{ + struct parse_events__term *term; + + ABORT_ON(parse_events__term_str(&term, $1, NULL, $3)); $$ = term; } | @@ -203,7 +249,7 @@ PE_TERM '=' PE_VALUE { struct parse_events__term *term; - ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, $3)); + ABORT_ON(parse_events__term_num(&term, $1, NULL, $3)); $$ = term; } | @@ -211,7 +257,7 @@ PE_TERM { struct parse_events__term *term; - ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, 1)); + ABORT_ON(parse_events__term_num(&term, $1, NULL, 1)); $$ = term; } @@ -222,7 +268,6 @@ sep_slash_dc: '/' | ':' | %% void parse_events_error(struct list_head *list_all __used, - struct list_head *list_event __used, int *idx __used, char const *msg __used) { diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index cb08a118e81..a119a537169 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -225,7 +225,7 @@ static int pmu_config_term(struct list_head *formats, if (parse_events__is_hardcoded_term(term)) return 0; - if (term->type != PARSE_EVENTS__TERM_TYPE_NUM) + if (term->type_val != PARSE_EVENTS__TERM_TYPE_NUM) return -EINVAL; format = pmu_find_format(formats, term->config); @@ -246,6 +246,11 @@ static int pmu_config_term(struct list_head *formats, return -EINVAL; } + /* + * XXX If we ever decide to go with string values for + * non-hardcoded terms, here's the place to translate + * them into value. + */ *vp |= pmu_format_value(format->bits, term->val.num); return 0; } @@ -253,9 +258,9 @@ static int pmu_config_term(struct list_head *formats, static int pmu_config(struct list_head *formats, struct perf_event_attr *attr, struct list_head *head_terms) { - struct parse_events__term *term, *h; + struct parse_events__term *term; - list_for_each_entry_safe(term, h, head_terms, list) + list_for_each_entry(term, head_terms, list) if (pmu_config_term(formats, attr, term)) return -EINVAL; @@ -324,49 +329,58 @@ static struct test_format { /* Simulated users input. */ static struct parse_events__term test_terms[] = { { - .config = (char *) "krava01", - .val.num = 15, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava01", + .val.num = 15, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava02", - .val.num = 170, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava02", + .val.num = 170, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava03", - .val.num = 1, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava03", + .val.num = 1, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava11", - .val.num = 27, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava11", + .val.num = 27, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava12", - .val.num = 1, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava12", + .val.num = 1, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava13", - .val.num = 2, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava13", + .val.num = 2, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava21", - .val.num = 119, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava21", + .val.num = 119, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava22", - .val.num = 11, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava22", + .val.num = 11, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, { - .config = (char *) "krava23", - .val.num = 2, - .type = PARSE_EVENTS__TERM_TYPE_NUM, + .config = (char *) "krava23", + .val.num = 2, + .type_val = PARSE_EVENTS__TERM_TYPE_NUM, + .type_term = PARSE_EVENTS__TERM_TYPE_USER, }, }; #define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term)) diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 8a8ee64e72d..59dccc98b55 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -44,6 +44,7 @@ #include "trace-event.h" /* For __unused */ #include "probe-event.h" #include "probe-finder.h" +#include "session.h" #define MAX_CMDLEN 256 #define MAX_PROBE_ARGS 128 @@ -70,6 +71,8 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) } static char *synthesize_perf_probe_point(struct perf_probe_point *pp); +static int convert_name_to_addr(struct perf_probe_event *pev, + const char *exec); static struct machine machine; /* Initialize symbol maps and path of vmlinux/modules */ @@ -170,6 +173,34 @@ const char *kernel_get_module_path(const char *module) return (dso) ? dso->long_name : NULL; } +static int init_user_exec(void) +{ + int ret = 0; + + symbol_conf.try_vmlinux_path = false; + symbol_conf.sort_by_name = true; + ret = symbol__init(); + + if (ret < 0) + pr_debug("Failed to init symbol map.\n"); + + return ret; +} + +static int convert_to_perf_probe_point(struct probe_trace_point *tp, + struct perf_probe_point *pp) +{ + pp->function = strdup(tp->symbol); + + if (pp->function == NULL) + return -ENOMEM; + + pp->offset = tp->offset; + pp->retprobe = tp->retprobe; + + return 0; +} + #ifdef DWARF_SUPPORT /* Open new debuginfo of given module */ static struct debuginfo *open_debuginfo(const char *module) @@ -224,10 +255,7 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, if (ret <= 0) { pr_debug("Failed to find corresponding probes from " "debuginfo. Use kprobe event information.\n"); - pp->function = strdup(tp->symbol); - if (pp->function == NULL) - return -ENOMEM; - pp->offset = tp->offset; + return convert_to_perf_probe_point(tp, pp); } pp->retprobe = tp->retprobe; @@ -275,9 +303,20 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, int max_tevs, const char *target) { bool need_dwarf = perf_probe_event_need_dwarf(pev); - struct debuginfo *dinfo = open_debuginfo(target); + struct debuginfo *dinfo; int ntevs, ret = 0; + if (pev->uprobes) { + if (need_dwarf) { + pr_warning("Debuginfo-analysis is not yet supported" + " with -x/--exec option.\n"); + return -ENOSYS; + } + return convert_name_to_addr(pev, target); + } + + dinfo = open_debuginfo(target); + if (!dinfo) { if (need_dwarf) { pr_warning("Failed to open debuginfo file.\n"); @@ -603,23 +642,22 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, pr_err("Failed to find symbol %s in kernel.\n", tp->symbol); return -ENOENT; } - pp->function = strdup(tp->symbol); - if (pp->function == NULL) - return -ENOMEM; - pp->offset = tp->offset; - pp->retprobe = tp->retprobe; - return 0; + return convert_to_perf_probe_point(tp, pp); } static int try_to_find_probe_trace_events(struct perf_probe_event *pev, struct probe_trace_event **tevs __unused, - int max_tevs __unused, const char *mod __unused) + int max_tevs __unused, const char *target) { if (perf_probe_event_need_dwarf(pev)) { pr_warning("Debuginfo-analysis is not supported.\n"); return -ENOSYS; } + + if (pev->uprobes) + return convert_name_to_addr(pev, target); + return 0; } @@ -1341,11 +1379,18 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev) if (buf == NULL) return NULL; - len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu", - tp->retprobe ? 'r' : 'p', - tev->group, tev->event, - tp->module ?: "", tp->module ? ":" : "", - tp->symbol, tp->offset); + if (tev->uprobes) + len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s:%s", + tp->retprobe ? 'r' : 'p', + tev->group, tev->event, + tp->module, tp->symbol); + else + len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu", + tp->retprobe ? 'r' : 'p', + tev->group, tev->event, + tp->module ?: "", tp->module ? ":" : "", + tp->symbol, tp->offset); + if (len <= 0) goto error; @@ -1364,7 +1409,7 @@ error: } static int convert_to_perf_probe_event(struct probe_trace_event *tev, - struct perf_probe_event *pev) + struct perf_probe_event *pev, bool is_kprobe) { char buf[64] = ""; int i, ret; @@ -1376,7 +1421,11 @@ static int convert_to_perf_probe_event(struct probe_trace_event *tev, return -ENOMEM; /* Convert trace_point to probe_point */ - ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point); + if (is_kprobe) + ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point); + else + ret = convert_to_perf_probe_point(&tev->point, &pev->point); + if (ret < 0) return ret; @@ -1472,7 +1521,26 @@ static void clear_probe_trace_event(struct probe_trace_event *tev) memset(tev, 0, sizeof(*tev)); } -static int open_kprobe_events(bool readwrite) +static void print_warn_msg(const char *file, bool is_kprobe) +{ + + if (errno == ENOENT) { + const char *config; + + if (!is_kprobe) + config = "CONFIG_UPROBE_EVENTS"; + else + config = "CONFIG_KPROBE_EVENTS"; + + pr_warning("%s file does not exist - please rebuild kernel" + " with %s.\n", file, config); + } else + pr_warning("Failed to open %s file: %s\n", file, + strerror(errno)); +} + +static int open_probe_events(const char *trace_file, bool readwrite, + bool is_kprobe) { char buf[PATH_MAX]; const char *__debugfs; @@ -1484,27 +1552,31 @@ static int open_kprobe_events(bool readwrite) return -ENOENT; } - ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events", __debugfs); + ret = e_snprintf(buf, PATH_MAX, "%s/%s", __debugfs, trace_file); if (ret >= 0) { pr_debug("Opening %s write=%d\n", buf, readwrite); if (readwrite && !probe_event_dry_run) ret = open(buf, O_RDWR, O_APPEND); else ret = open(buf, O_RDONLY, 0); - } - if (ret < 0) { - if (errno == ENOENT) - pr_warning("kprobe_events file does not exist - please" - " rebuild kernel with CONFIG_KPROBE_EVENT.\n"); - else - pr_warning("Failed to open kprobe_events file: %s\n", - strerror(errno)); + if (ret < 0) + print_warn_msg(buf, is_kprobe); } return ret; } -/* Get raw string list of current kprobe_events */ +static int open_kprobe_events(bool readwrite) +{ + return open_probe_events("tracing/kprobe_events", readwrite, true); +} + +static int open_uprobe_events(bool readwrite) +{ + return open_probe_events("tracing/uprobe_events", readwrite, false); +} + +/* Get raw string list of current kprobe_events or uprobe_events */ static struct strlist *get_probe_trace_command_rawlist(int fd) { int ret, idx; @@ -1569,36 +1641,26 @@ static int show_perf_probe_event(struct perf_probe_event *pev) return ret; } -/* List up current perf-probe events */ -int show_perf_probe_events(void) +static int __show_perf_probe_events(int fd, bool is_kprobe) { - int fd, ret; + int ret = 0; struct probe_trace_event tev; struct perf_probe_event pev; struct strlist *rawlist; struct str_node *ent; - setup_pager(); - ret = init_vmlinux(); - if (ret < 0) - return ret; - memset(&tev, 0, sizeof(tev)); memset(&pev, 0, sizeof(pev)); - fd = open_kprobe_events(false); - if (fd < 0) - return fd; - rawlist = get_probe_trace_command_rawlist(fd); - close(fd); if (!rawlist) return -ENOENT; strlist__for_each(ent, rawlist) { ret = parse_probe_trace_command(ent->s, &tev); if (ret >= 0) { - ret = convert_to_perf_probe_event(&tev, &pev); + ret = convert_to_perf_probe_event(&tev, &pev, + is_kprobe); if (ret >= 0) ret = show_perf_probe_event(&pev); } @@ -1612,6 +1674,33 @@ int show_perf_probe_events(void) return ret; } +/* List up current perf-probe events */ +int show_perf_probe_events(void) +{ + int fd, ret; + + setup_pager(); + fd = open_kprobe_events(false); + + if (fd < 0) + return fd; + + ret = init_vmlinux(); + if (ret < 0) + return ret; + + ret = __show_perf_probe_events(fd, true); + close(fd); + + fd = open_uprobe_events(false); + if (fd >= 0) { + ret = __show_perf_probe_events(fd, false); + close(fd); + } + + return ret; +} + /* Get current perf-probe event names */ static struct strlist *get_probe_trace_event_names(int fd, bool include_group) { @@ -1717,7 +1806,11 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, const char *event, *group; struct strlist *namelist; - fd = open_kprobe_events(true); + if (pev->uprobes) + fd = open_uprobe_events(true); + else + fd = open_kprobe_events(true); + if (fd < 0) return fd; /* Get current event names */ @@ -1829,6 +1922,8 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, tev->point.offset = pev->point.offset; tev->point.retprobe = pev->point.retprobe; tev->nargs = pev->nargs; + tev->uprobes = pev->uprobes; + if (tev->nargs) { tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); @@ -1859,6 +1954,9 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, } } + if (pev->uprobes) + return 1; + /* Currently just checking function name from symbol map */ sym = __find_kernel_function_by_name(tev->point.symbol, NULL); if (!sym) { @@ -1894,12 +1992,18 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, int i, j, ret; struct __event_package *pkgs; + ret = 0; pkgs = zalloc(sizeof(struct __event_package) * npevs); + if (pkgs == NULL) return -ENOMEM; - /* Init vmlinux path */ - ret = init_vmlinux(); + if (!pevs->uprobes) + /* Init vmlinux path */ + ret = init_vmlinux(); + else + ret = init_user_exec(); + if (ret < 0) { free(pkgs); return ret; @@ -1971,23 +2075,15 @@ error: return ret; } -static int del_trace_probe_event(int fd, const char *group, - const char *event, struct strlist *namelist) +static int del_trace_probe_event(int fd, const char *buf, + struct strlist *namelist) { - char buf[128]; struct str_node *ent, *n; - int found = 0, ret = 0; - - ret = e_snprintf(buf, 128, "%s:%s", group, event); - if (ret < 0) { - pr_err("Failed to copy event.\n"); - return ret; - } + int ret = -1; if (strpbrk(buf, "*?")) { /* Glob-exp */ strlist__for_each_safe(ent, n, namelist) if (strglobmatch(ent->s, buf)) { - found++; ret = __del_trace_probe_event(fd, ent); if (ret < 0) break; @@ -1996,40 +2092,43 @@ static int del_trace_probe_event(int fd, const char *group, } else { ent = strlist__find(namelist, buf); if (ent) { - found++; ret = __del_trace_probe_event(fd, ent); if (ret >= 0) strlist__remove(namelist, ent); } } - if (found == 0 && ret >= 0) - pr_info("Info: Event \"%s\" does not exist.\n", buf); return ret; } int del_perf_probe_events(struct strlist *dellist) { - int fd, ret = 0; + int ret = -1, ufd = -1, kfd = -1; + char buf[128]; const char *group, *event; char *p, *str; struct str_node *ent; - struct strlist *namelist; - - fd = open_kprobe_events(true); - if (fd < 0) - return fd; + struct strlist *namelist = NULL, *unamelist = NULL; /* Get current event names */ - namelist = get_probe_trace_event_names(fd, true); - if (namelist == NULL) - return -EINVAL; + kfd = open_kprobe_events(true); + if (kfd < 0) + return kfd; + + namelist = get_probe_trace_event_names(kfd, true); + ufd = open_uprobe_events(true); + + if (ufd >= 0) + unamelist = get_probe_trace_event_names(ufd, true); + + if (namelist == NULL && unamelist == NULL) + goto error; strlist__for_each(ent, dellist) { str = strdup(ent->s); if (str == NULL) { ret = -ENOMEM; - break; + goto error; } pr_debug("Parsing: %s\n", str); p = strchr(str, ':'); @@ -2041,17 +2140,46 @@ int del_perf_probe_events(struct strlist *dellist) group = "*"; event = str; } + + ret = e_snprintf(buf, 128, "%s:%s", group, event); + if (ret < 0) { + pr_err("Failed to copy event."); + free(str); + goto error; + } + pr_debug("Group: %s, Event: %s\n", group, event); - ret = del_trace_probe_event(fd, group, event, namelist); + + if (namelist) + ret = del_trace_probe_event(kfd, buf, namelist); + + if (unamelist && ret != 0) + ret = del_trace_probe_event(ufd, buf, unamelist); + + if (ret != 0) + pr_info("Info: Event \"%s\" does not exist.\n", buf); + free(str); - if (ret < 0) - break; } - strlist__delete(namelist); - close(fd); + +error: + if (kfd >= 0) { + if (namelist) + strlist__delete(namelist); + + close(kfd); + } + + if (ufd >= 0) { + if (unamelist) + strlist__delete(unamelist); + + close(ufd); + } return ret; } + /* TODO: don't use a global variable for filter ... */ static struct strfilter *available_func_filter; @@ -2068,30 +2196,152 @@ static int filter_available_functions(struct map *map __unused, return 1; } -int show_available_funcs(const char *target, struct strfilter *_filter) +static int __show_available_funcs(struct map *map) +{ + if (map__load(map, filter_available_functions)) { + pr_err("Failed to load map.\n"); + return -EINVAL; + } + if (!dso__sorted_by_name(map->dso, map->type)) + dso__sort_by_name(map->dso, map->type); + + dso__fprintf_symbols_by_name(map->dso, map->type, stdout); + return 0; +} + +static int available_kernel_funcs(const char *module) { struct map *map; int ret; - setup_pager(); - ret = init_vmlinux(); if (ret < 0) return ret; - map = kernel_get_module_map(target); + map = kernel_get_module_map(module); if (!map) { - pr_err("Failed to find %s map.\n", (target) ? : "kernel"); + pr_err("Failed to find %s map.\n", (module) ? : "kernel"); return -EINVAL; } + return __show_available_funcs(map); +} + +static int available_user_funcs(const char *target) +{ + struct map *map; + int ret; + + ret = init_user_exec(); + if (ret < 0) + return ret; + + map = dso__new_map(target); + ret = __show_available_funcs(map); + dso__delete(map->dso); + map__delete(map); + return ret; +} + +int show_available_funcs(const char *target, struct strfilter *_filter, + bool user) +{ + setup_pager(); available_func_filter = _filter; + + if (!user) + return available_kernel_funcs(target); + + return available_user_funcs(target); +} + +/* + * uprobe_events only accepts address: + * Convert function and any offset to address + */ +static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec) +{ + struct perf_probe_point *pp = &pev->point; + struct symbol *sym; + struct map *map = NULL; + char *function = NULL, *name = NULL; + int ret = -EINVAL; + unsigned long long vaddr = 0; + + if (!pp->function) { + pr_warning("No function specified for uprobes"); + goto out; + } + + function = strdup(pp->function); + if (!function) { + pr_warning("Failed to allocate memory by strdup.\n"); + ret = -ENOMEM; + goto out; + } + + name = realpath(exec, NULL); + if (!name) { + pr_warning("Cannot find realpath for %s.\n", exec); + goto out; + } + map = dso__new_map(name); + if (!map) { + pr_warning("Cannot find appropriate DSO for %s.\n", exec); + goto out; + } + available_func_filter = strfilter__new(function, NULL); if (map__load(map, filter_available_functions)) { pr_err("Failed to load map.\n"); - return -EINVAL; + goto out; } - if (!dso__sorted_by_name(map->dso, map->type)) - dso__sort_by_name(map->dso, map->type); - dso__fprintf_symbols_by_name(map->dso, map->type, stdout); - return 0; + sym = map__find_symbol_by_name(map, function, NULL); + if (!sym) { + pr_warning("Cannot find %s in DSO %s\n", function, exec); + goto out; + } + + if (map->start > sym->start) + vaddr = map->start; + vaddr += sym->start + pp->offset + map->pgoff; + pp->offset = 0; + + if (!pev->event) { + pev->event = function; + function = NULL; + } + if (!pev->group) { + char *ptr1, *ptr2; + + pev->group = zalloc(sizeof(char *) * 64); + ptr1 = strdup(basename(exec)); + if (ptr1) { + ptr2 = strpbrk(ptr1, "-._"); + if (ptr2) + *ptr2 = '\0'; + e_snprintf(pev->group, 64, "%s_%s", PERFPROBE_GROUP, + ptr1); + free(ptr1); + } + } + free(pp->function); + pp->function = zalloc(sizeof(char *) * MAX_PROBE_ARGS); + if (!pp->function) { + ret = -ENOMEM; + pr_warning("Failed to allocate memory by zalloc.\n"); + goto out; + } + e_snprintf(pp->function, MAX_PROBE_ARGS, "0x%llx", vaddr); + ret = 0; + +out: + if (map) { + dso__delete(map->dso); + map__delete(map); + } + if (function) + free(function); + if (name) + free(name); + return ret; } diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index a7dee835f49..f9f3de8b422 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -7,7 +7,7 @@ extern bool probe_event_dry_run; -/* kprobe-tracer tracing point */ +/* kprobe-tracer and uprobe-tracer tracing point */ struct probe_trace_point { char *symbol; /* Base symbol */ char *module; /* Module name */ @@ -21,7 +21,7 @@ struct probe_trace_arg_ref { long offset; /* Offset value */ }; -/* kprobe-tracer tracing argument */ +/* kprobe-tracer and uprobe-tracer tracing argument */ struct probe_trace_arg { char *name; /* Argument name */ char *value; /* Base value */ @@ -29,12 +29,13 @@ struct probe_trace_arg { struct probe_trace_arg_ref *ref; /* Referencing offset */ }; -/* kprobe-tracer tracing event (point + arg) */ +/* kprobe-tracer and uprobe-tracer tracing event (point + arg) */ struct probe_trace_event { char *event; /* Event name */ char *group; /* Group name */ struct probe_trace_point point; /* Trace point */ int nargs; /* Number of args */ + bool uprobes; /* uprobes only */ struct probe_trace_arg *args; /* Arguments */ }; @@ -70,6 +71,7 @@ struct perf_probe_event { char *group; /* Group name */ struct perf_probe_point point; /* Probe point */ int nargs; /* Number of arguments */ + bool uprobes; struct perf_probe_arg *args; /* Arguments */ }; @@ -129,8 +131,8 @@ extern int show_line_range(struct line_range *lr, const char *module); extern int show_available_vars(struct perf_probe_event *pevs, int npevs, int max_probe_points, const char *module, struct strfilter *filter, bool externs); -extern int show_available_funcs(const char *module, struct strfilter *filter); - +extern int show_available_funcs(const char *module, struct strfilter *filter, + bool user); /* Maximum index number of event-name postfix */ #define MAX_EVENT_INDEX 1024 diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index e30749e38a9..4c1b3d72a1d 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -56,7 +56,7 @@ INTERP my_perl; #define FTRACE_MAX_EVENT \ ((1 << (sizeof(unsigned short) * 8)) - 1) -struct event *events[FTRACE_MAX_EVENT]; +struct event_format *events[FTRACE_MAX_EVENT]; extern struct scripting_context *scripting_context; @@ -181,7 +181,7 @@ static void define_flag_field(const char *ev_name, LEAVE; } -static void define_event_symbols(struct event *event, +static void define_event_symbols(struct event_format *event, const char *ev_name, struct print_arg *args) { @@ -209,6 +209,8 @@ static void define_event_symbols(struct event *event, define_symbolic_values(args->symbol.symbols, ev_name, cur_field_name); break; + case PRINT_BSTRING: + case PRINT_DYNAMIC_ARRAY: case PRINT_STRING: break; case PRINT_TYPE: @@ -220,7 +222,9 @@ static void define_event_symbols(struct event *event, define_event_symbols(event, ev_name, args->op.left); define_event_symbols(event, ev_name, args->op.right); break; + case PRINT_FUNC: default: + pr_err("Unsupported print arg type\n"); /* we should warn... */ return; } @@ -229,10 +233,10 @@ static void define_event_symbols(struct event *event, define_event_symbols(event, ev_name, args->next); } -static inline struct event *find_cache_event(int type) +static inline struct event_format *find_cache_event(int type) { static char ev_name[256]; - struct event *event; + struct event_format *event; if (events[type]) return events[type]; @@ -258,7 +262,7 @@ static void perl_process_tracepoint(union perf_event *pevent __unused, static char handler[256]; unsigned long long val; unsigned long s, ns; - struct event *event; + struct event_format *event; int type; int pid; int cpu = sample->cpu; @@ -446,7 +450,7 @@ static int perl_stop_script(void) static int perl_generate_script(const char *outfile) { - struct event *event = NULL; + struct event_format *event = NULL; struct format_field *f; char fname[PATH_MAX]; int not_first, count; diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index c2623c6f9b5..acb9795286c 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -37,7 +37,7 @@ PyMODINIT_FUNC initperf_trace_context(void); #define FTRACE_MAX_EVENT \ ((1 << (sizeof(unsigned short) * 8)) - 1) -struct event *events[FTRACE_MAX_EVENT]; +struct event_format *events[FTRACE_MAX_EVENT]; #define MAX_FIELDS 64 #define N_COMMON_FIELDS 7 @@ -136,7 +136,7 @@ static void define_field(enum print_arg_type field_type, Py_DECREF(t); } -static void define_event_symbols(struct event *event, +static void define_event_symbols(struct event_format *event, const char *ev_name, struct print_arg *args) { @@ -178,6 +178,10 @@ static void define_event_symbols(struct event *event, define_event_symbols(event, ev_name, args->op.right); break; default: + /* gcc warns for these? */ + case PRINT_BSTRING: + case PRINT_DYNAMIC_ARRAY: + case PRINT_FUNC: /* we should warn... */ return; } @@ -186,10 +190,10 @@ static void define_event_symbols(struct event *event, define_event_symbols(event, ev_name, args->next); } -static inline struct event *find_cache_event(int type) +static inline struct event_format *find_cache_event(int type) { static char ev_name[256]; - struct event *event; + struct event_format *event; if (events[type]) return events[type]; @@ -216,7 +220,7 @@ static void python_process_event(union perf_event *pevent __unused, struct format_field *field; unsigned long long val; unsigned long s, ns; - struct event *event; + struct event_format *event; unsigned n = 0; int type; int pid; @@ -436,7 +440,7 @@ out: static int python_generate_script(const char *outfile) { - struct event *event = NULL; + struct event_format *event = NULL; struct format_field *f; char fname[PATH_MAX]; int not_first, count; diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 1efd3bee633..93d355d2710 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -481,6 +481,38 @@ static void perf_event__read_swap(union perf_event *event) event->read.id = bswap_64(event->read.id); } +static u8 revbyte(u8 b) +{ + int rev = (b >> 4) | ((b & 0xf) << 4); + rev = ((rev & 0xcc) >> 2) | ((rev & 0x33) << 2); + rev = ((rev & 0xaa) >> 1) | ((rev & 0x55) << 1); + return (u8) rev; +} + +/* + * XXX this is hack in attempt to carry flags bitfield + * throught endian village. ABI says: + * + * Bit-fields are allocated from right to left (least to most significant) + * on little-endian implementations and from left to right (most to least + * significant) on big-endian implementations. + * + * The above seems to be byte specific, so we need to reverse each + * byte of the bitfield. 'Internet' also says this might be implementation + * specific and we probably need proper fix and carry perf_event_attr + * bitfield flags in separate data file FEAT_ section. Thought this seems + * to work for now. + */ +static void swap_bitfield(u8 *p, unsigned len) +{ + unsigned i; + + for (i = 0; i < len; i++) { + *p = revbyte(*p); + p++; + } +} + /* exported for swapping attributes in file header */ void perf_event__attr_swap(struct perf_event_attr *attr) { @@ -494,6 +526,8 @@ void perf_event__attr_swap(struct perf_event_attr *attr) attr->bp_type = bswap_32(attr->bp_type); attr->bp_addr = bswap_64(attr->bp_addr); attr->bp_len = bswap_64(attr->bp_len); + + swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64)); } static void perf_event__hdr_attr_swap(union perf_event *event) @@ -1064,8 +1098,9 @@ volatile int session_done; static int __perf_session__process_pipe_events(struct perf_session *self, struct perf_tool *tool) { - union perf_event event; - uint32_t size; + union perf_event *event; + uint32_t size, cur_size = 0; + void *buf = NULL; int skip = 0; u64 head; int err; @@ -1074,8 +1109,14 @@ static int __perf_session__process_pipe_events(struct perf_session *self, perf_tool__fill_defaults(tool); head = 0; + cur_size = sizeof(union perf_event); + + buf = malloc(cur_size); + if (!buf) + return -errno; more: - err = readn(self->fd, &event, sizeof(struct perf_event_header)); + event = buf; + err = readn(self->fd, event, sizeof(struct perf_event_header)); if (err <= 0) { if (err == 0) goto done; @@ -1085,13 +1126,23 @@ more: } if (self->header.needs_swap) - perf_event_header__bswap(&event.header); + perf_event_header__bswap(&event->header); - size = event.header.size; + size = event->header.size; if (size == 0) size = 8; - p = &event; + if (size > cur_size) { + void *new = realloc(buf, size); + if (!new) { + pr_err("failed to allocate memory to read event\n"); + goto out_err; + } + buf = new; + cur_size = size; + event = buf; + } + p = event; p += sizeof(struct perf_event_header); if (size - sizeof(struct perf_event_header)) { @@ -1107,17 +1158,11 @@ more: } } - if ((skip = perf_session__process_event(self, &event, tool, head)) < 0) { - dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n", - head, event.header.size, event.header.type); - /* - * assume we lost track of the stream, check alignment, and - * increment a single u64 in the hope to catch on again 'soon'. - */ - if (unlikely(head & 7)) - head &= ~7ULL; - - size = 8; + if ((skip = perf_session__process_event(self, event, tool, head)) < 0) { + pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", + head, event->header.size, event->header.type); + err = -EINVAL; + goto out_err; } head += size; @@ -1130,6 +1175,7 @@ more: done: err = 0; out_err: + free(buf); perf_session__warn_about_errors(self, tool); perf_session_free_sample_buffers(self); return err; @@ -1226,17 +1272,11 @@ more: if (size == 0 || perf_session__process_event(session, event, tool, file_pos) < 0) { - dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n", - file_offset + head, event->header.size, - event->header.type); - /* - * assume we lost track of the stream, check alignment, and - * increment a single u64 in the hope to catch on again 'soon'. - */ - if (unlikely(head & 7)) - head &= ~7ULL; - - size = 8; + pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", + file_offset + head, event->header.size, + event->header.type); + err = -EINVAL; + goto out_err; } head += size; diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index ab9867b2b43..e2ba8858f3e 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -2783,3 +2783,11 @@ int machine__load_vmlinux_path(struct machine *machine, enum map_type type, return ret; } + +struct map *dso__new_map(const char *name) +{ + struct dso *dso = dso__new(name); + struct map *map = map__new2(0, dso, MAP__FUNCTION); + + return map; +} diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index ac49ef208a5..5649d63798c 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -65,6 +65,11 @@ struct symbol { void symbol__delete(struct symbol *sym); +static inline size_t symbol__size(const struct symbol *sym) +{ + return sym->end - sym->start + 1; +} + struct strlist; struct symbol_conf { @@ -237,6 +242,7 @@ void dso__set_long_name(struct dso *dso, char *name); void dso__set_build_id(struct dso *dso, void *build_id); void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine); +struct map *dso__new_map(const char *name); struct symbol *dso__find_symbol(struct dso *dso, enum map_type type, u64 addr); struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c new file mode 100644 index 00000000000..1064d5b148a --- /dev/null +++ b/tools/perf/util/target.c @@ -0,0 +1,142 @@ +/* + * Helper functions for handling target threads/cpus + * + * Copyright (C) 2012, LG Electronics, Namhyung Kim <namhyung.kim@lge.com> + * + * Released under the GPL v2. + */ + +#include "target.h" +#include "debug.h" + +#include <pwd.h> +#include <string.h> + + +enum perf_target_errno perf_target__validate(struct perf_target *target) +{ + enum perf_target_errno ret = PERF_ERRNO_TARGET__SUCCESS; + + if (target->pid) + target->tid = target->pid; + + /* CPU and PID are mutually exclusive */ + if (target->tid && target->cpu_list) { + target->cpu_list = NULL; + if (ret == PERF_ERRNO_TARGET__SUCCESS) + ret = PERF_ERRNO_TARGET__PID_OVERRIDE_CPU; + } + + /* UID and PID are mutually exclusive */ + if (target->tid && target->uid_str) { + target->uid_str = NULL; + if (ret == PERF_ERRNO_TARGET__SUCCESS) + ret = PERF_ERRNO_TARGET__PID_OVERRIDE_UID; + } + + /* UID and CPU are mutually exclusive */ + if (target->uid_str && target->cpu_list) { + target->cpu_list = NULL; + if (ret == PERF_ERRNO_TARGET__SUCCESS) + ret = PERF_ERRNO_TARGET__UID_OVERRIDE_CPU; + } + + /* PID and SYSTEM are mutually exclusive */ + if (target->tid && target->system_wide) { + target->system_wide = false; + if (ret == PERF_ERRNO_TARGET__SUCCESS) + ret = PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM; + } + + /* UID and SYSTEM are mutually exclusive */ + if (target->uid_str && target->system_wide) { + target->system_wide = false; + if (ret == PERF_ERRNO_TARGET__SUCCESS) + ret = PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM; + } + + return ret; +} + +enum perf_target_errno perf_target__parse_uid(struct perf_target *target) +{ + struct passwd pwd, *result; + char buf[1024]; + const char *str = target->uid_str; + + target->uid = UINT_MAX; + if (str == NULL) + return PERF_ERRNO_TARGET__SUCCESS; + + /* Try user name first */ + getpwnam_r(str, &pwd, buf, sizeof(buf), &result); + + if (result == NULL) { + /* + * The user name not found. Maybe it's a UID number. + */ + char *endptr; + int uid = strtol(str, &endptr, 10); + + if (*endptr != '\0') + return PERF_ERRNO_TARGET__INVALID_UID; + + getpwuid_r(uid, &pwd, buf, sizeof(buf), &result); + + if (result == NULL) + return PERF_ERRNO_TARGET__USER_NOT_FOUND; + } + + target->uid = result->pw_uid; + return PERF_ERRNO_TARGET__SUCCESS; +} + +/* + * This must have a same ordering as the enum perf_target_errno. + */ +static const char *perf_target__error_str[] = { + "PID/TID switch overriding CPU", + "PID/TID switch overriding UID", + "UID switch overriding CPU", + "PID/TID switch overriding SYSTEM", + "UID switch overriding SYSTEM", + "Invalid User: %s", + "Problems obtaining information for user %s", +}; + +int perf_target__strerror(struct perf_target *target, int errnum, + char *buf, size_t buflen) +{ + int idx; + const char *msg; + + if (errnum >= 0) { + strerror_r(errnum, buf, buflen); + return 0; + } + + if (errnum < __PERF_ERRNO_TARGET__START || + errnum >= __PERF_ERRNO_TARGET__END) + return -1; + + idx = errnum - __PERF_ERRNO_TARGET__START; + msg = perf_target__error_str[idx]; + + switch (errnum) { + case PERF_ERRNO_TARGET__PID_OVERRIDE_CPU + ... PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM: + snprintf(buf, buflen, "%s", msg); + break; + + case PERF_ERRNO_TARGET__INVALID_UID: + case PERF_ERRNO_TARGET__USER_NOT_FOUND: + snprintf(buf, buflen, msg, target->uid_str); + break; + + default: + /* cannot reach here */ + break; + } + + return 0; +} diff --git a/tools/perf/util/target.h b/tools/perf/util/target.h new file mode 100644 index 00000000000..a4be8575fda --- /dev/null +++ b/tools/perf/util/target.h @@ -0,0 +1,65 @@ +#ifndef _PERF_TARGET_H +#define _PERF_TARGET_H + +#include <stdbool.h> +#include <sys/types.h> + +struct perf_target { + const char *pid; + const char *tid; + const char *cpu_list; + const char *uid_str; + uid_t uid; + bool system_wide; + bool uses_mmap; +}; + +enum perf_target_errno { + PERF_ERRNO_TARGET__SUCCESS = 0, + + /* + * Choose an arbitrary negative big number not to clash with standard + * errno since SUS requires the errno has distinct positive values. + * See 'Issue 6' in the link below. + * + * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html + */ + __PERF_ERRNO_TARGET__START = -10000, + + + /* for perf_target__validate() */ + PERF_ERRNO_TARGET__PID_OVERRIDE_CPU = __PERF_ERRNO_TARGET__START, + PERF_ERRNO_TARGET__PID_OVERRIDE_UID, + PERF_ERRNO_TARGET__UID_OVERRIDE_CPU, + PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM, + PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM, + + /* for perf_target__parse_uid() */ + PERF_ERRNO_TARGET__INVALID_UID, + PERF_ERRNO_TARGET__USER_NOT_FOUND, + + __PERF_ERRNO_TARGET__END, +}; + +enum perf_target_errno perf_target__validate(struct perf_target *target); +enum perf_target_errno perf_target__parse_uid(struct perf_target *target); + +int perf_target__strerror(struct perf_target *target, int errnum, char *buf, + size_t buflen); + +static inline bool perf_target__has_task(struct perf_target *target) +{ + return target->tid || target->pid || target->uid_str; +} + +static inline bool perf_target__has_cpu(struct perf_target *target) +{ + return target->system_wide || target->cpu_list; +} + +static inline bool perf_target__none(struct perf_target *target) +{ + return !perf_target__has_task(target) && !perf_target__has_cpu(target); +} + +#endif /* _PERF_TARGET_H */ diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index 7da80f14418..f718df8a3c5 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h @@ -6,7 +6,7 @@ struct thread_map { int nr; - int map[]; + pid_t map[]; }; struct thread_map *thread_map__new_by_pid(pid_t pid); diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index 09fe579ccaf..abe0e8e9506 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c @@ -69,23 +69,24 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) ret += SNPRINTF(bf + ret, size - ret, "], "); - if (top->target_pid) + if (top->target.pid) ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %s", - top->target_pid); - else if (top->target_tid) + top->target.pid); + else if (top->target.tid) ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %s", - top->target_tid); - else if (top->uid_str != NULL) + top->target.tid); + else if (top->target.uid_str != NULL) ret += SNPRINTF(bf + ret, size - ret, " (uid: %s", - top->uid_str); + top->target.uid_str); else ret += SNPRINTF(bf + ret, size - ret, " (all"); - if (top->cpu_list) + if (top->target.cpu_list) ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", - top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list); + top->evlist->cpus->nr > 1 ? "s" : "", + top->target.cpu_list); else { - if (top->target_tid) + if (top->target.tid) ret += SNPRINTF(bf + ret, size - ret, ")"); else ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)", diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index ce61cb2d1ac..33347ca89ee 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -13,6 +13,7 @@ struct perf_session; struct perf_top { struct perf_tool tool; struct perf_evlist *evlist; + struct perf_target target; /* * Symbols will be added here in perf_event__process_sample and will * get out after decayed. @@ -23,10 +24,7 @@ struct perf_top { u64 guest_us_samples, guest_kernel_samples; int print_entries, count_filter, delay_secs; int freq; - const char *target_pid, *target_tid; - uid_t uid; bool hide_kernel_symbols, hide_user_symbols, zero; - bool system_wide; bool use_tui, use_stdio; bool sort_has_symbols; bool dont_use_callchains; @@ -37,7 +35,6 @@ struct perf_top { bool sample_id_all_missing; bool exclude_guest_missing; bool dump_symtab; - const char *cpu_list; struct hist_entry *sym_filter_entry; struct perf_evsel *sym_evsel; struct perf_session *session; @@ -47,7 +44,6 @@ struct perf_top { int realtime_prio; int sym_pcnt_filter; const char *sym_filter; - const char *uid_str; }; size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index fc22cf5c605..a8d81c35ef6 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -68,7 +68,7 @@ struct events { }; -void *malloc_or_die(unsigned int size) +static void *malloc_or_die(unsigned int size) { void *data; @@ -448,6 +448,8 @@ static void tracing_data_header(void) else buf[0] = 0; + read_trace_init(buf[0], buf[0]); + write_or_die(buf, 1); /* save size of long */ diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index dfd1bd8371a..df2fddbf0cd 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -17,2169 +17,305 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * The parts for function graph printing was taken and modified from the - * Linux Kernel that were written by Frederic Weisbecker. */ - #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <ctype.h> #include <errno.h> #include "../perf.h" #include "util.h" #include "trace-event.h" -int header_page_ts_offset; -int header_page_ts_size; -int header_page_size_offset; int header_page_size_size; -int header_page_overwrite_offset; -int header_page_overwrite_size; +int header_page_ts_size; int header_page_data_offset; -int header_page_data_size; - -bool latency_format; - -static char *input_buf; -static unsigned long long input_buf_ptr; -static unsigned long long input_buf_siz; - -static int cpus; -static int long_size; -static int is_flag_field; -static int is_symbolic_field; - -static struct format_field * -find_any_field(struct event *event, const char *name); - -static void init_input_buf(char *buf, unsigned long long size) -{ - input_buf = buf; - input_buf_siz = size; - input_buf_ptr = 0; -} - -struct cmdline { - char *comm; - int pid; -}; - -static struct cmdline *cmdlines; -static int cmdline_count; - -static int cmdline_cmp(const void *a, const void *b) -{ - const struct cmdline *ca = a; - const struct cmdline *cb = b; - - if (ca->pid < cb->pid) - return -1; - if (ca->pid > cb->pid) - return 1; - - return 0; -} -void parse_cmdlines(char *file, int size __unused) -{ - struct cmdline_list { - struct cmdline_list *next; - char *comm; - int pid; - } *list = NULL, *item; - char *line; - char *next = NULL; - int i; +struct pevent *perf_pevent; +static struct pevent *pevent; - line = strtok_r(file, "\n", &next); - while (line) { - item = malloc_or_die(sizeof(*item)); - sscanf(line, "%d %as", &item->pid, - (float *)(void *)&item->comm); /* workaround gcc warning */ - item->next = list; - list = item; - line = strtok_r(NULL, "\n", &next); - cmdline_count++; - } - - cmdlines = malloc_or_die(sizeof(*cmdlines) * cmdline_count); - - i = 0; - while (list) { - cmdlines[i].pid = list->pid; - cmdlines[i].comm = list->comm; - i++; - item = list; - list = list->next; - free(item); - } - - qsort(cmdlines, cmdline_count, sizeof(*cmdlines), cmdline_cmp); -} - -static struct func_map { - unsigned long long addr; - char *func; - char *mod; -} *func_list; -static unsigned int func_count; - -static int func_cmp(const void *a, const void *b) -{ - const struct func_map *fa = a; - const struct func_map *fb = b; - - if (fa->addr < fb->addr) - return -1; - if (fa->addr > fb->addr) - return 1; - - return 0; -} - -void parse_proc_kallsyms(char *file, unsigned int size __unused) -{ - struct func_list { - struct func_list *next; - unsigned long long addr; - char *func; - char *mod; - } *list = NULL, *item; - char *line; - char *next = NULL; - char *addr_str; - char ch; - int ret __used; - int i; - - line = strtok_r(file, "\n", &next); - while (line) { - item = malloc_or_die(sizeof(*item)); - item->mod = NULL; - ret = sscanf(line, "%as %c %as\t[%as", - (float *)(void *)&addr_str, /* workaround gcc warning */ - &ch, - (float *)(void *)&item->func, - (float *)(void *)&item->mod); - item->addr = strtoull(addr_str, NULL, 16); - free(addr_str); - - /* truncate the extra ']' */ - if (item->mod) - item->mod[strlen(item->mod) - 1] = 0; - - - item->next = list; - list = item; - line = strtok_r(NULL, "\n", &next); - func_count++; - } - - func_list = malloc_or_die(sizeof(*func_list) * (func_count + 1)); - - i = 0; - while (list) { - func_list[i].func = list->func; - func_list[i].addr = list->addr; - func_list[i].mod = list->mod; - i++; - item = list; - list = list->next; - free(item); - } - - qsort(func_list, func_count, sizeof(*func_list), func_cmp); - - /* - * Add a special record at the end. - */ - func_list[func_count].func = NULL; - func_list[func_count].addr = 0; - func_list[func_count].mod = NULL; -} +bool latency_format; -/* - * We are searching for a record in between, not an exact - * match. - */ -static int func_bcmp(const void *a, const void *b) +int read_trace_init(int file_bigendian, int host_bigendian) { - const struct func_map *fa = a; - const struct func_map *fb = b; - - if ((fa->addr == fb->addr) || - - (fa->addr > fb->addr && - fa->addr < (fb+1)->addr)) + if (pevent) return 0; - if (fa->addr < fb->addr) - return -1; - - return 1; -} - -static struct func_map *find_func(unsigned long long addr) -{ - struct func_map *func; - struct func_map key; - - key.addr = addr; - - func = bsearch(&key, func_list, func_count, sizeof(*func_list), - func_bcmp); - - return func; -} - -void print_funcs(void) -{ - int i; - - for (i = 0; i < (int)func_count; i++) { - printf("%016llx %s", - func_list[i].addr, - func_list[i].func); - if (func_list[i].mod) - printf(" [%s]\n", func_list[i].mod); - else - printf("\n"); - } -} + perf_pevent = pevent_alloc(); + pevent = perf_pevent; -static struct printk_map { - unsigned long long addr; - char *printk; -} *printk_list; -static unsigned int printk_count; - -static int printk_cmp(const void *a, const void *b) -{ - const struct func_map *fa = a; - const struct func_map *fb = b; - - if (fa->addr < fb->addr) - return -1; - if (fa->addr > fb->addr) - return 1; + pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT); + pevent_set_file_bigendian(pevent, file_bigendian); + pevent_set_host_bigendian(pevent, host_bigendian); return 0; } -static struct printk_map *find_printk(unsigned long long addr) -{ - struct printk_map *printk; - struct printk_map key; - - key.addr = addr; - - printk = bsearch(&key, printk_list, printk_count, sizeof(*printk_list), - printk_cmp); - - return printk; -} - -void parse_ftrace_printk(char *file, unsigned int size __unused) -{ - struct printk_list { - struct printk_list *next; - unsigned long long addr; - char *printk; - } *list = NULL, *item; - char *line; - char *next = NULL; - char *addr_str; - int i; - - line = strtok_r(file, "\n", &next); - while (line) { - addr_str = strsep(&line, ":"); - if (!line) { - warning("error parsing print strings"); - break; - } - item = malloc_or_die(sizeof(*item)); - item->addr = strtoull(addr_str, NULL, 16); - /* fmt still has a space, skip it */ - item->printk = strdup(line+1); - item->next = list; - list = item; - line = strtok_r(NULL, "\n", &next); - printk_count++; - } - - printk_list = malloc_or_die(sizeof(*printk_list) * printk_count + 1); - - i = 0; - while (list) { - printk_list[i].printk = list->printk; - printk_list[i].addr = list->addr; - i++; - item = list; - list = list->next; - free(item); - } - - qsort(printk_list, printk_count, sizeof(*printk_list), printk_cmp); -} - -void print_printk(void) -{ - int i; - - for (i = 0; i < (int)printk_count; i++) { - printf("%016llx %s\n", - printk_list[i].addr, - printk_list[i].printk); - } -} - -static struct event *alloc_event(void) -{ - struct event *event; - - event = malloc_or_die(sizeof(*event)); - memset(event, 0, sizeof(*event)); - - return event; -} - -enum event_type { - EVENT_ERROR, - EVENT_NONE, - EVENT_SPACE, - EVENT_NEWLINE, - EVENT_OP, - EVENT_DELIM, - EVENT_ITEM, - EVENT_DQUOTE, - EVENT_SQUOTE, -}; - -static struct event *event_list; - -static void add_event(struct event *event) +static int get_common_field(struct scripting_context *context, + int *offset, int *size, const char *type) { - event->next = event_list; - event_list = event; -} - -static int event_item_type(enum event_type type) -{ - switch (type) { - case EVENT_ITEM ... EVENT_SQUOTE: - return 1; - case EVENT_ERROR ... EVENT_DELIM: - default: - return 0; - } -} - -static void free_arg(struct print_arg *arg) -{ - if (!arg) - return; - - switch (arg->type) { - case PRINT_ATOM: - if (arg->atom.atom) - free(arg->atom.atom); - break; - case PRINT_NULL: - case PRINT_FIELD ... PRINT_OP: - default: - /* todo */ - break; - } - - free(arg); -} - -static enum event_type get_type(int ch) -{ - if (ch == '\n') - return EVENT_NEWLINE; - if (isspace(ch)) - return EVENT_SPACE; - if (isalnum(ch) || ch == '_') - return EVENT_ITEM; - if (ch == '\'') - return EVENT_SQUOTE; - if (ch == '"') - return EVENT_DQUOTE; - if (!isprint(ch)) - return EVENT_NONE; - if (ch == '(' || ch == ')' || ch == ',') - return EVENT_DELIM; - - return EVENT_OP; -} - -static int __read_char(void) -{ - if (input_buf_ptr >= input_buf_siz) - return -1; - - return input_buf[input_buf_ptr++]; -} - -static int __peek_char(void) -{ - if (input_buf_ptr >= input_buf_siz) - return -1; - - return input_buf[input_buf_ptr]; -} - -static enum event_type __read_token(char **tok) -{ - char buf[BUFSIZ]; - int ch, last_ch, quote_ch, next_ch; - int i = 0; - int tok_size = 0; - enum event_type type; - - *tok = NULL; - - - ch = __read_char(); - if (ch < 0) - return EVENT_NONE; - - type = get_type(ch); - if (type == EVENT_NONE) - return type; - - buf[i++] = ch; - - switch (type) { - case EVENT_NEWLINE: - case EVENT_DELIM: - *tok = malloc_or_die(2); - (*tok)[0] = ch; - (*tok)[1] = 0; - return type; - - case EVENT_OP: - switch (ch) { - case '-': - next_ch = __peek_char(); - if (next_ch == '>') { - buf[i++] = __read_char(); - break; - } - /* fall through */ - case '+': - case '|': - case '&': - case '>': - case '<': - last_ch = ch; - ch = __peek_char(); - if (ch != last_ch) - goto test_equal; - buf[i++] = __read_char(); - switch (last_ch) { - case '>': - case '<': - goto test_equal; - default: - break; - } - break; - case '!': - case '=': - goto test_equal; - default: /* what should we do instead? */ - break; - } - buf[i] = 0; - *tok = strdup(buf); - return type; - - test_equal: - ch = __peek_char(); - if (ch == '=') - buf[i++] = __read_char(); - break; - - case EVENT_DQUOTE: - case EVENT_SQUOTE: - /* don't keep quotes */ - i--; - quote_ch = ch; - last_ch = 0; - do { - if (i == (BUFSIZ - 1)) { - buf[i] = 0; - if (*tok) { - *tok = realloc(*tok, tok_size + BUFSIZ); - if (!*tok) - return EVENT_NONE; - strcat(*tok, buf); - } else - *tok = strdup(buf); - - if (!*tok) - return EVENT_NONE; - tok_size += BUFSIZ; - i = 0; - } - last_ch = ch; - ch = __read_char(); - buf[i++] = ch; - /* the '\' '\' will cancel itself */ - if (ch == '\\' && last_ch == '\\') - last_ch = 0; - } while (ch != quote_ch || last_ch == '\\'); - /* remove the last quote */ - i--; - goto out; - - case EVENT_ERROR ... EVENT_SPACE: - case EVENT_ITEM: - default: - break; - } - - while (get_type(__peek_char()) == type) { - if (i == (BUFSIZ - 1)) { - buf[i] = 0; - if (*tok) { - *tok = realloc(*tok, tok_size + BUFSIZ); - if (!*tok) - return EVENT_NONE; - strcat(*tok, buf); - } else - *tok = strdup(buf); - - if (!*tok) - return EVENT_NONE; - tok_size += BUFSIZ; - i = 0; - } - ch = __read_char(); - buf[i++] = ch; - } - - out: - buf[i] = 0; - if (*tok) { - *tok = realloc(*tok, tok_size + i); - if (!*tok) - return EVENT_NONE; - strcat(*tok, buf); - } else - *tok = strdup(buf); - if (!*tok) - return EVENT_NONE; - - return type; -} - -static void free_token(char *tok) -{ - if (tok) - free(tok); -} - -static enum event_type read_token(char **tok) -{ - enum event_type type; - - for (;;) { - type = __read_token(tok); - if (type != EVENT_SPACE) - return type; - - free_token(*tok); - } - - /* not reached */ - return EVENT_NONE; -} - -/* no newline */ -static enum event_type read_token_item(char **tok) -{ - enum event_type type; - - for (;;) { - type = __read_token(tok); - if (type != EVENT_SPACE && type != EVENT_NEWLINE) - return type; - - free_token(*tok); - } - - /* not reached */ - return EVENT_NONE; -} - -static int test_type(enum event_type type, enum event_type expect) -{ - if (type != expect) { - warning("Error: expected type %d but read %d", - expect, type); - return -1; - } - return 0; -} + struct event_format *event; + struct format_field *field; -static int __test_type_token(enum event_type type, char *token, - enum event_type expect, const char *expect_tok, - bool warn) -{ - if (type != expect) { - if (warn) - warning("Error: expected type %d but read %d", - expect, type); - return -1; - } + if (!*size) { + if (!pevent->events) + return 0; - if (strcmp(token, expect_tok) != 0) { - if (warn) - warning("Error: expected '%s' but read '%s'", - expect_tok, token); - return -1; + event = pevent->events[0]; + field = pevent_find_common_field(event, type); + if (!field) + return 0; + *offset = field->offset; + *size = field->size; } - return 0; -} -static int test_type_token(enum event_type type, char *token, - enum event_type expect, const char *expect_tok) -{ - return __test_type_token(type, token, expect, expect_tok, true); + return pevent_read_number(pevent, context->event_data + *offset, *size); } -static int __read_expect_type(enum event_type expect, char **tok, int newline_ok) -{ - enum event_type type; - - if (newline_ok) - type = read_token(tok); - else - type = read_token_item(tok); - return test_type(type, expect); -} - -static int read_expect_type(enum event_type expect, char **tok) -{ - return __read_expect_type(expect, tok, 1); -} - -static int __read_expected(enum event_type expect, const char *str, - int newline_ok, bool warn) +int common_lock_depth(struct scripting_context *context) { - enum event_type type; - char *token; + static int offset; + static int size; int ret; - if (newline_ok) - type = read_token(&token); - else - type = read_token_item(&token); - - ret = __test_type_token(type, token, expect, str, warn); - - free_token(token); + ret = get_common_field(context, &size, &offset, + "common_lock_depth"); + if (ret < 0) + return -1; return ret; } -static int read_expected(enum event_type expect, const char *str) -{ - return __read_expected(expect, str, 1, true); -} - -static int read_expected_item(enum event_type expect, const char *str) -{ - return __read_expected(expect, str, 0, true); -} - -static char *event_read_name(void) -{ - char *token; - - if (read_expected(EVENT_ITEM, "name") < 0) - return NULL; - - if (read_expected(EVENT_OP, ":") < 0) - return NULL; - - if (read_expect_type(EVENT_ITEM, &token) < 0) - goto fail; - - return token; - - fail: - free_token(token); - return NULL; -} - -static int event_read_id(void) +int common_flags(struct scripting_context *context) { - char *token; - int id = -1; - - if (read_expected_item(EVENT_ITEM, "ID") < 0) - return -1; + static int offset; + static int size; + int ret; - if (read_expected(EVENT_OP, ":") < 0) + ret = get_common_field(context, &size, &offset, + "common_flags"); + if (ret < 0) return -1; - if (read_expect_type(EVENT_ITEM, &token) < 0) - goto free; - - id = strtoul(token, NULL, 0); - - free: - free_token(token); - return id; -} - -static int field_is_string(struct format_field *field) -{ - if ((field->flags & FIELD_IS_ARRAY) && - (!strstr(field->type, "char") || !strstr(field->type, "u8") || - !strstr(field->type, "s8"))) - return 1; - - return 0; -} - -static int field_is_dynamic(struct format_field *field) -{ - if (!strncmp(field->type, "__data_loc", 10)) - return 1; - - return 0; -} - -static int event_read_fields(struct event *event, struct format_field **fields) -{ - struct format_field *field = NULL; - enum event_type type; - char *token; - char *last_token; - int count = 0; - - do { - type = read_token(&token); - if (type == EVENT_NEWLINE) { - free_token(token); - return count; - } - - count++; - - if (test_type_token(type, token, EVENT_ITEM, "field")) - goto fail; - free_token(token); - - type = read_token(&token); - /* - * The ftrace fields may still use the "special" name. - * Just ignore it. - */ - if (event->flags & EVENT_FL_ISFTRACE && - type == EVENT_ITEM && strcmp(token, "special") == 0) { - free_token(token); - type = read_token(&token); - } - - if (test_type_token(type, token, EVENT_OP, ":") < 0) - return -1; - - if (read_expect_type(EVENT_ITEM, &token) < 0) - goto fail; - - last_token = token; - - field = malloc_or_die(sizeof(*field)); - memset(field, 0, sizeof(*field)); - - /* read the rest of the type */ - for (;;) { - type = read_token(&token); - if (type == EVENT_ITEM || - (type == EVENT_OP && strcmp(token, "*") == 0) || - /* - * Some of the ftrace fields are broken and have - * an illegal "." in them. - */ - (event->flags & EVENT_FL_ISFTRACE && - type == EVENT_OP && strcmp(token, ".") == 0)) { - - if (strcmp(token, "*") == 0) - field->flags |= FIELD_IS_POINTER; - - if (field->type) { - field->type = realloc(field->type, - strlen(field->type) + - strlen(last_token) + 2); - strcat(field->type, " "); - strcat(field->type, last_token); - } else - field->type = last_token; - last_token = token; - continue; - } - - break; - } - - if (!field->type) { - die("no type found"); - goto fail; - } - field->name = last_token; - - if (test_type(type, EVENT_OP)) - goto fail; - - if (strcmp(token, "[") == 0) { - enum event_type last_type = type; - char *brackets = token; - int len; - - field->flags |= FIELD_IS_ARRAY; - - type = read_token(&token); - while (strcmp(token, "]") != 0) { - if (last_type == EVENT_ITEM && - type == EVENT_ITEM) - len = 2; - else - len = 1; - last_type = type; - - brackets = realloc(brackets, - strlen(brackets) + - strlen(token) + len); - if (len == 2) - strcat(brackets, " "); - strcat(brackets, token); - free_token(token); - type = read_token(&token); - if (type == EVENT_NONE) { - die("failed to find token"); - goto fail; - } - } - - free_token(token); - - brackets = realloc(brackets, strlen(brackets) + 2); - strcat(brackets, "]"); - - /* add brackets to type */ - - type = read_token(&token); - /* - * If the next token is not an OP, then it is of - * the format: type [] item; - */ - if (type == EVENT_ITEM) { - field->type = realloc(field->type, - strlen(field->type) + - strlen(field->name) + - strlen(brackets) + 2); - strcat(field->type, " "); - strcat(field->type, field->name); - free_token(field->name); - strcat(field->type, brackets); - field->name = token; - type = read_token(&token); - } else { - field->type = realloc(field->type, - strlen(field->type) + - strlen(brackets) + 1); - strcat(field->type, brackets); - } - free(brackets); - } - - if (field_is_string(field)) { - field->flags |= FIELD_IS_STRING; - if (field_is_dynamic(field)) - field->flags |= FIELD_IS_DYNAMIC; - } - - if (test_type_token(type, token, EVENT_OP, ";")) - goto fail; - free_token(token); - - if (read_expected(EVENT_ITEM, "offset") < 0) - goto fail_expect; - - if (read_expected(EVENT_OP, ":") < 0) - goto fail_expect; - - if (read_expect_type(EVENT_ITEM, &token)) - goto fail; - field->offset = strtoul(token, NULL, 0); - free_token(token); - - if (read_expected(EVENT_OP, ";") < 0) - goto fail_expect; - - if (read_expected(EVENT_ITEM, "size") < 0) - goto fail_expect; - - if (read_expected(EVENT_OP, ":") < 0) - goto fail_expect; - - if (read_expect_type(EVENT_ITEM, &token)) - goto fail; - field->size = strtoul(token, NULL, 0); - free_token(token); - - if (read_expected(EVENT_OP, ";") < 0) - goto fail_expect; - - type = read_token(&token); - if (type != EVENT_NEWLINE) { - /* newer versions of the kernel have a "signed" type */ - if (test_type_token(type, token, EVENT_ITEM, "signed")) - goto fail; - - free_token(token); - - if (read_expected(EVENT_OP, ":") < 0) - goto fail_expect; - - if (read_expect_type(EVENT_ITEM, &token)) - goto fail; - - if (strtoul(token, NULL, 0)) - field->flags |= FIELD_IS_SIGNED; - - free_token(token); - if (read_expected(EVENT_OP, ";") < 0) - goto fail_expect; - - if (read_expect_type(EVENT_NEWLINE, &token)) - goto fail; - } - - free_token(token); - - *fields = field; - fields = &field->next; - - } while (1); - - return 0; - -fail: - free_token(token); -fail_expect: - if (field) - free(field); - return -1; + return ret; } -static int event_read_format(struct event *event) +int common_pc(struct scripting_context *context) { - char *token; + static int offset; + static int size; int ret; - if (read_expected_item(EVENT_ITEM, "format") < 0) - return -1; - - if (read_expected(EVENT_OP, ":") < 0) - return -1; - - if (read_expect_type(EVENT_NEWLINE, &token)) - goto fail; - free_token(token); - - ret = event_read_fields(event, &event->format.common_fields); + ret = get_common_field(context, &size, &offset, + "common_preempt_count"); if (ret < 0) - return ret; - event->format.nr_common = ret; - - ret = event_read_fields(event, &event->format.fields); - if (ret < 0) - return ret; - event->format.nr_fields = ret; - - return 0; - - fail: - free_token(token); - return -1; -} - -enum event_type -process_arg_token(struct event *event, struct print_arg *arg, - char **tok, enum event_type type); - -static enum event_type -process_arg(struct event *event, struct print_arg *arg, char **tok) -{ - enum event_type type; - char *token; - - type = read_token(&token); - *tok = token; + return -1; - return process_arg_token(event, arg, tok, type); + return ret; } -static enum event_type -process_cond(struct event *event, struct print_arg *top, char **tok) +unsigned long long +raw_field_value(struct event_format *event, const char *name, void *data) { - struct print_arg *arg, *left, *right; - enum event_type type; - char *token = NULL; - - arg = malloc_or_die(sizeof(*arg)); - memset(arg, 0, sizeof(*arg)); - - left = malloc_or_die(sizeof(*left)); - - right = malloc_or_die(sizeof(*right)); - - arg->type = PRINT_OP; - arg->op.left = left; - arg->op.right = right; - - *tok = NULL; - type = process_arg(event, left, &token); - if (test_type_token(type, token, EVENT_OP, ":")) - goto out_free; - - arg->op.op = token; - - type = process_arg(event, right, &token); + struct format_field *field; + unsigned long long val; - top->op.right = arg; + field = pevent_find_any_field(event, name); + if (!field) + return 0ULL; - *tok = token; - return type; + pevent_read_number_field(field, data, &val); -out_free: - free_token(*tok); - free(right); - free(left); - free_arg(arg); - return EVENT_ERROR; + return val; } -static enum event_type -process_array(struct event *event, struct print_arg *top, char **tok) +void *raw_field_ptr(struct event_format *event, const char *name, void *data) { - struct print_arg *arg; - enum event_type type; - char *token = NULL; - - arg = malloc_or_die(sizeof(*arg)); - memset(arg, 0, sizeof(*arg)); - - *tok = NULL; - type = process_arg(event, arg, &token); - if (test_type_token(type, token, EVENT_OP, "]")) - goto out_free; - - top->op.right = arg; - - free_token(token); - type = read_token_item(&token); - *tok = token; - - return type; + struct format_field *field; -out_free: - free_token(*tok); - free_arg(arg); - return EVENT_ERROR; -} + field = pevent_find_any_field(event, name); + if (!field) + return NULL; -static int get_op_prio(char *op) -{ - if (!op[1]) { - switch (op[0]) { - case '*': - case '/': - case '%': - return 6; - case '+': - case '-': - return 7; - /* '>>' and '<<' are 8 */ - case '<': - case '>': - return 9; - /* '==' and '!=' are 10 */ - case '&': - return 11; - case '^': - return 12; - case '|': - return 13; - case '?': - return 16; - default: - die("unknown op '%c'", op[0]); - return -1; - } - } else { - if (strcmp(op, "++") == 0 || - strcmp(op, "--") == 0) { - return 3; - } else if (strcmp(op, ">>") == 0 || - strcmp(op, "<<") == 0) { - return 8; - } else if (strcmp(op, ">=") == 0 || - strcmp(op, "<=") == 0) { - return 9; - } else if (strcmp(op, "==") == 0 || - strcmp(op, "!=") == 0) { - return 10; - } else if (strcmp(op, "&&") == 0) { - return 14; - } else if (strcmp(op, "||") == 0) { - return 15; - } else { - die("unknown op '%s'", op); - return -1; - } - } -} + if (field->flags & FIELD_IS_DYNAMIC) { + int offset; -static void set_op_prio(struct print_arg *arg) -{ + offset = *(int *)(data + field->offset); + offset &= 0xffff; - /* single ops are the greatest */ - if (!arg->op.left || arg->op.left->type == PRINT_NULL) { - arg->op.prio = 0; - return; + return data + offset; } - arg->op.prio = get_op_prio(arg->op.op); + return data + field->offset; } -static enum event_type -process_op(struct event *event, struct print_arg *arg, char **tok) +int trace_parse_common_type(void *data) { - struct print_arg *left, *right = NULL; - enum event_type type; - char *token; - - /* the op is passed in via tok */ - token = *tok; - - if (arg->type == PRINT_OP && !arg->op.left) { - /* handle single op */ - if (token[1]) { - die("bad op token %s", token); - return EVENT_ERROR; - } - switch (token[0]) { - case '!': - case '+': - case '-': - break; - default: - die("bad op token %s", token); - return EVENT_ERROR; - } - - /* make an empty left */ - left = malloc_or_die(sizeof(*left)); - left->type = PRINT_NULL; - arg->op.left = left; - - right = malloc_or_die(sizeof(*right)); - arg->op.right = right; - - type = process_arg(event, right, tok); - - } else if (strcmp(token, "?") == 0) { - - left = malloc_or_die(sizeof(*left)); - /* copy the top arg to the left */ - *left = *arg; - - arg->type = PRINT_OP; - arg->op.op = token; - arg->op.left = left; - arg->op.prio = 0; - - type = process_cond(event, arg, tok); - - } else if (strcmp(token, ">>") == 0 || - strcmp(token, "<<") == 0 || - strcmp(token, "&") == 0 || - strcmp(token, "|") == 0 || - strcmp(token, "&&") == 0 || - strcmp(token, "||") == 0 || - strcmp(token, "-") == 0 || - strcmp(token, "+") == 0 || - strcmp(token, "*") == 0 || - strcmp(token, "^") == 0 || - strcmp(token, "/") == 0 || - strcmp(token, "<") == 0 || - strcmp(token, ">") == 0 || - strcmp(token, "==") == 0 || - strcmp(token, "!=") == 0) { - - left = malloc_or_die(sizeof(*left)); - - /* copy the top arg to the left */ - *left = *arg; - - arg->type = PRINT_OP; - arg->op.op = token; - arg->op.left = left; - - set_op_prio(arg); - - right = malloc_or_die(sizeof(*right)); - - type = read_token_item(&token); - *tok = token; - - /* could just be a type pointer */ - if ((strcmp(arg->op.op, "*") == 0) && - type == EVENT_DELIM && (strcmp(token, ")") == 0)) { - if (left->type != PRINT_ATOM) - die("bad pointer type"); - left->atom.atom = realloc(left->atom.atom, - sizeof(left->atom.atom) + 3); - strcat(left->atom.atom, " *"); - *arg = *left; - free(arg); - - return type; - } - - type = process_arg_token(event, right, tok, type); - - arg->op.right = right; - - } else if (strcmp(token, "[") == 0) { - - left = malloc_or_die(sizeof(*left)); - *left = *arg; - - arg->type = PRINT_OP; - arg->op.op = token; - arg->op.left = left; - - arg->op.prio = 0; - type = process_array(event, arg, tok); - - } else { - warning("unknown op '%s'", token); - event->flags |= EVENT_FL_FAILED; - /* the arg is now the left side */ - return EVENT_NONE; - } - - if (type == EVENT_OP) { - int prio; - - /* higher prios need to be closer to the root */ - prio = get_op_prio(*tok); - - if (prio > arg->op.prio) - return process_op(event, arg, tok); + struct pevent_record record; - return process_op(event, right, tok); - } - - return type; + record.data = data; + return pevent_data_type(pevent, &record); } -static enum event_type -process_entry(struct event *event __unused, struct print_arg *arg, - char **tok) +int trace_parse_common_pid(void *data) { - enum event_type type; - char *field; - char *token; - - if (read_expected(EVENT_OP, "->") < 0) - return EVENT_ERROR; - - if (read_expect_type(EVENT_ITEM, &token) < 0) - goto fail; - field = token; - - arg->type = PRINT_FIELD; - arg->field.name = field; - - if (is_flag_field) { - arg->field.field = find_any_field(event, arg->field.name); - arg->field.field->flags |= FIELD_IS_FLAG; - is_flag_field = 0; - } else if (is_symbolic_field) { - arg->field.field = find_any_field(event, arg->field.name); - arg->field.field->flags |= FIELD_IS_SYMBOLIC; - is_symbolic_field = 0; - } - - type = read_token(&token); - *tok = token; + struct pevent_record record; - return type; - -fail: - free_token(token); - return EVENT_ERROR; + record.data = data; + return pevent_data_pid(pevent, &record); } -static char *arg_eval (struct print_arg *arg); - -static long long arg_num_eval(struct print_arg *arg) +unsigned long long read_size(void *ptr, int size) { - long long left, right; - long long val = 0; - - switch (arg->type) { - case PRINT_ATOM: - val = strtoll(arg->atom.atom, NULL, 0); - break; - case PRINT_TYPE: - val = arg_num_eval(arg->typecast.item); - break; - case PRINT_OP: - switch (arg->op.op[0]) { - case '|': - left = arg_num_eval(arg->op.left); - right = arg_num_eval(arg->op.right); - if (arg->op.op[1]) - val = left || right; - else - val = left | right; - break; - case '&': - left = arg_num_eval(arg->op.left); - right = arg_num_eval(arg->op.right); - if (arg->op.op[1]) - val = left && right; - else - val = left & right; - break; - case '<': - left = arg_num_eval(arg->op.left); - right = arg_num_eval(arg->op.right); - switch (arg->op.op[1]) { - case 0: - val = left < right; - break; - case '<': - val = left << right; - break; - case '=': - val = left <= right; - break; - default: - die("unknown op '%s'", arg->op.op); - } - break; - case '>': - left = arg_num_eval(arg->op.left); - right = arg_num_eval(arg->op.right); - switch (arg->op.op[1]) { - case 0: - val = left > right; - break; - case '>': - val = left >> right; - break; - case '=': - val = left >= right; - break; - default: - die("unknown op '%s'", arg->op.op); - } - break; - case '=': - left = arg_num_eval(arg->op.left); - right = arg_num_eval(arg->op.right); - - if (arg->op.op[1] != '=') - die("unknown op '%s'", arg->op.op); - - val = left == right; - break; - case '!': - left = arg_num_eval(arg->op.left); - right = arg_num_eval(arg->op.right); - - switch (arg->op.op[1]) { - case '=': - val = left != right; - break; - default: - die("unknown op '%s'", arg->op.op); - } - break; - case '+': - left = arg_num_eval(arg->op.left); - right = arg_num_eval(arg->op.right); - val = left + right; - break; - default: - die("unknown op '%s'", arg->op.op); - } - break; - - case PRINT_NULL: - case PRINT_FIELD ... PRINT_SYMBOL: - case PRINT_STRING: - default: - die("invalid eval type %d", arg->type); - - } - return val; + return pevent_read_number(pevent, ptr, size); } -static char *arg_eval (struct print_arg *arg) +struct event_format *trace_find_event(int type) { - long long val; - static char buf[20]; - - switch (arg->type) { - case PRINT_ATOM: - return arg->atom.atom; - case PRINT_TYPE: - return arg_eval(arg->typecast.item); - case PRINT_OP: - val = arg_num_eval(arg); - sprintf(buf, "%lld", val); - return buf; - - case PRINT_NULL: - case PRINT_FIELD ... PRINT_SYMBOL: - case PRINT_STRING: - default: - die("invalid eval type %d", arg->type); - break; - } - - return NULL; + return pevent_find_event(pevent, type); } -static enum event_type -process_fields(struct event *event, struct print_flag_sym **list, char **tok) -{ - enum event_type type; - struct print_arg *arg = NULL; - struct print_flag_sym *field; - char *token = NULL; - char *value; - - do { - free_token(token); - type = read_token_item(&token); - if (test_type_token(type, token, EVENT_OP, "{")) - break; - - arg = malloc_or_die(sizeof(*arg)); - - free_token(token); - type = process_arg(event, arg, &token); - - if (type == EVENT_OP) - type = process_op(event, arg, &token); - - if (type == EVENT_ERROR) - goto out_free; - - if (test_type_token(type, token, EVENT_DELIM, ",")) - goto out_free; - - field = malloc_or_die(sizeof(*field)); - memset(field, 0, sizeof(*field)); - - value = arg_eval(arg); - field->value = strdup(value); - - free_token(token); - type = process_arg(event, arg, &token); - if (test_type_token(type, token, EVENT_OP, "}")) - goto out_free; - - value = arg_eval(arg); - field->str = strdup(value); - free_arg(arg); - arg = NULL; - - *list = field; - list = &field->next; - free_token(token); - type = read_token_item(&token); - } while (type == EVENT_DELIM && strcmp(token, ",") == 0); - - *tok = token; - return type; - -out_free: - free_arg(arg); - free_token(token); - - return EVENT_ERROR; -} - -static enum event_type -process_flags(struct event *event, struct print_arg *arg, char **tok) +void print_trace_event(int cpu, void *data, int size) { - struct print_arg *field; - enum event_type type; - char *token; - - memset(arg, 0, sizeof(*arg)); - arg->type = PRINT_FLAGS; - - if (read_expected_item(EVENT_DELIM, "(") < 0) - return EVENT_ERROR; - - field = malloc_or_die(sizeof(*field)); - - type = process_arg(event, field, &token); - while (type == EVENT_OP) - type = process_op(event, field, &token); - if (test_type_token(type, token, EVENT_DELIM, ",")) - goto out_free; + struct event_format *event; + struct pevent_record record; + struct trace_seq s; + int type; - arg->flags.field = field; + type = trace_parse_common_type(data); - type = read_token_item(&token); - if (event_item_type(type)) { - arg->flags.delim = token; - type = read_token_item(&token); + event = trace_find_event(type); + if (!event) { + warning("ug! no event found for type %d", type); + return; } - if (test_type_token(type, token, EVENT_DELIM, ",")) - goto out_free; - - type = process_fields(event, &arg->flags.flags, &token); - if (test_type_token(type, token, EVENT_DELIM, ")")) - goto out_free; + memset(&record, 0, sizeof(record)); + record.cpu = cpu; + record.size = size; + record.data = data; - free_token(token); - type = read_token_item(tok); - return type; - -out_free: - free_token(token); - return EVENT_ERROR; + trace_seq_init(&s); + pevent_print_event(pevent, &s, &record); + trace_seq_do_printf(&s); + printf("\n"); } -static enum event_type -process_symbols(struct event *event, struct print_arg *arg, char **tok) +void print_event(int cpu, void *data, int size, unsigned long long nsecs, + char *comm) { - struct print_arg *field; - enum event_type type; - char *token; - - memset(arg, 0, sizeof(*arg)); - arg->type = PRINT_SYMBOL; - - if (read_expected_item(EVENT_DELIM, "(") < 0) - return EVENT_ERROR; - - field = malloc_or_die(sizeof(*field)); - - type = process_arg(event, field, &token); - if (test_type_token(type, token, EVENT_DELIM, ",")) - goto out_free; + struct pevent_record record; + struct trace_seq s; + int pid; - arg->symbol.field = field; + pevent->latency_format = latency_format; - type = process_fields(event, &arg->symbol.symbols, &token); - if (test_type_token(type, token, EVENT_DELIM, ")")) - goto out_free; + record.ts = nsecs; + record.cpu = cpu; + record.size = size; + record.data = data; + pid = pevent_data_pid(pevent, &record); - free_token(token); - type = read_token_item(tok); - return type; + if (!pevent_pid_is_registered(pevent, pid)) + pevent_register_comm(pevent, comm, pid); -out_free: - free_token(token); - return EVENT_ERROR; + trace_seq_init(&s); + pevent_print_event(pevent, &s, &record); + trace_seq_do_printf(&s); + printf("\n"); } -static enum event_type -process_paren(struct event *event, struct print_arg *arg, char **tok) +void parse_proc_kallsyms(char *file, unsigned int size __unused) { - struct print_arg *item_arg; - enum event_type type; - char *token; - - type = process_arg(event, arg, &token); - - if (type == EVENT_ERROR) - return EVENT_ERROR; - - if (type == EVENT_OP) - type = process_op(event, arg, &token); - - if (type == EVENT_ERROR) - return EVENT_ERROR; - - if (test_type_token(type, token, EVENT_DELIM, ")")) { - free_token(token); - return EVENT_ERROR; - } - - free_token(token); - type = read_token_item(&token); - - /* - * If the next token is an item or another open paren, then - * this was a typecast. - */ - if (event_item_type(type) || - (type == EVENT_DELIM && strcmp(token, "(") == 0)) { - - /* make this a typecast and contine */ + unsigned long long addr; + char *func; + char *line; + char *next = NULL; + char *addr_str; + char *mod; + char ch; - /* prevous must be an atom */ - if (arg->type != PRINT_ATOM) - die("previous needed to be PRINT_ATOM"); + line = strtok_r(file, "\n", &next); + while (line) { + mod = NULL; + sscanf(line, "%as %c %as\t[%as", + (float *)(void *)&addr_str, /* workaround gcc warning */ + &ch, (float *)(void *)&func, (float *)(void *)&mod); + addr = strtoull(addr_str, NULL, 16); + free(addr_str); - item_arg = malloc_or_die(sizeof(*item_arg)); + /* truncate the extra ']' */ + if (mod) + mod[strlen(mod) - 1] = 0; - arg->type = PRINT_TYPE; - arg->typecast.type = arg->atom.atom; - arg->typecast.item = item_arg; - type = process_arg_token(event, item_arg, &token, type); + pevent_register_function(pevent, func, addr, mod); + free(func); + free(mod); + line = strtok_r(NULL, "\n", &next); } - - *tok = token; - return type; } - -static enum event_type -process_str(struct event *event __unused, struct print_arg *arg, char **tok) +void parse_ftrace_printk(char *file, unsigned int size __unused) { - enum event_type type; - char *token; - - if (read_expected(EVENT_DELIM, "(") < 0) - return EVENT_ERROR; - - if (read_expect_type(EVENT_ITEM, &token) < 0) - goto fail; - - arg->type = PRINT_STRING; - arg->string.string = token; - arg->string.offset = -1; - - if (read_expected(EVENT_DELIM, ")") < 0) - return EVENT_ERROR; - - type = read_token(&token); - *tok = token; - - return type; -fail: - free_token(token); - return EVENT_ERROR; -} + unsigned long long addr; + char *printk; + char *line; + char *next = NULL; + char *addr_str; + char *fmt; -enum event_type -process_arg_token(struct event *event, struct print_arg *arg, - char **tok, enum event_type type) -{ - char *token; - char *atom; - - token = *tok; - - switch (type) { - case EVENT_ITEM: - if (strcmp(token, "REC") == 0) { - free_token(token); - type = process_entry(event, arg, &token); - } else if (strcmp(token, "__print_flags") == 0) { - free_token(token); - is_flag_field = 1; - type = process_flags(event, arg, &token); - } else if (strcmp(token, "__print_symbolic") == 0) { - free_token(token); - is_symbolic_field = 1; - type = process_symbols(event, arg, &token); - } else if (strcmp(token, "__get_str") == 0) { - free_token(token); - type = process_str(event, arg, &token); - } else { - atom = token; - /* test the next token */ - type = read_token_item(&token); - - /* atoms can be more than one token long */ - while (type == EVENT_ITEM) { - atom = realloc(atom, strlen(atom) + strlen(token) + 2); - strcat(atom, " "); - strcat(atom, token); - free_token(token); - type = read_token_item(&token); - } - - /* todo, test for function */ - - arg->type = PRINT_ATOM; - arg->atom.atom = atom; - } - break; - case EVENT_DQUOTE: - case EVENT_SQUOTE: - arg->type = PRINT_ATOM; - arg->atom.atom = token; - type = read_token_item(&token); - break; - case EVENT_DELIM: - if (strcmp(token, "(") == 0) { - free_token(token); - type = process_paren(event, arg, &token); + line = strtok_r(file, "\n", &next); + while (line) { + addr_str = strtok_r(line, ":", &fmt); + if (!addr_str) { + warning("printk format with empty entry"); break; } - case EVENT_OP: - /* handle single ops */ - arg->type = PRINT_OP; - arg->op.op = token; - arg->op.left = NULL; - type = process_op(event, arg, &token); - - break; - - case EVENT_ERROR ... EVENT_NEWLINE: - default: - die("unexpected type %d", type); - } - *tok = token; - - return type; -} - -static int event_read_print_args(struct event *event, struct print_arg **list) -{ - enum event_type type = EVENT_ERROR; - struct print_arg *arg; - char *token; - int args = 0; - - do { - if (type == EVENT_NEWLINE) { - free_token(token); - type = read_token_item(&token); - continue; - } - - arg = malloc_or_die(sizeof(*arg)); - memset(arg, 0, sizeof(*arg)); - - type = process_arg(event, arg, &token); - - if (type == EVENT_ERROR) { - free_arg(arg); - return -1; - } - - *list = arg; - args++; - - if (type == EVENT_OP) { - type = process_op(event, arg, &token); - list = &arg->next; - continue; - } - - if (type == EVENT_DELIM && strcmp(token, ",") == 0) { - free_token(token); - *list = arg; - list = &arg->next; - continue; - } - break; - } while (type != EVENT_NONE); - - if (type != EVENT_NONE) - free_token(token); - - return args; -} - -static int event_read_print(struct event *event) -{ - enum event_type type; - char *token; - int ret; - - if (read_expected_item(EVENT_ITEM, "print") < 0) - return -1; - - if (read_expected(EVENT_ITEM, "fmt") < 0) - return -1; - - if (read_expected(EVENT_OP, ":") < 0) - return -1; - - if (read_expect_type(EVENT_DQUOTE, &token) < 0) - goto fail; - - concat: - event->print_fmt.format = token; - event->print_fmt.args = NULL; - - /* ok to have no arg */ - type = read_token_item(&token); - - if (type == EVENT_NONE) - return 0; - - /* Handle concatination of print lines */ - if (type == EVENT_DQUOTE) { - char *cat; - - cat = malloc_or_die(strlen(event->print_fmt.format) + - strlen(token) + 1); - strcpy(cat, event->print_fmt.format); - strcat(cat, token); - free_token(token); - free_token(event->print_fmt.format); - event->print_fmt.format = NULL; - token = cat; - goto concat; - } - - if (test_type_token(type, token, EVENT_DELIM, ",")) - goto fail; - - free_token(token); - - ret = event_read_print_args(event, &event->print_fmt.args); - if (ret < 0) - return -1; - - return ret; - - fail: - free_token(token); - return -1; -} - -static struct format_field * -find_common_field(struct event *event, const char *name) -{ - struct format_field *format; - - for (format = event->format.common_fields; - format; format = format->next) { - if (strcmp(format->name, name) == 0) - break; - } - - return format; -} - -static struct format_field * -find_field(struct event *event, const char *name) -{ - struct format_field *format; - - for (format = event->format.fields; - format; format = format->next) { - if (strcmp(format->name, name) == 0) - break; + addr = strtoull(addr_str, NULL, 16); + /* fmt still has a space, skip it */ + printk = strdup(fmt+1); + line = strtok_r(NULL, "\n", &next); + pevent_register_print_string(pevent, printk, addr); } - - return format; } -static struct format_field * -find_any_field(struct event *event, const char *name) +int parse_ftrace_file(char *buf, unsigned long size) { - struct format_field *format; - - format = find_common_field(event, name); - if (format) - return format; - return find_field(event, name); + return pevent_parse_event(pevent, buf, size, "ftrace"); } -unsigned long long read_size(void *ptr, int size) +int parse_event_file(char *buf, unsigned long size, char *sys) { - switch (size) { - case 1: - return *(unsigned char *)ptr; - case 2: - return data2host2(ptr); - case 4: - return data2host4(ptr); - case 8: - return data2host8(ptr); - default: - /* BUG! */ - return 0; - } + return pevent_parse_event(pevent, buf, size, sys); } -unsigned long long -raw_field_value(struct event *event, const char *name, void *data) +struct event_format *trace_find_next_event(struct event_format *event) { - struct format_field *field; - - field = find_any_field(event, name); - if (!field) - return 0ULL; + static int idx; - return read_size(data + field->offset, field->size); -} - -void *raw_field_ptr(struct event *event, const char *name, void *data) -{ - struct format_field *field; - - field = find_any_field(event, name); - if (!field) + if (!pevent->events) return NULL; - if (field->flags & FIELD_IS_DYNAMIC) { - int offset; - - offset = *(int *)(data + field->offset); - offset &= 0xffff; - - return data + offset; - } - - return data + field->offset; -} - -static int get_common_info(const char *type, int *offset, int *size) -{ - struct event *event; - struct format_field *field; - - /* - * All events should have the same common elements. - * Pick any event to find where the type is; - */ - if (!event_list) - die("no event_list!"); - - event = event_list; - field = find_common_field(event, type); - if (!field) - die("field '%s' not found", type); - - *offset = field->offset; - *size = field->size; - - return 0; -} - -static int __parse_common(void *data, int *size, int *offset, - const char *name) -{ - int ret; - - if (!*size) { - ret = get_common_info(name, offset, size); - if (ret < 0) - return ret; + if (!event) { + idx = 0; + return pevent->events[0]; } - return read_size(data + *offset, *size); -} - -int trace_parse_common_type(void *data) -{ - static int type_offset; - static int type_size; - - return __parse_common(data, &type_size, &type_offset, - "common_type"); -} - -int trace_parse_common_pid(void *data) -{ - static int pid_offset; - static int pid_size; - - return __parse_common(data, &pid_size, &pid_offset, - "common_pid"); -} - -int parse_common_pc(void *data) -{ - static int pc_offset; - static int pc_size; - - return __parse_common(data, &pc_size, &pc_offset, - "common_preempt_count"); -} - -int parse_common_flags(void *data) -{ - static int flags_offset; - static int flags_size; - - return __parse_common(data, &flags_size, &flags_offset, - "common_flags"); -} - -int parse_common_lock_depth(void *data) -{ - static int ld_offset; - static int ld_size; - int ret; - ret = __parse_common(data, &ld_size, &ld_offset, - "common_lock_depth"); - if (ret < 0) - return -1; - - return ret; -} - -struct event *trace_find_event(int id) -{ - struct event *event; - - for (event = event_list; event; event = event->next) { - if (event->id == id) - break; + if (idx < pevent->nr_events && event == pevent->events[idx]) { + idx++; + if (idx == pevent->nr_events) + return NULL; + return pevent->events[idx]; } - return event; -} - -struct event *trace_find_next_event(struct event *event) -{ - if (!event) - return event_list; - - return event->next; -} - -static unsigned long long eval_num_arg(void *data, int size, - struct event *event, struct print_arg *arg) -{ - unsigned long long val = 0; - unsigned long long left, right; - struct print_arg *larg; - switch (arg->type) { - case PRINT_NULL: - /* ?? */ - return 0; - case PRINT_ATOM: - return strtoull(arg->atom.atom, NULL, 0); - case PRINT_FIELD: - if (!arg->field.field) { - arg->field.field = find_any_field(event, arg->field.name); - if (!arg->field.field) - die("field %s not found", arg->field.name); - } - /* must be a number */ - val = read_size(data + arg->field.field->offset, - arg->field.field->size); - break; - case PRINT_FLAGS: - case PRINT_SYMBOL: - break; - case PRINT_TYPE: - return eval_num_arg(data, size, event, arg->typecast.item); - case PRINT_STRING: - return 0; - break; - case PRINT_OP: - if (strcmp(arg->op.op, "[") == 0) { - /* - * Arrays are special, since we don't want - * to read the arg as is. - */ - if (arg->op.left->type != PRINT_FIELD) - goto default_op; /* oops, all bets off */ - larg = arg->op.left; - if (!larg->field.field) { - larg->field.field = - find_any_field(event, larg->field.name); - if (!larg->field.field) - die("field %s not found", larg->field.name); - } - right = eval_num_arg(data, size, event, arg->op.right); - val = read_size(data + larg->field.field->offset + - right * long_size, long_size); - break; - } - default_op: - left = eval_num_arg(data, size, event, arg->op.left); - right = eval_num_arg(data, size, event, arg->op.right); - switch (arg->op.op[0]) { - case '|': - if (arg->op.op[1]) - val = left || right; - else - val = left | right; - break; - case '&': - if (arg->op.op[1]) - val = left && right; - else - val = left & right; - break; - case '<': - switch (arg->op.op[1]) { - case 0: - val = left < right; - break; - case '<': - val = left << right; - break; - case '=': - val = left <= right; - break; - default: - die("unknown op '%s'", arg->op.op); - } - break; - case '>': - switch (arg->op.op[1]) { - case 0: - val = left > right; - break; - case '>': - val = left >> right; - break; - case '=': - val = left >= right; - break; - default: - die("unknown op '%s'", arg->op.op); - } - break; - case '=': - if (arg->op.op[1] != '=') - die("unknown op '%s'", arg->op.op); - val = left == right; - break; - case '-': - val = left - right; - break; - case '+': - val = left + right; - break; - default: - die("unknown op '%s'", arg->op.op); - } - break; - default: /* not sure what to do there */ - return 0; + for (idx = 1; idx < pevent->nr_events; idx++) { + if (event == pevent->events[idx - 1]) + return pevent->events[idx]; } - return val; + return NULL; } struct flag { @@ -2221,933 +357,3 @@ unsigned long long eval_flag(const char *flag) return 0; } - -static void print_str_arg(void *data, int size, - struct event *event, struct print_arg *arg) -{ - struct print_flag_sym *flag; - unsigned long long val, fval; - char *str; - int print; - - switch (arg->type) { - case PRINT_NULL: - /* ?? */ - return; - case PRINT_ATOM: - printf("%s", arg->atom.atom); - return; - case PRINT_FIELD: - if (!arg->field.field) { - arg->field.field = find_any_field(event, arg->field.name); - if (!arg->field.field) - die("field %s not found", arg->field.name); - } - str = malloc_or_die(arg->field.field->size + 1); - memcpy(str, data + arg->field.field->offset, - arg->field.field->size); - str[arg->field.field->size] = 0; - printf("%s", str); - free(str); - break; - case PRINT_FLAGS: - val = eval_num_arg(data, size, event, arg->flags.field); - print = 0; - for (flag = arg->flags.flags; flag; flag = flag->next) { - fval = eval_flag(flag->value); - if (!val && !fval) { - printf("%s", flag->str); - break; - } - if (fval && (val & fval) == fval) { - if (print && arg->flags.delim) - printf("%s", arg->flags.delim); - printf("%s", flag->str); - print = 1; - val &= ~fval; - } - } - break; - case PRINT_SYMBOL: - val = eval_num_arg(data, size, event, arg->symbol.field); - for (flag = arg->symbol.symbols; flag; flag = flag->next) { - fval = eval_flag(flag->value); - if (val == fval) { - printf("%s", flag->str); - break; - } - } - break; - - case PRINT_TYPE: - break; - case PRINT_STRING: { - int str_offset; - - if (arg->string.offset == -1) { - struct format_field *f; - - f = find_any_field(event, arg->string.string); - arg->string.offset = f->offset; - } - str_offset = *(int *)(data + arg->string.offset); - str_offset &= 0xffff; - printf("%s", ((char *)data) + str_offset); - break; - } - case PRINT_OP: - /* - * The only op for string should be ? : - */ - if (arg->op.op[0] != '?') - return; - val = eval_num_arg(data, size, event, arg->op.left); - if (val) - print_str_arg(data, size, event, arg->op.right->op.left); - else - print_str_arg(data, size, event, arg->op.right->op.right); - break; - default: - /* well... */ - break; - } -} - -static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struct event *event) -{ - static struct format_field *field, *ip_field; - struct print_arg *args, *arg, **next; - unsigned long long ip, val; - char *ptr; - void *bptr; - - if (!field) { - field = find_field(event, "buf"); - if (!field) - die("can't find buffer field for binary printk"); - ip_field = find_field(event, "ip"); - if (!ip_field) - die("can't find ip field for binary printk"); - } - - ip = read_size(data + ip_field->offset, ip_field->size); - - /* - * The first arg is the IP pointer. - */ - args = malloc_or_die(sizeof(*args)); - arg = args; - arg->next = NULL; - next = &arg->next; - - arg->type = PRINT_ATOM; - arg->atom.atom = malloc_or_die(32); - sprintf(arg->atom.atom, "%lld", ip); - - /* skip the first "%pf : " */ - for (ptr = fmt + 6, bptr = data + field->offset; - bptr < data + size && *ptr; ptr++) { - int ls = 0; - - if (*ptr == '%') { - process_again: - ptr++; - switch (*ptr) { - case '%': - break; - case 'l': - ls++; - goto process_again; - case 'L': - ls = 2; - goto process_again; - case '0' ... '9': - goto process_again; - case 'p': - ls = 1; - /* fall through */ - case 'd': - case 'u': - case 'x': - case 'i': - /* the pointers are always 4 bytes aligned */ - bptr = (void *)(((unsigned long)bptr + 3) & - ~3); - switch (ls) { - case 0: - case 1: - ls = long_size; - break; - case 2: - ls = 8; - default: - break; - } - val = read_size(bptr, ls); - bptr += ls; - arg = malloc_or_die(sizeof(*arg)); - arg->next = NULL; - arg->type = PRINT_ATOM; - arg->atom.atom = malloc_or_die(32); - sprintf(arg->atom.atom, "%lld", val); - *next = arg; - next = &arg->next; - break; - case 's': - arg = malloc_or_die(sizeof(*arg)); - arg->next = NULL; - arg->type = PRINT_STRING; - arg->string.string = strdup(bptr); - bptr += strlen(bptr) + 1; - *next = arg; - next = &arg->next; - default: - break; - } - } - } - - return args; -} - -static void free_args(struct print_arg *args) -{ - struct print_arg *next; - - while (args) { - next = args->next; - - if (args->type == PRINT_ATOM) - free(args->atom.atom); - else - free(args->string.string); - free(args); - args = next; - } -} - -static char *get_bprint_format(void *data, int size __unused, struct event *event) -{ - unsigned long long addr; - static struct format_field *field; - struct printk_map *printk; - char *format; - char *p; - - if (!field) { - field = find_field(event, "fmt"); - if (!field) - die("can't find format field for binary printk"); - printf("field->offset = %d size=%d\n", field->offset, field->size); - } - - addr = read_size(data + field->offset, field->size); - - printk = find_printk(addr); - if (!printk) { - format = malloc_or_die(45); - sprintf(format, "%%pf : (NO FORMAT FOUND at %llx)\n", - addr); - return format; - } - - p = printk->printk; - /* Remove any quotes. */ - if (*p == '"') - p++; - format = malloc_or_die(strlen(p) + 10); - sprintf(format, "%s : %s", "%pf", p); - /* remove ending quotes and new line since we will add one too */ - p = format + strlen(format) - 1; - if (*p == '"') - *p = 0; - - p -= 2; - if (strcmp(p, "\\n") == 0) - *p = 0; - - return format; -} - -static void pretty_print(void *data, int size, struct event *event) -{ - struct print_fmt *print_fmt = &event->print_fmt; - struct print_arg *arg = print_fmt->args; - struct print_arg *args = NULL; - const char *ptr = print_fmt->format; - unsigned long long val; - struct func_map *func; - const char *saveptr; - char *bprint_fmt = NULL; - char format[32]; - int show_func; - int len; - int ls; - - if (event->flags & EVENT_FL_ISFUNC) - ptr = " %pF <-- %pF"; - - if (event->flags & EVENT_FL_ISBPRINT) { - bprint_fmt = get_bprint_format(data, size, event); - args = make_bprint_args(bprint_fmt, data, size, event); - arg = args; - ptr = bprint_fmt; - } - - for (; *ptr; ptr++) { - ls = 0; - if (*ptr == '\\') { - ptr++; - switch (*ptr) { - case 'n': - printf("\n"); - break; - case 't': - printf("\t"); - break; - case 'r': - printf("\r"); - break; - case '\\': - printf("\\"); - break; - default: - printf("%c", *ptr); - break; - } - - } else if (*ptr == '%') { - saveptr = ptr; - show_func = 0; - cont_process: - ptr++; - switch (*ptr) { - case '%': - printf("%%"); - break; - case 'l': - ls++; - goto cont_process; - case 'L': - ls = 2; - goto cont_process; - case 'z': - case 'Z': - case '0' ... '9': - goto cont_process; - case 'p': - if (long_size == 4) - ls = 1; - else - ls = 2; - - if (*(ptr+1) == 'F' || - *(ptr+1) == 'f') { - ptr++; - show_func = *ptr; - } - - /* fall through */ - case 'd': - case 'i': - case 'x': - case 'X': - case 'u': - if (!arg) - die("no argument match"); - - len = ((unsigned long)ptr + 1) - - (unsigned long)saveptr; - - /* should never happen */ - if (len > 32) - die("bad format!"); - - memcpy(format, saveptr, len); - format[len] = 0; - - val = eval_num_arg(data, size, event, arg); - arg = arg->next; - - if (show_func) { - func = find_func(val); - if (func) { - printf("%s", func->func); - if (show_func == 'F') - printf("+0x%llx", - val - func->addr); - break; - } - } - switch (ls) { - case 0: - printf(format, (int)val); - break; - case 1: - printf(format, (long)val); - break; - case 2: - printf(format, (long long)val); - break; - default: - die("bad count (%d)", ls); - } - break; - case 's': - if (!arg) - die("no matching argument"); - - print_str_arg(data, size, event, arg); - arg = arg->next; - break; - default: - printf(">%c<", *ptr); - - } - } else - printf("%c", *ptr); - } - - if (args) { - free_args(args); - free(bprint_fmt); - } -} - -static inline int log10_cpu(int nb) -{ - if (nb / 100) - return 3; - if (nb / 10) - return 2; - return 1; -} - -static void print_lat_fmt(void *data, int size __unused) -{ - unsigned int lat_flags; - unsigned int pc; - int lock_depth; - int hardirq; - int softirq; - - lat_flags = parse_common_flags(data); - pc = parse_common_pc(data); - lock_depth = parse_common_lock_depth(data); - - hardirq = lat_flags & TRACE_FLAG_HARDIRQ; - softirq = lat_flags & TRACE_FLAG_SOFTIRQ; - - printf("%c%c%c", - (lat_flags & TRACE_FLAG_IRQS_OFF) ? 'd' : - (lat_flags & TRACE_FLAG_IRQS_NOSUPPORT) ? - 'X' : '.', - (lat_flags & TRACE_FLAG_NEED_RESCHED) ? - 'N' : '.', - (hardirq && softirq) ? 'H' : - hardirq ? 'h' : softirq ? 's' : '.'); - - if (pc) - printf("%x", pc); - else - printf("."); - - if (lock_depth < 0) - printf(". "); - else - printf("%d ", lock_depth); -} - -#define TRACE_GRAPH_INDENT 2 - -static struct record * -get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func, - struct record *next) -{ - struct format_field *field; - struct event *event; - unsigned long val; - int type; - int pid; - - type = trace_parse_common_type(next->data); - event = trace_find_event(type); - if (!event) - return NULL; - - if (!(event->flags & EVENT_FL_ISFUNCRET)) - return NULL; - - pid = trace_parse_common_pid(next->data); - field = find_field(event, "func"); - if (!field) - die("function return does not have field func"); - - val = read_size(next->data + field->offset, field->size); - - if (cur_pid != pid || cur_func != val) - return NULL; - - /* this is a leaf, now advance the iterator */ - return trace_read_data(cpu); -} - -/* Signal a overhead of time execution to the output */ -static void print_graph_overhead(unsigned long long duration) -{ - /* Non nested entry or return */ - if (duration == ~0ULL) - return (void)printf(" "); - - /* Duration exceeded 100 msecs */ - if (duration > 100000ULL) - return (void)printf("! "); - - /* Duration exceeded 10 msecs */ - if (duration > 10000ULL) - return (void)printf("+ "); - - printf(" "); -} - -static void print_graph_duration(unsigned long long duration) -{ - unsigned long usecs = duration / 1000; - unsigned long nsecs_rem = duration % 1000; - /* log10(ULONG_MAX) + '\0' */ - char msecs_str[21]; - char nsecs_str[5]; - int len; - int i; - - sprintf(msecs_str, "%lu", usecs); - - /* Print msecs */ - len = printf("%lu", usecs); - - /* Print nsecs (we don't want to exceed 7 numbers) */ - if (len < 7) { - snprintf(nsecs_str, 8 - len, "%03lu", nsecs_rem); - len += printf(".%s", nsecs_str); - } - - printf(" us "); - - /* Print remaining spaces to fit the row's width */ - for (i = len; i < 7; i++) - printf(" "); - - printf("| "); -} - -static void -print_graph_entry_leaf(struct event *event, void *data, struct record *ret_rec) -{ - unsigned long long rettime, calltime; - unsigned long long duration, depth; - unsigned long long val; - struct format_field *field; - struct func_map *func; - struct event *ret_event; - int type; - int i; - - type = trace_parse_common_type(ret_rec->data); - ret_event = trace_find_event(type); - - field = find_field(ret_event, "rettime"); - if (!field) - die("can't find rettime in return graph"); - rettime = read_size(ret_rec->data + field->offset, field->size); - - field = find_field(ret_event, "calltime"); - if (!field) - die("can't find rettime in return graph"); - calltime = read_size(ret_rec->data + field->offset, field->size); - - duration = rettime - calltime; - - /* Overhead */ - print_graph_overhead(duration); - - /* Duration */ - print_graph_duration(duration); - - field = find_field(event, "depth"); - if (!field) - die("can't find depth in entry graph"); - depth = read_size(data + field->offset, field->size); - - /* Function */ - for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++) - printf(" "); - - field = find_field(event, "func"); - if (!field) - die("can't find func in entry graph"); - val = read_size(data + field->offset, field->size); - func = find_func(val); - - if (func) - printf("%s();", func->func); - else - printf("%llx();", val); -} - -static void print_graph_nested(struct event *event, void *data) -{ - struct format_field *field; - unsigned long long depth; - unsigned long long val; - struct func_map *func; - int i; - - /* No overhead */ - print_graph_overhead(-1); - - /* No time */ - printf(" | "); - - field = find_field(event, "depth"); - if (!field) - die("can't find depth in entry graph"); - depth = read_size(data + field->offset, field->size); - - /* Function */ - for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++) - printf(" "); - - field = find_field(event, "func"); - if (!field) - die("can't find func in entry graph"); - val = read_size(data + field->offset, field->size); - func = find_func(val); - - if (func) - printf("%s() {", func->func); - else - printf("%llx() {", val); -} - -static void -pretty_print_func_ent(void *data, int size, struct event *event, - int cpu, int pid) -{ - struct format_field *field; - struct record *rec; - void *copy_data; - unsigned long val; - - if (latency_format) { - print_lat_fmt(data, size); - printf(" | "); - } - - field = find_field(event, "func"); - if (!field) - die("function entry does not have func field"); - - val = read_size(data + field->offset, field->size); - - /* - * peek_data may unmap the data pointer. Copy it first. - */ - copy_data = malloc_or_die(size); - memcpy(copy_data, data, size); - data = copy_data; - - rec = trace_peek_data(cpu); - if (rec) { - rec = get_return_for_leaf(cpu, pid, val, rec); - if (rec) { - print_graph_entry_leaf(event, data, rec); - goto out_free; - } - } - print_graph_nested(event, data); -out_free: - free(data); -} - -static void -pretty_print_func_ret(void *data, int size __unused, struct event *event) -{ - unsigned long long rettime, calltime; - unsigned long long duration, depth; - struct format_field *field; - int i; - - if (latency_format) { - print_lat_fmt(data, size); - printf(" | "); - } - - field = find_field(event, "rettime"); - if (!field) - die("can't find rettime in return graph"); - rettime = read_size(data + field->offset, field->size); - - field = find_field(event, "calltime"); - if (!field) - die("can't find calltime in return graph"); - calltime = read_size(data + field->offset, field->size); - - duration = rettime - calltime; - - /* Overhead */ - print_graph_overhead(duration); - - /* Duration */ - print_graph_duration(duration); - - field = find_field(event, "depth"); - if (!field) - die("can't find depth in entry graph"); - depth = read_size(data + field->offset, field->size); - - /* Function */ - for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++) - printf(" "); - - printf("}"); -} - -static void -pretty_print_func_graph(void *data, int size, struct event *event, - int cpu, int pid) -{ - if (event->flags & EVENT_FL_ISFUNCENT) - pretty_print_func_ent(data, size, event, cpu, pid); - else if (event->flags & EVENT_FL_ISFUNCRET) - pretty_print_func_ret(data, size, event); - printf("\n"); -} - -void print_trace_event(int cpu, void *data, int size) -{ - struct event *event; - int type; - int pid; - - type = trace_parse_common_type(data); - - event = trace_find_event(type); - if (!event) { - warning("ug! no event found for type %d", type); - return; - } - - pid = trace_parse_common_pid(data); - - if (event->flags & (EVENT_FL_ISFUNCENT | EVENT_FL_ISFUNCRET)) - return pretty_print_func_graph(data, size, event, cpu, pid); - - if (latency_format) - print_lat_fmt(data, size); - - if (event->flags & EVENT_FL_FAILED) { - printf("EVENT '%s' FAILED TO PARSE\n", - event->name); - return; - } - - pretty_print(data, size, event); -} - -static void print_fields(struct print_flag_sym *field) -{ - printf("{ %s, %s }", field->value, field->str); - if (field->next) { - printf(", "); - print_fields(field->next); - } -} - -static void print_args(struct print_arg *args) -{ - int print_paren = 1; - - switch (args->type) { - case PRINT_NULL: - printf("null"); - break; - case PRINT_ATOM: - printf("%s", args->atom.atom); - break; - case PRINT_FIELD: - printf("REC->%s", args->field.name); - break; - case PRINT_FLAGS: - printf("__print_flags("); - print_args(args->flags.field); - printf(", %s, ", args->flags.delim); - print_fields(args->flags.flags); - printf(")"); - break; - case PRINT_SYMBOL: - printf("__print_symbolic("); - print_args(args->symbol.field); - printf(", "); - print_fields(args->symbol.symbols); - printf(")"); - break; - case PRINT_STRING: - printf("__get_str(%s)", args->string.string); - break; - case PRINT_TYPE: - printf("(%s)", args->typecast.type); - print_args(args->typecast.item); - break; - case PRINT_OP: - if (strcmp(args->op.op, ":") == 0) - print_paren = 0; - if (print_paren) - printf("("); - print_args(args->op.left); - printf(" %s ", args->op.op); - print_args(args->op.right); - if (print_paren) - printf(")"); - break; - default: - /* we should warn... */ - return; - } - if (args->next) { - printf("\n"); - print_args(args->next); - } -} - -int parse_ftrace_file(char *buf, unsigned long size) -{ - struct format_field *field; - struct print_arg *arg, **list; - struct event *event; - int ret; - - init_input_buf(buf, size); - - event = alloc_event(); - if (!event) - return -ENOMEM; - - event->flags |= EVENT_FL_ISFTRACE; - - event->name = event_read_name(); - if (!event->name) - die("failed to read ftrace event name"); - - if (strcmp(event->name, "function") == 0) - event->flags |= EVENT_FL_ISFUNC; - - else if (strcmp(event->name, "funcgraph_entry") == 0) - event->flags |= EVENT_FL_ISFUNCENT; - - else if (strcmp(event->name, "funcgraph_exit") == 0) - event->flags |= EVENT_FL_ISFUNCRET; - - else if (strcmp(event->name, "bprint") == 0) - event->flags |= EVENT_FL_ISBPRINT; - - event->id = event_read_id(); - if (event->id < 0) - die("failed to read ftrace event id"); - - add_event(event); - - ret = event_read_format(event); - if (ret < 0) - die("failed to read ftrace event format"); - - ret = event_read_print(event); - if (ret < 0) - die("failed to read ftrace event print fmt"); - - /* New ftrace handles args */ - if (ret > 0) - return 0; - /* - * The arguments for ftrace files are parsed by the fields. - * Set up the fields as their arguments. - */ - list = &event->print_fmt.args; - for (field = event->format.fields; field; field = field->next) { - arg = malloc_or_die(sizeof(*arg)); - memset(arg, 0, sizeof(*arg)); - *list = arg; - list = &arg->next; - arg->type = PRINT_FIELD; - arg->field.name = field->name; - arg->field.field = field; - } - return 0; -} - -int parse_event_file(char *buf, unsigned long size, char *sys) -{ - struct event *event; - int ret; - - init_input_buf(buf, size); - - event = alloc_event(); - if (!event) - return -ENOMEM; - - event->name = event_read_name(); - if (!event->name) - die("failed to read event name"); - - event->id = event_read_id(); - if (event->id < 0) - die("failed to read event id"); - - ret = event_read_format(event); - if (ret < 0) { - warning("failed to read event format for %s", event->name); - goto event_failed; - } - - ret = event_read_print(event); - if (ret < 0) { - warning("failed to read event print fmt for %s", event->name); - goto event_failed; - } - - event->system = strdup(sys); - -#define PRINT_ARGS 0 - if (PRINT_ARGS && event->print_fmt.args) - print_args(event->print_fmt.args); - - add_event(event); - return 0; - - event_failed: - event->flags |= EVENT_FL_FAILED; - /* still add it even if it failed */ - add_event(event); - return -1; -} - -void parse_set_info(int nr_cpus, int long_sz) -{ - cpus = nr_cpus; - long_size = long_sz; -} - -int common_pc(struct scripting_context *context) -{ - return parse_common_pc(context->event_data); -} - -int common_flags(struct scripting_context *context) -{ - return parse_common_flags(context->event_data); -} - -int common_lock_depth(struct scripting_context *context) -{ - return parse_common_lock_depth(context->event_data); -} diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index b9592e0de8d..f097e0dd6c5 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -52,6 +52,16 @@ static unsigned long page_size; static ssize_t calc_data_size; static bool repipe; +static void *malloc_or_die(int size) +{ + void *ret; + + ret = malloc(size); + if (!ret) + die("malloc"); + return ret; +} + static int do_read(int fd, void *buf, int size) { int rsize = size; @@ -109,7 +119,7 @@ static unsigned int read4(void) unsigned int data; read_or_die(&data, 4); - return __data2host4(data); + return __data2host4(perf_pevent, data); } static unsigned long long read8(void) @@ -117,7 +127,7 @@ static unsigned long long read8(void) unsigned long long data; read_or_die(&data, 8); - return __data2host8(data); + return __data2host8(perf_pevent, data); } static char *read_string(void) @@ -282,7 +292,7 @@ struct cpu_data { unsigned long long offset; unsigned long long size; unsigned long long timestamp; - struct record *next; + struct pevent_record *next; char *page; int cpu; int index; @@ -367,9 +377,9 @@ static int calc_index(void *ptr, int cpu) return (unsigned long)ptr - (unsigned long)cpu_data[cpu].page; } -struct record *trace_peek_data(int cpu) +struct pevent_record *trace_peek_data(int cpu) { - struct record *data; + struct pevent_record *data; void *page = cpu_data[cpu].page; int idx = cpu_data[cpu].index; void *ptr = page + idx; @@ -389,15 +399,15 @@ struct record *trace_peek_data(int cpu) /* FIXME: handle header page */ if (header_page_ts_size != 8) die("expected a long long type for timestamp"); - cpu_data[cpu].timestamp = data2host8(ptr); + cpu_data[cpu].timestamp = data2host8(perf_pevent, ptr); ptr += 8; switch (header_page_size_size) { case 4: - cpu_data[cpu].page_size = data2host4(ptr); + cpu_data[cpu].page_size = data2host4(perf_pevent, ptr); ptr += 4; break; case 8: - cpu_data[cpu].page_size = data2host8(ptr); + cpu_data[cpu].page_size = data2host8(perf_pevent, ptr); ptr += 8; break; default: @@ -414,7 +424,7 @@ read_again: return trace_peek_data(cpu); } - type_len_ts = data2host4(ptr); + type_len_ts = data2host4(perf_pevent, ptr); ptr += 4; type_len = type_len4host(type_len_ts); @@ -424,14 +434,14 @@ read_again: case RINGBUF_TYPE_PADDING: if (!delta) die("error, hit unexpected end of page"); - length = data2host4(ptr); + length = data2host4(perf_pevent, ptr); ptr += 4; length *= 4; ptr += length; goto read_again; case RINGBUF_TYPE_TIME_EXTEND: - extend = data2host4(ptr); + extend = data2host4(perf_pevent, ptr); ptr += 4; extend <<= TS_SHIFT; extend += delta; @@ -442,7 +452,7 @@ read_again: ptr += 12; break; case 0: - length = data2host4(ptr); + length = data2host4(perf_pevent, ptr); ptr += 4; die("here! length=%d", length); break; @@ -467,9 +477,9 @@ read_again: return data; } -struct record *trace_read_data(int cpu) +struct pevent_record *trace_read_data(int cpu) { - struct record *data; + struct pevent_record *data; data = trace_peek_data(cpu); cpu_data[cpu].next = NULL; @@ -509,6 +519,8 @@ ssize_t trace_report(int fd, bool __repipe) file_bigendian = buf[0]; host_bigendian = bigendian(); + read_trace_init(file_bigendian, host_bigendian); + read_or_die(buf, 1); long_size = buf[0]; @@ -526,11 +538,11 @@ ssize_t trace_report(int fd, bool __repipe) repipe = false; if (show_funcs) { - print_funcs(); + pevent_print_funcs(perf_pevent); return size; } if (show_printk) { - print_printk(); + pevent_print_printk(perf_pevent); return size; } diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 58ae14c5baa..639852ac111 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -1,20 +1,21 @@ -#ifndef __PERF_TRACE_EVENTS_H -#define __PERF_TRACE_EVENTS_H +#ifndef _PERF_UTIL_TRACE_EVENT_H +#define _PERF_UTIL_TRACE_EVENT_H -#include <stdbool.h> #include "parse-events.h" +#include "event-parse.h" +#include "session.h" struct machine; struct perf_sample; union perf_event; struct thread; -#define __unused __attribute__((unused)) - +extern int header_page_size_size; +extern int header_page_ts_size; +extern int header_page_data_offset; -#ifndef PAGE_MASK -#define PAGE_MASK (page_size - 1) -#endif +extern bool latency_format; +extern struct pevent *perf_pevent; enum { RINGBUF_TYPE_PADDING = 29, @@ -26,246 +27,37 @@ enum { #define TS_SHIFT 27 #endif -#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_FLAG = 32, - FIELD_IS_SYMBOLIC = 64, -}; - -struct format_field { - struct format_field *next; - char *type; - char *name; - int offset; - int size; - unsigned long flags; -}; - -struct format { - int nr_common; - int nr_fields; - struct format_field *common_fields; - struct format_field *fields; -}; - -struct print_arg_atom { - char *atom; -}; - -struct print_arg_string { - char *string; - int offset; -}; - -struct print_arg_field { - char *name; - struct format_field *field; -}; - -struct print_flag_sym { - struct print_flag_sym *next; - char *value; - char *str; -}; - -struct print_arg_typecast { - char *type; - struct print_arg *item; -}; - -struct print_arg_flags { - struct print_arg *field; - char *delim; - struct print_flag_sym *flags; -}; - -struct print_arg_symbol { - struct print_arg *field; - struct print_flag_sym *symbols; -}; - -struct print_arg; - -struct print_arg_op { - char *op; - int prio; - struct print_arg *left; - struct print_arg *right; -}; - -struct print_arg_func { - char *name; - struct print_arg *args; -}; - -enum print_arg_type { - PRINT_NULL, - PRINT_ATOM, - PRINT_FIELD, - PRINT_FLAGS, - PRINT_SYMBOL, - PRINT_TYPE, - PRINT_STRING, - PRINT_OP, -}; - -struct print_arg { - struct print_arg *next; - enum print_arg_type type; - union { - struct print_arg_atom atom; - struct print_arg_field field; - struct print_arg_typecast typecast; - struct print_arg_flags flags; - struct print_arg_symbol symbol; - struct print_arg_func func; - struct print_arg_string string; - struct print_arg_op op; - }; -}; - -struct print_fmt { - char *format; - struct print_arg *args; -}; - -struct event { - struct event *next; - char *name; - int id; - int flags; - struct format format; - struct print_fmt print_fmt; - char *system; -}; - -enum { - EVENT_FL_ISFTRACE = 0x01, - EVENT_FL_ISPRINT = 0x02, - EVENT_FL_ISBPRINT = 0x04, - EVENT_FL_ISFUNC = 0x08, - EVENT_FL_ISFUNCENT = 0x10, - EVENT_FL_ISFUNCRET = 0x20, - - EVENT_FL_FAILED = 0x80000000 -}; - -struct record { - unsigned long long ts; - int size; - void *data; -}; - -struct record *trace_peek_data(int cpu); -struct record *trace_read_data(int cpu); - -void parse_set_info(int nr_cpus, int long_sz); - -ssize_t trace_report(int fd, bool repipe); - -void *malloc_or_die(unsigned int size); +int bigendian(void); -void parse_cmdlines(char *file, int size); -void parse_proc_kallsyms(char *file, unsigned int size); -void parse_ftrace_printk(char *file, unsigned int size); +int read_trace_init(int file_bigendian, int host_bigendian); +void print_trace_event(int cpu, void *data, int size); -void print_funcs(void); -void print_printk(void); +void print_event(int cpu, void *data, int size, unsigned long long nsecs, + char *comm); int parse_ftrace_file(char *buf, unsigned long size); int parse_event_file(char *buf, unsigned long size, char *sys); -void print_trace_event(int cpu, void *data, int size); - -extern int file_bigendian; -extern int host_bigendian; - -int bigendian(void); - -static inline unsigned short __data2host2(unsigned short data) -{ - unsigned short swap; - - if (host_bigendian == file_bigendian) - return data; - swap = ((data & 0xffULL) << 8) | - ((data & (0xffULL << 8)) >> 8); +struct pevent_record *trace_peek_data(int cpu); +struct event_format *trace_find_event(int type); - return swap; -} - -static inline unsigned int __data2host4(unsigned int data) -{ - unsigned int swap; - - if (host_bigendian == 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(unsigned long long data) -{ - unsigned long long swap; - - if (host_bigendian == 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; -} +unsigned long long +raw_field_value(struct event_format *event, const char *name, void *data); +void *raw_field_ptr(struct event_format *event, const char *name, void *data); -#define data2host2(ptr) __data2host2(*(unsigned short *)ptr) -#define data2host4(ptr) __data2host4(*(unsigned int *)ptr) -#define data2host8(ptr) ({ \ - unsigned long long __val; \ - \ - memcpy(&__val, (ptr), sizeof(unsigned long long)); \ - __data2host8(__val); \ -}) +void parse_proc_kallsyms(char *file, unsigned int size __unused); +void parse_ftrace_printk(char *file, unsigned int size __unused); -extern int header_page_ts_offset; -extern int header_page_ts_size; -extern int header_page_size_offset; -extern int header_page_size_size; -extern int header_page_data_offset; -extern int header_page_data_size; - -extern bool latency_format; +ssize_t trace_report(int fd, bool repipe); int trace_parse_common_type(void *data); int trace_parse_common_pid(void *data); -int parse_common_pc(void *data); -int parse_common_flags(void *data); -int parse_common_lock_depth(void *data); -struct event *trace_find_event(int id); -struct event *trace_find_next_event(struct event *event); + +struct event_format *trace_find_next_event(struct event_format *event); unsigned long long read_size(void *ptr, int size); -unsigned long long -raw_field_value(struct event *event, const char *name, void *data); -void *raw_field_ptr(struct event *event, const char *name, void *data); unsigned long long eval_flag(const char *flag); +struct pevent_record *trace_read_data(int cpu); int read_tracing_data(int fd, struct list_head *pattrs); struct tracing_data { @@ -280,15 +72,6 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, void tracing_data_put(struct tracing_data *tdata); -/* 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, -}; - struct scripting_ops { const char *name; int (*start_script) (const char *script, int argc, const char **argv); @@ -314,4 +97,4 @@ int common_pc(struct scripting_context *context); int common_flags(struct scripting_context *context); int common_lock_depth(struct scripting_context *context); -#endif /* __PERF_TRACE_EVENTS_H */ +#endif /* _PERF_UTIL_TRACE_EVENT_H */ diff --git a/tools/perf/util/types.h b/tools/perf/util/types.h index 5f3689a3d08..c51fa6b70a2 100644 --- a/tools/perf/util/types.h +++ b/tools/perf/util/types.h @@ -16,4 +16,9 @@ typedef signed short s16; typedef unsigned char u8; typedef signed char s8; +union u64_swap { + u64 val64; + u32 val32[2]; +}; + #endif /* __PERF_TYPES_H */ diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c deleted file mode 100644 index 57a4c6ef3fd..00000000000 --- a/tools/perf/util/ui/browsers/annotate.c +++ /dev/null @@ -1,433 +0,0 @@ -#include "../../util.h" -#include "../browser.h" -#include "../helpline.h" -#include "../libslang.h" -#include "../ui.h" -#include "../util.h" -#include "../../annotate.h" -#include "../../hist.h" -#include "../../sort.h" -#include "../../symbol.h" -#include <pthread.h> -#include <newt.h> - -struct annotate_browser { - struct ui_browser b; - struct rb_root entries; - struct rb_node *curr_hot; - struct objdump_line *selection; - int nr_asm_entries; - int nr_entries; - bool hide_src_code; -}; - -struct objdump_line_rb_node { - struct rb_node rb_node; - double percent; - u32 idx; - int idx_asm; -}; - -static inline -struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self) -{ - return (struct objdump_line_rb_node *)(self + 1); -} - -static bool objdump_line__filter(struct ui_browser *browser, void *entry) -{ - struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); - - if (ab->hide_src_code) { - struct objdump_line *ol = list_entry(entry, struct objdump_line, node); - return ol->offset == -1; - } - - return false; -} - -static void annotate_browser__write(struct ui_browser *self, void *entry, int row) -{ - struct annotate_browser *ab = container_of(self, struct annotate_browser, b); - struct objdump_line *ol = list_entry(entry, struct objdump_line, node); - bool current_entry = ui_browser__is_current_entry(self, row); - int width = self->width; - - if (ol->offset != -1) { - struct objdump_line_rb_node *olrb = objdump_line__rb(ol); - ui_browser__set_percent_color(self, olrb->percent, current_entry); - slsmg_printf(" %7.2f ", olrb->percent); - } else { - ui_browser__set_percent_color(self, 0, current_entry); - slsmg_write_nstring(" ", 9); - } - - SLsmg_write_char(':'); - slsmg_write_nstring(" ", 8); - - /* The scroll bar isn't being used */ - if (!self->navkeypressed) - width += 1; - - if (!ab->hide_src_code && ol->offset != -1) - if (!current_entry || (self->use_navkeypressed && - !self->navkeypressed)) - ui_browser__set_color(self, HE_COLORSET_CODE); - - if (!*ol->line) - slsmg_write_nstring(" ", width - 18); - else - slsmg_write_nstring(ol->line, width - 18); - - if (current_entry) - ab->selection = ol; -} - -static double objdump_line__calc_percent(struct objdump_line *self, - struct symbol *sym, int evidx) -{ - double percent = 0.0; - - if (self->offset != -1) { - int len = sym->end - sym->start; - unsigned int hits = 0; - struct annotation *notes = symbol__annotation(sym); - struct source_line *src_line = notes->src->lines; - struct sym_hist *h = annotation__histogram(notes, evidx); - s64 offset = self->offset; - struct objdump_line *next; - - next = objdump__get_next_ip_line(¬es->src->source, self); - while (offset < (s64)len && - (next == NULL || offset < next->offset)) { - if (src_line) { - percent += src_line[offset].percent; - } else - hits += h->addr[offset]; - - ++offset; - } - /* - * If the percentage wasn't already calculated in - * symbol__get_source_line, do it now: - */ - if (src_line == NULL && h->sum) - percent = 100.0 * hits / h->sum; - } - - return percent; -} - -static void objdump__insert_line(struct rb_root *self, - struct objdump_line_rb_node *line) -{ - struct rb_node **p = &self->rb_node; - struct rb_node *parent = NULL; - struct objdump_line_rb_node *l; - - while (*p != NULL) { - parent = *p; - l = rb_entry(parent, struct objdump_line_rb_node, rb_node); - if (line->percent < l->percent) - p = &(*p)->rb_left; - else - p = &(*p)->rb_right; - } - rb_link_node(&line->rb_node, parent, p); - rb_insert_color(&line->rb_node, self); -} - -static void annotate_browser__set_top(struct annotate_browser *self, - struct rb_node *nd) -{ - struct objdump_line_rb_node *rbpos; - struct objdump_line *pos; - unsigned back; - - ui_browser__refresh_dimensions(&self->b); - back = self->b.height / 2; - rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node); - pos = ((struct objdump_line *)rbpos) - 1; - self->b.top_idx = self->b.index = rbpos->idx; - - while (self->b.top_idx != 0 && back != 0) { - pos = list_entry(pos->node.prev, struct objdump_line, node); - - --self->b.top_idx; - --back; - } - - self->b.top = pos; - self->curr_hot = nd; -} - -static void annotate_browser__calc_percent(struct annotate_browser *browser, - int evidx) -{ - struct map_symbol *ms = browser->b.priv; - struct symbol *sym = ms->sym; - struct annotation *notes = symbol__annotation(sym); - struct objdump_line *pos; - - browser->entries = RB_ROOT; - - pthread_mutex_lock(¬es->lock); - - list_for_each_entry(pos, ¬es->src->source, node) { - struct objdump_line_rb_node *rbpos = objdump_line__rb(pos); - rbpos->percent = objdump_line__calc_percent(pos, sym, evidx); - if (rbpos->percent < 0.01) { - RB_CLEAR_NODE(&rbpos->rb_node); - continue; - } - objdump__insert_line(&browser->entries, rbpos); - } - pthread_mutex_unlock(¬es->lock); - - browser->curr_hot = rb_last(&browser->entries); -} - -static bool annotate_browser__toggle_source(struct annotate_browser *browser) -{ - struct objdump_line *ol; - struct objdump_line_rb_node *olrb; - off_t offset = browser->b.index - browser->b.top_idx; - - browser->b.seek(&browser->b, offset, SEEK_CUR); - ol = list_entry(browser->b.top, struct objdump_line, node); - olrb = objdump_line__rb(ol); - - if (browser->hide_src_code) { - if (olrb->idx_asm < offset) - offset = olrb->idx; - - browser->b.nr_entries = browser->nr_entries; - browser->hide_src_code = false; - browser->b.seek(&browser->b, -offset, SEEK_CUR); - browser->b.top_idx = olrb->idx - offset; - browser->b.index = olrb->idx; - } else { - if (olrb->idx_asm < 0) { - ui_helpline__puts("Only available for assembly lines."); - browser->b.seek(&browser->b, -offset, SEEK_CUR); - return false; - } - - if (olrb->idx_asm < offset) - offset = olrb->idx_asm; - - browser->b.nr_entries = browser->nr_asm_entries; - browser->hide_src_code = true; - browser->b.seek(&browser->b, -offset, SEEK_CUR); - browser->b.top_idx = olrb->idx_asm - offset; - browser->b.index = olrb->idx_asm; - } - - return true; -} - -static int annotate_browser__run(struct annotate_browser *self, int evidx, - void(*timer)(void *arg), - void *arg, int delay_secs) -{ - struct rb_node *nd = NULL; - struct map_symbol *ms = self->b.priv; - struct symbol *sym = ms->sym; - const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, " - "H: Go to hottest line, ->/ENTER: Line action, " - "S: Toggle source code view"; - int key; - - if (ui_browser__show(&self->b, sym->name, help) < 0) - return -1; - - annotate_browser__calc_percent(self, evidx); - - if (self->curr_hot) - annotate_browser__set_top(self, self->curr_hot); - - nd = self->curr_hot; - - while (1) { - key = ui_browser__run(&self->b, delay_secs); - - if (delay_secs != 0) { - annotate_browser__calc_percent(self, evidx); - /* - * Current line focus got out of the list of most active - * lines, NULL it so that if TAB|UNTAB is pressed, we - * move to curr_hot (current hottest line). - */ - if (nd != NULL && RB_EMPTY_NODE(nd)) - nd = NULL; - } - - switch (key) { - case K_TIMER: - if (timer != NULL) - timer(arg); - - if (delay_secs != 0) - symbol__annotate_decay_histogram(sym, evidx); - continue; - case K_TAB: - if (nd != NULL) { - nd = rb_prev(nd); - if (nd == NULL) - nd = rb_last(&self->entries); - } else - nd = self->curr_hot; - break; - case K_UNTAB: - if (nd != NULL) - nd = rb_next(nd); - if (nd == NULL) - nd = rb_first(&self->entries); - else - nd = self->curr_hot; - break; - case 'H': - case 'h': - nd = self->curr_hot; - break; - case 'S': - case 's': - if (annotate_browser__toggle_source(self)) - ui_helpline__puts(help); - continue; - case K_ENTER: - case K_RIGHT: - if (self->selection == NULL) { - ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); - continue; - } - - if (self->selection->offset == -1) { - ui_helpline__puts("Actions are only available for assembly lines."); - continue; - } else { - char *s = strstr(self->selection->line, "callq "); - struct annotation *notes; - struct symbol *target; - u64 ip; - - if (s == NULL) { - ui_helpline__puts("Actions are only available for the 'callq' instruction."); - continue; - } - - s = strchr(s, ' '); - if (s++ == NULL) { - ui_helpline__puts("Invallid callq instruction."); - continue; - } - - ip = strtoull(s, NULL, 16); - ip = ms->map->map_ip(ms->map, ip); - target = map__find_symbol(ms->map, ip, NULL); - if (target == NULL) { - ui_helpline__puts("The called function was not found."); - continue; - } - - notes = symbol__annotation(target); - pthread_mutex_lock(¬es->lock); - - if (notes->src == NULL && symbol__alloc_hist(target) < 0) { - pthread_mutex_unlock(¬es->lock); - ui__warning("Not enough memory for annotating '%s' symbol!\n", - target->name); - continue; - } - - pthread_mutex_unlock(¬es->lock); - symbol__tui_annotate(target, ms->map, evidx, - timer, arg, delay_secs); - ui_browser__show_title(&self->b, sym->name); - } - continue; - case K_LEFT: - case K_ESC: - case 'q': - case CTRL('c'): - goto out; - default: - continue; - } - - if (nd != NULL) - annotate_browser__set_top(self, nd); - } -out: - ui_browser__hide(&self->b); - return key; -} - -int hist_entry__tui_annotate(struct hist_entry *he, int evidx, - void(*timer)(void *arg), void *arg, int delay_secs) -{ - return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, - timer, arg, delay_secs); -} - -int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, - void(*timer)(void *arg), void *arg, - int delay_secs) -{ - struct objdump_line *pos, *n; - struct annotation *notes; - struct map_symbol ms = { - .map = map, - .sym = sym, - }; - struct annotate_browser browser = { - .b = { - .refresh = ui_browser__list_head_refresh, - .seek = ui_browser__list_head_seek, - .write = annotate_browser__write, - .filter = objdump_line__filter, - .priv = &ms, - .use_navkeypressed = true, - }, - }; - int ret; - - if (sym == NULL) - return -1; - - if (map->dso->annotate_warned) - return -1; - - if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) { - ui__error("%s", ui_helpline__last_msg); - return -1; - } - - ui_helpline__push("Press <- or ESC to exit"); - - notes = symbol__annotation(sym); - - list_for_each_entry(pos, ¬es->src->source, node) { - struct objdump_line_rb_node *rbpos; - size_t line_len = strlen(pos->line); - - if (browser.b.width < line_len) - browser.b.width = line_len; - rbpos = objdump_line__rb(pos); - rbpos->idx = browser.nr_entries++; - if (pos->offset != -1) - rbpos->idx_asm = browser.nr_asm_entries++; - else - rbpos->idx_asm = -1; - } - - browser.b.nr_entries = browser.nr_entries; - browser.b.entries = ¬es->src->source, - browser.b.width += 18; /* Percentage */ - ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs); - list_for_each_entry_safe(pos, n, ¬es->src->source, node) { - list_del(&pos->node); - objdump_line__free(pos); - } - return ret; -} diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c index 52bb07c6442..4007aca8e0c 100644 --- a/tools/perf/util/usage.c +++ b/tools/perf/util/usage.c @@ -82,41 +82,3 @@ void warning(const char *warn, ...) warn_routine(warn, params); va_end(params); } - -uid_t parse_target_uid(const char *str, const char *tid, const char *pid) -{ - struct passwd pwd, *result; - char buf[1024]; - - if (str == NULL) - return UINT_MAX; - - /* UID and PID are mutually exclusive */ - if (tid || pid) { - ui__warning("PID/TID switch overriding UID\n"); - sleep(1); - return UINT_MAX; - } - - getpwnam_r(str, &pwd, buf, sizeof(buf), &result); - - if (result == NULL) { - char *endptr; - int uid = strtol(str, &endptr, 10); - - if (*endptr != '\0') { - ui__error("Invalid user %s\n", str); - return UINT_MAX - 1; - } - - getpwuid_r(uid, &pwd, buf, sizeof(buf), &result); - - if (result == NULL) { - ui__error("Problems obtaining information for user %s\n", - str); - return UINT_MAX - 1; - } - } - - return result->pw_uid; -} diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 8109a907841..d03599fbe78 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -148,3 +148,13 @@ int readn(int fd, void *buf, size_t n) return buf - buf_start; } + +size_t hex_width(u64 v) +{ + size_t n = 1; + + while ((v >>= 4)) + ++n; + + return n; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 0f99f394d8e..2daaedb83d8 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -74,7 +74,6 @@ #include <netinet/tcp.h> #include <arpa/inet.h> #include <netdb.h> -#include <pwd.h> #include <inttypes.h> #include "../../../include/linux/magic.h" #include "types.h" @@ -249,8 +248,6 @@ struct perf_event_attr; void event_attr_init(struct perf_event_attr *attr); -uid_t parse_target_uid(const char *str, const char *tid, const char *pid); - #define _STR(x) #x #define STR(x) _STR(x) @@ -265,4 +262,6 @@ bool is_power_of_2(unsigned long n) return (n != 0 && ((n & (n - 1)) == 0)); } +size_t hex_width(u64 v); + #endif |