diff options
Diffstat (limited to 'tools')
81 files changed, 3324 insertions, 1264 deletions
diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index f759f4f097c7..fd2f9221b241 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c @@ -1299,6 +1299,7 @@ static struct device *new_device(const char *name, u16 type) dev->feature_len = 0; dev->num_vq = 0; dev->running = false; + dev->next = NULL; /* * Append to device list. Prepending to a single-linked list is diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 47264b4652b9..f2989c525e48 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c @@ -2602,6 +2602,9 @@ find_func_handler(struct pevent *pevent, char *func_name) { struct pevent_function_handler *func; + if (!pevent) + return NULL; + for (func = pevent->func_handlers; func; func = func->next) { if (strcmp(func->name, func_name) == 0) break; @@ -4938,6 +4941,9 @@ enum pevent_errno __pevent_parse_format(struct event_format **eventp, goto event_alloc_failed; } + /* Add pevent to event so that it can be referenced */ + event->pevent = pevent; + ret = event_read_format(event); if (ret < 0) { ret = PEVENT_ERRNO__READ_FORMAT_FAILED; @@ -5041,9 +5047,6 @@ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, if (event == NULL) return ret; - /* Add pevent to event so that it can be referenced */ - event->pevent = pevent; - if (add_event(pevent, event)) { ret = PEVENT_ERRNO__MEM_ALLOC_FAILED; goto event_add_failed; diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c index ad17855528f9..5ea4326ad11f 100644 --- a/tools/lib/traceevent/parse-filter.c +++ b/tools/lib/traceevent/parse-filter.c @@ -209,7 +209,16 @@ static void free_arg(struct filter_arg *arg) switch (arg->type) { case FILTER_ARG_NONE: case FILTER_ARG_BOOLEAN: + break; + case FILTER_ARG_NUM: + free_arg(arg->num.left); + free_arg(arg->num.right); + break; + + case FILTER_ARG_EXP: + free_arg(arg->exp.left); + free_arg(arg->exp.right); break; case FILTER_ARG_STR: @@ -218,6 +227,12 @@ static void free_arg(struct filter_arg *arg) free(arg->str.buffer); break; + case FILTER_ARG_VALUE: + if (arg->value.type == FILTER_STRING || + arg->value.type == FILTER_CHAR) + free(arg->value.str); + break; + case FILTER_ARG_OP: free_arg(arg->op.left); free_arg(arg->op.right); diff --git a/tools/perf/Makefile b/tools/perf/Makefile index e5e71e7d95a0..00deed4d6159 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -45,6 +45,8 @@ include config/utilities.mak # # Define NO_LIBUNWIND if you do not want libunwind dependency for dwarf # backtrace post unwind. +# +# Define NO_BACKTRACE if you do not want stack backtrace debug feature $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) @@ -72,7 +74,7 @@ ifeq ($(ARCH),x86_64) override ARCH := x86 IS_X86_64 := 0 ifeq (, $(findstring m32,$(EXTRA_CFLAGS))) - IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -xc - | tail -n 1) + IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -x c - | tail -n 1) endif ifeq (${IS_X86_64}, 1) RAW_ARCH := x86_64 @@ -182,10 +184,23 @@ SCRIPT_SH += perf-archive.sh grep-libs = $(filter -l%,$(1)) strip-libs = $(filter-out -l%,$(1)) +TRACE_EVENT_DIR = ../lib/traceevent/ + +ifneq ($(OUTPUT),) + TE_PATH=$(OUTPUT) +else + TE_PATH=$(TRACE_EVENT_DIR) +endif + +LIBTRACEEVENT = $(TE_PATH)libtraceevent.a +TE_LIB := -L$(TE_PATH) -ltraceevent + PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py -$(OUTPUT)python/perf.so: $(PYRF_OBJS) $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) +export LIBTRACEEVENT + +$(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) $(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \ --quiet build_ext; \ mkdir -p $(OUTPUT)python && \ @@ -196,17 +211,6 @@ $(OUTPUT)python/perf.so: $(PYRF_OBJS) $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) -TRACE_EVENT_DIR = ../lib/traceevent/ - -ifneq ($(OUTPUT),) - 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: # @@ -250,10 +254,10 @@ $(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c LIB_FILE=$(OUTPUT)libperf.a -LIB_H += ../../include/linux/perf_event.h +LIB_H += ../../include/uapi/linux/perf_event.h LIB_H += ../../include/linux/rbtree.h LIB_H += ../../include/linux/list.h -LIB_H += ../../include/linux/const.h +LIB_H += ../../include/uapi/linux/const.h LIB_H += ../../include/linux/hash.h LIB_H += ../../include/linux/stringify.h LIB_H += util/include/linux/bitmap.h @@ -268,6 +272,7 @@ LIB_H += util/include/linux/magic.h LIB_H += util/include/linux/poison.h LIB_H += util/include/linux/prefetch.h LIB_H += util/include/linux/rbtree.h +LIB_H += util/include/linux/rbtree_augmented.h LIB_H += util/include/linux/string.h LIB_H += util/include/linux/types.h LIB_H += util/include/linux/linkage.h @@ -446,20 +451,6 @@ BUILTIN_OBJS += $(OUTPUT)builtin-inject.o PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT) -# Files needed for the python binding, perf.so -# pyrf is just an internal name needed for all those wrappers. -# This has to be in sync with what is in the 'sources' variable in -# tools/perf/util/setup.py - -PYRF_OBJS += $(OUTPUT)util/cpumap.o -PYRF_OBJS += $(OUTPUT)util/ctype.o -PYRF_OBJS += $(OUTPUT)util/evlist.o -PYRF_OBJS += $(OUTPUT)util/evsel.o -PYRF_OBJS += $(OUTPUT)util/python.o -PYRF_OBJS += $(OUTPUT)util/thread_map.o -PYRF_OBJS += $(OUTPUT)util/util.o -PYRF_OBJS += $(OUTPUT)util/xyarray.o - # # Platform specific tweaks # @@ -486,7 +477,13 @@ ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF)),y) NO_DWARF := 1 NO_DEMANGLE := 1 endif -endif +else + FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS) + ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y) + msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); + NO_DWARF := 1 + endif # Dwarf support +endif # SOURCE_LIBELF endif # NO_LIBELF ifndef NO_LIBUNWIND @@ -511,8 +508,6 @@ ifneq ($(OUTPUT),) endif ifdef NO_LIBELF -BASIC_CFLAGS += -DNO_LIBELF_SUPPORT - EXTLIBS := $(filter-out -lelf,$(EXTLIBS)) # Remove ELF/DWARF dependent codes @@ -527,17 +522,12 @@ BUILTIN_OBJS := $(filter-out $(OUTPUT)builtin-probe.o,$(BUILTIN_OBJS)) LIB_OBJS += $(OUTPUT)util/symbol-minimal.o else # NO_LIBELF +BASIC_CFLAGS += -DLIBELF_SUPPORT -ifneq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y) - BASIC_CFLAGS += -DLIBELF_NO_MMAP +ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y) + BASIC_CFLAGS += -DLIBELF_MMAP endif -FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS) -ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y) - msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); - NO_DWARF := 1 -endif # Dwarf support - ifndef NO_DWARF ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); @@ -550,38 +540,33 @@ endif # PERF_HAVE_DWARF_REGS endif # NO_DWARF endif # NO_LIBELF -ifdef NO_LIBUNWIND - BASIC_CFLAGS += -DNO_LIBUNWIND_SUPPORT -else +ifndef NO_LIBUNWIND + BASIC_CFLAGS += -DLIBUNWIND_SUPPORT EXTLIBS += $(LIBUNWIND_LIBS) BASIC_CFLAGS := $(LIBUNWIND_CFLAGS) $(BASIC_CFLAGS) BASIC_LDFLAGS := $(LIBUNWIND_LDFLAGS) $(BASIC_LDFLAGS) LIB_OBJS += $(OUTPUT)util/unwind.o endif -ifdef NO_LIBAUDIT - BASIC_CFLAGS += -DNO_LIBAUDIT_SUPPORT -else +ifndef NO_LIBAUDIT FLAGS_LIBAUDIT = $(ALL_CFLAGS) $(ALL_LDFLAGS) -laudit ifneq ($(call try-cc,$(SOURCE_LIBAUDIT),$(FLAGS_LIBAUDIT)),y) msg := $(warning No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev); - BASIC_CFLAGS += -DNO_LIBAUDIT_SUPPORT else + BASIC_CFLAGS += -DLIBAUDIT_SUPPORT BUILTIN_OBJS += $(OUTPUT)builtin-trace.o EXTLIBS += -laudit endif endif -ifdef NO_NEWT - BASIC_CFLAGS += -DNO_NEWT_SUPPORT -else +ifndef NO_NEWT FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT)),y) msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); - BASIC_CFLAGS += -DNO_NEWT_SUPPORT else # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h BASIC_CFLAGS += -I/usr/include/slang + BASIC_CFLAGS += -DNEWT_SUPPORT EXTLIBS += -lnewt -lslang LIB_OBJS += $(OUTPUT)ui/setup.o LIB_OBJS += $(OUTPUT)ui/browser.o @@ -603,17 +588,15 @@ else endif endif -ifdef NO_GTK2 - BASIC_CFLAGS += -DNO_GTK2_SUPPORT -else +ifndef NO_GTK2 FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2)),y) msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev); - BASIC_CFLAGS += -DNO_GTK2_SUPPORT else ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2)),y) BASIC_CFLAGS += -DHAVE_GTK_INFO_BAR endif + BASIC_CFLAGS += -DGTK2_SUPPORT BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null) EXTLIBS += $(shell pkg-config --libs gtk+-2.0 2>/dev/null) LIB_OBJS += $(OUTPUT)ui/gtk/browser.o @@ -621,7 +604,7 @@ else LIB_OBJS += $(OUTPUT)ui/gtk/util.o LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o # Make sure that it'd be included only once. - ifneq ($(findstring -DNO_NEWT_SUPPORT,$(BASIC_CFLAGS)),) + ifeq ($(findstring -DNEWT_SUPPORT,$(BASIC_CFLAGS)),) LIB_OBJS += $(OUTPUT)ui/setup.o LIB_OBJS += $(OUTPUT)ui/util.o endif @@ -762,23 +745,18 @@ ifeq ($(NO_PERF_REGS),0) ifeq ($(ARCH),x86) LIB_H += arch/x86/include/perf_regs.h endif -else - BASIC_CFLAGS += -DNO_PERF_REGS + BASIC_CFLAGS += -DHAVE_PERF_REGS endif -ifdef NO_STRLCPY - BASIC_CFLAGS += -DNO_STRLCPY -else - ifneq ($(call try-cc,$(SOURCE_STRLCPY),),y) - BASIC_CFLAGS += -DNO_STRLCPY +ifndef NO_STRLCPY + ifeq ($(call try-cc,$(SOURCE_STRLCPY),),y) + BASIC_CFLAGS += -DHAVE_STRLCPY endif endif -ifdef NO_BACKTRACE - BASIC_CFLAGS += -DNO_BACKTRACE -else - ifneq ($(call try-cc,$(SOURCE_BACKTRACE),),y) - BASIC_CFLAGS += -DNO_BACKTRACE +ifndef NO_BACKTRACE + ifeq ($(call try-cc,$(SOURCE_BACKTRACE),),y) + BASIC_CFLAGS += -DBACKTRACE_SUPPORT endif endif @@ -906,7 +884,7 @@ $(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 - $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< + $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< $(OUTPUT)util/parse-events.o: util/parse-events.c $(OUTPUT)PERF-CFLAGS $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-redundant-decls $< diff --git a/tools/perf/bash_completion b/tools/perf/bash_completion index 1958fa539d0f..56e6a12aab59 100644 --- a/tools/perf/bash_completion +++ b/tools/perf/bash_completion @@ -1,23 +1,59 @@ # perf completion +function_exists() +{ + declare -F $1 > /dev/null + return $? +} + +function_exists __ltrim_colon_completions || +__ltrim_colon_completions() +{ + if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then + # Remove colon-word prefix from COMPREPLY items + local colon_word=${1%${1##*:}} + local i=${#COMPREPLY[*]} + while [[ $((--i)) -ge 0 ]]; do + COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"} + done + fi +} + have perf && _perf() { - local cur cmd + local cur prev cmd COMPREPLY=() - _get_comp_words_by_ref cur prev + if function_exists _get_comp_words_by_ref; then + _get_comp_words_by_ref -n : cur prev + else + cur=$(_get_cword :) + prev=${COMP_WORDS[COMP_CWORD-1]} + fi cmd=${COMP_WORDS[0]} - # List perf subcommands + # List perf subcommands or long options if [ $COMP_CWORD -eq 1 ]; then - cmds=$($cmd --list-cmds) - COMPREPLY=( $( compgen -W '$cmds' -- "$cur" ) ) + if [[ $cur == --* ]]; then + COMPREPLY=( $( compgen -W '--help --version \ + --exec-path --html-path --paginate --no-pager \ + --perf-dir --work-tree --debugfs-dir' -- "$cur" ) ) + else + cmds=$($cmd --list-cmds) + COMPREPLY=( $( compgen -W '$cmds' -- "$cur" ) ) + fi # List possible events for -e option elif [[ $prev == "-e" && "${COMP_WORDS[1]}" == @(record|stat|top) ]]; then - cmds=$($cmd list --raw-dump) - COMPREPLY=( $( compgen -W '$cmds' -- "$cur" ) ) + evts=$($cmd list --raw-dump) + COMPREPLY=( $( compgen -W '$evts' -- "$cur" ) ) + __ltrim_colon_completions $cur + # List long option names + elif [[ $cur == --* ]]; then + subcmd=${COMP_WORDS[1]} + opts=$($cmd $subcmd --list-opts) + COMPREPLY=( $( compgen -W '$opts' -- "$cur" ) ) # Fall down to list regular files else _filedir diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index 83654557e108..d37e077f4b14 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c @@ -15,22 +15,6 @@ #include "util/strlist.h" #include "util/symbol.h" -static char const *add_name_list_str, *remove_name_list_str; - -static const char * const buildid_cache_usage[] = { - "perf buildid-cache [<options>]", - NULL -}; - -static const struct option buildid_cache_options[] = { - OPT_STRING('a', "add", &add_name_list_str, - "file list", "file(s) to add"), - OPT_STRING('r', "remove", &remove_name_list_str, "file list", - "file(s) to remove"), - OPT_INCR('v', "verbose", &verbose, "be more verbose"), - OPT_END() -}; - static int build_id_cache__add_file(const char *filename, const char *debugdir) { char sbuild_id[BUILD_ID_SIZE * 2 + 1]; @@ -51,8 +35,8 @@ static int build_id_cache__add_file(const char *filename, const char *debugdir) return err; } -static int build_id_cache__remove_file(const char *filename __maybe_unused, - const char *debugdir __maybe_unused) +static int build_id_cache__remove_file(const char *filename, + const char *debugdir) { u8 build_id[BUILD_ID_SIZE]; char sbuild_id[BUILD_ID_SIZE * 2 + 1]; @@ -73,11 +57,34 @@ static int build_id_cache__remove_file(const char *filename __maybe_unused, return err; } -static int __cmd_buildid_cache(void) +int cmd_buildid_cache(int argc, const char **argv, + const char *prefix __maybe_unused) { struct strlist *list; struct str_node *pos; char debugdir[PATH_MAX]; + char const *add_name_list_str = NULL, + *remove_name_list_str = NULL; + const struct option buildid_cache_options[] = { + OPT_STRING('a', "add", &add_name_list_str, + "file list", "file(s) to add"), + OPT_STRING('r', "remove", &remove_name_list_str, "file list", + "file(s) to remove"), + OPT_INCR('v', "verbose", &verbose, "be more verbose"), + OPT_END() + }; + const char * const buildid_cache_usage[] = { + "perf buildid-cache [<options>]", + NULL + }; + + argc = parse_options(argc, argv, buildid_cache_options, + buildid_cache_usage, 0); + + if (symbol__init() < 0) + return -1; + + setup_pager(); snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir); @@ -119,16 +126,3 @@ static int __cmd_buildid_cache(void) return 0; } - -int cmd_buildid_cache(int argc, const char **argv, - const char *prefix __maybe_unused) -{ - argc = parse_options(argc, argv, buildid_cache_options, - buildid_cache_usage, 0); - - if (symbol__init() < 0) - return -1; - - setup_pager(); - return __cmd_buildid_cache(); -} diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 1159feeebb19..a0e94fffa03e 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -16,27 +16,6 @@ #include "util/session.h" #include "util/symbol.h" -static const char *input_name; -static bool force; -static bool show_kernel; -static bool with_hits; - -static const char * const buildid_list_usage[] = { - "perf buildid-list [<options>]", - NULL -}; - -static const struct option options[] = { - OPT_BOOLEAN('H', "with-hits", &with_hits, "Show only DSOs with hits"), - OPT_STRING('i', "input", &input_name, "file", - "input file name"), - OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), - OPT_BOOLEAN('k', "kernel", &show_kernel, "Show current kernel build id"), - OPT_INCR('v', "verbose", &verbose, - "be more verbose"), - OPT_END() -}; - static int sysfs__fprintf_build_id(FILE *fp) { u8 kallsyms_build_id[BUILD_ID_SIZE]; @@ -65,7 +44,8 @@ static int filename__fprintf_build_id(const char *name, FILE *fp) return fprintf(fp, "%s\n", sbuild_id); } -static int perf_session__list_build_ids(void) +static int perf_session__list_build_ids(const char *input_name, + bool force, bool with_hits) { struct perf_session *session; @@ -95,18 +75,31 @@ out: return 0; } -static int __cmd_buildid_list(void) -{ - if (show_kernel) - return sysfs__fprintf_build_id(stdout); - - return perf_session__list_build_ids(); -} - int cmd_buildid_list(int argc, const char **argv, const char *prefix __maybe_unused) { + bool show_kernel = false; + bool with_hits = false; + bool force = false; + const char *input_name = NULL; + const struct option options[] = { + OPT_BOOLEAN('H', "with-hits", &with_hits, "Show only DSOs with hits"), + OPT_STRING('i', "input", &input_name, "file", "input file name"), + OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), + OPT_BOOLEAN('k', "kernel", &show_kernel, "Show current kernel build id"), + OPT_INCR('v', "verbose", &verbose, "be more verbose"), + OPT_END() + }; + const char * const buildid_list_usage[] = { + "perf buildid-list [<options>]", + NULL + }; + argc = parse_options(argc, argv, options, buildid_list_usage, 0); setup_pager(); - return __cmd_buildid_list(); + + if (show_kernel) + return sysfs__fprintf_build_id(stdout); + + return perf_session__list_build_ids(input_name, force, with_hits); } diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 761f4197a9e2..a0b531c14b97 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -70,8 +70,8 @@ static struct perf_tool tool = { .ordering_requires_timestamps = true, }; -static void perf_session__insert_hist_entry_by_name(struct rb_root *root, - struct hist_entry *he) +static void insert_hist_entry_by_name(struct rb_root *root, + struct hist_entry *he) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; @@ -90,7 +90,7 @@ static void perf_session__insert_hist_entry_by_name(struct rb_root *root, rb_insert_color(&he->rb_node, root); } -static void hists__resort_entries(struct hists *self) +static void hists__name_resort(struct hists *self, bool sort) { unsigned long position = 1; struct rb_root tmp = RB_ROOT; @@ -100,12 +100,16 @@ static void hists__resort_entries(struct hists *self) struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node); next = rb_next(&n->rb_node); - rb_erase(&n->rb_node, &self->entries); n->position = position++; - perf_session__insert_hist_entry_by_name(&tmp, n); + + if (sort) { + rb_erase(&n->rb_node, &self->entries); + insert_hist_entry_by_name(&tmp, n); + } } - self->entries = tmp; + if (sort) + self->entries = tmp; } static struct hist_entry *hists__find_entry(struct hists *self, @@ -121,7 +125,7 @@ static struct hist_entry *hists__find_entry(struct hists *self, n = n->rb_left; else if (cmp > 0) n = n->rb_right; - else + else return iter; } @@ -150,6 +154,24 @@ static struct perf_evsel *evsel_match(struct perf_evsel *evsel, return NULL; } +static void perf_evlist__resort_hists(struct perf_evlist *evlist, bool name) +{ + struct perf_evsel *evsel; + + list_for_each_entry(evsel, &evlist->entries, node) { + struct hists *hists = &evsel->hists; + + hists__output_resort(hists); + + /* + * The hists__name_resort only sets possition + * if name is false. + */ + if (name || ((!name) && show_displacement)) + hists__name_resort(hists, name); + } +} + static int __cmd_diff(void) { int ret, i; @@ -176,15 +198,8 @@ static int __cmd_diff(void) evlist_old = older->evlist; evlist_new = newer->evlist; - list_for_each_entry(evsel, &evlist_new->entries, node) - hists__output_resort(&evsel->hists); - - list_for_each_entry(evsel, &evlist_old->entries, node) { - hists__output_resort(&evsel->hists); - - if (show_displacement) - hists__resort_entries(&evsel->hists); - } + perf_evlist__resort_hists(evlist_old, true); + perf_evlist__resort_hists(evlist_new, false); list_for_each_entry(evsel, &evlist_new->entries, node) { struct perf_evsel *evsel_old; @@ -199,8 +214,7 @@ static int __cmd_diff(void) first = false; hists__match(&evsel_old->hists, &evsel->hists); - hists__fprintf(&evsel->hists, &evsel_old->hists, - show_displacement, true, 0, 0, stdout); + hists__fprintf(&evsel->hists, true, 0, 0, stdout); } out_delete: @@ -242,6 +256,21 @@ static const struct option options[] = { OPT_END() }; +static void ui_init(void) +{ + perf_hpp__init(); + + /* No overhead column. */ + perf_hpp__column_enable(PERF_HPP__OVERHEAD, false); + + /* Display baseline/delta/displacement columns. */ + perf_hpp__column_enable(PERF_HPP__BASELINE, true); + perf_hpp__column_enable(PERF_HPP__DELTA, true); + + if (show_displacement) + perf_hpp__column_enable(PERF_HPP__DISPL, true); +} + int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) { sort_order = diff__default_sort_order; @@ -264,7 +293,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) if (symbol__init() < 0) return -1; - perf_hpp__init(true, show_displacement); + ui_init(); + setup_sorting(diff_usage, options); setup_pager(); diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index 1fb164164fd0..997afb82691b 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c @@ -108,23 +108,20 @@ static int __cmd_evlist(const char *input_name, struct perf_attr_details *detail return 0; } -static const char * const evlist_usage[] = { - "perf evlist [<options>]", - NULL -}; - int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused) { struct perf_attr_details details = { .verbose = false, }; const char *input_name = NULL; 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() + 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() + }; + const char * const evlist_usage[] = { + "perf evlist [<options>]", + NULL }; argc = parse_options(argc, argv, options, evlist_usage, 0); diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 25c8b942ff85..178b88ae3d2f 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c @@ -30,23 +30,6 @@ enum help_format { HELP_FORMAT_WEB, }; -static bool show_all = false; -static enum help_format help_format = HELP_FORMAT_NONE; -static struct option builtin_help_options[] = { - OPT_BOOLEAN('a', "all", &show_all, "print all available commands"), - OPT_SET_UINT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN), - OPT_SET_UINT('w', "web", &help_format, "show manual in web browser", - HELP_FORMAT_WEB), - OPT_SET_UINT('i', "info", &help_format, "show info page", - HELP_FORMAT_INFO), - OPT_END(), -}; - -static const char * const builtin_help_usage[] = { - "perf help [--all] [--man|--web|--info] [command]", - NULL -}; - static enum help_format parse_help_format(const char *format) { if (!strcmp(format, "man")) @@ -258,11 +241,13 @@ static int add_man_viewer_info(const char *var, const char *value) static int perf_help_config(const char *var, const char *value, void *cb) { + enum help_format *help_formatp = cb; + if (!strcmp(var, "help.format")) { if (!value) return config_error_nonbool(var); - help_format = parse_help_format(value); - if (help_format == HELP_FORMAT_NONE) + *help_formatp = parse_help_format(value); + if (*help_formatp == HELP_FORMAT_NONE) return -1; return 0; } @@ -428,12 +413,27 @@ static int show_html_page(const char *perf_cmd) int cmd_help(int argc, const char **argv, const char *prefix __maybe_unused) { + bool show_all = false; + enum help_format help_format = HELP_FORMAT_MAN; + struct option builtin_help_options[] = { + OPT_BOOLEAN('a', "all", &show_all, "print all available commands"), + OPT_SET_UINT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN), + OPT_SET_UINT('w', "web", &help_format, "show manual in web browser", + HELP_FORMAT_WEB), + OPT_SET_UINT('i', "info", &help_format, "show info page", + HELP_FORMAT_INFO), + OPT_END(), + }; + const char * const builtin_help_usage[] = { + "perf help [--all] [--man|--web|--info] [command]", + NULL + }; const char *alias; int rc = 0; load_command_list("perf-", &main_cmds, &other_cmds); - perf_config(perf_help_config, NULL); + perf_config(perf_help_config, &help_format); argc = parse_options(argc, argv, builtin_help_options, builtin_help_usage, 0); diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 1eaa6617c814..4688bea95c12 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -14,8 +14,10 @@ #include "util/parse-options.h" -static char const *input_name = "-"; -static bool inject_build_ids; +struct perf_inject { + struct perf_tool tool; + bool build_ids; +}; static int perf_event__repipe_synth(struct perf_tool *tool __maybe_unused, union perf_event *event, @@ -194,7 +196,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool, * account this as unresolved. */ } else { -#ifndef NO_LIBELF_SUPPORT +#ifdef LIBELF_SUPPORT pr_warning("no symbols found in %s, maybe " "install a debug package?\n", al.map->dso->long_name); @@ -208,22 +210,6 @@ repipe: return 0; } -struct perf_tool perf_inject = { - .sample = perf_event__repipe_sample, - .mmap = perf_event__repipe, - .comm = perf_event__repipe, - .fork = perf_event__repipe, - .exit = perf_event__repipe, - .lost = perf_event__repipe, - .read = perf_event__repipe_sample, - .throttle = perf_event__repipe, - .unthrottle = perf_event__repipe, - .attr = perf_event__repipe_attr, - .event_type = perf_event__repipe_event_type_synth, - .tracing_data = perf_event__repipe_tracing_data_synth, - .build_id = perf_event__repipe_op2_synth, -}; - extern volatile int session_done; static void sig_handler(int sig __maybe_unused) @@ -231,56 +217,72 @@ static void sig_handler(int sig __maybe_unused) session_done = 1; } -static int __cmd_inject(void) +static int __cmd_inject(struct perf_inject *inject) { struct perf_session *session; int ret = -EINVAL; signal(SIGINT, sig_handler); - if (inject_build_ids) { - perf_inject.sample = perf_event__inject_buildid; - perf_inject.mmap = perf_event__repipe_mmap; - perf_inject.fork = perf_event__repipe_task; - perf_inject.tracing_data = perf_event__repipe_tracing_data; + if (inject->build_ids) { + inject->tool.sample = perf_event__inject_buildid; + inject->tool.mmap = perf_event__repipe_mmap; + inject->tool.fork = perf_event__repipe_task; + inject->tool.tracing_data = perf_event__repipe_tracing_data; } - session = perf_session__new(input_name, O_RDONLY, false, true, &perf_inject); + session = perf_session__new("-", O_RDONLY, false, true, &inject->tool); if (session == NULL) return -ENOMEM; - ret = perf_session__process_events(session, &perf_inject); + ret = perf_session__process_events(session, &inject->tool); perf_session__delete(session); return ret; } -static const char * const report_usage[] = { - "perf inject [<options>]", - NULL -}; - -static const struct option options[] = { - OPT_BOOLEAN('b', "build-ids", &inject_build_ids, - "Inject build-ids into the output stream"), - OPT_INCR('v', "verbose", &verbose, - "be more verbose (show build ids, etc)"), - OPT_END() -}; - int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) { - argc = parse_options(argc, argv, options, report_usage, 0); + struct perf_inject inject = { + .tool = { + .sample = perf_event__repipe_sample, + .mmap = perf_event__repipe, + .comm = perf_event__repipe, + .fork = perf_event__repipe, + .exit = perf_event__repipe, + .lost = perf_event__repipe, + .read = perf_event__repipe_sample, + .throttle = perf_event__repipe, + .unthrottle = perf_event__repipe, + .attr = perf_event__repipe_attr, + .event_type = perf_event__repipe_event_type_synth, + .tracing_data = perf_event__repipe_tracing_data_synth, + .build_id = perf_event__repipe_op2_synth, + }, + }; + const struct option options[] = { + OPT_BOOLEAN('b', "build-ids", &inject.build_ids, + "Inject build-ids into the output stream"), + OPT_INCR('v', "verbose", &verbose, + "be more verbose (show build ids, etc)"), + OPT_END() + }; + const char * const inject_usage[] = { + "perf inject [<options>]", + NULL + }; + + argc = parse_options(argc, argv, options, inject_usage, 0); /* * Any (unrecognized) arguments left? */ if (argc) - usage_with_options(report_usage, options); + usage_with_options(inject_usage, options); if (symbol__init() < 0) return -1; - return __cmd_inject(); + return __cmd_inject(&inject); } diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index bc912c68f49a..14bf82f63659 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -21,8 +21,6 @@ struct alloc_stat; typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); -static const char *input_name; - static int alloc_flag; static int caller_flag; @@ -31,8 +29,6 @@ static int caller_lines = -1; static bool raw_ip; -static char default_sort_order[] = "frag,hit,bytes"; - static int *cpunode_map; static int max_cpu_num; @@ -481,7 +477,7 @@ static void sort_result(void) __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort); } -static int __cmd_kmem(void) +static int __cmd_kmem(const char *input_name) { int err = -EINVAL; struct perf_session *session; @@ -520,11 +516,6 @@ out_delete: return err; } -static const char * const kmem_usage[] = { - "perf kmem [<options>] {record|stat}", - NULL -}; - static int ptr_cmp(struct alloc_stat *l, struct alloc_stat *r) { if (l->ptr < r->ptr) @@ -720,41 +711,17 @@ static int parse_line_opt(const struct option *opt __maybe_unused, return 0; } -static const struct option kmem_options[] = { - OPT_STRING('i', "input", &input_name, "file", - "input file name"), - OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL, - "show per-callsite statistics", - parse_caller_opt), - OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL, - "show per-allocation statistics", - parse_alloc_opt), - OPT_CALLBACK('s', "sort", NULL, "key[,key2...]", - "sort by keys: ptr, call_site, bytes, hit, pingpong, frag", - parse_sort_opt), - OPT_CALLBACK('l', "line", NULL, "num", - "show n lines", - parse_line_opt), - OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"), - OPT_END() -}; - -static const char *record_args[] = { - "record", - "-a", - "-R", - "-f", - "-c", "1", +static int __cmd_record(int argc, const char **argv) +{ + const char * const record_args[] = { + "record", "-a", "-R", "-f", "-c", "1", "-e", "kmem:kmalloc", "-e", "kmem:kmalloc_node", "-e", "kmem:kfree", "-e", "kmem:kmem_cache_alloc", "-e", "kmem:kmem_cache_alloc_node", "-e", "kmem:kmem_cache_free", -}; - -static int __cmd_record(int argc, const char **argv) -{ + }; unsigned int rec_argc, i, j; const char **rec_argv; @@ -775,6 +742,25 @@ static int __cmd_record(int argc, const char **argv) int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) { + const char * const default_sort_order = "frag,hit,bytes"; + const char *input_name = NULL; + const struct option kmem_options[] = { + OPT_STRING('i', "input", &input_name, "file", "input file name"), + OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL, + "show per-callsite statistics", parse_caller_opt), + OPT_CALLBACK_NOOPT(0, "alloc", NULL, NULL, + "show per-allocation statistics", parse_alloc_opt), + OPT_CALLBACK('s', "sort", NULL, "key[,key2...]", + "sort by keys: ptr, call_site, bytes, hit, pingpong, frag", + parse_sort_opt), + OPT_CALLBACK('l', "line", NULL, "num", "show n lines", parse_line_opt), + OPT_BOOLEAN(0, "raw-ip", &raw_ip, "show raw ip instead of symbol"), + OPT_END() + }; + const char * const kmem_usage[] = { + "perf kmem [<options>] {record|stat}", + NULL + }; argc = parse_options(argc, argv, kmem_options, kmem_usage, 0); if (!argc) @@ -793,7 +779,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) if (list_empty(&alloc_sort)) setup_sorting(&alloc_sort, default_sort_order); - return __cmd_kmem(); + return __cmd_kmem(input_name); } else usage_with_options(kmem_usage, kmem_options); diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index a28c9cad9048..260abc535b5b 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -32,16 +32,76 @@ struct event_key { int info; }; +struct kvm_event_stats { + u64 time; + struct stats stats; +}; + +struct kvm_event { + struct list_head hash_entry; + struct rb_node rb; + + struct event_key key; + + struct kvm_event_stats total; + + #define DEFAULT_VCPU_NUM 8 + int max_vcpu; + struct kvm_event_stats *vcpu; +}; + +typedef int (*key_cmp_fun)(struct kvm_event*, struct kvm_event*, int); + +struct kvm_event_key { + const char *name; + key_cmp_fun key; +}; + + +struct perf_kvm; + struct kvm_events_ops { bool (*is_begin_event)(struct perf_evsel *evsel, struct perf_sample *sample, struct event_key *key); bool (*is_end_event)(struct perf_evsel *evsel, struct perf_sample *sample, struct event_key *key); - void (*decode_key)(struct event_key *key, char decode[20]); + void (*decode_key)(struct perf_kvm *kvm, struct event_key *key, + char decode[20]); const char *name; }; +struct exit_reasons_table { + unsigned long exit_code; + const char *reason; +}; + +#define EVENTS_BITS 12 +#define EVENTS_CACHE_SIZE (1UL << EVENTS_BITS) + +struct perf_kvm { + struct perf_tool tool; + struct perf_session *session; + + const char *file_name; + const char *report_event; + const char *sort_key; + int trace_vcpu; + + struct exit_reasons_table *exit_reasons; + int exit_reasons_size; + const char *exit_reasons_isa; + + struct kvm_events_ops *events_ops; + key_cmp_fun compare; + struct list_head kvm_events_cache[EVENTS_CACHE_SIZE]; + u64 total_time; + u64 total_count; + + struct rb_root result; +}; + + static void exit_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample, struct event_key *key) @@ -78,45 +138,35 @@ static bool exit_event_end(struct perf_evsel *evsel, return kvm_entry_event(evsel); } -struct exit_reasons_table { - unsigned long exit_code; - const char *reason; -}; - -struct exit_reasons_table vmx_exit_reasons[] = { +static struct exit_reasons_table vmx_exit_reasons[] = { VMX_EXIT_REASONS }; -struct exit_reasons_table svm_exit_reasons[] = { +static struct exit_reasons_table svm_exit_reasons[] = { SVM_EXIT_REASONS }; -static int cpu_isa; - -static const char *get_exit_reason(u64 exit_code) +static const char *get_exit_reason(struct perf_kvm *kvm, u64 exit_code) { - int table_size = ARRAY_SIZE(svm_exit_reasons); - struct exit_reasons_table *table = svm_exit_reasons; - - if (cpu_isa == 1) { - table = vmx_exit_reasons; - table_size = ARRAY_SIZE(vmx_exit_reasons); - } + int i = kvm->exit_reasons_size; + struct exit_reasons_table *tbl = kvm->exit_reasons; - while (table_size--) { - if (table->exit_code == exit_code) - return table->reason; - table++; + while (i--) { + if (tbl->exit_code == exit_code) + return tbl->reason; + tbl++; } pr_err("unknown kvm exit code:%lld on %s\n", - (unsigned long long)exit_code, cpu_isa ? "VMX" : "SVM"); + (unsigned long long)exit_code, kvm->exit_reasons_isa); return "UNKNOWN"; } -static void exit_event_decode_key(struct event_key *key, char decode[20]) +static void exit_event_decode_key(struct perf_kvm *kvm, + struct event_key *key, + char decode[20]) { - const char *exit_reason = get_exit_reason(key->key); + const char *exit_reason = get_exit_reason(kvm, key->key); scnprintf(decode, 20, "%s", exit_reason); } @@ -128,11 +178,11 @@ static struct kvm_events_ops exit_events = { .name = "VM-EXIT" }; - /* - * For the mmio events, we treat: - * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry - * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...). - */ +/* + * For the mmio events, we treat: + * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry + * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...). + */ static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample, struct event_key *key) { @@ -178,7 +228,9 @@ static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample, return false; } -static void mmio_event_decode_key(struct event_key *key, char decode[20]) +static void mmio_event_decode_key(struct perf_kvm *kvm __maybe_unused, + struct event_key *key, + char decode[20]) { scnprintf(decode, 20, "%#lx:%s", (unsigned long)key->key, key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R"); @@ -219,7 +271,9 @@ static bool ioport_event_end(struct perf_evsel *evsel, return kvm_entry_event(evsel); } -static void ioport_event_decode_key(struct event_key *key, char decode[20]) +static void ioport_event_decode_key(struct perf_kvm *kvm __maybe_unused, + struct event_key *key, + char decode[20]) { scnprintf(decode, 20, "%#llx:%s", (unsigned long long)key->key, key->info ? "POUT" : "PIN"); @@ -232,64 +286,37 @@ static struct kvm_events_ops ioport_events = { .name = "IO Port Access" }; -static const char *report_event = "vmexit"; -struct kvm_events_ops *events_ops; - -static bool register_kvm_events_ops(void) +static bool register_kvm_events_ops(struct perf_kvm *kvm) { bool ret = true; - if (!strcmp(report_event, "vmexit")) - events_ops = &exit_events; - else if (!strcmp(report_event, "mmio")) - events_ops = &mmio_events; - else if (!strcmp(report_event, "ioport")) - events_ops = &ioport_events; + if (!strcmp(kvm->report_event, "vmexit")) + kvm->events_ops = &exit_events; + else if (!strcmp(kvm->report_event, "mmio")) + kvm->events_ops = &mmio_events; + else if (!strcmp(kvm->report_event, "ioport")) + kvm->events_ops = &ioport_events; else { - pr_err("Unknown report event:%s\n", report_event); + pr_err("Unknown report event:%s\n", kvm->report_event); ret = false; } return ret; } -struct kvm_event_stats { - u64 time; - struct stats stats; -}; - -struct kvm_event { - struct list_head hash_entry; - struct rb_node rb; - - struct event_key key; - - struct kvm_event_stats total; - - #define DEFAULT_VCPU_NUM 8 - int max_vcpu; - struct kvm_event_stats *vcpu; -}; - struct vcpu_event_record { int vcpu_id; u64 start_time; struct kvm_event *last_event; }; -#define EVENTS_BITS 12 -#define EVENTS_CACHE_SIZE (1UL << EVENTS_BITS) - -static u64 total_time; -static u64 total_count; -static struct list_head kvm_events_cache[EVENTS_CACHE_SIZE]; -static void init_kvm_event_record(void) +static void init_kvm_event_record(struct perf_kvm *kvm) { int i; for (i = 0; i < (int)EVENTS_CACHE_SIZE; i++) - INIT_LIST_HEAD(&kvm_events_cache[i]); + INIT_LIST_HEAD(&kvm->kvm_events_cache[i]); } static int kvm_events_hash_fn(u64 key) @@ -333,14 +360,15 @@ static struct kvm_event *kvm_alloc_init_event(struct event_key *key) return event; } -static struct kvm_event *find_create_kvm_event(struct event_key *key) +static struct kvm_event *find_create_kvm_event(struct perf_kvm *kvm, + struct event_key *key) { struct kvm_event *event; struct list_head *head; BUG_ON(key->key == INVALID_KEY); - head = &kvm_events_cache[kvm_events_hash_fn(key->key)]; + head = &kvm->kvm_events_cache[kvm_events_hash_fn(key->key)]; list_for_each_entry(event, head, hash_entry) if (event->key.key == key->key && event->key.info == key->info) return event; @@ -353,13 +381,14 @@ static struct kvm_event *find_create_kvm_event(struct event_key *key) return event; } -static bool handle_begin_event(struct vcpu_event_record *vcpu_record, +static bool handle_begin_event(struct perf_kvm *kvm, + struct vcpu_event_record *vcpu_record, struct event_key *key, u64 timestamp) { struct kvm_event *event = NULL; if (key->key != INVALID_KEY) - event = find_create_kvm_event(key); + event = find_create_kvm_event(kvm, key); vcpu_record->last_event = event; vcpu_record->start_time = timestamp; @@ -396,8 +425,10 @@ static bool update_kvm_event(struct kvm_event *event, int vcpu_id, return true; } -static bool handle_end_event(struct vcpu_event_record *vcpu_record, - struct event_key *key, u64 timestamp) +static bool handle_end_event(struct perf_kvm *kvm, + struct vcpu_event_record *vcpu_record, + struct event_key *key, + u64 timestamp) { struct kvm_event *event; u64 time_begin, time_diff; @@ -419,7 +450,7 @@ static bool handle_end_event(struct vcpu_event_record *vcpu_record, return true; if (!event) - event = find_create_kvm_event(key); + event = find_create_kvm_event(kvm, key); if (!event) return false; @@ -455,7 +486,9 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread, return thread->priv; } -static bool handle_kvm_event(struct thread *thread, struct perf_evsel *evsel, +static bool handle_kvm_event(struct perf_kvm *kvm, + struct thread *thread, + struct perf_evsel *evsel, struct perf_sample *sample) { struct vcpu_event_record *vcpu_record; @@ -465,22 +498,15 @@ static bool handle_kvm_event(struct thread *thread, struct perf_evsel *evsel, if (!vcpu_record) return true; - if (events_ops->is_begin_event(evsel, sample, &key)) - return handle_begin_event(vcpu_record, &key, sample->time); + if (kvm->events_ops->is_begin_event(evsel, sample, &key)) + return handle_begin_event(kvm, vcpu_record, &key, sample->time); - if (events_ops->is_end_event(evsel, sample, &key)) - return handle_end_event(vcpu_record, &key, sample->time); + if (kvm->events_ops->is_end_event(evsel, sample, &key)) + return handle_end_event(kvm, vcpu_record, &key, sample->time); return true; } -typedef int (*key_cmp_fun)(struct kvm_event*, struct kvm_event*, int); -struct kvm_event_key { - const char *name; - key_cmp_fun key; -}; - -static int trace_vcpu = -1; #define GET_EVENT_KEY(func, field) \ static u64 get_event_ ##func(struct kvm_event *event, int vcpu) \ { \ @@ -515,29 +541,25 @@ static struct kvm_event_key keys[] = { { NULL, NULL } }; -static const char *sort_key = "sample"; -static key_cmp_fun compare; - -static bool select_key(void) +static bool select_key(struct perf_kvm *kvm) { int i; for (i = 0; keys[i].name; i++) { - if (!strcmp(keys[i].name, sort_key)) { - compare = keys[i].key; + if (!strcmp(keys[i].name, kvm->sort_key)) { + kvm->compare = keys[i].key; return true; } } - pr_err("Unknown compare key:%s\n", sort_key); + pr_err("Unknown compare key:%s\n", kvm->sort_key); return false; } -static struct rb_root result; -static void insert_to_result(struct kvm_event *event, key_cmp_fun bigger, - int vcpu) +static void insert_to_result(struct rb_root *result, struct kvm_event *event, + key_cmp_fun bigger, int vcpu) { - struct rb_node **rb = &result.rb_node; + struct rb_node **rb = &result->rb_node; struct rb_node *parent = NULL; struct kvm_event *p; @@ -552,13 +574,15 @@ static void insert_to_result(struct kvm_event *event, key_cmp_fun bigger, } rb_link_node(&event->rb, parent, rb); - rb_insert_color(&event->rb, &result); + rb_insert_color(&event->rb, result); } -static void update_total_count(struct kvm_event *event, int vcpu) +static void update_total_count(struct perf_kvm *kvm, struct kvm_event *event) { - total_count += get_event_count(event, vcpu); - total_time += get_event_time(event, vcpu); + int vcpu = kvm->trace_vcpu; + + kvm->total_count += get_event_count(event, vcpu); + kvm->total_time += get_event_time(event, vcpu); } static bool event_is_valid(struct kvm_event *event, int vcpu) @@ -566,28 +590,30 @@ static bool event_is_valid(struct kvm_event *event, int vcpu) return !!get_event_count(event, vcpu); } -static void sort_result(int vcpu) +static void sort_result(struct perf_kvm *kvm) { unsigned int i; + int vcpu = kvm->trace_vcpu; struct kvm_event *event; for (i = 0; i < EVENTS_CACHE_SIZE; i++) - list_for_each_entry(event, &kvm_events_cache[i], hash_entry) + list_for_each_entry(event, &kvm->kvm_events_cache[i], hash_entry) if (event_is_valid(event, vcpu)) { - update_total_count(event, vcpu); - insert_to_result(event, compare, vcpu); + update_total_count(kvm, event); + insert_to_result(&kvm->result, event, + kvm->compare, vcpu); } } /* returns left most element of result, and erase it */ -static struct kvm_event *pop_from_result(void) +static struct kvm_event *pop_from_result(struct rb_root *result) { - struct rb_node *node = rb_first(&result); + struct rb_node *node = rb_first(result); if (!node) return NULL; - rb_erase(node, &result); + rb_erase(node, result); return container_of(node, struct kvm_event, rb); } @@ -601,14 +627,15 @@ static void print_vcpu_info(int vcpu) pr_info("VCPU %d:\n\n", vcpu); } -static void print_result(int vcpu) +static void print_result(struct perf_kvm *kvm) { char decode[20]; struct kvm_event *event; + int vcpu = kvm->trace_vcpu; pr_info("\n\n"); print_vcpu_info(vcpu); - pr_info("%20s ", events_ops->name); + pr_info("%20s ", kvm->events_ops->name); pr_info("%10s ", "Samples"); pr_info("%9s ", "Samples%"); @@ -616,33 +643,34 @@ static void print_result(int vcpu) pr_info("%16s ", "Avg time"); pr_info("\n\n"); - while ((event = pop_from_result())) { + while ((event = pop_from_result(&kvm->result))) { u64 ecount, etime; ecount = get_event_count(event, vcpu); etime = get_event_time(event, vcpu); - events_ops->decode_key(&event->key, decode); + kvm->events_ops->decode_key(kvm, &event->key, decode); pr_info("%20s ", decode); pr_info("%10llu ", (unsigned long long)ecount); - pr_info("%8.2f%% ", (double)ecount / total_count * 100); - pr_info("%8.2f%% ", (double)etime / total_time * 100); + pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100); + pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100); pr_info("%9.2fus ( +-%7.2f%% )", (double)etime / ecount/1e3, kvm_event_rel_stddev(vcpu, event)); pr_info("\n"); } pr_info("\nTotal Samples:%lld, Total events handled time:%.2fus.\n\n", - (unsigned long long)total_count, total_time / 1e3); + (unsigned long long)kvm->total_count, kvm->total_time / 1e3); } -static int process_sample_event(struct perf_tool *tool __maybe_unused, +static int process_sample_event(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, struct machine *machine) { struct thread *thread = machine__findnew_thread(machine, sample->tid); + struct perf_kvm *kvm = container_of(tool, struct perf_kvm, tool); if (thread == NULL) { pr_debug("problem processing %d event, skipping it.\n", @@ -650,18 +678,12 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, return -1; } - if (!handle_kvm_event(thread, evsel, sample)) + if (!handle_kvm_event(kvm, thread, evsel, sample)) return -1; return 0; } -static struct perf_tool eops = { - .sample = process_sample_event, - .comm = perf_event__process_comm, - .ordered_samples = true, -}; - static int get_cpu_isa(struct perf_session *session) { char *cpuid = session->header.env.cpuid; @@ -679,34 +701,43 @@ static int get_cpu_isa(struct perf_session *session) return isa; } -static const char *file_name; - -static int read_events(void) +static int read_events(struct perf_kvm *kvm) { - struct perf_session *kvm_session; int ret; - kvm_session = perf_session__new(file_name, O_RDONLY, 0, false, &eops); - if (!kvm_session) { + struct perf_tool eops = { + .sample = process_sample_event, + .comm = perf_event__process_comm, + .ordered_samples = true, + }; + + kvm->tool = eops; + kvm->session = perf_session__new(kvm->file_name, O_RDONLY, 0, false, + &kvm->tool); + if (!kvm->session) { pr_err("Initializing perf session failed\n"); return -EINVAL; } - if (!perf_session__has_traces(kvm_session, "kvm record")) + if (!perf_session__has_traces(kvm->session, "kvm record")) return -EINVAL; /* * Do not use 'isa' recorded in kvm_exit tracepoint since it is not * traced in the old kernel. */ - ret = get_cpu_isa(kvm_session); + ret = get_cpu_isa(kvm->session); if (ret < 0) return ret; - cpu_isa = ret; + if (ret == 1) { + kvm->exit_reasons = vmx_exit_reasons; + kvm->exit_reasons_size = ARRAY_SIZE(vmx_exit_reasons); + kvm->exit_reasons_isa = "VMX"; + } - return perf_session__process_events(kvm_session, &eops); + return perf_session__process_events(kvm->session, &kvm->tool); } static bool verify_vcpu(int vcpu) @@ -719,28 +750,30 @@ static bool verify_vcpu(int vcpu) return true; } -static int kvm_events_report_vcpu(int vcpu) +static int kvm_events_report_vcpu(struct perf_kvm *kvm) { int ret = -EINVAL; + int vcpu = kvm->trace_vcpu; if (!verify_vcpu(vcpu)) goto exit; - if (!select_key()) + if (!select_key(kvm)) goto exit; - if (!register_kvm_events_ops()) + if (!register_kvm_events_ops(kvm)) goto exit; - init_kvm_event_record(); + init_kvm_event_record(kvm); setup_pager(); - ret = read_events(); + ret = read_events(kvm); if (ret) goto exit; - sort_result(vcpu); - print_result(vcpu); + sort_result(kvm); + print_result(kvm); + exit: return ret; } @@ -765,7 +798,7 @@ static const char * const record_args[] = { _p; \ }) -static int kvm_events_record(int argc, const char **argv) +static int kvm_events_record(struct perf_kvm *kvm, int argc, const char **argv) { unsigned int rec_argc, i, j; const char **rec_argv; @@ -780,7 +813,7 @@ static int kvm_events_record(int argc, const char **argv) rec_argv[i] = STRDUP_FAIL_EXIT(record_args[i]); rec_argv[i++] = STRDUP_FAIL_EXIT("-o"); - rec_argv[i++] = STRDUP_FAIL_EXIT(file_name); + rec_argv[i++] = STRDUP_FAIL_EXIT(kvm->file_name); for (j = 1; j < (unsigned int)argc; j++, i++) rec_argv[i] = argv[j]; @@ -788,24 +821,24 @@ static int kvm_events_record(int argc, const char **argv) return cmd_record(i, rec_argv, NULL); } -static const char * const kvm_events_report_usage[] = { - "perf kvm stat report [<options>]", - NULL -}; +static int kvm_events_report(struct perf_kvm *kvm, int argc, const char **argv) +{ + const struct option kvm_events_report_options[] = { + OPT_STRING(0, "event", &kvm->report_event, "report event", + "event for reporting: vmexit, mmio, ioport"), + OPT_INTEGER(0, "vcpu", &kvm->trace_vcpu, + "vcpu id to report"), + OPT_STRING('k', "key", &kvm->sort_key, "sort-key", + "key for sorting: sample(sort by samples number)" + " time (sort by avg time)"), + OPT_END() + }; -static const struct option kvm_events_report_options[] = { - OPT_STRING(0, "event", &report_event, "report event", - "event for reporting: vmexit, mmio, ioport"), - OPT_INTEGER(0, "vcpu", &trace_vcpu, - "vcpu id to report"), - OPT_STRING('k', "key", &sort_key, "sort-key", - "key for sorting: sample(sort by samples number)" - " time (sort by avg time)"), - OPT_END() -}; + const char * const kvm_events_report_usage[] = { + "perf kvm stat report [<options>]", + NULL + }; -static int kvm_events_report(int argc, const char **argv) -{ symbol__init(); if (argc) { @@ -817,7 +850,7 @@ static int kvm_events_report(int argc, const char **argv) kvm_events_report_options); } - return kvm_events_report_vcpu(trace_vcpu); + return kvm_events_report_vcpu(kvm); } static void print_kvm_stat_usage(void) @@ -831,7 +864,7 @@ static void print_kvm_stat_usage(void) printf("\nOtherwise, it is the alias of 'perf stat':\n"); } -static int kvm_cmd_stat(int argc, const char **argv) +static int kvm_cmd_stat(struct perf_kvm *kvm, int argc, const char **argv) { if (argc == 1) { print_kvm_stat_usage(); @@ -839,44 +872,16 @@ static int kvm_cmd_stat(int argc, const char **argv) } if (!strncmp(argv[1], "rec", 3)) - return kvm_events_record(argc - 1, argv + 1); + return kvm_events_record(kvm, argc - 1, argv + 1); if (!strncmp(argv[1], "rep", 3)) - return kvm_events_report(argc - 1 , argv + 1); + return kvm_events_report(kvm, argc - 1 , argv + 1); perf_stat: return cmd_stat(argc, argv, NULL); } -static char name_buffer[256]; - -static const char * const kvm_usage[] = { - "perf kvm [<options>] {top|record|report|diff|buildid-list|stat}", - NULL -}; - -static const struct option kvm_options[] = { - OPT_STRING('i', "input", &file_name, "file", - "Input file name"), - OPT_STRING('o', "output", &file_name, "file", - "Output file name"), - OPT_BOOLEAN(0, "guest", &perf_guest, - "Collect guest os data"), - OPT_BOOLEAN(0, "host", &perf_host, - "Collect host os data"), - OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory", - "guest mount directory under which every guest os" - " instance has a subdir"), - OPT_STRING(0, "guestvmlinux", &symbol_conf.default_guest_vmlinux_name, - "file", "file saving guest os vmlinux"), - OPT_STRING(0, "guestkallsyms", &symbol_conf.default_guest_kallsyms, - "file", "file saving guest os /proc/kallsyms"), - OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules, - "file", "file saving guest os /proc/modules"), - OPT_END() -}; - -static int __cmd_record(int argc, const char **argv) +static int __cmd_record(struct perf_kvm *kvm, int argc, const char **argv) { int rec_argc, i = 0, j; const char **rec_argv; @@ -885,7 +890,7 @@ static int __cmd_record(int argc, const char **argv) rec_argv = calloc(rec_argc + 1, sizeof(char *)); rec_argv[i++] = strdup("record"); rec_argv[i++] = strdup("-o"); - rec_argv[i++] = strdup(file_name); + rec_argv[i++] = strdup(kvm->file_name); for (j = 1; j < argc; j++, i++) rec_argv[i] = argv[j]; @@ -894,7 +899,7 @@ static int __cmd_record(int argc, const char **argv) return cmd_record(i, rec_argv, NULL); } -static int __cmd_report(int argc, const char **argv) +static int __cmd_report(struct perf_kvm *kvm, int argc, const char **argv) { int rec_argc, i = 0, j; const char **rec_argv; @@ -903,7 +908,7 @@ static int __cmd_report(int argc, const char **argv) rec_argv = calloc(rec_argc + 1, sizeof(char *)); rec_argv[i++] = strdup("report"); rec_argv[i++] = strdup("-i"); - rec_argv[i++] = strdup(file_name); + rec_argv[i++] = strdup(kvm->file_name); for (j = 1; j < argc; j++, i++) rec_argv[i] = argv[j]; @@ -912,7 +917,7 @@ static int __cmd_report(int argc, const char **argv) return cmd_report(i, rec_argv, NULL); } -static int __cmd_buildid_list(int argc, const char **argv) +static int __cmd_buildid_list(struct perf_kvm *kvm, int argc, const char **argv) { int rec_argc, i = 0, j; const char **rec_argv; @@ -921,7 +926,7 @@ static int __cmd_buildid_list(int argc, const char **argv) rec_argv = calloc(rec_argc + 1, sizeof(char *)); rec_argv[i++] = strdup("buildid-list"); rec_argv[i++] = strdup("-i"); - rec_argv[i++] = strdup(file_name); + rec_argv[i++] = strdup(kvm->file_name); for (j = 1; j < argc; j++, i++) rec_argv[i] = argv[j]; @@ -932,6 +937,43 @@ static int __cmd_buildid_list(int argc, const char **argv) int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused) { + struct perf_kvm kvm = { + .trace_vcpu = -1, + .report_event = "vmexit", + .sort_key = "sample", + + .exit_reasons = svm_exit_reasons, + .exit_reasons_size = ARRAY_SIZE(svm_exit_reasons), + .exit_reasons_isa = "SVM", + }; + + const struct option kvm_options[] = { + OPT_STRING('i', "input", &kvm.file_name, "file", + "Input file name"), + OPT_STRING('o', "output", &kvm.file_name, "file", + "Output file name"), + OPT_BOOLEAN(0, "guest", &perf_guest, + "Collect guest os data"), + OPT_BOOLEAN(0, "host", &perf_host, + "Collect host os data"), + OPT_STRING(0, "guestmount", &symbol_conf.guestmount, "directory", + "guest mount directory under which every guest os" + " instance has a subdir"), + OPT_STRING(0, "guestvmlinux", &symbol_conf.default_guest_vmlinux_name, + "file", "file saving guest os vmlinux"), + OPT_STRING(0, "guestkallsyms", &symbol_conf.default_guest_kallsyms, + "file", "file saving guest os /proc/kallsyms"), + OPT_STRING(0, "guestmodules", &symbol_conf.default_guest_modules, + "file", "file saving guest os /proc/modules"), + OPT_END() + }; + + + const char * const kvm_usage[] = { + "perf kvm [<options>] {top|record|report|diff|buildid-list|stat}", + NULL + }; + perf_host = 0; perf_guest = 1; @@ -943,28 +985,32 @@ int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused) if (!perf_host) perf_guest = 1; - if (!file_name) { + if (!kvm.file_name) { if (perf_host && !perf_guest) - sprintf(name_buffer, "perf.data.host"); + kvm.file_name = strdup("perf.data.host"); else if (!perf_host && perf_guest) - sprintf(name_buffer, "perf.data.guest"); + kvm.file_name = strdup("perf.data.guest"); else - sprintf(name_buffer, "perf.data.kvm"); - file_name = name_buffer; + kvm.file_name = strdup("perf.data.kvm"); + + if (!kvm.file_name) { + pr_err("Failed to allocate memory for filename\n"); + return -ENOMEM; + } } if (!strncmp(argv[0], "rec", 3)) - return __cmd_record(argc, argv); + return __cmd_record(&kvm, argc, argv); else if (!strncmp(argv[0], "rep", 3)) - return __cmd_report(argc, argv); + return __cmd_report(&kvm, argc, argv); else if (!strncmp(argv[0], "diff", 4)) return cmd_diff(argc, argv, NULL); else if (!strncmp(argv[0], "top", 3)) return cmd_top(argc, argv, NULL); else if (!strncmp(argv[0], "buildid-list", 12)) - return __cmd_buildid_list(argc, argv); + return __cmd_buildid_list(&kvm, argc, argv); else if (!strncmp(argv[0], "stat", 4)) - return kvm_cmd_stat(argc, argv); + return kvm_cmd_stat(&kvm, argc, argv); else usage_with_options(kvm_usage, kvm_options); diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 7d6e09949880..6f5f328157aa 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -823,12 +823,6 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, return 0; } -static struct perf_tool eops = { - .sample = process_sample_event, - .comm = perf_event__process_comm, - .ordered_samples = true, -}; - static const struct perf_evsel_str_handler lock_tracepoints[] = { { "lock:lock_acquire", perf_evsel__process_lock_acquire, }, /* CONFIG_LOCKDEP */ { "lock:lock_acquired", perf_evsel__process_lock_acquired, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */ @@ -838,6 +832,11 @@ static const struct perf_evsel_str_handler lock_tracepoints[] = { static int read_events(void) { + struct perf_tool eops = { + .sample = process_sample_event, + .comm = perf_event__process_comm, + .ordered_samples = true, + }; session = perf_session__new(input_name, O_RDONLY, 0, false, &eops); if (!session) { pr_err("Initializing perf session failed\n"); @@ -878,53 +877,11 @@ static int __cmd_report(void) return 0; } -static const char * const report_usage[] = { - "perf lock report [<options>]", - NULL -}; - -static const struct option report_options[] = { - OPT_STRING('k', "key", &sort_key, "acquired", - "key for sorting (acquired / contended / wait_total / wait_max / wait_min)"), - /* TODO: type */ - OPT_END() -}; - -static const char * const info_usage[] = { - "perf lock info [<options>]", - NULL -}; - -static const struct option info_options[] = { - OPT_BOOLEAN('t', "threads", &info_threads, - "dump thread list in perf.data"), - OPT_BOOLEAN('m', "map", &info_map, - "map of lock instances (address:name table)"), - OPT_END() -}; - -static const char * const lock_usage[] = { - "perf lock [<options>] {record|report|script|info}", - NULL -}; - -static const struct option lock_options[] = { - OPT_STRING('i', "input", &input_name, "file", "input file name"), - OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), - OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), - OPT_END() -}; - -static const char *record_args[] = { - "record", - "-R", - "-f", - "-m", "1024", - "-c", "1", -}; - static int __cmd_record(int argc, const char **argv) { + const char *record_args[] = { + "record", "-R", "-f", "-m", "1024", "-c", "1", + }; unsigned int rec_argc, i, j; const char **rec_argv; @@ -963,6 +920,37 @@ static int __cmd_record(int argc, const char **argv) int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused) { + const struct option info_options[] = { + OPT_BOOLEAN('t', "threads", &info_threads, + "dump thread list in perf.data"), + OPT_BOOLEAN('m', "map", &info_map, + "map of lock instances (address:name table)"), + OPT_END() + }; + const struct option lock_options[] = { + OPT_STRING('i', "input", &input_name, "file", "input file name"), + OPT_INCR('v', "verbose", &verbose, "be more verbose (show symbol address, etc)"), + OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), + OPT_END() + }; + const struct option report_options[] = { + OPT_STRING('k', "key", &sort_key, "acquired", + "key for sorting (acquired / contended / wait_total / wait_max / wait_min)"), + /* TODO: type */ + OPT_END() + }; + const char * const info_usage[] = { + "perf lock info [<options>]", + NULL + }; + const char * const lock_usage[] = { + "perf lock [<options>] {record|report|script|info}", + NULL + }; + const char * const report_usage[] = { + "perf lock report [<options>]", + NULL + }; unsigned int i; int rc = 0; diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 118aa8946573..de38a034b129 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -250,19 +250,20 @@ static int opt_set_filter(const struct option *opt __maybe_unused, return 0; } -static const char * const probe_usage[] = { - "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]", - "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", - "perf probe [<options>] --del '[GROUP:]EVENT' ...", - "perf probe --list", +int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) +{ + const char * const probe_usage[] = { + "perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]", + "perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]", + "perf probe [<options>] --del '[GROUP:]EVENT' ...", + "perf probe --list", #ifdef DWARF_SUPPORT - "perf probe [<options>] --line 'LINEDESC'", - "perf probe [<options>] --vars 'PROBEPOINT'", + "perf probe [<options>] --line 'LINEDESC'", + "perf probe [<options>] --vars 'PROBEPOINT'", #endif - NULL + NULL }; - -static const struct option options[] = { + const struct option options[] = { OPT_INCR('v', "verbose", &verbose, "be more verbose (show parsed arguments, etc)"), OPT_BOOLEAN('l', "list", ¶ms.list_events, @@ -325,10 +326,7 @@ static const struct option options[] = { OPT_CALLBACK('x', "exec", NULL, "executable|path", "target executable name or path", opt_set_target), OPT_END() -}; - -int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused) -{ + }; int ret; argc = parse_options(argc, argv, options, probe_usage, diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index f14cb5fdb91f..e9231659754d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -31,15 +31,6 @@ #include <sched.h> #include <sys/mman.h> -#define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: " - -#ifdef NO_LIBUNWIND_SUPPORT -static char callchain_help[] = CALLCHAIN_HELP "[fp]"; -#else -static unsigned long default_stack_dump_size = 8192; -static char callchain_help[] = CALLCHAIN_HELP "[fp] dwarf"; -#endif - enum write_mode_t { WRITE_FORCE, WRITE_APPEND @@ -800,7 +791,7 @@ error: return ret; } -#ifndef NO_LIBUNWIND_SUPPORT +#ifdef LIBUNWIND_SUPPORT static int get_stack_size(char *str, unsigned long *_size) { char *endptr; @@ -826,7 +817,7 @@ static int get_stack_size(char *str, unsigned long *_size) max_size, str); return -1; } -#endif /* !NO_LIBUNWIND_SUPPORT */ +#endif /* LIBUNWIND_SUPPORT */ static int parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg, @@ -865,9 +856,11 @@ parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg, "needed for -g fp\n"); break; -#ifndef NO_LIBUNWIND_SUPPORT +#ifdef LIBUNWIND_SUPPORT /* Dwarf style */ } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) { + const unsigned long default_stack_dump_size = 8192; + ret = 0; rec->opts.call_graph = CALLCHAIN_DWARF; rec->opts.stack_dump_size = default_stack_dump_size; @@ -883,7 +876,7 @@ parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg, if (!ret) pr_debug("callchain: stack dump size %d\n", rec->opts.stack_dump_size); -#endif /* !NO_LIBUNWIND_SUPPORT */ +#endif /* LIBUNWIND_SUPPORT */ } else { pr_err("callchain: Unknown -g option " "value: %s\n", arg); @@ -930,6 +923,14 @@ static struct perf_record record = { .file_new = true, }; +#define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: " + +#ifdef LIBUNWIND_SUPPORT +static const char callchain_help[] = CALLCHAIN_HELP "[fp] dwarf"; +#else +static const char callchain_help[] = CALLCHAIN_HELP "[fp]"; +#endif + /* * XXX Will stay a global variable till we fix builtin-script.c to stop messing * with it and switch to use the library functions in perf_evlist that came diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 1da243dfbc3e..a61725d89d3e 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -320,7 +320,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, const char *evname = perf_evsel__name(pos); hists__fprintf_nr_sample_events(hists, evname, stdout); - hists__fprintf(hists, NULL, false, true, 0, 0, stdout); + hists__fprintf(hists, true, 0, 0, stdout); fprintf(stdout, "\n\n"); } @@ -691,7 +691,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) setup_browser(true); else { use_browser = 0; - perf_hpp__init(false, false); + perf_hpp__init(); } setup_sorting(report_usage, options); diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 9b9e32eaa805..3488ead3b60c 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1426,7 +1426,7 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_ struct perf_evsel *evsel, struct machine *machine) { - struct thread *thread = machine__findnew_thread(machine, sample->pid); + struct thread *thread = machine__findnew_thread(machine, sample->tid); int err = 0; if (thread == NULL) { diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 1be843aa1546..fb9625083a2e 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -24,7 +24,6 @@ static u64 last_timestamp; static u64 nr_unordered; extern const struct option record_options[]; static bool no_callchain; -static bool show_full_info; static bool system_wide; static const char *cpu_list; static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); @@ -473,8 +472,6 @@ static int cleanup_scripting(void) return scripting_ops->stop_script(); } -static const char *input_name; - static int process_sample_event(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, @@ -1156,20 +1153,40 @@ out: return n_args; } -static const char * const script_usage[] = { - "perf script [<options>]", - "perf script [<options>] record <script> [<record-options>] <command>", - "perf script [<options>] report <script> [script-args]", - "perf script [<options>] <script> [<record-options>] <command>", - "perf script [<options>] <top-script> [script-args]", - NULL -}; +static int have_cmd(int argc, const char **argv) +{ + char **__argv = malloc(sizeof(const char *) * argc); + + if (!__argv) { + pr_err("malloc failed\n"); + return -1; + } + + memcpy(__argv, argv, sizeof(const char *) * argc); + argc = parse_options(argc, (const char **)__argv, record_options, + NULL, PARSE_OPT_STOP_AT_NON_OPTION); + free(__argv); -static const struct option options[] = { + system_wide = (argc == 0); + + return 0; +} + +int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) +{ + bool show_full_info = false; + const char *input_name = NULL; + char *rec_script_path = NULL; + char *rep_script_path = NULL; + struct perf_session *session; + char *script_path = NULL; + const char **__argv; + int i, j, err; + const struct option options[] = { OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), OPT_INCR('v', "verbose", &verbose, - "be more verbose (show symbol address, etc)"), + "be more verbose (show symbol address, etc)"), OPT_BOOLEAN('L', "Latency", &latency_format, "show latency attributes (irqs/preemption disabled, etc)"), OPT_CALLBACK_NOOPT('l', "list", NULL, NULL, "list available scripts", @@ -1179,8 +1196,7 @@ static const struct option options[] = { parse_scriptname), OPT_STRING('g', "gen-script", &generate_script_lang, "lang", "generate perf-script.xx script in specified language"), - OPT_STRING('i', "input", &input_name, "file", - "input file name"), + OPT_STRING('i', "input", &input_name, "file", "input file name"), OPT_BOOLEAN('d', "debug-mode", &debug_mode, "do various checks like samples ordering and lost events"), OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, @@ -1195,10 +1211,9 @@ static const struct option options[] = { "comma separated output fields prepend with 'type:'. " "Valid types: hw,sw,trace,raw. " "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso," - "addr,symoff", - parse_output_fields), + "addr,symoff", parse_output_fields), OPT_BOOLEAN('a', "all-cpus", &system_wide, - "system-wide collection from all CPUs"), + "system-wide collection from all CPUs"), OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", "only consider these symbols"), OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"), @@ -1208,37 +1223,16 @@ static const struct option options[] = { "display extended information from perf.data file"), OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path, "Show the path of [kernel.kallsyms]"), - OPT_END() -}; - -static int have_cmd(int argc, const char **argv) -{ - char **__argv = malloc(sizeof(const char *) * argc); - - if (!__argv) { - pr_err("malloc failed\n"); - return -1; - } - - memcpy(__argv, argv, sizeof(const char *) * argc); - argc = parse_options(argc, (const char **)__argv, record_options, - NULL, PARSE_OPT_STOP_AT_NON_OPTION); - free(__argv); - - system_wide = (argc == 0); - - return 0; -} - -int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) -{ - char *rec_script_path = NULL; - char *rep_script_path = NULL; - struct perf_session *session; - char *script_path = NULL; - const char **__argv; - int i, j, err; + }; + const char * const script_usage[] = { + "perf script [<options>]", + "perf script [<options>] record <script> [<record-options>] <command>", + "perf script [<options>] report <script> [script-args]", + "perf script [<options>] <script> [<record-options>] <command>", + "perf script [<options>] <top-script> [script-args]", + NULL + }; setup_scripting(); diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index e8cd4d81b06e..93b9011fa3e2 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -64,122 +64,12 @@ #define CNTR_NOT_SUPPORTED "<not supported>" #define CNTR_NOT_COUNTED "<not counted>" -static struct perf_event_attr default_attrs[] = { - - { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, - { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES }, - { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_MIGRATIONS }, - { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS }, - - { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES }, - { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_FRONTEND }, - { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_BACKEND }, - { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS }, - { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, - { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES }, - -}; - -/* - * Detailed stats (-d), covering the L1 and last level data caches: - */ -static struct perf_event_attr detailed_attrs[] = { - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_L1D << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_L1D << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_LL << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_LL << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, -}; - -/* - * Very detailed stats (-d -d), covering the instruction cache and the TLB caches: - */ -static struct perf_event_attr very_detailed_attrs[] = { - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_L1I << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_L1I << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_DTLB << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_DTLB << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_ITLB << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_ITLB << 0 | - (PERF_COUNT_HW_CACHE_OP_READ << 8) | - (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, - -}; - -/* - * Very, very detailed stats (-d -d -d), adding prefetch events: - */ -static struct perf_event_attr very_very_detailed_attrs[] = { - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_L1D << 0 | - (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | - (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, - - { .type = PERF_TYPE_HW_CACHE, - .config = - PERF_COUNT_HW_CACHE_L1D << 0 | - (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | - (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, -}; - - - static struct perf_evlist *evsel_list; 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; @@ -187,15 +77,12 @@ static bool no_aggr = false; 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 *csv_sep = NULL; static bool csv_output = false; static bool group = false; -static const char *output_name = NULL; static FILE *output = NULL; -static int output_fd; static volatile int done = 0; @@ -1028,11 +915,6 @@ static void sig_atexit(void) kill(getpid(), signr); } -static const char * const stat_usage[] = { - "perf stat [<options>] [<command>]", - NULL -}; - static int stat__set_big_num(const struct option *opt __maybe_unused, const char *s __maybe_unused, int unset) { @@ -1040,62 +922,119 @@ static int stat__set_big_num(const struct option *opt __maybe_unused, return 0; } -static bool append_file; - -static const struct option options[] = { - OPT_CALLBACK('e', "event", &evsel_list, "event", - "event selector. use 'perf list' to list available events", - parse_events_option), - OPT_CALLBACK(0, "filter", &evsel_list, "filter", - "event filter", parse_filter), - OPT_BOOLEAN('i', "no-inherit", &no_inherit, - "child tasks do not inherit counters"), - OPT_STRING('p', "pid", &target.pid, "pid", - "stat events on existing process id"), - OPT_STRING('t', "tid", &target.tid, "tid", - "stat events on existing thread id"), - 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"), - OPT_BOOLEAN('c', "scale", &scale, - "scale/normalize counters"), - OPT_INCR('v', "verbose", &verbose, - "be more verbose (show counter open errors, etc)"), - OPT_INTEGER('r', "repeat", &run_count, - "repeat command and print average + stddev (max: 100)"), - OPT_BOOLEAN('n', "null", &null_run, - "null run - dont start any counters"), - OPT_INCR('d', "detailed", &detailed_run, - "detailed run - start a lot of events"), - OPT_BOOLEAN('S', "sync", &sync_run, - "call sync() before starting a run"), - OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL, - "print large numbers with thousands\' separators", - stat__set_big_num), - 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"), - OPT_STRING('x', "field-separator", &csv_sep, "separator", - "print counts with custom separator"), - OPT_CALLBACK('G', "cgroup", &evsel_list, "name", - "monitor event in cgroup name only", - parse_cgroups), - OPT_STRING('o', "output", &output_name, "file", - "output file name"), - OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), - OPT_INTEGER(0, "log-fd", &output_fd, - "log output to fd, instead of stderr"), - OPT_END() -}; - /* * Add default attributes, if there were no attributes specified or * if -d/--detailed, -d -d or -d -d -d is used: */ static int add_default_attributes(void) { + struct perf_event_attr default_attrs[] = { + + { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, + { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES }, + { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_MIGRATIONS }, + { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS }, + + { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES }, + { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_FRONTEND }, + { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_BACKEND }, + { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS }, + { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, + { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES }, + +}; + +/* + * Detailed stats (-d), covering the L1 and last level data caches: + */ + struct perf_event_attr detailed_attrs[] = { + + { .type = PERF_TYPE_HW_CACHE, + .config = + PERF_COUNT_HW_CACHE_L1D << 0 | + (PERF_COUNT_HW_CACHE_OP_READ << 8) | + (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, + + { .type = PERF_TYPE_HW_CACHE, + .config = + PERF_COUNT_HW_CACHE_L1D << 0 | + (PERF_COUNT_HW_CACHE_OP_READ << 8) | + (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, + + { .type = PERF_TYPE_HW_CACHE, + .config = + PERF_COUNT_HW_CACHE_LL << 0 | + (PERF_COUNT_HW_CACHE_OP_READ << 8) | + (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, + + { .type = PERF_TYPE_HW_CACHE, + .config = + PERF_COUNT_HW_CACHE_LL << 0 | + (PERF_COUNT_HW_CACHE_OP_READ << 8) | + (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, +}; + +/* + * Very detailed stats (-d -d), covering the instruction cache and the TLB caches: + */ + struct perf_event_attr very_detailed_attrs[] = { + + { .type = PERF_TYPE_HW_CACHE, + .config = + PERF_COUNT_HW_CACHE_L1I << 0 | + (PERF_COUNT_HW_CACHE_OP_READ << 8) | + (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, + + { .type = PERF_TYPE_HW_CACHE, + .config = + PERF_COUNT_HW_CACHE_L1I << 0 | + (PERF_COUNT_HW_CACHE_OP_READ << 8) | + (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, + + { .type = PERF_TYPE_HW_CACHE, + .config = + PERF_COUNT_HW_CACHE_DTLB << 0 | + (PERF_COUNT_HW_CACHE_OP_READ << 8) | + (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, + + { .type = PERF_TYPE_HW_CACHE, + .config = + PERF_COUNT_HW_CACHE_DTLB << 0 | + (PERF_COUNT_HW_CACHE_OP_READ << 8) | + (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, + + { .type = PERF_TYPE_HW_CACHE, + .config = + PERF_COUNT_HW_CACHE_ITLB << 0 | + (PERF_COUNT_HW_CACHE_OP_READ << 8) | + (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, + + { .type = PERF_TYPE_HW_CACHE, + .config = + PERF_COUNT_HW_CACHE_ITLB << 0 | + (PERF_COUNT_HW_CACHE_OP_READ << 8) | + (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, + +}; + +/* + * Very, very detailed stats (-d -d -d), adding prefetch events: + */ + struct perf_event_attr very_very_detailed_attrs[] = { + + { .type = PERF_TYPE_HW_CACHE, + .config = + PERF_COUNT_HW_CACHE_L1D << 0 | + (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | + (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) }, + + { .type = PERF_TYPE_HW_CACHE, + .config = + PERF_COUNT_HW_CACHE_L1D << 0 | + (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | + (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) }, +}; + /* Set attrs if no event is selected and !null_run: */ if (null_run) return 0; @@ -1130,8 +1069,59 @@ static int add_default_attributes(void) int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) { + bool append_file = false, + sync_run = false; + int output_fd = 0; + const char *output_name = NULL; + const struct option options[] = { + OPT_CALLBACK('e', "event", &evsel_list, "event", + "event selector. use 'perf list' to list available events", + parse_events_option), + OPT_CALLBACK(0, "filter", &evsel_list, "filter", + "event filter", parse_filter), + OPT_BOOLEAN('i', "no-inherit", &no_inherit, + "child tasks do not inherit counters"), + OPT_STRING('p', "pid", &target.pid, "pid", + "stat events on existing process id"), + OPT_STRING('t', "tid", &target.tid, "tid", + "stat events on existing thread id"), + 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"), + OPT_BOOLEAN('c', "scale", &scale, "scale/normalize counters"), + OPT_INCR('v', "verbose", &verbose, + "be more verbose (show counter open errors, etc)"), + OPT_INTEGER('r', "repeat", &run_count, + "repeat command and print average + stddev (max: 100)"), + OPT_BOOLEAN('n', "null", &null_run, + "null run - dont start any counters"), + OPT_INCR('d', "detailed", &detailed_run, + "detailed run - start a lot of events"), + OPT_BOOLEAN('S', "sync", &sync_run, + "call sync() before starting a run"), + OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL, + "print large numbers with thousands\' separators", + stat__set_big_num), + 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"), + OPT_STRING('x', "field-separator", &csv_sep, "separator", + "print counts with custom separator"), + OPT_CALLBACK('G', "cgroup", &evsel_list, "name", + "monitor event in cgroup name only", parse_cgroups), + OPT_STRING('o', "output", &output_name, "file", "output file name"), + OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), + OPT_INTEGER(0, "log-fd", &output_fd, + "log output to fd, instead of stderr"), + OPT_END() + }; + const char * const stat_usage[] = { + "perf stat [<options>] [<command>]", + NULL + }; struct perf_evsel *pos; - int status = -ENOMEM; + int status = -ENOMEM, run_idx; const char *mode; setlocale(LC_ALL, ""); diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index b1a8a3b841cc..f251b613b2f3 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -38,9 +38,6 @@ #define PWR_EVENT_EXIT -1 -static const char *input_name; -static const char *output_name = "output.svg"; - static unsigned int numcpus; static u64 min_freq; /* Lowest CPU frequency seen */ static u64 max_freq; /* Highest CPU frequency seen */ @@ -968,16 +965,15 @@ static void write_svg_file(const char *filename) svg_close(); } -static struct perf_tool perf_timechart = { - .comm = process_comm_event, - .fork = process_fork_event, - .exit = process_exit_event, - .sample = process_sample_event, - .ordered_samples = true, -}; - -static int __cmd_timechart(void) +static int __cmd_timechart(const char *input_name, const char *output_name) { + struct perf_tool perf_timechart = { + .comm = process_comm_event, + .fork = process_fork_event, + .exit = process_exit_event, + .sample = process_sample_event, + .ordered_samples = true, + }; struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0, false, &perf_timechart); int ret = -EINVAL; @@ -1005,40 +1001,25 @@ out_delete: return ret; } -static const char * const timechart_usage[] = { - "perf timechart [<options>] {record}", - NULL -}; - -#ifdef SUPPORT_OLD_POWER_EVENTS -static const char * const record_old_args[] = { - "record", - "-a", - "-R", - "-f", - "-c", "1", - "-e", "power:power_start", - "-e", "power:power_end", - "-e", "power:power_frequency", - "-e", "sched:sched_wakeup", - "-e", "sched:sched_switch", -}; -#endif - -static const char * const record_new_args[] = { - "record", - "-a", - "-R", - "-f", - "-c", "1", - "-e", "power:cpu_frequency", - "-e", "power:cpu_idle", - "-e", "sched:sched_wakeup", - "-e", "sched:sched_switch", -}; - static int __cmd_record(int argc, const char **argv) { +#ifdef SUPPORT_OLD_POWER_EVENTS + const char * const record_old_args[] = { + "record", "-a", "-R", "-f", "-c", "1", + "-e", "power:power_start", + "-e", "power:power_end", + "-e", "power:power_frequency", + "-e", "sched:sched_wakeup", + "-e", "sched:sched_switch", + }; +#endif + const char * const record_new_args[] = { + "record", "-a", "-R", "-f", "-c", "1", + "-e", "power:cpu_frequency", + "-e", "power:cpu_idle", + "-e", "sched:sched_wakeup", + "-e", "sched:sched_switch", + }; unsigned int rec_argc, i, j; const char **rec_argv; const char * const *record_args = record_new_args; @@ -1077,27 +1058,28 @@ parse_process(const struct option *opt __maybe_unused, const char *arg, return 0; } -static const struct option options[] = { - OPT_STRING('i', "input", &input_name, "file", - "input file name"), - OPT_STRING('o', "output", &output_name, "file", - "output file name"), - OPT_INTEGER('w', "width", &svg_page_width, - "page width"), - OPT_BOOLEAN('P', "power-only", &power_only, - "output power data only"), +int cmd_timechart(int argc, const char **argv, + const char *prefix __maybe_unused) +{ + const char *input_name; + const char *output_name = "output.svg"; + const struct option options[] = { + OPT_STRING('i', "input", &input_name, "file", "input file name"), + OPT_STRING('o', "output", &output_name, "file", "output file name"), + OPT_INTEGER('w', "width", &svg_page_width, "page width"), + OPT_BOOLEAN('P', "power-only", &power_only, "output power data only"), OPT_CALLBACK('p', "process", NULL, "process", "process selector. Pass a pid or process name.", parse_process), OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", "Look for files with symbols relative to this directory"), OPT_END() -}; - + }; + const char * const timechart_usage[] = { + "perf timechart [<options>] {record}", + NULL + }; -int cmd_timechart(int argc, const char **argv, - const char *prefix __maybe_unused) -{ argc = parse_options(argc, argv, options, timechart_usage, PARSE_OPT_STOP_AT_NON_OPTION); @@ -1110,5 +1092,5 @@ int cmd_timechart(int argc, const char **argv, setup_pager(); - return __cmd_timechart(); + return __cmd_timechart(input_name, output_name); } diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index e434a16bb5ac..ff6db8086805 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -316,7 +316,7 @@ static void perf_top__print_sym_table(struct perf_top *top) hists__output_recalc_col_len(&top->sym_evsel->hists, top->winsize.ws_row - 3); putchar('\n'); - hists__fprintf(&top->sym_evsel->hists, NULL, false, false, + hists__fprintf(&top->sym_evsel->hists, false, top->winsize.ws_row - 4 - printed, win_width, stdout); } @@ -1159,11 +1159,6 @@ setup: return 0; } -static const char * const top_usage[] = { - "perf top [<options>]", - NULL -}; - int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) { struct perf_evsel *pos; @@ -1250,6 +1245,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) OPT_STRING('u', "uid", &top.target.uid_str, "user", "user to profile"), OPT_END() }; + const char * const top_usage[] = { + "perf top [<options>]", + NULL + }; top.evlist = perf_evlist__new(NULL, NULL); if (top.evlist == NULL) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 8f113dab8bf1..7aaee39f6774 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -56,6 +56,10 @@ static int trace__read_syscall_info(struct trace *trace, int id) { char tp_name[128]; struct syscall *sc; + const char *name = audit_syscall_to_name(id, trace->audit_machine); + + if (name == NULL) + return -1; if (id > trace->syscalls.max) { struct syscall *nsyscalls = realloc(trace->syscalls.table, (id + 1) * sizeof(*sc)); @@ -75,11 +79,8 @@ static int trace__read_syscall_info(struct trace *trace, int id) } sc = trace->syscalls.table + id; - sc->name = audit_syscall_to_name(id, trace->audit_machine); - if (sc->name == NULL) - return -1; - - sc->fmt = syscall_fmt__find(sc->name); + sc->name = name; + sc->fmt = syscall_fmt__find(sc->name); snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name); sc->tp_format = event_format__new("syscalls", tp_name); @@ -114,10 +115,85 @@ static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FIL return printed; } +typedef int (*tracepoint_handler)(struct trace *trace, struct perf_evsel *evsel, + struct perf_sample *sample); + +static struct syscall *trace__syscall_info(struct trace *trace, + struct perf_evsel *evsel, + struct perf_sample *sample) +{ + int id = perf_evsel__intval(evsel, sample, "id"); + + if (id < 0) { + printf("Invalid syscall %d id, skipping...\n", id); + return NULL; + } + + if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) && + trace__read_syscall_info(trace, id)) + goto out_cant_read; + + if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL)) + goto out_cant_read; + + return &trace->syscalls.table[id]; + +out_cant_read: + printf("Problems reading syscall %d information\n", id); + return NULL; +} + +static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, + struct perf_sample *sample) +{ + void *args; + struct syscall *sc = trace__syscall_info(trace, evsel, sample); + + if (sc == NULL) + return -1; + + args = perf_evsel__rawptr(evsel, sample, "args"); + if (args == NULL) { + printf("Problems reading syscall arguments\n"); + return -1; + } + + printf("%s(", sc->name); + syscall__fprintf_args(sc, args, stdout); + + return 0; +} + +static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, + struct perf_sample *sample) +{ + int ret; + struct syscall *sc = trace__syscall_info(trace, evsel, sample); + + if (sc == NULL) + return -1; + + ret = perf_evsel__intval(evsel, sample, "ret"); + + if (ret < 0 && sc->fmt && sc->fmt->errmsg) { + char bf[256]; + const char *emsg = strerror_r(-ret, bf, sizeof(bf)), + *e = audit_errno_to_name(-ret); + + printf(") = -1 %s %s", e, emsg); + } else if (ret == 0 && sc->fmt && sc->fmt->timeout) + printf(") = 0 Timeout"); + else + printf(") = %d", ret); + + putchar('\n'); + return 0; +} + static int trace__run(struct trace *trace) { struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); - struct perf_evsel *evsel, *evsel_enter, *evsel_exit; + struct perf_evsel *evsel; int err = -1, i, nr_events = 0, before; if (evlist == NULL) { @@ -125,22 +201,12 @@ static int trace__run(struct trace *trace) goto out; } - evsel_enter = perf_evsel__newtp("raw_syscalls", "sys_enter", 0); - if (evsel_enter == NULL) { - printf("Couldn't read the raw_syscalls:sys_enter tracepoint information!\n"); + if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) || + perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) { + printf("Couldn't read the raw_syscalls tracepoints information!\n"); goto out_delete_evlist; } - perf_evlist__add(evlist, evsel_enter); - - evsel_exit = perf_evsel__newtp("raw_syscalls", "sys_exit", 1); - if (evsel_exit == NULL) { - printf("Couldn't read the raw_syscalls:sys_exit tracepoint information!\n"); - goto out_delete_evlist; - } - - perf_evlist__add(evlist, evsel_exit); - err = perf_evlist__create_maps(evlist, &trace->opts.target); if (err < 0) { printf("Problems parsing the target to trace, check your options!\n"); @@ -170,9 +236,8 @@ again: while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { const u32 type = event->header.type; - struct syscall *sc; + tracepoint_handler handler; struct perf_sample sample; - int id; ++nr_events; @@ -200,45 +265,18 @@ again: continue; } - id = perf_evsel__intval(evsel, &sample, "id"); - if (id < 0) { - printf("Invalid syscall %d id, skipping...\n", id); - continue; - } - - if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) && - trace__read_syscall_info(trace, id)) - continue; - - if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL)) - continue; - - sc = &trace->syscalls.table[id]; - if (evlist->threads->map[0] == -1 || evlist->threads->nr > 1) printf("%d ", sample.tid); - if (evsel == evsel_enter) { - void *args = perf_evsel__rawptr(evsel, &sample, "args"); - - printf("%s(", sc->name); - syscall__fprintf_args(sc, args, stdout); - } else if (evsel == evsel_exit) { - int ret = perf_evsel__intval(evsel, &sample, "ret"); - - if (ret < 0 && sc->fmt && sc->fmt->errmsg) { - char bf[256]; - const char *emsg = strerror_r(-ret, bf, sizeof(bf)), - *e = audit_errno_to_name(-ret); - - printf(") = -1 %s %s", e, emsg); - } else if (ret == 0 && sc->fmt && sc->fmt->timeout) - printf(") = 0 Timeout"); - else - printf(") = %d", ret); - - putchar('\n'); + if (sample.raw_data == NULL) { + printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", + perf_evsel__name(evsel), sample.tid, + sample.cpu, sample.raw_size); + continue; } + + handler = evsel->handler.func; + handler(trace, evsel, &sample); } } diff --git a/tools/perf/perf.c b/tools/perf/perf.c index fc2f770e3027..6d50eb0b4251 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -48,14 +48,14 @@ static struct cmd_struct commands[] = { { "version", cmd_version, 0 }, { "script", cmd_script, 0 }, { "sched", cmd_sched, 0 }, -#ifndef NO_LIBELF_SUPPORT +#ifdef LIBELF_SUPPORT { "probe", cmd_probe, 0 }, #endif { "kmem", cmd_kmem, 0 }, { "lock", cmd_lock, 0 }, { "kvm", cmd_kvm, 0 }, { "test", cmd_test, 0 }, -#ifndef NO_LIBAUDIT_SUPPORT +#ifdef LIBAUDIT_SUPPORT { "trace", cmd_trace, 0 }, #endif { "inject", cmd_inject, 0 }, diff --git a/tools/perf/perf.h b/tools/perf/perf.h index a89cbbb61801..c50985eaec41 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -57,7 +57,7 @@ void get_term_dimensions(struct winsize *ws); #endif #ifdef __sparc__ -#include "../../arch/sparc/include/asm/unistd.h" +#include "../../arch/sparc/include/uapi/asm/unistd.h" #define rmb() asm volatile("":::"memory") #define cpu_relax() asm volatile("":::"memory") #define CPUINFO_PROC "cpu" @@ -112,7 +112,7 @@ void get_term_dimensions(struct winsize *ws); #include <sys/types.h> #include <sys/syscall.h> -#include "../../include/linux/perf_event.h" +#include "../../include/uapi/linux/perf_event.h" #include "util/types.h" #include <stdbool.h> diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index a21f40bebbac..ef2f93ca7496 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -569,7 +569,8 @@ static int hist_browser__show_callchain(struct hist_browser *browser, static int hist_browser__hpp_color_ ## _name(struct perf_hpp *hpp, \ struct hist_entry *he) \ { \ - double percent = 100.0 * he->_field / hpp->total_period; \ + struct hists *hists = he->hists; \ + double percent = 100.0 * he->stat._field / hists->stats.total_period; \ *(double *)hpp->ptr = percent; \ return scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); \ } @@ -584,7 +585,7 @@ HPP__COLOR_FN(overhead_guest_us, period_guest_us) void hist_browser__init_hpp(void) { - perf_hpp__init(false, false); + perf_hpp__init(); perf_hpp__format[PERF_HPP__OVERHEAD].color = hist_browser__hpp_color_overhead; @@ -609,6 +610,7 @@ static int hist_browser__show_entry(struct hist_browser *browser, char folded_sign = ' '; bool current_entry = ui_browser__is_current_entry(&browser->b, row); off_t row_offset = entry->row_offset; + bool first = true; if (current_entry) { browser->he_selection = entry; @@ -624,7 +626,6 @@ static int hist_browser__show_entry(struct hist_browser *browser, struct perf_hpp hpp = { .buf = s, .size = sizeof(s), - .total_period = browser->hists->stats.total_period, }; ui_browser__gotorc(&browser->b, row, 0); @@ -633,10 +634,11 @@ static int hist_browser__show_entry(struct hist_browser *browser, if (!perf_hpp__format[i].cond) continue; - if (i) { + if (!first) { slsmg_printf(" "); width -= 2; } + first = false; if (perf_hpp__format[i].color) { hpp.ptr = &percent; @@ -645,7 +647,7 @@ static int hist_browser__show_entry(struct hist_browser *browser, ui_browser__set_percent_color(&browser->b, percent, current_entry); - if (i == 0 && symbol_conf.use_callchain) { + if (i == PERF_HPP__OVERHEAD && symbol_conf.use_callchain) { slsmg_printf("%c ", folded_sign); width -= 2; } @@ -982,7 +984,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, folded_sign = hist_entry__folded(he); hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists); - percent = (he->period * 100.0) / browser->hists->stats.total_period; + percent = (he->stat.period * 100.0) / browser->hists->stats.total_period; if (symbol_conf.use_callchain) printed += fprintf(fp, "%c ", folded_sign); @@ -990,10 +992,10 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser, printed += fprintf(fp, " %5.2f%%", percent); if (symbol_conf.show_nr_samples) - printed += fprintf(fp, " %11u", he->nr_events); + printed += fprintf(fp, " %11u", he->stat.nr_events); if (symbol_conf.show_total_period) - printed += fprintf(fp, " %12" PRIu64, he->period); + printed += fprintf(fp, " %12" PRIu64, he->stat.period); printed += fprintf(fp, "%s\n", rtrim(s)); diff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c index 7ff99ec1d95e..4125c6284114 100644 --- a/tools/perf/ui/gtk/browser.c +++ b/tools/perf/ui/gtk/browser.c @@ -49,7 +49,8 @@ static const char *perf_gtk__get_percent_color(double percent) static int perf_gtk__hpp_color_ ## _name(struct perf_hpp *hpp, \ struct hist_entry *he) \ { \ - double percent = 100.0 * he->_field / hpp->total_period; \ + struct hists *hists = he->hists; \ + double percent = 100.0 * he->stat._field / hists->stats.total_period; \ const char *markup; \ int ret = 0; \ \ @@ -73,7 +74,7 @@ HPP__COLOR_FN(overhead_guest_us, period_guest_us) void perf_gtk__init_hpp(void) { - perf_hpp__init(false, false); + perf_hpp__init(); perf_hpp__format[PERF_HPP__OVERHEAD].color = perf_gtk__hpp_color_overhead; @@ -102,7 +103,6 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists) struct perf_hpp hpp = { .buf = s, .size = sizeof(s), - .total_period = hists->stats.total_period, }; nr_cols = 0; diff --git a/tools/perf/ui/gtk/util.c b/tools/perf/ui/gtk/util.c index 8aada5b3c04c..ccb046aac98b 100644 --- a/tools/perf/ui/gtk/util.c +++ b/tools/perf/ui/gtk/util.c @@ -116,7 +116,7 @@ struct perf_error_ops perf_gtk_eops = { * FIXME: Functions below should be implemented properly. * For now, just add stubs for NO_NEWT=1 build. */ -#ifdef NO_NEWT_SUPPORT +#ifndef NEWT_SUPPORT void ui_progress__update(u64 curr __maybe_unused, u64 total __maybe_unused, const char *title __maybe_unused) { diff --git a/tools/perf/ui/helpline.h b/tools/perf/ui/helpline.h index 2b667ee454c3..baa28a4d16b9 100644 --- a/tools/perf/ui/helpline.h +++ b/tools/perf/ui/helpline.h @@ -23,25 +23,25 @@ void ui_helpline__puts(const char *msg); extern char ui_helpline__current[512]; -#ifdef NO_NEWT_SUPPORT +#ifdef NEWT_SUPPORT +extern char ui_helpline__last_msg[]; +int ui_helpline__show_help(const char *format, va_list ap); +#else static inline int ui_helpline__show_help(const char *format __maybe_unused, va_list ap __maybe_unused) { return 0; } -#else -extern char ui_helpline__last_msg[]; -int ui_helpline__show_help(const char *format, va_list ap); -#endif /* NO_NEWT_SUPPORT */ +#endif /* NEWT_SUPPORT */ -#ifdef NO_GTK2_SUPPORT +#ifdef GTK2_SUPPORT +int perf_gtk__show_helpline(const char *format, va_list ap); +#else static inline int perf_gtk__show_helpline(const char *format __maybe_unused, va_list ap __maybe_unused) { return 0; } -#else -int perf_gtk__show_helpline(const char *format, va_list ap); -#endif /* NO_GTK2_SUPPORT */ +#endif /* GTK2_SUPPORT */ #endif /* _PERF_UI_HELPLINE_H_ */ diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index e3f8cd46e7d7..f5a1e4f65263 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -8,9 +8,7 @@ /* hist period print (hpp) functions */ static int hpp__header_overhead(struct perf_hpp *hpp) { - const char *fmt = hpp->ptr ? "Baseline" : "Overhead"; - - return scnprintf(hpp->buf, hpp->size, fmt); + return scnprintf(hpp->buf, hpp->size, "Overhead"); } static int hpp__width_overhead(struct perf_hpp *hpp __maybe_unused) @@ -20,38 +18,18 @@ static int hpp__width_overhead(struct perf_hpp *hpp __maybe_unused) static int hpp__color_overhead(struct perf_hpp *hpp, struct hist_entry *he) { - double percent = 100.0 * he->period / hpp->total_period; - - if (hpp->ptr) { - struct hists *old_hists = hpp->ptr; - u64 total_period = old_hists->stats.total_period; - u64 base_period = he->pair ? he->pair->period : 0; - - if (total_period) - percent = 100.0 * base_period / total_period; - else - percent = 0.0; - } + struct hists *hists = he->hists; + double percent = 100.0 * he->stat.period / hists->stats.total_period; return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); } static int hpp__entry_overhead(struct perf_hpp *hpp, struct hist_entry *he) { - double percent = 100.0 * he->period / hpp->total_period; + struct hists *hists = he->hists; + double percent = 100.0 * he->stat.period / hists->stats.total_period; const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%"; - if (hpp->ptr) { - struct hists *old_hists = hpp->ptr; - u64 total_period = old_hists->stats.total_period; - u64 base_period = he->pair ? he->pair->period : 0; - - if (total_period) - percent = 100.0 * base_period / total_period; - else - percent = 0.0; - } - return scnprintf(hpp->buf, hpp->size, fmt, percent); } @@ -69,13 +47,16 @@ static int hpp__width_overhead_sys(struct perf_hpp *hpp __maybe_unused) static int hpp__color_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he) { - double percent = 100.0 * he->period_sys / hpp->total_period; + struct hists *hists = he->hists; + double percent = 100.0 * he->stat.period_sys / hists->stats.total_period; + return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent); } static int hpp__entry_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he) { - double percent = 100.0 * he->period_sys / hpp->total_period; + struct hists *hists = he->hists; + double percent = 100.0 * he->stat.period_sys / hists->stats.total_period; const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%"; return scnprintf(hpp->buf, hpp->size, fmt, percent); @@ -95,13 +76,16 @@ static int hpp__width_overhead_us(struct perf_hpp *hpp __maybe_unused) static int hpp__color_overhead_us(struct perf_hpp *hpp, struct hist_entry *he) { - double percent = 100.0 * he->period_us / hpp->total_period; + struct hists *hists = he->hists; + double percent = 100.0 * he->stat.period_us / hists->stats.total_period; + return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent); } static int hpp__entry_overhead_us(struct perf_hpp *hpp, struct hist_entry *he) { - double percent = 100.0 * he->period_us / hpp->total_period; + struct hists *hists = he->hists; + double percent = 100.0 * he->stat.period_us / hists->stats.total_period; const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%"; return scnprintf(hpp->buf, hpp->size, fmt, percent); @@ -120,14 +104,17 @@ static int hpp__width_overhead_guest_sys(struct perf_hpp *hpp __maybe_unused) static int hpp__color_overhead_guest_sys(struct perf_hpp *hpp, struct hist_entry *he) { - double percent = 100.0 * he->period_guest_sys / hpp->total_period; + struct hists *hists = he->hists; + double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period; + return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent); } static int hpp__entry_overhead_guest_sys(struct perf_hpp *hpp, struct hist_entry *he) { - double percent = 100.0 * he->period_guest_sys / hpp->total_period; + struct hists *hists = he->hists; + double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period; const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% "; return scnprintf(hpp->buf, hpp->size, fmt, percent); @@ -146,19 +133,63 @@ static int hpp__width_overhead_guest_us(struct perf_hpp *hpp __maybe_unused) static int hpp__color_overhead_guest_us(struct perf_hpp *hpp, struct hist_entry *he) { - double percent = 100.0 * he->period_guest_us / hpp->total_period; + struct hists *hists = he->hists; + double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period; + return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent); } static int hpp__entry_overhead_guest_us(struct perf_hpp *hpp, struct hist_entry *he) { - double percent = 100.0 * he->period_guest_us / hpp->total_period; + struct hists *hists = he->hists; + double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period; const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% "; return scnprintf(hpp->buf, hpp->size, fmt, percent); } +static int hpp__header_baseline(struct perf_hpp *hpp) +{ + return scnprintf(hpp->buf, hpp->size, "Baseline"); +} + +static int hpp__width_baseline(struct perf_hpp *hpp __maybe_unused) +{ + return 8; +} + +static double baseline_percent(struct hist_entry *he) +{ + struct hist_entry *pair = he->pair; + struct hists *pair_hists = pair ? pair->hists : NULL; + double percent = 0.0; + + if (pair) { + u64 total_period = pair_hists->stats.total_period; + u64 base_period = pair->stat.period; + + percent = 100.0 * base_period / total_period; + } + + return percent; +} + +static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he) +{ + double percent = baseline_percent(he); + + return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); +} + +static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he) +{ + double percent = baseline_percent(he); + const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%"; + + return scnprintf(hpp->buf, hpp->size, fmt, percent); +} + static int hpp__header_samples(struct perf_hpp *hpp) { const char *fmt = symbol_conf.field_sep ? "%s" : "%11s"; @@ -175,7 +206,7 @@ static int hpp__entry_samples(struct perf_hpp *hpp, struct hist_entry *he) { const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%11" PRIu64; - return scnprintf(hpp->buf, hpp->size, fmt, he->nr_events); + return scnprintf(hpp->buf, hpp->size, fmt, he->stat.nr_events); } static int hpp__header_period(struct perf_hpp *hpp) @@ -194,7 +225,7 @@ static int hpp__entry_period(struct perf_hpp *hpp, struct hist_entry *he) { const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64; - return scnprintf(hpp->buf, hpp->size, fmt, he->period); + return scnprintf(hpp->buf, hpp->size, fmt, he->stat.period); } static int hpp__header_delta(struct perf_hpp *hpp) @@ -211,20 +242,22 @@ static int hpp__width_delta(struct perf_hpp *hpp __maybe_unused) static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he) { - struct hists *pair_hists = hpp->ptr; + struct hist_entry *pair = he->pair; + struct hists *pair_hists = pair ? pair->hists : NULL; + struct hists *hists = he->hists; u64 old_total, new_total; double old_percent = 0, new_percent = 0; double diff; const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s"; char buf[32] = " "; - old_total = pair_hists->stats.total_period; - if (old_total > 0 && he->pair) - old_percent = 100.0 * he->pair->period / old_total; + old_total = pair_hists ? pair_hists->stats.total_period : 0; + if (old_total > 0 && pair) + old_percent = 100.0 * pair->stat.period / old_total; - new_total = hpp->total_period; + new_total = hists->stats.total_period; if (new_total > 0) - new_percent = 100.0 * he->period / new_total; + new_percent = 100.0 * he->stat.period / new_total; diff = new_percent - old_percent; if (fabs(diff) >= 0.01) @@ -244,13 +277,15 @@ static int hpp__width_displ(struct perf_hpp *hpp __maybe_unused) } static int hpp__entry_displ(struct perf_hpp *hpp, - struct hist_entry *he __maybe_unused) + struct hist_entry *he) { + struct hist_entry *pair = he->pair; + long displacement = pair ? pair->position - he->position : 0; const char *fmt = symbol_conf.field_sep ? "%s" : "%6.6s"; char buf[32] = " "; - if (hpp->displacement) - scnprintf(buf, sizeof(buf), "%+4ld", hpp->displacement); + if (displacement) + scnprintf(buf, sizeof(buf), "%+4ld", displacement); return scnprintf(hpp->buf, hpp->size, fmt, buf); } @@ -267,6 +302,7 @@ static int hpp__entry_displ(struct perf_hpp *hpp, .entry = hpp__entry_ ## _name struct perf_hpp_fmt perf_hpp__format[] = { + { .cond = false, HPP__COLOR_PRINT_FNS(baseline) }, { .cond = true, HPP__COLOR_PRINT_FNS(overhead) }, { .cond = false, HPP__COLOR_PRINT_FNS(overhead_sys) }, { .cond = false, HPP__COLOR_PRINT_FNS(overhead_us) }, @@ -281,7 +317,7 @@ struct perf_hpp_fmt perf_hpp__format[] = { #undef HPP__COLOR_PRINT_FNS #undef HPP__PRINT_FNS -void perf_hpp__init(bool need_pair, bool show_displacement) +void perf_hpp__init(void) { if (symbol_conf.show_cpu_utilization) { perf_hpp__format[PERF_HPP__OVERHEAD_SYS].cond = true; @@ -298,13 +334,12 @@ void perf_hpp__init(bool need_pair, bool show_displacement) if (symbol_conf.show_total_period) perf_hpp__format[PERF_HPP__PERIOD].cond = true; +} - if (need_pair) { - perf_hpp__format[PERF_HPP__DELTA].cond = true; - - if (show_displacement) - perf_hpp__format[PERF_HPP__DISPL].cond = true; - } +void perf_hpp__column_enable(unsigned col, bool enable) +{ + BUG_ON(col >= PERF_HPP__MAX_INDEX); + perf_hpp__format[col].cond = enable; } static inline void advance_hpp(struct perf_hpp *hpp, int inc) @@ -319,6 +354,7 @@ int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, const char *sep = symbol_conf.field_sep; char *start = hpp->buf; int i, ret; + bool first = true; if (symbol_conf.exclude_other && !he->parent) return 0; @@ -327,9 +363,10 @@ int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, if (!perf_hpp__format[i].cond) continue; - if (!sep || i > 0) { + if (!sep || !first) { ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " "); advance_hpp(hpp, ret); + first = false; } if (color && perf_hpp__format[i].color) diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c index bd7d460f844c..ebb4cc107876 100644 --- a/tools/perf/ui/setup.c +++ b/tools/perf/ui/setup.c @@ -30,7 +30,7 @@ void setup_browser(bool fallback_to_pager) if (fallback_to_pager) setup_pager(); - perf_hpp__init(false, false); + perf_hpp__init(); break; } } diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 882461a42830..fbd4e32d0743 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -271,7 +271,7 @@ static size_t hist_entry_callchain__fprintf(struct hist_entry *he, { switch (callchain_param.mode) { case CHAIN_GRAPH_REL: - return callchain__fprintf_graph(fp, &he->sorted_chain, he->period, + return callchain__fprintf_graph(fp, &he->sorted_chain, he->stat.period, left_margin); break; case CHAIN_GRAPH_ABS: @@ -292,9 +292,10 @@ static size_t hist_entry_callchain__fprintf(struct hist_entry *he, static size_t hist_entry__callchain_fprintf(struct hist_entry *he, struct hists *hists, - u64 total_period, FILE *fp) + FILE *fp) { int left_margin = 0; + u64 total_period = hists->stats.total_period; if (sort__first_dimension == SORT_COMM) { struct sort_entry *se = list_first_entry(&hist_entry__sort_list, @@ -307,17 +308,13 @@ static size_t hist_entry__callchain_fprintf(struct hist_entry *he, } static int hist_entry__fprintf(struct hist_entry *he, size_t size, - struct hists *hists, struct hists *pair_hists, - long displacement, u64 total_period, FILE *fp) + struct hists *hists, FILE *fp) { char bf[512]; int ret; struct perf_hpp hpp = { .buf = bf, .size = size, - .total_period = total_period, - .displacement = displacement, - .ptr = pair_hists, }; bool color = !symbol_conf.field_sep; @@ -330,22 +327,17 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size, ret = fprintf(fp, "%s\n", bf); if (symbol_conf.use_callchain) - ret += hist_entry__callchain_fprintf(he, hists, - total_period, fp); + ret += hist_entry__callchain_fprintf(he, hists, fp); return ret; } -size_t hists__fprintf(struct hists *hists, struct hists *pair, - bool show_displacement, bool show_header, int max_rows, +size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, int max_cols, FILE *fp) { struct sort_entry *se; struct rb_node *nd; size_t ret = 0; - u64 total_period; - unsigned long position = 1; - long displacement = 0; unsigned int width; const char *sep = symbol_conf.field_sep; const char *col_width = symbol_conf.col_width_list_str; @@ -354,8 +346,8 @@ size_t hists__fprintf(struct hists *hists, struct hists *pair, struct perf_hpp dummy_hpp = { .buf = bf, .size = sizeof(bf), - .ptr = pair, }; + bool first = true; init_rem_hits(); @@ -367,8 +359,10 @@ size_t hists__fprintf(struct hists *hists, struct hists *pair, if (!perf_hpp__format[idx].cond) continue; - if (idx) + if (!first) fprintf(fp, "%s", sep ?: " "); + else + first = false; perf_hpp__format[idx].header(&dummy_hpp); fprintf(fp, "%s", bf); @@ -403,6 +397,8 @@ size_t hists__fprintf(struct hists *hists, struct hists *pair, if (sep) goto print_entries; + first = true; + fprintf(fp, "# "); for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) { unsigned int i; @@ -410,8 +406,10 @@ size_t hists__fprintf(struct hists *hists, struct hists *pair, if (!perf_hpp__format[idx].cond) continue; - if (idx) + if (!first) fprintf(fp, "%s", sep ?: " "); + else + first = false; width = perf_hpp__format[idx].width(&dummy_hpp); for (i = 0; i < width; i++) @@ -441,24 +439,13 @@ size_t hists__fprintf(struct hists *hists, struct hists *pair, goto out; print_entries: - total_period = hists->stats.total_period; - for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); if (h->filtered) continue; - if (show_displacement) { - if (h->pair != NULL) - displacement = ((long)h->pair->position - - (long)position); - else - displacement = 0; - ++position; - } - ret += hist_entry__fprintf(h, max_cols, hists, pair, displacement, - total_period, fp); + ret += hist_entry__fprintf(h, max_cols, hists, fp); if (max_rows && ++nr_rows >= max_rows) goto out; diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 9b5b21e7b032..39242dcee8f2 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -138,7 +138,10 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, bool print_lines, bool full_paths, int min_pcnt, int max_lines); -#ifdef NO_NEWT_SUPPORT +#ifdef NEWT_SUPPORT +int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, + void(*timer)(void *arg), void *arg, int delay_secs); +#else static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, struct map *map __maybe_unused, int evidx __maybe_unused, @@ -148,9 +151,6 @@ static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, { return 0; } -#else -int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, - void(*timer)(void *arg), void *arg, int delay_secs); #endif extern const char *disassembler_style; diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index ab1769426541..2bd51370ad28 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h @@ -33,39 +33,41 @@ extern int pager_use_color; extern int use_browser; -#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT) -static inline void setup_browser(bool fallback_to_pager) -{ - if (fallback_to_pager) - setup_pager(); -} -static inline void exit_browser(bool wait_for_ok __maybe_unused) {} -#else +#if defined(NEWT_SUPPORT) || defined(GTK2_SUPPORT) void setup_browser(bool fallback_to_pager); void exit_browser(bool wait_for_ok); -#ifdef NO_NEWT_SUPPORT +#ifdef NEWT_SUPPORT +int ui__init(void); +void ui__exit(bool wait_for_ok); +#else static inline int ui__init(void) { return -1; } static inline void ui__exit(bool wait_for_ok __maybe_unused) {} -#else -int ui__init(void); -void ui__exit(bool wait_for_ok); #endif -#ifdef NO_GTK2_SUPPORT +#ifdef GTK2_SUPPORT +int perf_gtk__init(void); +void perf_gtk__exit(bool wait_for_ok); +#else static inline int perf_gtk__init(void) { return -1; } static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {} -#else -int perf_gtk__init(void); -void perf_gtk__exit(bool wait_for_ok); #endif -#endif /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */ + +#else /* NEWT_SUPPORT || GTK2_SUPPORT */ + +static inline void setup_browser(bool fallback_to_pager) +{ + if (fallback_to_pager) + setup_pager(); +} +static inline void exit_browser(bool wait_for_ok __maybe_unused) {} +#endif /* NEWT_SUPPORT || GTK2_SUPPORT */ char *alias_lookup(const char *alias); int split_cmdline(char *cmdline, const char ***argv); @@ -105,7 +107,7 @@ extern char *perf_path(const char *fmt, ...) __attribute__((format (printf, 1, 2 extern char *perf_pathdup(const char *fmt, ...) __attribute__((format (printf, 1, 2))); -#ifdef NO_STRLCPY +#ifndef HAVE_STRLCPY extern size_t strlcpy(char *dest, const char *src, size_t size); #endif diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 66eb3828ceb5..03f830b48148 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -49,7 +49,7 @@ int dump_printf(const char *fmt, ...) return ret; } -#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT) +#if !defined(NEWT_SUPPORT) && !defined(GTK2_SUPPORT) int ui__warning(const char *format, ...) { va_list args; diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index bb2e7d1007ab..dec98750b484 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -15,7 +15,14 @@ void trace_event(union perf_event *event); struct ui_progress; struct perf_error_ops; -#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT) +#if defined(NEWT_SUPPORT) || defined(GTK2_SUPPORT) + +#include "../ui/progress.h" +int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); +#include "../ui/util.h" + +#else + static inline void ui_progress__update(u64 curr __maybe_unused, u64 total __maybe_unused, const char *title __maybe_unused) {} @@ -34,13 +41,7 @@ perf_error__unregister(struct perf_error_ops *eops __maybe_unused) return 0; } -#else /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */ - -#include "../ui/progress.h" -int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); -#include "../ui/util.h" - -#endif /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */ +#endif /* NEWT_SUPPORT || GTK2_SUPPORT */ int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); int ui__error_paranoid(void); diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index ae89686102f4..186b87730396 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -154,8 +154,8 @@ error: return -ENOMEM; } -int perf_evlist__add_attrs(struct perf_evlist *evlist, - struct perf_event_attr *attrs, size_t nr_attrs) +static int perf_evlist__add_attrs(struct perf_evlist *evlist, + struct perf_event_attr *attrs, size_t nr_attrs) { struct perf_evsel *evsel, *n; LIST_HEAD(head); @@ -189,60 +189,6 @@ int __perf_evlist__add_default_attrs(struct perf_evlist *evlist, return perf_evlist__add_attrs(evlist, attrs, nr_attrs); } -static int trace_event__id(const char *evname) -{ - char *filename, *colon; - int err = -1, fd; - - if (asprintf(&filename, "%s/%s/id", tracing_events_path, evname) < 0) - return -1; - - colon = strrchr(filename, ':'); - if (colon != NULL) - *colon = '/'; - - fd = open(filename, O_RDONLY); - if (fd >= 0) { - char id[16]; - if (read(fd, id, sizeof(id)) > 0) - err = atoi(id); - close(fd); - } - - free(filename); - return err; -} - -int perf_evlist__add_tracepoints(struct perf_evlist *evlist, - const char *tracepoints[], - size_t nr_tracepoints) -{ - int err; - size_t i; - struct perf_event_attr *attrs = zalloc(nr_tracepoints * sizeof(*attrs)); - - if (attrs == NULL) - return -1; - - for (i = 0; i < nr_tracepoints; i++) { - err = trace_event__id(tracepoints[i]); - - if (err < 0) - goto out_free_attrs; - - attrs[i].type = PERF_TYPE_TRACEPOINT; - attrs[i].config = err; - attrs[i].sample_type = (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | - PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD); - attrs[i].sample_period = 1; - } - - err = perf_evlist__add_attrs(evlist, attrs, nr_tracepoints); -out_free_attrs: - free(attrs); - return err; -} - struct perf_evsel * perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id) { @@ -257,32 +203,18 @@ perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id) return NULL; } -int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist, - const struct perf_evsel_str_handler *assocs, - size_t nr_assocs) +int perf_evlist__add_newtp(struct perf_evlist *evlist, + const char *sys, const char *name, void *handler) { struct perf_evsel *evsel; - int err; - size_t i; - - for (i = 0; i < nr_assocs; i++) { - err = trace_event__id(assocs[i].name); - if (err < 0) - goto out; - - evsel = perf_evlist__find_tracepoint_by_id(evlist, err); - if (evsel == NULL) - continue; - err = -EEXIST; - if (evsel->handler.func != NULL) - goto out; - evsel->handler.func = assocs[i].handler; - } + evsel = perf_evsel__newtp(sys, name, evlist->nr_entries); + if (evsel == NULL) + return -1; - err = 0; -out: - return err; + evsel->handler.func = handler; + perf_evlist__add(evlist, evsel); + return 0; } void perf_evlist__disable(struct perf_evlist *evlist) diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 3f1fb66be022..56003f779e60 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -51,26 +51,14 @@ void perf_evlist__delete(struct perf_evlist *evlist); void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry); int perf_evlist__add_default(struct perf_evlist *evlist); -int perf_evlist__add_attrs(struct perf_evlist *evlist, - struct perf_event_attr *attrs, size_t nr_attrs); int __perf_evlist__add_default_attrs(struct perf_evlist *evlist, struct perf_event_attr *attrs, size_t nr_attrs); -int perf_evlist__add_tracepoints(struct perf_evlist *evlist, - const char *tracepoints[], size_t nr_tracepoints); -int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist, - const struct perf_evsel_str_handler *assocs, - size_t nr_assocs); - -#define perf_evlist__add_attrs_array(evlist, array) \ - perf_evlist__add_attrs(evlist, array, ARRAY_SIZE(array)) + #define perf_evlist__add_default_attrs(evlist, array) \ __perf_evlist__add_default_attrs(evlist, array, ARRAY_SIZE(array)) -#define perf_evlist__add_tracepoints_array(evlist, array) \ - perf_evlist__add_tracepoints(evlist, array, ARRAY_SIZE(array)) - -#define perf_evlist__set_tracepoints_handlers_array(evlist, array) \ - perf_evlist__set_tracepoints_handlers(evlist, array, ARRAY_SIZE(array)) +int perf_evlist__add_newtp(struct perf_evlist *evlist, + const char *sys, const char *name, void *handler); int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index ffdd94e9c9c3..618d41140abd 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -19,7 +19,7 @@ #include "thread_map.h" #include "target.h" #include "../../../include/linux/hw_breakpoint.h" -#include "../../include/linux/perf_event.h" +#include "../../../include/uapi/linux/perf_event.h" #include "perf_regs.h" #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 3ead0d59c03d..6f94d6dea00f 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -3,7 +3,7 @@ #include <linux/list.h> #include <stdbool.h> -#include "../../../include/linux/perf_event.h" +#include "../../../include/uapi/linux/perf_event.h" #include "types.h" #include "xyarray.h" #include "cgroup.h" diff --git a/tools/perf/util/generate-cmdlist.sh b/tools/perf/util/generate-cmdlist.sh index 389590c1ad21..3ac38031d534 100755 --- a/tools/perf/util/generate-cmdlist.sh +++ b/tools/perf/util/generate-cmdlist.sh @@ -22,7 +22,7 @@ do }' "Documentation/perf-$cmd.txt" done -echo "#ifndef NO_LIBELF_SUPPORT" +echo "#ifdef LIBELF_SUPPORT" sed -n -e 's/^perf-\([^ ]*\)[ ].* full.*/\1/p' command-list.txt | sort | while read cmd @@ -35,5 +35,5 @@ do p }' "Documentation/perf-$cmd.txt" done -echo "#endif /* NO_LIBELF_SUPPORT */" +echo "#endif /* LIBELF_SUPPORT */" echo "};" diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 99bdd3abce59..879d215cdac9 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -1,7 +1,7 @@ #ifndef __PERF_HEADER_H #define __PERF_HEADER_H -#include "../../../include/linux/perf_event.h" +#include "../../../include/uapi/linux/perf_event.h" #include <sys/types.h> #include <stdbool.h> #include "types.h" diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 236bc9d98ff2..277947a669b2 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -135,31 +135,47 @@ static void hist_entry__add_cpumode_period(struct hist_entry *he, { switch (cpumode) { case PERF_RECORD_MISC_KERNEL: - he->period_sys += period; + he->stat.period_sys += period; break; case PERF_RECORD_MISC_USER: - he->period_us += period; + he->stat.period_us += period; break; case PERF_RECORD_MISC_GUEST_KERNEL: - he->period_guest_sys += period; + he->stat.period_guest_sys += period; break; case PERF_RECORD_MISC_GUEST_USER: - he->period_guest_us += period; + he->stat.period_guest_us += period; break; default: break; } } +static void he_stat__add_period(struct he_stat *he_stat, u64 period) +{ + he_stat->period += period; + he_stat->nr_events += 1; +} + +static void he_stat__add_stat(struct he_stat *dest, struct he_stat *src) +{ + dest->period += src->period; + dest->period_sys += src->period_sys; + dest->period_us += src->period_us; + dest->period_guest_sys += src->period_guest_sys; + dest->period_guest_us += src->period_guest_us; + dest->nr_events += src->nr_events; +} + static void hist_entry__decay(struct hist_entry *he) { - he->period = (he->period * 7) / 8; - he->nr_events = (he->nr_events * 7) / 8; + he->stat.period = (he->stat.period * 7) / 8; + he->stat.nr_events = (he->stat.nr_events * 7) / 8; } static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) { - u64 prev_period = he->period; + u64 prev_period = he->stat.period; if (prev_period == 0) return true; @@ -167,9 +183,9 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) hist_entry__decay(he); if (!he->filtered) - hists->stats.total_period -= prev_period - he->period; + hists->stats.total_period -= prev_period - he->stat.period; - return he->period == 0; + return he->stat.period == 0; } static void __hists__decay_entries(struct hists *hists, bool zap_user, @@ -223,7 +239,7 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) if (he != NULL) { *he = *template; - he->nr_events = 1; + if (he->ms.map) he->ms.map->referenced = true; if (symbol_conf.use_callchain) @@ -238,7 +254,7 @@ static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) if (!h->filtered) { hists__calc_col_len(hists, h); ++hists->nr_entries; - hists->stats.total_period += h->period; + hists->stats.total_period += h->stat.period; } } @@ -270,8 +286,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists, cmp = hist_entry__cmp(entry, he); if (!cmp) { - he->period += period; - ++he->nr_events; + he_stat__add_period(&he->stat, period); /* If the map of an existing hist_entry has * become out-of-date due to an exec() or @@ -321,10 +336,14 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self, .cpu = al->cpu, .ip = bi->to.addr, .level = al->level, - .period = period, + .stat = { + .period = period, + .nr_events = 1, + }, .parent = sym_parent, .filtered = symbol__parent_filter(sym_parent), .branch_info = bi, + .hists = self, }; return add_hist_entry(self, &entry, al, period); @@ -343,9 +362,13 @@ struct hist_entry *__hists__add_entry(struct hists *self, .cpu = al->cpu, .ip = al->addr, .level = al->level, - .period = period, + .stat = { + .period = period, + .nr_events = 1, + }, .parent = sym_parent, .filtered = symbol__parent_filter(sym_parent), + .hists = self, }; return add_hist_entry(self, &entry, al, period); @@ -410,12 +433,7 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused, cmp = hist_entry__collapse(iter, he); if (!cmp) { - iter->period += he->period; - iter->period_sys += he->period_sys; - iter->period_us += he->period_us; - iter->period_guest_sys += he->period_guest_sys; - iter->period_guest_us += he->period_guest_us; - iter->nr_events += he->nr_events; + he_stat__add_stat(&iter->stat, &he->stat); if (symbol_conf.use_callchain) { callchain_cursor_reset(&callchain_cursor); @@ -518,7 +536,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, parent = *p; iter = rb_entry(parent, struct hist_entry, rb_node); - if (he->period > iter->period) + if (he->stat.period > iter->stat.period) p = &(*p)->rb_left; else p = &(*p)->rb_right; @@ -579,8 +597,8 @@ static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h if (h->ms.unfolded) hists->nr_entries += h->nr_rows; h->row_offset = 0; - hists->stats.total_period += h->period; - hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; + hists->stats.total_period += h->stat.period; + hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->stat.nr_events; hists__calc_col_len(hists, h); } diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index f011ad4756e8..66cb31fe81d2 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -98,9 +98,8 @@ void hists__output_recalc_col_len(struct hists *hists, int max_rows); void hists__inc_nr_events(struct hists *self, u32 type); size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); -size_t hists__fprintf(struct hists *self, struct hists *pair, - bool show_displacement, bool show_header, - int max_rows, int max_cols, FILE *fp); +size_t hists__fprintf(struct hists *self, bool show_header, int max_rows, + int max_cols, FILE *fp); int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); int hist_entry__annotate(struct hist_entry *self, size_t privsize); @@ -118,9 +117,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *he); struct perf_hpp { char *buf; size_t size; - u64 total_period; const char *sep; - long displacement; void *ptr; }; @@ -135,6 +132,7 @@ struct perf_hpp_fmt { extern struct perf_hpp_fmt perf_hpp__format[]; enum { + PERF_HPP__BASELINE, PERF_HPP__OVERHEAD, PERF_HPP__OVERHEAD_SYS, PERF_HPP__OVERHEAD_US, @@ -148,13 +146,22 @@ enum { PERF_HPP__MAX_INDEX }; -void perf_hpp__init(bool need_pair, bool show_displacement); +void perf_hpp__init(void); +void perf_hpp__column_enable(unsigned col, bool enable); int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, bool color); struct perf_evlist; -#ifdef NO_NEWT_SUPPORT +#ifdef NEWT_SUPPORT +#include "../ui/keysyms.h" +int hist_entry__tui_annotate(struct hist_entry *he, int evidx, + void(*timer)(void *arg), void *arg, int delay_secs); + +int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, + void(*timer)(void *arg), void *arg, + int refresh); +#else static inline int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, const char *help __maybe_unused, @@ -177,17 +184,13 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self } #define K_LEFT -1 #define K_RIGHT -2 -#else -#include "../ui/keysyms.h" -int hist_entry__tui_annotate(struct hist_entry *he, int evidx, - void(*timer)(void *arg), void *arg, int delay_secs); +#endif -int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, +#ifdef GTK2_SUPPORT +int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, void(*timer)(void *arg), void *arg, int refresh); -#endif - -#ifdef NO_GTK2_SUPPORT +#else static inline int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, const char *help __maybe_unused, @@ -197,11 +200,6 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, { return 0; } - -#else -int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, - void(*timer)(void *arg), void *arg, - int refresh); #endif unsigned int hists__sort_list_width(struct hists *self); diff --git a/tools/perf/util/include/asm/byteorder.h b/tools/perf/util/include/asm/byteorder.h index b722abe3a626..2a9bdc066307 100644 --- a/tools/perf/util/include/asm/byteorder.h +++ b/tools/perf/util/include/asm/byteorder.h @@ -1,2 +1,2 @@ #include <asm/types.h> -#include "../../../../include/linux/swab.h" +#include "../../../../include/uapi/linux/swab.h" diff --git a/tools/perf/util/include/linux/const.h b/tools/perf/util/include/linux/const.h index 1b476c9ae649..c10a35e1afb8 100644 --- a/tools/perf/util/include/linux/const.h +++ b/tools/perf/util/include/linux/const.h @@ -1 +1 @@ -#include "../../../../include/linux/const.h" +#include "../../../../include/uapi/linux/const.h" diff --git a/tools/perf/util/include/linux/rbtree_augmented.h b/tools/perf/util/include/linux/rbtree_augmented.h new file mode 100644 index 000000000000..9d6fcdf1788b --- /dev/null +++ b/tools/perf/util/include/linux/rbtree_augmented.h @@ -0,0 +1,2 @@ +#include <stdbool.h> +#include "../../../../include/linux/rbtree_augmented.h" diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index ead5316b3f89..6109fa4d14cd 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -162,7 +162,7 @@ int map__load(struct map *self, symbol_filter_t filter) pr_warning(", continuing without symbols\n"); return -1; } else if (nr == 0) { -#ifndef NO_LIBELF_SUPPORT +#ifdef LIBELF_SUPPORT const size_t len = strlen(name); const size_t real_len = len - sizeof(DSO__DELETED); diff --git a/tools/perf/util/parse-events-test.c b/tools/perf/util/parse-events-test.c index 28c18d1d52c3..516ecd9ddd6e 100644 --- a/tools/perf/util/parse-events-test.c +++ b/tools/perf/util/parse-events-test.c @@ -513,7 +513,8 @@ static int test__group1(struct perf_evlist *evlist) 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 exclude guest", !evsel->attr.exclude_guest); + /* use of precise requires exclude_guest */ + TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); @@ -599,7 +600,8 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) 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 exclude guest", !evsel->attr.exclude_guest); + /* use of precise requires exclude_guest */ + TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 3); TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); @@ -662,7 +664,8 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused) 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 exclude guest", !evsel->attr.exclude_guest); + /* use of precise requires exclude_guest */ + TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1); TEST_ASSERT_VAL("wrong group name", !evsel->group_name); @@ -676,7 +679,8 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused) 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 exclude guest", !evsel->attr.exclude_guest); + /* use of precise requires exclude_guest */ + TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index aed38e4b9dfa..75c7b0fca6d9 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -690,6 +690,9 @@ static int get_event_modifier(struct event_modifier *mod, char *str, eH = 0; } else if (*str == 'p') { precise++; + /* use of precise requires exclude_guest */ + if (!exclude_GH) + eG = 1; } else break; diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index c356e443448d..839230ceb18b 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -7,7 +7,7 @@ #include <linux/list.h> #include <stdbool.h> #include "types.h" -#include "../../../include/linux/perf_event.h" +#include "../../../include/uapi/linux/perf_event.h" #include "types.h" struct list_head; diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 443fc116512b..2bc9e70df7e2 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -384,6 +384,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, return usage_with_options_internal(usagestr, options, 1); if (internal_help && !strcmp(arg + 2, "help")) return parse_options_usage(usagestr, options); + if (!strcmp(arg + 2, "list-opts")) + return PARSE_OPT_LIST; switch (parse_long_opt(ctx, arg + 2, options)) { case -1: return parse_options_usage(usagestr, options); @@ -422,6 +424,12 @@ int parse_options(int argc, const char **argv, const struct option *options, exit(129); case PARSE_OPT_DONE: break; + case PARSE_OPT_LIST: + while (options->type != OPTION_END) { + printf("--%s ", options->long_name); + options++; + } + exit(130); default: /* PARSE_OPT_UNKNOWN */ if (ctx.argv[0][1] == '-') { error("unknown option `%s'", ctx.argv[0] + 2); diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index abc31a1dac1a..7bb5999940ca 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -140,6 +140,7 @@ extern NORETURN void usage_with_options(const char * const *usagestr, enum { PARSE_OPT_HELP = -1, PARSE_OPT_DONE, + PARSE_OPT_LIST, PARSE_OPT_UNKNOWN, }; diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c index bd7497711424..a8c49548ca48 100644 --- a/tools/perf/util/path.c +++ b/tools/perf/util/path.c @@ -22,7 +22,7 @@ static const char *get_perf_dir(void) return "."; } -#ifdef NO_STRLCPY +#ifndef HAVE_STRLCPY size_t strlcpy(char *dest, const char *src, size_t size) { size_t ret = strlen(src); diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h index 316dbe7f86ed..5a4f2b6f3738 100644 --- a/tools/perf/util/perf_regs.h +++ b/tools/perf/util/perf_regs.h @@ -1,7 +1,7 @@ #ifndef __PERF_REGS_H #define __PERF_REGS_H -#ifndef NO_PERF_REGS +#ifdef HAVE_PERF_REGS #include <perf_regs.h> #else #define PERF_REGS_MASK 0 @@ -10,5 +10,5 @@ static inline const char *perf_reg_name(int id __maybe_unused) { return NULL; } -#endif /* NO_PERF_REGS */ +#endif /* HAVE_PERF_REGS */ #endif /* __PERF_REGS_H */ diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 53c7794fc4be..39f3abac7744 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -2,7 +2,7 @@ #define __PMU_H #include <linux/bitops.h> -#include "../../../include/linux/perf_event.h" +#include "../../../include/uapi/linux/perf_event.h" enum { PERF_PMU_FORMAT_VALUE_CONFIG, diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index aab414fbb64b..dd6426163ba6 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -7,7 +7,7 @@ #include "symbol.h" #include "thread.h" #include <linux/rbtree.h> -#include "../../../include/linux/perf_event.h" +#include "../../../include/uapi/linux/perf_event.h" struct sample_queue; struct ip_callchain; diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index d0f9f29cf181..73d510269784 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py @@ -23,6 +23,7 @@ cflags += getenv('CFLAGS', '').split() build_lib = getenv('PYTHON_EXTBUILD_LIB') build_tmp = getenv('PYTHON_EXTBUILD_TMP') +libtraceevent = getenv('LIBTRACEEVENT') ext_sources = [f.strip() for f in file('util/python-ext-sources') if len(f.strip()) > 0 and f[0] != '#'] @@ -31,6 +32,7 @@ perf = Extension('perf', sources = ext_sources, include_dirs = ['util/include'], extra_compile_args = cflags, + extra_objects = [libtraceevent], ) setup(name='perf', diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index b5b1b9211960..cfd1c0feb32d 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -260,6 +260,12 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, if (path != NULL) goto out_path; + if (!self->ms.map) + goto out_ip; + + if (!strncmp(self->ms.map->dso->long_name, "/tmp/perf-", 10)) + goto out_ip; + snprintf(cmd, sizeof(cmd), "addr2line -e %s %016" PRIx64, self->ms.map->dso->long_name, self->ip); fp = popen(cmd, "r"); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 12d634792de5..5786f323b597 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -43,6 +43,15 @@ extern struct sort_entry sort_sym_from; extern struct sort_entry sort_sym_to; extern enum sort_type sort__first_dimension; +struct he_stat { + u64 period; + u64 period_sys; + u64 period_us; + u64 period_guest_sys; + u64 period_guest_us; + u32 nr_events; +}; + /** * struct hist_entry - histogram entry * @@ -52,16 +61,11 @@ extern enum sort_type sort__first_dimension; struct hist_entry { struct rb_node rb_node_in; struct rb_node rb_node; - u64 period; - u64 period_sys; - u64 period_us; - u64 period_guest_sys; - u64 period_guest_us; + struct he_stat stat; struct map_symbol ms; struct thread *thread; u64 ip; s32 cpu; - u32 nr_events; /* XXX These two should move to some tree widget lib */ u16 row_offset; @@ -73,12 +77,13 @@ struct hist_entry { u8 filtered; char *srcline; struct symbol *parent; + unsigned long position; union { - unsigned long position; struct hist_entry *pair; struct rb_root sorted_chain; }; struct branch_info *branch_info; + struct hists *hists; struct callchain_root callchain[0]; }; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index b441b07172b7..8b6ef7fac745 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -12,7 +12,7 @@ #include <byteswap.h> #include <libgen.h> -#ifndef NO_LIBELF_SUPPORT +#ifdef LIBELF_SUPPORT #include <libelf.h> #include <gelf.h> #include <elf.h> @@ -46,10 +46,10 @@ char *strxfrchar(char *s, char from, char to); * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; * for newer versions we can use mmap to reduce memory usage: */ -#ifdef LIBELF_NO_MMAP -# define PERF_ELF_C_READ_MMAP ELF_C_READ -#else +#ifdef LIBELF_MMAP # define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP +#else +# define PERF_ELF_C_READ_MMAP ELF_C_READ #endif #ifndef DMGL_PARAMS @@ -233,7 +233,7 @@ struct symsrc { int fd; enum dso_binary_type type; -#ifndef NO_LIBELF_SUPPORT +#ifdef LIBELF_SUPPORT Elf *elf; GElf_Ehdr ehdr; diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index fb4b7ea6752f..8b3e5939afb6 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -39,7 +39,6 @@ int thread__set_comm(struct thread *self, const char *comm) err = self->comm == NULL ? -ENOMEM : 0; if (!err) { self->comm_set = true; - map_groups__flush(&self->mg); } return err; } diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h index a78c8b303bb5..cb6bc503a792 100644 --- a/tools/perf/util/unwind.h +++ b/tools/perf/util/unwind.h @@ -13,7 +13,7 @@ struct unwind_entry { typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); -#ifndef NO_LIBUNWIND_SUPPORT +#ifdef LIBUNWIND_SUPPORT int unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct machine *machine, struct thread *thread, @@ -31,5 +31,5 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, { return 0; } -#endif /* NO_LIBUNWIND_SUPPORT */ +#endif /* LIBUNWIND_SUPPORT */ #endif /* __UNWIND_H */ diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 2055cf38041c..99664598bc1a 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -1,7 +1,7 @@ #include "../perf.h" #include "util.h" #include <sys/mman.h> -#ifndef NO_BACKTRACE +#ifdef BACKTRACE_SUPPORT #include <execinfo.h> #endif #include <stdio.h> @@ -165,7 +165,7 @@ size_t hex_width(u64 v) } /* Obtain a backtrace and print it to stdout. */ -#ifndef NO_BACKTRACE +#ifdef BACKTRACE_SUPPORT void dump_stack(void) { void *array[16]; diff --git a/tools/power/acpi/Makefile b/tools/power/acpi/Makefile new file mode 100644 index 000000000000..6b9cf7a987c7 --- /dev/null +++ b/tools/power/acpi/Makefile @@ -0,0 +1,18 @@ +PROG= acpidump +SRCS= acpidump.c +KERNEL_INCLUDE := ../../../include +CFLAGS += -Wall -Wstrict-prototypes -Wdeclaration-after-statement -Os -s -D_LINUX -DDEFINE_ALTERNATE_TYPES -I$(KERNEL_INCLUDE) + +all: acpidump +$(PROG) : $(SRCS) + $(CC) $(CFLAGS) $(SRCS) -o $(PROG) + +CLEANFILES= $(PROG) + +clean : + rm -f $(CLEANFILES) $(patsubst %.c,%.o, $(SRCS)) *~ + +install : + install acpidump /usr/bin/acpidump + install acpidump.8 /usr/share/man/man8 + diff --git a/tools/power/acpi/acpidump.8 b/tools/power/acpi/acpidump.8 new file mode 100644 index 000000000000..adfa99166e5e --- /dev/null +++ b/tools/power/acpi/acpidump.8 @@ -0,0 +1,59 @@ +.TH ACPIDUMP 8 +.SH NAME +acpidump \- Dump system's ACPI tables to an ASCII file. +.SH SYNOPSIS +.ft B +.B acpidump > acpidump.out +.SH DESCRIPTION +\fBacpidump \fP dumps the systems ACPI tables to an ASCII file +appropriate for attaching to a bug report. + +Subsequently, they can be processed by utilities in the ACPICA package. +.SS Options +no options worth worrying about. +.PP +.SH EXAMPLE + +.nf +# acpidump > acpidump.out + +$ acpixtract -a acpidump.out + Acpi table [DSDT] - 15974 bytes written to DSDT.dat + Acpi table [FACS] - 64 bytes written to FACS.dat + Acpi table [FACP] - 116 bytes written to FACP.dat + Acpi table [APIC] - 120 bytes written to APIC.dat + Acpi table [MCFG] - 60 bytes written to MCFG.dat + Acpi table [SSDT] - 444 bytes written to SSDT1.dat + Acpi table [SSDT] - 439 bytes written to SSDT2.dat + Acpi table [SSDT] - 439 bytes written to SSDT3.dat + Acpi table [SSDT] - 439 bytes written to SSDT4.dat + Acpi table [SSDT] - 439 bytes written to SSDT5.dat + Acpi table [RSDT] - 76 bytes written to RSDT.dat + Acpi table [RSDP] - 20 bytes written to RSDP.dat + +$ iasl -d *.dat +... +.fi +creates *.dsl, a human readable form which can be edited +and compiled using iasl. + + +.SH NOTES + +.B "acpidump " +must be run as root. + +.SH REFERENCES +ACPICA: https://acpica.org/ + +.SH FILES +.ta +.nf +/dev/mem +/sys/firmware/acpi/tables/dynamic/* +.fi + +.PP +.SH AUTHOR +.nf +Written by Len Brown <len.brown@intel.com> diff --git a/tools/power/acpi/acpidump.c b/tools/power/acpi/acpidump.c new file mode 100644 index 000000000000..a84553a0e0df --- /dev/null +++ b/tools/power/acpi/acpidump.c @@ -0,0 +1,559 @@ +/* + * (c) Alexey Starikovskiy, Intel, 2005-2006. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#ifdef DEFINE_ALTERNATE_TYPES +/* hack to enable building old application with new headers -lenb */ +#define acpi_fadt_descriptor acpi_table_fadt +#define acpi_rsdp_descriptor acpi_table_rsdp +#define DSDT_SIG ACPI_SIG_DSDT +#define FACS_SIG ACPI_SIG_FACS +#define FADT_SIG ACPI_SIG_FADT +#define xfirmware_ctrl Xfacs +#define firmware_ctrl facs + +typedef int s32; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef long long s64; +#endif + +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <getopt.h> + +#include <dirent.h> + +#include <acpi/acconfig.h> +#include <acpi/platform/acenv.h> +#include <acpi/actypes.h> +#include <acpi/actbl.h> + +static inline u8 checksum(u8 * buffer, u32 length) +{ + u8 sum = 0, *i = buffer; + buffer += length; + for (; i < buffer; sum += *(i++)); + return sum; +} + +static unsigned long psz, addr, length; +static int print, connect, skip; +static u8 select_sig[4]; + +static unsigned long read_efi_systab( void ) +{ + char buffer[80]; + unsigned long addr; + FILE *f = fopen("/sys/firmware/efi/systab", "r"); + if (f) { + while (fgets(buffer, 80, f)) { + if (sscanf(buffer, "ACPI20=0x%lx", &addr) == 1) + return addr; + } + fclose(f); + } + return 0; +} + +static u8 *acpi_map_memory(unsigned long where, unsigned length) +{ + unsigned long offset; + u8 *there; + int fd = open("/dev/mem", O_RDONLY); + if (fd < 0) { + fprintf(stderr, "acpi_os_map_memory: cannot open /dev/mem\n"); + exit(1); + } + offset = where % psz; + there = mmap(NULL, length + offset, PROT_READ, MAP_PRIVATE, + fd, where - offset); + close(fd); + if (there == MAP_FAILED) return 0; + return (there + offset); +} + +static void acpi_unmap_memory(u8 * there, unsigned length) +{ + unsigned long offset = (unsigned long)there % psz; + munmap(there - offset, length + offset); +} + +static struct acpi_table_header *acpi_map_table(unsigned long where, char *sig) +{ + unsigned size; + struct acpi_table_header *tbl = (struct acpi_table_header *) + acpi_map_memory(where, sizeof(struct acpi_table_header)); + if (!tbl || (sig && memcmp(sig, tbl->signature, 4))) return 0; + size = tbl->length; + acpi_unmap_memory((u8 *) tbl, sizeof(struct acpi_table_header)); + return (struct acpi_table_header *)acpi_map_memory(where, size); +} + +static void acpi_unmap_table(struct acpi_table_header *tbl) +{ + acpi_unmap_memory((u8 *)tbl, tbl->length); +} + +static struct acpi_rsdp_descriptor *acpi_scan_for_rsdp(u8 *begin, u32 length) +{ + struct acpi_rsdp_descriptor *rsdp; + u8 *i, *end = begin + length; + /* Search from given start address for the requested length */ + for (i = begin; i < end; i += ACPI_RSDP_SCAN_STEP) { + /* The signature and checksum must both be correct */ + if (memcmp((char *)i, "RSD PTR ", 8)) continue; + rsdp = (struct acpi_rsdp_descriptor *)i; + /* Signature matches, check the appropriate checksum */ + if (!checksum((u8 *) rsdp, (rsdp->revision < 2) ? + ACPI_RSDP_CHECKSUM_LENGTH : + ACPI_RSDP_XCHECKSUM_LENGTH)) + /* Checksum valid, we have found a valid RSDP */ + return rsdp; + } + /* Searched entire block, no RSDP was found */ + return 0; +} + +/* + * Output data + */ +static void acpi_show_data(int fd, u8 * data, int size) +{ + char buffer[256]; + int len; + int i, remain = size; + while (remain > 0) { + len = snprintf(buffer, 256, " %04x:", size - remain); + for (i = 0; i < 16 && i < remain; i++) { + len += + snprintf(&buffer[len], 256 - len, " %02x", data[i]); + } + for (; i < 16; i++) { + len += snprintf(&buffer[len], 256 - len, " "); + } + len += snprintf(&buffer[len], 256 - len, " "); + for (i = 0; i < 16 && i < remain; i++) { + buffer[len++] = (isprint(data[i])) ? data[i] : '.'; + } + buffer[len++] = '\n'; + write(fd, buffer, len); + data += 16; + remain -= 16; + } +} + +/* + * Output ACPI table + */ +static void acpi_show_table(int fd, struct acpi_table_header *table, unsigned long addr) +{ + char buff[80]; + int len = snprintf(buff, 80, "%.4s @ %p\n", table->signature, (void *)addr); + write(fd, buff, len); + acpi_show_data(fd, (u8 *) table, table->length); + buff[0] = '\n'; + write(fd, buff, 1); +} + +static void write_table(int fd, struct acpi_table_header *tbl, unsigned long addr) +{ + static int select_done = 0; + if (!select_sig[0]) { + if (print) { + acpi_show_table(fd, tbl, addr); + } else { + write(fd, tbl, tbl->length); + } + } else if (!select_done && !memcmp(select_sig, tbl->signature, 4)) { + if (skip > 0) { + --skip; + return; + } + if (print) { + acpi_show_table(fd, tbl, addr); + } else { + write(fd, tbl, tbl->length); + } + select_done = 1; + } +} + +static void acpi_dump_FADT(int fd, struct acpi_table_header *tbl, unsigned long xaddr) { + struct acpi_fadt_descriptor x; + unsigned long addr; + size_t len = sizeof(struct acpi_fadt_descriptor); + if (len > tbl->length) len = tbl->length; + memcpy(&x, tbl, len); + x.header.length = len; + if (checksum((u8 *)tbl, len)) { + fprintf(stderr, "Wrong checksum for FADT!\n"); + } + if (x.header.length >= 148 && x.Xdsdt) { + addr = (unsigned long)x.Xdsdt; + if (connect) { + x.Xdsdt = lseek(fd, 0, SEEK_CUR); + } + } else if (x.header.length >= 44 && x.dsdt) { + addr = (unsigned long)x.dsdt; + if (connect) { + x.dsdt = lseek(fd, 0, SEEK_CUR); + } + } else { + fprintf(stderr, "No DSDT in FADT!\n"); + goto no_dsdt; + } + tbl = acpi_map_table(addr, DSDT_SIG); + if (!tbl) goto no_dsdt; + if (checksum((u8 *)tbl, tbl->length)) + fprintf(stderr, "Wrong checksum for DSDT!\n"); + write_table(fd, tbl, addr); + acpi_unmap_table(tbl); +no_dsdt: + if (x.header.length >= 140 && x.xfirmware_ctrl) { + addr = (unsigned long)x.xfirmware_ctrl; + if (connect) { + x.xfirmware_ctrl = lseek(fd, 0, SEEK_CUR); + } + } else if (x.header.length >= 40 && x.firmware_ctrl) { + addr = (unsigned long)x.firmware_ctrl; + if (connect) { + x.firmware_ctrl = lseek(fd, 0, SEEK_CUR); + } + } else { + fprintf(stderr, "No FACS in FADT!\n"); + goto no_facs; + } + tbl = acpi_map_table(addr, FACS_SIG); + if (!tbl) goto no_facs; + /* do not checksum FACS */ + write_table(fd, tbl, addr); + acpi_unmap_table(tbl); +no_facs: + write_table(fd, (struct acpi_table_header *)&x, xaddr); +} + +static int acpi_dump_SDT(int fd, struct acpi_rsdp_descriptor *rsdp) +{ + struct acpi_table_header *sdt, *tbl = 0; + int xsdt = 1, i, num; + char *offset; + unsigned long addr; + if (rsdp->revision > 1 && rsdp->xsdt_physical_address) { + tbl = acpi_map_table(rsdp->xsdt_physical_address, "XSDT"); + } + if (!tbl && rsdp->rsdt_physical_address) { + xsdt = 0; + tbl = acpi_map_table(rsdp->rsdt_physical_address, "RSDT"); + } + if (!tbl) return 0; + sdt = malloc(tbl->length); + memcpy(sdt, tbl, tbl->length); + acpi_unmap_table(tbl); + if (checksum((u8 *)sdt, sdt->length)) + fprintf(stderr, "Wrong checksum for %s!\n", (xsdt)?"XSDT":"RSDT"); + num = (sdt->length - sizeof(struct acpi_table_header))/((xsdt)?sizeof(u64):sizeof(u32)); + offset = (char *)sdt + sizeof(struct acpi_table_header); + for (i = 0; i < num; ++i, offset += ((xsdt) ? sizeof(u64) : sizeof(u32))) { + addr = (xsdt) ? (unsigned long)(*(u64 *)offset): + (unsigned long)(*(u32 *)offset); + if (!addr) continue; + tbl = acpi_map_table(addr, 0); + if (!tbl) continue; + if (!memcmp(tbl->signature, FADT_SIG, 4)) { + acpi_dump_FADT(fd, tbl, addr); + } else { + if (checksum((u8 *)tbl, tbl->length)) + fprintf(stderr, "Wrong checksum for generic table!\n"); + write_table(fd, tbl, addr); + } + acpi_unmap_table(tbl); + if (connect) { + if (xsdt) + (*(u64*)offset) = lseek(fd, 0, SEEK_CUR); + else + (*(u32*)offset) = lseek(fd, 0, SEEK_CUR); + } + } + if (xsdt) { + addr = (unsigned long)rsdp->xsdt_physical_address; + if (connect) { + rsdp->xsdt_physical_address = lseek(fd, 0, SEEK_CUR); + } + } else { + addr = (unsigned long)rsdp->rsdt_physical_address; + if (connect) { + rsdp->rsdt_physical_address = lseek(fd, 0, SEEK_CUR); + } + } + write_table(fd, sdt, addr); + free (sdt); + return 1; +} + +#define DYNAMIC_SSDT "/sys/firmware/acpi/tables/dynamic" + +static void acpi_dump_dynamic_SSDT(int fd) +{ + struct stat file_stat; + char filename[256], *ptr; + DIR *tabledir; + struct dirent *entry; + FILE *fp; + int count, readcount, length; + struct acpi_table_header table_header, *ptable; + + if (stat(DYNAMIC_SSDT, &file_stat) == -1) { + /* The directory doesn't exist */ + return; + } + tabledir = opendir(DYNAMIC_SSDT); + if(!tabledir){ + /*can't open the directory */ + return; + } + + while ((entry = readdir(tabledir)) != 0){ + /* skip the file of . /.. */ + if (entry->d_name[0] == '.') + continue; + + sprintf(filename, "%s/%s", DYNAMIC_SSDT, entry->d_name); + fp = fopen(filename, "r"); + if (fp == NULL) { + fprintf(stderr, "Can't open the file of %s\n", + filename); + continue; + } + /* Read the Table header to parse the table length */ + count = fread(&table_header, 1, sizeof(struct acpi_table_header), fp); + if (count < sizeof(table_header)) { + /* the length is lessn than ACPI table header. skip it */ + fclose(fp); + continue; + } + length = table_header.length; + ptr = malloc(table_header.length); + fseek(fp, 0, SEEK_SET); + readcount = 0; + while(!feof(fp) && readcount < length) { + count = fread(ptr + readcount, 1, 256, fp); + readcount += count; + } + fclose(fp); + ptable = (struct acpi_table_header *) ptr; + if (checksum((u8 *) ptable, ptable->length)) + fprintf(stderr, "Wrong checksum " + "for dynamic SSDT table!\n"); + write_table(fd, ptable, 0); + free(ptr); + } + closedir(tabledir); + return; +} + +static void usage(const char *progname) +{ + puts("Usage:"); + printf("%s [--addr 0x1234][--table DSDT][--output filename]" + "[--binary][--length 0x456][--help]\n", progname); + puts("\t--addr 0x1234 or -a 0x1234 -- look for tables at this physical address"); + puts("\t--table DSDT or -t DSDT -- only dump table with DSDT signature"); + puts("\t--output filename or -o filename -- redirect output from stdin to filename"); + puts("\t--binary or -b -- dump data in binary form rather than in hex-dump format"); + puts("\t--length 0x456 or -l 0x456 -- works only with --addr, dump physical memory" + "\n\t\tregion without trying to understand it's contents"); + puts("\t--skip 2 or -s 2 -- skip 2 tables of the given name and output only 3rd one"); + puts("\t--help or -h -- this help message"); + exit(0); +} + +static struct option long_options[] = { + {"addr", 1, 0, 0}, + {"table", 1, 0, 0}, + {"output", 1, 0, 0}, + {"binary", 0, 0, 0}, + {"length", 1, 0, 0}, + {"skip", 1, 0, 0}, + {"help", 0, 0, 0}, + {0, 0, 0, 0} +}; +int main(int argc, char **argv) +{ + int option_index, c, fd; + u8 *raw; + struct acpi_rsdp_descriptor rsdpx, *x = 0; + char *filename = 0; + char buff[80]; + memset(select_sig, 0, 4); + print = 1; + connect = 0; + addr = length = 0; + skip = 0; + while (1) { + option_index = 0; + c = getopt_long(argc, argv, "a:t:o:bl:s:h", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 0: + switch (option_index) { + case 0: + addr = strtoul(optarg, (char **)NULL, 16); + break; + case 1: + memcpy(select_sig, optarg, 4); + break; + case 2: + filename = optarg; + break; + case 3: + print = 0; + break; + case 4: + length = strtoul(optarg, (char **)NULL, 16); + break; + case 5: + skip = strtoul(optarg, (char **)NULL, 10); + break; + case 6: + usage(argv[0]); + exit(0); + } + break; + case 'a': + addr = strtoul(optarg, (char **)NULL, 16); + break; + case 't': + memcpy(select_sig, optarg, 4); + break; + case 'o': + filename = optarg; + break; + case 'b': + print = 0; + break; + case 'l': + length = strtoul(optarg, (char **)NULL, 16); + break; + case 's': + skip = strtoul(optarg, (char **)NULL, 10); + break; + case 'h': + usage(argv[0]); + exit(0); + default: + printf("Unknown option!\n"); + usage(argv[0]); + exit(0); + } + } + + fd = STDOUT_FILENO; + if (filename) { + fd = creat(filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (fd < 0) + return fd; + } + + if (!select_sig[0] && !print) { + connect = 1; + } + + psz = sysconf(_SC_PAGESIZE); + if (length && addr) { + /* We know length and address, it means we just want a memory dump */ + if (!(raw = acpi_map_memory(addr, length))) + goto not_found; + write(fd, raw, length); + acpi_unmap_memory(raw, length); + close(fd); + return 0; + } + + length = sizeof(struct acpi_rsdp_descriptor); + if (!addr) { + addr = read_efi_systab(); + if (!addr) { + addr = ACPI_HI_RSDP_WINDOW_BASE; + length = ACPI_HI_RSDP_WINDOW_SIZE; + } + } + + if (!(raw = acpi_map_memory(addr, length)) || + !(x = acpi_scan_for_rsdp(raw, length))) + goto not_found; + + /* Find RSDP and print all found tables */ + memcpy(&rsdpx, x, sizeof(struct acpi_rsdp_descriptor)); + acpi_unmap_memory(raw, length); + if (connect) { + lseek(fd, sizeof(struct acpi_rsdp_descriptor), SEEK_SET); + } + if (!acpi_dump_SDT(fd, &rsdpx)) + goto not_found; + if (connect) { + lseek(fd, 0, SEEK_SET); + write(fd, x, (rsdpx.revision < 2) ? + ACPI_RSDP_CHECKSUM_LENGTH : ACPI_RSDP_XCHECKSUM_LENGTH); + } else if (!select_sig[0] || !memcmp("RSD PTR ", select_sig, 4)) { + addr += (long)x - (long)raw; + length = snprintf(buff, 80, "RSD PTR @ %p\n", (void *)addr); + write(fd, buff, length); + acpi_show_data(fd, (u8 *) & rsdpx, (rsdpx.revision < 2) ? + ACPI_RSDP_CHECKSUM_LENGTH : ACPI_RSDP_XCHECKSUM_LENGTH); + buff[0] = '\n'; + write(fd, buff, 1); + } + acpi_dump_dynamic_SSDT(fd); + close(fd); + return 0; +not_found: + close(fd); + fprintf(stderr, "ACPI tables were not found. If you know location " + "of RSD PTR table (from dmesg, etc), " + "supply it with either --addr or -a option\n"); + return 1; +} diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index a93e06cfcc2a..cf397bd26d0c 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -111,7 +111,7 @@ GMO_FILES = ${shell for HLANG in ${LANGUAGES}; do echo $(OUTPUT)po/$$HLANG.gmo; export CROSS CC AR STRIP RANLIB CFLAGS LDFLAGS LIB_OBJS # check if compiler option is supported -cc-supports = ${shell if $(CC) ${1} -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo "$(1)"; fi;} +cc-supports = ${shell if $(CC) ${1} -S -o /dev/null -x c /dev/null > /dev/null 2>&1; then echo "$(1)"; fi;} # use '-Os' optimization if available, else use -O2 OPTIMIZATION := $(call cc-supports,-Os,-O2) diff --git a/tools/power/x86/turbostat/turbostat.8 b/tools/power/x86/turbostat/turbostat.8 index 74e44507dfe9..e4d0690cccf9 100644 --- a/tools/power/x86/turbostat/turbostat.8 +++ b/tools/power/x86/turbostat/turbostat.8 @@ -4,15 +4,11 @@ turbostat \- Report processor frequency and idle statistics .SH SYNOPSIS .ft B .B turbostat -.RB [ "\-s" ] -.RB [ "\-v" ] -.RB [ "\-M MSR#" ] +.RB [ Options ] .RB command .br .B turbostat -.RB [ "\-s" ] -.RB [ "\-v" ] -.RB [ "\-M MSR#" ] +.RB [ Options ] .RB [ "\-i interval_sec" ] .SH DESCRIPTION \fBturbostat \fP reports processor topology, frequency @@ -27,16 +23,23 @@ supports an "invariant" TSC, plus the APERF and MPERF MSRs. on processors that additionally support C-state residency counters. .SS Options -The \fB-s\fP option limits output to a 1-line system summary for each interval. +The \fB-p\fP option limits output to the 1st thread in 1st core of each package. .PP -The \fB-c\fP option limits output to the 1st thread in each core. +The \fB-P\fP option limits output to the 1st thread in each Package. .PP -The \fB-p\fP option limits output to the 1st thread in each package. +The \fB-S\fP option limits output to a 1-line System Summary for each interval. .PP The \fB-v\fP option increases verbosity. .PP -The \fB-M MSR#\fP option dumps the specified MSR, -in addition to the usual frequency and idle statistics. +The \fB-s\fP option prints the SMI counter, equivalent to "-c 0x34" +.PP +The \fB-c MSR#\fP option includes the delta of the specified 32-bit MSR counter. +.PP +The \fB-C MSR#\fP option includes the delta of the specified 64-bit MSR counter. +.PP +The \fB-m MSR#\fP option includes the the specified 32-bit MSR value. +.PP +The \fB-M MSR#\fP option includes the the specified 64-bit MSR value. .PP The \fB-i interval_sec\fP option prints statistics every \fiinterval_sec\fP seconds. The default is 5 seconds. @@ -150,6 +153,29 @@ Note that turbostat reports average GHz of 3.63, while the arithmetic average of the GHz column above is lower. This is a weighted average, where the weight is %c0. ie. it is the total number of un-halted cycles elapsed per time divided by the number of CPUs. +.SH SMI COUNTING EXAMPLE +On Intel Nehalem and newer processors, MSR 0x34 is a System Management Mode Interrupt (SMI) counter. +Using the -m option, you can display how many SMIs have fired since reset, or if there +are SMIs during the measurement interval, you can display the delta using the -d option. +.nf +[root@x980 ~]# turbostat -m 0x34 +cor CPU %c0 GHz TSC MSR 0x034 %c1 %c3 %c6 %pc3 %pc6 + 1.41 1.82 3.38 0x00000000 8.92 37.82 51.85 17.37 0.55 + 0 0 3.73 2.03 3.38 0x00000055 1.72 48.25 46.31 17.38 0.55 + 0 6 0.14 1.63 3.38 0x00000056 5.30 + 1 2 2.51 1.80 3.38 0x00000056 15.65 29.33 52.52 + 1 8 0.10 1.65 3.38 0x00000056 18.05 + 2 4 1.16 1.68 3.38 0x00000056 5.87 24.47 68.50 + 2 10 0.10 1.63 3.38 0x00000056 6.93 + 8 1 3.84 1.91 3.38 0x00000056 1.36 50.65 44.16 + 8 7 0.08 1.64 3.38 0x00000056 5.12 + 9 3 1.82 1.73 3.38 0x00000056 7.59 24.21 66.38 + 9 9 0.09 1.68 3.38 0x00000056 9.32 + 10 5 1.66 1.65 3.38 0x00000056 15.10 50.00 33.23 + 10 11 1.72 1.65 3.38 0x00000056 15.05 +^C +[root@x980 ~]# +.fi .SH NOTES .B "turbostat " @@ -165,6 +191,13 @@ may work poorly on Linux-2.6.20 through 2.6.29, as \fBacpi-cpufreq \fPperiodically cleared the APERF and MPERF in those kernels. +If the TSC column does not make sense, then +the other numbers will also make no sense. +Turbostat is lightweight, and its data collection is not atomic. +These issues are usually caused by an extremely short measurement +interval (much less than 1 second), or system activity that prevents +turbostat from being able to run on all CPUS to quickly collect data. + The APERF, MPERF MSRs are defined to count non-halted cycles. Although it is not guaranteed by the architecture, turbostat assumes that they count at TSC rate, which is true on all processors tested to date. diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 861d77190206..2655ae9a3ad8 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c @@ -35,9 +35,9 @@ #include <ctype.h> #include <sched.h> -#define MSR_TSC 0x10 #define MSR_NEHALEM_PLATFORM_INFO 0xCE #define MSR_NEHALEM_TURBO_RATIO_LIMIT 0x1AD +#define MSR_IVT_TURBO_RATIO_LIMIT 0x1AE #define MSR_APERF 0xE8 #define MSR_MPERF 0xE7 #define MSR_PKG_C2_RESIDENCY 0x60D /* SNB only */ @@ -62,7 +62,11 @@ unsigned int genuine_intel; unsigned int has_invariant_tsc; unsigned int do_nehalem_platform_info; unsigned int do_nehalem_turbo_ratio_limit; -unsigned int extra_msr_offset; +unsigned int do_ivt_turbo_ratio_limit; +unsigned int extra_msr_offset32; +unsigned int extra_msr_offset64; +unsigned int extra_delta_offset32; +unsigned int extra_delta_offset64; double bclk; unsigned int show_pkg; unsigned int show_core; @@ -83,7 +87,10 @@ struct thread_data { unsigned long long aperf; unsigned long long mperf; unsigned long long c1; /* derived */ - unsigned long long extra_msr; + unsigned long long extra_msr64; + unsigned long long extra_delta64; + unsigned long long extra_msr32; + unsigned long long extra_delta32; unsigned int cpu_id; unsigned int flags; #define CPU_IS_FIRST_THREAD_IN_CORE 0x2 @@ -222,6 +229,14 @@ void print_header(void) if (has_aperf) outp += sprintf(outp, " GHz"); outp += sprintf(outp, " TSC"); + if (extra_delta_offset32) + outp += sprintf(outp, " count 0x%03X", extra_delta_offset32); + if (extra_delta_offset64) + outp += sprintf(outp, " COUNT 0x%03X", extra_delta_offset64); + if (extra_msr_offset32) + outp += sprintf(outp, " MSR 0x%03X", extra_msr_offset32); + if (extra_msr_offset64) + outp += sprintf(outp, " MSR 0x%03X", extra_msr_offset64); if (do_nhm_cstates) outp += sprintf(outp, " %%c1"); if (do_nhm_cstates) @@ -238,8 +253,6 @@ void print_header(void) outp += sprintf(outp, " %%pc6"); if (do_snb_cstates) outp += sprintf(outp, " %%pc7"); - if (extra_msr_offset) - outp += sprintf(outp, " MSR 0x%x ", extra_msr_offset); outp += sprintf(outp, "\n"); } @@ -255,8 +268,14 @@ int dump_counters(struct thread_data *t, struct core_data *c, fprintf(stderr, "aperf: %016llX\n", t->aperf); fprintf(stderr, "mperf: %016llX\n", t->mperf); fprintf(stderr, "c1: %016llX\n", t->c1); + fprintf(stderr, "msr0x%x: %08llX\n", + extra_delta_offset32, t->extra_delta32); fprintf(stderr, "msr0x%x: %016llX\n", - extra_msr_offset, t->extra_msr); + extra_delta_offset64, t->extra_delta64); + fprintf(stderr, "msr0x%x: %08llX\n", + extra_msr_offset32, t->extra_msr32); + fprintf(stderr, "msr0x%x: %016llX\n", + extra_msr_offset64, t->extra_msr64); } if (c) { @@ -360,6 +379,21 @@ int format_counters(struct thread_data *t, struct core_data *c, /* TSC */ outp += sprintf(outp, "%5.2f", 1.0 * t->tsc/units/interval_float); + /* delta */ + if (extra_delta_offset32) + outp += sprintf(outp, " %11llu", t->extra_delta32); + + /* DELTA */ + if (extra_delta_offset64) + outp += sprintf(outp, " %11llu", t->extra_delta64); + /* msr */ + if (extra_msr_offset32) + outp += sprintf(outp, " 0x%08llx", t->extra_msr32); + + /* MSR */ + if (extra_msr_offset64) + outp += sprintf(outp, " 0x%016llx", t->extra_msr64); + if (do_nhm_cstates) { if (!skip_c1) outp += sprintf(outp, " %6.2f", 100.0 * t->c1/t->tsc); @@ -391,8 +425,6 @@ int format_counters(struct thread_data *t, struct core_data *c, if (do_snb_cstates) outp += sprintf(outp, " %6.2f", 100.0 * p->pc7/t->tsc); done: - if (extra_msr_offset) - outp += sprintf(outp, " 0x%016llx", t->extra_msr); outp += sprintf(outp, "\n"); return 0; @@ -502,10 +534,16 @@ delta_thread(struct thread_data *new, struct thread_data *old, old->mperf = 1; /* divide by 0 protection */ } + old->extra_delta32 = new->extra_delta32 - old->extra_delta32; + old->extra_delta32 &= 0xFFFFFFFF; + + old->extra_delta64 = new->extra_delta64 - old->extra_delta64; + /* - * for "extra msr", just copy the latest w/o subtracting + * Extra MSR is just a snapshot, simply copy latest w/o subtracting */ - old->extra_msr = new->extra_msr; + old->extra_msr32 = new->extra_msr32; + old->extra_msr64 = new->extra_msr64; } int delta_cpu(struct thread_data *t, struct core_data *c, @@ -533,6 +571,9 @@ void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data t->mperf = 0; t->c1 = 0; + t->extra_delta32 = 0; + t->extra_delta64 = 0; + /* tells format_counters to dump all fields from this set */ t->flags = CPU_IS_FIRST_THREAD_IN_CORE | CPU_IS_FIRST_CORE_IN_PACKAGE; @@ -553,6 +594,9 @@ int sum_counters(struct thread_data *t, struct core_data *c, average.threads.mperf += t->mperf; average.threads.c1 += t->c1; + average.threads.extra_delta32 += t->extra_delta32; + average.threads.extra_delta64 += t->extra_delta64; + /* sum per-core values only for 1st thread in core */ if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) return 0; @@ -588,6 +632,11 @@ void compute_average(struct thread_data *t, struct core_data *c, average.threads.mperf /= topo.num_cpus; average.threads.c1 /= topo.num_cpus; + average.threads.extra_delta32 /= topo.num_cpus; + average.threads.extra_delta32 &= 0xFFFFFFFF; + + average.threads.extra_delta64 /= topo.num_cpus; + average.cores.c3 /= topo.num_cores; average.cores.c6 /= topo.num_cores; average.cores.c7 /= topo.num_cores; @@ -629,8 +678,24 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) return -4; } - if (extra_msr_offset) - if (get_msr(cpu, extra_msr_offset, &t->extra_msr)) + if (extra_delta_offset32) { + if (get_msr(cpu, extra_delta_offset32, &t->extra_delta32)) + return -5; + t->extra_delta32 &= 0xFFFFFFFF; + } + + if (extra_delta_offset64) + if (get_msr(cpu, extra_delta_offset64, &t->extra_delta64)) + return -5; + + if (extra_msr_offset32) { + if (get_msr(cpu, extra_msr_offset32, &t->extra_msr32)) + return -5; + t->extra_msr32 &= 0xFFFFFFFF; + } + + if (extra_msr_offset64) + if (get_msr(cpu, extra_msr_offset64, &t->extra_msr64)) return -5; /* collect core counters only for 1st thread in core */ @@ -677,6 +742,9 @@ void print_verbose_header(void) get_msr(0, MSR_NEHALEM_PLATFORM_INFO, &msr); + if (verbose > 1) + fprintf(stderr, "MSR_NEHALEM_PLATFORM_INFO: 0x%llx\n", msr); + ratio = (msr >> 40) & 0xFF; fprintf(stderr, "%d * %.0f = %.0f MHz max efficiency\n", ratio, bclk, ratio * bclk); @@ -685,14 +753,84 @@ void print_verbose_header(void) fprintf(stderr, "%d * %.0f = %.0f MHz TSC frequency\n", ratio, bclk, ratio * bclk); + if (!do_ivt_turbo_ratio_limit) + goto print_nhm_turbo_ratio_limits; + + get_msr(0, MSR_IVT_TURBO_RATIO_LIMIT, &msr); + if (verbose > 1) - fprintf(stderr, "MSR_NEHALEM_PLATFORM_INFO: 0x%llx\n", msr); + fprintf(stderr, "MSR_IVT_TURBO_RATIO_LIMIT: 0x%llx\n", msr); + + ratio = (msr >> 56) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 16 active cores\n", + ratio, bclk, ratio * bclk); + + ratio = (msr >> 48) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 15 active cores\n", + ratio, bclk, ratio * bclk); + + ratio = (msr >> 40) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 14 active cores\n", + ratio, bclk, ratio * bclk); + + ratio = (msr >> 32) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 13 active cores\n", + ratio, bclk, ratio * bclk); + + ratio = (msr >> 24) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 12 active cores\n", + ratio, bclk, ratio * bclk); + + ratio = (msr >> 16) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 11 active cores\n", + ratio, bclk, ratio * bclk); + + ratio = (msr >> 8) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 10 active cores\n", + ratio, bclk, ratio * bclk); + + ratio = (msr >> 0) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 9 active cores\n", + ratio, bclk, ratio * bclk); + +print_nhm_turbo_ratio_limits: if (!do_nehalem_turbo_ratio_limit) return; get_msr(0, MSR_NEHALEM_TURBO_RATIO_LIMIT, &msr); + if (verbose > 1) + fprintf(stderr, "MSR_NEHALEM_TURBO_RATIO_LIMIT: 0x%llx\n", msr); + + ratio = (msr >> 56) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 8 active cores\n", + ratio, bclk, ratio * bclk); + + ratio = (msr >> 48) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 7 active cores\n", + ratio, bclk, ratio * bclk); + + ratio = (msr >> 40) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 6 active cores\n", + ratio, bclk, ratio * bclk); + + ratio = (msr >> 32) & 0xFF; + if (ratio) + fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 5 active cores\n", + ratio, bclk, ratio * bclk); + ratio = (msr >> 24) & 0xFF; if (ratio) fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 4 active cores\n", @@ -712,7 +850,6 @@ void print_verbose_header(void) if (ratio) fprintf(stderr, "%d * %.0f = %.0f MHz max turbo 1 active cores\n", ratio, bclk, ratio * bclk); - } void free_all_buffers(void) @@ -1038,7 +1175,7 @@ int has_nehalem_turbo_ratio_limit(unsigned int family, unsigned int model) case 0x2A: /* SNB */ case 0x2D: /* SNB Xeon */ case 0x3A: /* IVB */ - case 0x3D: /* IVB Xeon */ + case 0x3E: /* IVB Xeon */ return 1; case 0x2E: /* Nehalem-EX Xeon - Beckton */ case 0x2F: /* Westmere-EX Xeon - Eagleton */ @@ -1046,6 +1183,22 @@ int has_nehalem_turbo_ratio_limit(unsigned int family, unsigned int model) return 0; } } +int has_ivt_turbo_ratio_limit(unsigned int family, unsigned int model) +{ + if (!genuine_intel) + return 0; + + if (family != 6) + return 0; + + switch (model) { + case 0x3E: /* IVB Xeon */ + return 1; + default: + return 0; + } +} + int is_snb(unsigned int family, unsigned int model) { @@ -1056,7 +1209,7 @@ int is_snb(unsigned int family, unsigned int model) case 0x2A: case 0x2D: case 0x3A: /* IVB */ - case 0x3D: /* IVB Xeon */ + case 0x3E: /* IVB Xeon */ return 1; } return 0; @@ -1145,12 +1298,13 @@ void check_cpuid() bclk = discover_bclk(family, model); do_nehalem_turbo_ratio_limit = has_nehalem_turbo_ratio_limit(family, model); + do_ivt_turbo_ratio_limit = has_ivt_turbo_ratio_limit(family, model); } void usage() { - fprintf(stderr, "%s: [-v] [-M MSR#] [-i interval_sec | command ...]\n", + fprintf(stderr, "%s: [-v][-p|-P|-S][-c MSR# | -s]][-C MSR#][-m MSR#][-M MSR#][-i interval_sec | command ...]\n", progname); exit(1); } @@ -1440,15 +1594,15 @@ void cmdline(int argc, char **argv) progname = argv[0]; - while ((opt = getopt(argc, argv, "+cpsvi:M:")) != -1) { + while ((opt = getopt(argc, argv, "+pPSvisc:sC:m:M:")) != -1) { switch (opt) { - case 'c': + case 'p': show_core_only++; break; - case 'p': + case 'P': show_pkg_only++; break; - case 's': + case 'S': summary_only++; break; case 'v': @@ -1457,10 +1611,20 @@ void cmdline(int argc, char **argv) case 'i': interval_sec = atoi(optarg); break; + case 'c': + sscanf(optarg, "%x", &extra_delta_offset32); + break; + case 's': + extra_delta_offset32 = 0x34; /* SMI counter */ + break; + case 'C': + sscanf(optarg, "%x", &extra_delta_offset64); + break; + case 'm': + sscanf(optarg, "%x", &extra_msr_offset32); + break; case 'M': - sscanf(optarg, "%x", &extra_msr_offset); - if (verbose > 1) - fprintf(stderr, "MSR 0x%X\n", extra_msr_offset); + sscanf(optarg, "%x", &extra_msr_offset64); break; default: usage(); @@ -1473,7 +1637,7 @@ int main(int argc, char **argv) cmdline(argc, argv); if (verbose > 1) - fprintf(stderr, "turbostat v2.0 May 16, 2012" + fprintf(stderr, "turbostat v2.1 October 6, 2012" " - Len Brown <lenb@kernel.org>\n"); turbostat_init(); diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index c05bcd293d8c..c7ba7614061b 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -1740,8 +1740,10 @@ sub install { open(IN, "$output_config") or dodie("Can't read config file"); while (<IN>) { if (/CONFIG_MODULES(=y)?/) { - $install_mods = 1 if (defined($1)); - last; + if (defined($1)) { + $install_mods = 1; + last; + } } } close(IN); @@ -1873,10 +1875,10 @@ sub make_oldconfig { apply_min_config; } - if (!run_command "$make oldnoconfig") { - # Perhaps oldnoconfig doesn't exist in this version of the kernel + if (!run_command "$make olddefconfig") { + # Perhaps olddefconfig doesn't exist in this version of the kernel # try a yes '' | oldconfig - doprint "oldnoconfig failed, trying yes '' | make oldconfig\n"; + doprint "olddefconfig failed, trying yes '' | make oldconfig\n"; run_command "yes '' | $make oldconfig" or dodie "failed make config oldconfig"; } @@ -1929,7 +1931,7 @@ sub build { # old config can ask questions if ($type eq "oldconfig") { - $type = "oldnoconfig"; + $type = "olddefconfig"; # allow for empty configs run_command "touch $output_config"; @@ -1959,7 +1961,7 @@ sub build { load_force_config($minconfig); } - if ($type ne "oldnoconfig") { + if ($type ne "olddefconfig") { run_command "$make $type" or dodie "failed make config"; } @@ -2458,8 +2460,7 @@ my %config_set; # config_off holds the set of configs that the bad config had disabled. # We need to record them and set them in the .config when running -# oldnoconfig, because oldnoconfig does not turn off new symbols, but -# instead just keeps the defaults. +# olddefconfig, because olddefconfig keeps the defaults. my %config_off; # config_off_tmp holds a set of configs to turn off for now @@ -3250,7 +3251,7 @@ sub test_this_config { } # Remove this config from the list of configs - # do a make oldnoconfig and then read the resulting + # do a make olddefconfig and then read the resulting # .config to make sure it is missing the config that # we had before my %configs = %min_configs; diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 85baf11e2acd..43480149119e 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,4 +1,4 @@ -TARGETS = breakpoints kcmp mqueue vm cpu-hotplug memory-hotplug +TARGETS = breakpoints kcmp mqueue vm cpu-hotplug memory-hotplug epoll all: for TARGET in $(TARGETS); do \ diff --git a/tools/testing/selftests/epoll/Makefile b/tools/testing/selftests/epoll/Makefile new file mode 100644 index 000000000000..19806ed62f50 --- /dev/null +++ b/tools/testing/selftests/epoll/Makefile @@ -0,0 +1,11 @@ +# Makefile for epoll selftests + +all: test_epoll +%: %.c + gcc -pthread -g -o $@ $^ + +run_tests: all + ./test_epoll + +clean: + $(RM) test_epoll diff --git a/tools/testing/selftests/epoll/test_epoll.c b/tools/testing/selftests/epoll/test_epoll.c new file mode 100644 index 000000000000..f7525392ce84 --- /dev/null +++ b/tools/testing/selftests/epoll/test_epoll.c @@ -0,0 +1,344 @@ +/* + * tools/testing/selftests/epoll/test_epoll.c + * + * Copyright 2012 Adobe Systems Incorporated + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Paton J. Lewis <palewis@adobe.com> + * + */ + +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/epoll.h> +#include <sys/socket.h> + +/* + * A pointer to an epoll_item_private structure will be stored in the epoll + * item's event structure so that we can get access to the epoll_item_private + * data after calling epoll_wait: + */ +struct epoll_item_private { + int index; /* Position of this struct within the epoll_items array. */ + int fd; + uint32_t events; + pthread_mutex_t mutex; /* Guards the following variables... */ + int stop; + int status; /* Stores any error encountered while handling item. */ + /* The following variable allows us to test whether we have encountered + a problem while attempting to cancel and delete the associated + event. When the test program exits, 'deleted' should be exactly + one. If it is greater than one, then the failed test reflects a real + world situation where we would have tried to access the epoll item's + private data after deleting it: */ + int deleted; +}; + +struct epoll_item_private *epoll_items; + +/* + * Delete the specified item from the epoll set. In a real-world secneario this + * is where we would free the associated data structure, but in this testing + * environment we retain the structure so that we can test for double-deletion: + */ +void delete_item(int index) +{ + __sync_fetch_and_add(&epoll_items[index].deleted, 1); +} + +/* + * A pointer to a read_thread_data structure will be passed as the argument to + * each read thread: + */ +struct read_thread_data { + int stop; + int status; /* Indicates any error encountered by the read thread. */ + int epoll_set; +}; + +/* + * The function executed by the read threads: + */ +void *read_thread_function(void *function_data) +{ + struct read_thread_data *thread_data = + (struct read_thread_data *)function_data; + struct epoll_event event_data; + struct epoll_item_private *item_data; + char socket_data; + + /* Handle events until we encounter an error or this thread's 'stop' + condition is set: */ + while (1) { + int result = epoll_wait(thread_data->epoll_set, + &event_data, + 1, /* Number of desired events */ + 1000); /* Timeout in ms */ + if (result < 0) { + /* Breakpoints signal all threads. Ignore that while + debugging: */ + if (errno == EINTR) + continue; + thread_data->status = errno; + return 0; + } else if (thread_data->stop) + return 0; + else if (result == 0) /* Timeout */ + continue; + + /* We need the mutex here because checking for the stop + condition and re-enabling the epoll item need to be done + together as one atomic operation when EPOLL_CTL_DISABLE is + available: */ + item_data = (struct epoll_item_private *)event_data.data.ptr; + pthread_mutex_lock(&item_data->mutex); + + /* Remove the item from the epoll set if we want to stop + handling that event: */ + if (item_data->stop) + delete_item(item_data->index); + else { + /* Clear the data that was written to the other end of + our non-blocking socket: */ + do { + if (read(item_data->fd, &socket_data, 1) < 1) { + if ((errno == EAGAIN) || + (errno == EWOULDBLOCK)) + break; + else + goto error_unlock; + } + } while (item_data->events & EPOLLET); + + /* The item was one-shot, so re-enable it: */ + event_data.events = item_data->events; + if (epoll_ctl(thread_data->epoll_set, + EPOLL_CTL_MOD, + item_data->fd, + &event_data) < 0) + goto error_unlock; + } + + pthread_mutex_unlock(&item_data->mutex); + } + +error_unlock: + thread_data->status = item_data->status = errno; + pthread_mutex_unlock(&item_data->mutex); + return 0; +} + +/* + * A pointer to a write_thread_data structure will be passed as the argument to + * the write thread: + */ +struct write_thread_data { + int stop; + int status; /* Indicates any error encountered by the write thread. */ + int n_fds; + int *fds; +}; + +/* + * The function executed by the write thread. It writes a single byte to each + * socket in turn until the stop condition for this thread is set. If writing to + * a socket would block (i.e. errno was EAGAIN), we leave that socket alone for + * the moment and just move on to the next socket in the list. We don't care + * about the order in which we deliver events to the epoll set. In fact we don't + * care about the data we're writing to the pipes at all; we just want to + * trigger epoll events: + */ +void *write_thread_function(void *function_data) +{ + const char data = 'X'; + int index; + struct write_thread_data *thread_data = + (struct write_thread_data *)function_data; + while (!thread_data->stop) + for (index = 0; + !thread_data->stop && (index < thread_data->n_fds); + ++index) + if ((write(thread_data->fds[index], &data, 1) < 1) && + (errno != EAGAIN) && + (errno != EWOULDBLOCK)) { + thread_data->status = errno; + return; + } +} + +/* + * Arguments are currently ignored: + */ +int main(int argc, char **argv) +{ + const int n_read_threads = 100; + const int n_epoll_items = 500; + int index; + int epoll_set = epoll_create1(0); + struct write_thread_data write_thread_data = { + 0, 0, n_epoll_items, malloc(n_epoll_items * sizeof(int)) + }; + struct read_thread_data *read_thread_data = + malloc(n_read_threads * sizeof(struct read_thread_data)); + pthread_t *read_threads = malloc(n_read_threads * sizeof(pthread_t)); + pthread_t write_thread; + + printf("-----------------\n"); + printf("Runing test_epoll\n"); + printf("-----------------\n"); + + epoll_items = malloc(n_epoll_items * sizeof(struct epoll_item_private)); + + if (epoll_set < 0 || epoll_items == 0 || write_thread_data.fds == 0 || + read_thread_data == 0 || read_threads == 0) + goto error; + + if (sysconf(_SC_NPROCESSORS_ONLN) < 2) { + printf("Error: please run this test on a multi-core system.\n"); + goto error; + } + + /* Create the socket pairs and epoll items: */ + for (index = 0; index < n_epoll_items; ++index) { + int socket_pair[2]; + struct epoll_event event_data; + if (socketpair(AF_UNIX, + SOCK_STREAM | SOCK_NONBLOCK, + 0, + socket_pair) < 0) + goto error; + write_thread_data.fds[index] = socket_pair[0]; + epoll_items[index].index = index; + epoll_items[index].fd = socket_pair[1]; + if (pthread_mutex_init(&epoll_items[index].mutex, NULL) != 0) + goto error; + /* We always use EPOLLONESHOT because this test is currently + structured to demonstrate the need for EPOLL_CTL_DISABLE, + which only produces useful information in the EPOLLONESHOT + case (without EPOLLONESHOT, calling epoll_ctl with + EPOLL_CTL_DISABLE will never return EBUSY). If support for + testing events without EPOLLONESHOT is desired, it should + probably be implemented in a separate unit test. */ + epoll_items[index].events = EPOLLIN | EPOLLONESHOT; + if (index < n_epoll_items / 2) + epoll_items[index].events |= EPOLLET; + epoll_items[index].stop = 0; + epoll_items[index].status = 0; + epoll_items[index].deleted = 0; + event_data.events = epoll_items[index].events; + event_data.data.ptr = &epoll_items[index]; + if (epoll_ctl(epoll_set, + EPOLL_CTL_ADD, + epoll_items[index].fd, + &event_data) < 0) + goto error; + } + + /* Create and start the read threads: */ + for (index = 0; index < n_read_threads; ++index) { + read_thread_data[index].stop = 0; + read_thread_data[index].status = 0; + read_thread_data[index].epoll_set = epoll_set; + if (pthread_create(&read_threads[index], + NULL, + read_thread_function, + &read_thread_data[index]) != 0) + goto error; + } + + if (pthread_create(&write_thread, + NULL, + write_thread_function, + &write_thread_data) != 0) + goto error; + + /* Cancel all event pollers: */ +#ifdef EPOLL_CTL_DISABLE + for (index = 0; index < n_epoll_items; ++index) { + pthread_mutex_lock(&epoll_items[index].mutex); + ++epoll_items[index].stop; + if (epoll_ctl(epoll_set, + EPOLL_CTL_DISABLE, + epoll_items[index].fd, + NULL) == 0) + delete_item(index); + else if (errno != EBUSY) { + pthread_mutex_unlock(&epoll_items[index].mutex); + goto error; + } + /* EBUSY means events were being handled; allow the other thread + to delete the item. */ + pthread_mutex_unlock(&epoll_items[index].mutex); + } +#else + for (index = 0; index < n_epoll_items; ++index) { + pthread_mutex_lock(&epoll_items[index].mutex); + ++epoll_items[index].stop; + pthread_mutex_unlock(&epoll_items[index].mutex); + /* Wait in case a thread running read_thread_function is + currently executing code between epoll_wait and + pthread_mutex_lock with this item. Note that a longer delay + would make double-deletion less likely (at the expense of + performance), but there is no guarantee that any delay would + ever be sufficient. Note also that we delete all event + pollers at once for testing purposes, but in a real-world + environment we are likely to want to be able to cancel event + pollers at arbitrary times. Therefore we can't improve this + situation by just splitting this loop into two loops + (i.e. signal 'stop' for all items, sleep, and then delete all + items). We also can't fix the problem via EPOLL_CTL_DEL + because that command can't prevent the case where some other + thread is executing read_thread_function within the region + mentioned above: */ + usleep(1); + pthread_mutex_lock(&epoll_items[index].mutex); + if (!epoll_items[index].deleted) + delete_item(index); + pthread_mutex_unlock(&epoll_items[index].mutex); + } +#endif + + /* Shut down the read threads: */ + for (index = 0; index < n_read_threads; ++index) + __sync_fetch_and_add(&read_thread_data[index].stop, 1); + for (index = 0; index < n_read_threads; ++index) { + if (pthread_join(read_threads[index], NULL) != 0) + goto error; + if (read_thread_data[index].status) + goto error; + } + + /* Shut down the write thread: */ + __sync_fetch_and_add(&write_thread_data.stop, 1); + if ((pthread_join(write_thread, NULL) != 0) || write_thread_data.status) + goto error; + + /* Check for final error conditions: */ + for (index = 0; index < n_epoll_items; ++index) { + if (epoll_items[index].status != 0) + goto error; + if (pthread_mutex_destroy(&epoll_items[index].mutex) < 0) + goto error; + } + for (index = 0; index < n_epoll_items; ++index) + if (epoll_items[index].deleted != 1) { + printf("Error: item data deleted %1d times.\n", + epoll_items[index].deleted); + goto error; + } + + printf("[PASS]\n"); + return 0; + + error: + printf("[FAIL]\n"); + return errno; +} diff --git a/tools/virtio/virtio-trace/Makefile b/tools/virtio/virtio-trace/Makefile new file mode 100644 index 000000000000..0d2381633475 --- /dev/null +++ b/tools/virtio/virtio-trace/Makefile @@ -0,0 +1,13 @@ +CC = gcc +CFLAGS = -O2 -Wall -pthread + +all: trace-agent + +.c.o: + $(CC) $(CFLAGS) -c $^ -o $@ + +trace-agent: trace-agent.o trace-agent-ctl.o trace-agent-rw.o + $(CC) $(CFLAGS) -o $@ $^ + +clean: + rm -f *.o trace-agent diff --git a/tools/virtio/virtio-trace/README b/tools/virtio/virtio-trace/README new file mode 100644 index 000000000000..b64845b823ab --- /dev/null +++ b/tools/virtio/virtio-trace/README @@ -0,0 +1,118 @@ +Trace Agent for virtio-trace +============================ + +Trace agent is a user tool for sending trace data of a guest to a Host in low +overhead. Trace agent has the following functions: + - splice a page of ring-buffer to read_pipe without memory copying + - splice the page from write_pipe to virtio-console without memory copying + - write trace data to stdout by using -o option + - controlled by start/stop orders from a Host + +The trace agent operates as follows: + 1) Initialize all structures. + 2) Create a read/write thread per CPU. Each thread is bound to a CPU. + The read/write threads hold it. + 3) A controller thread does poll() for a start order of a host. + 4) After the controller of the trace agent receives a start order from a host, + the controller wake read/write threads. + 5) The read/write threads start to read trace data from ring-buffers and + write the data to virtio-serial. + 6) If the controller receives a stop order from a host, the read/write threads + stop to read trace data. + + +Files +===== + +README: this file +Makefile: Makefile of trace agent for virtio-trace +trace-agent.c: includes main function, sets up for operating trace agent +trace-agent.h: includes all structures and some macros +trace-agent-ctl.c: includes controller function for read/write threads +trace-agent-rw.c: includes read/write threads function + + +Setup +===== + +To use this trace agent for virtio-trace, we need to prepare some virtio-serial +I/Fs. + +1) Make FIFO in a host + virtio-trace uses virtio-serial pipe as trace data paths as to the number +of CPUs and a control path, so FIFO (named pipe) should be created as follows: + # mkdir /tmp/virtio-trace/ + # mkfifo /tmp/virtio-trace/trace-path-cpu{0,1,2,...,X}.{in,out} + # mkfifo /tmp/virtio-trace/agent-ctl-path.{in,out} + +For example, if a guest use three CPUs, the names are + trace-path-cpu{0,1,2}.{in.out} +and + agent-ctl-path.{in,out}. + +2) Set up of virtio-serial pipe in a host + Add qemu option to use virtio-serial pipe. + + ##virtio-serial device## + -device virtio-serial-pci,id=virtio-serial0\ + ##control path## + -chardev pipe,id=charchannel0,path=/tmp/virtio-trace/agent-ctl-path\ + -device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,\ + id=channel0,name=agent-ctl-path\ + ##data path## + -chardev pipe,id=charchannel1,path=/tmp/virtio-trace/trace-path-cpu0\ + -device virtserialport,bus=virtio-serial0.0,nr=2,chardev=charchannel0,\ + id=channel1,name=trace-path-cpu0\ + ... + +If you manage guests with libvirt, add the following tags to domain XML files. +Then, libvirt passes the same command option to qemu. + + <channel type='pipe'> + <source path='/tmp/virtio-trace/agent-ctl-path'/> + <target type='virtio' name='agent-ctl-path'/> + <address type='virtio-serial' controller='0' bus='0' port='0'/> + </channel> + <channel type='pipe'> + <source path='/tmp/virtio-trace/trace-path-cpu0'/> + <target type='virtio' name='trace-path-cpu0'/> + <address type='virtio-serial' controller='0' bus='0' port='1'/> + </channel> + ... +Here, chardev names are restricted to trace-path-cpuX and agent-ctl-path. For +example, if a guest use three CPUs, chardev names should be trace-path-cpu0, +trace-path-cpu1, trace-path-cpu2, and agent-ctl-path. + +3) Boot the guest + You can find some chardev in /dev/virtio-ports/ in the guest. + + +Run +=== + +0) Build trace agent in a guest + $ make + +1) Enable ftrace in the guest + <Example> + # echo 1 > /sys/kernel/debug/tracing/events/sched/enable + +2) Run trace agent in the guest + This agent must be operated as root. + # ./trace-agent +read/write threads in the agent wait for start order from host. If you add -o +option, trace data are output via stdout in the guest. + +3) Open FIFO in a host + # cat /tmp/virtio-trace/trace-path-cpu0.out +If a host does not open these, trace data get stuck in buffers of virtio. Then, +the guest will stop by specification of chardev in QEMU. This blocking mode may +be solved in the future. + +4) Start to read trace data by ordering from a host + A host injects read start order to the guest via virtio-serial. + # echo 1 > /tmp/virtio-trace/agent-ctl-path.in + +5) Stop to read trace data by ordering from a host + A host injects read stop order to the guest via virtio-serial. + # echo 0 > /tmp/virtio-trace/agent-ctl-path.in diff --git a/tools/virtio/virtio-trace/trace-agent-ctl.c b/tools/virtio/virtio-trace/trace-agent-ctl.c new file mode 100644 index 000000000000..a2d0403c4f94 --- /dev/null +++ b/tools/virtio/virtio-trace/trace-agent-ctl.c @@ -0,0 +1,137 @@ +/* + * Controller of read/write threads for virtio-trace + * + * Copyright (C) 2012 Hitachi, Ltd. + * Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com> + * Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> + * + * Licensed under GPL version 2 only. + * + */ + +#define _GNU_SOURCE +#include <fcntl.h> +#include <poll.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "trace-agent.h" + +#define HOST_MSG_SIZE 256 +#define EVENT_WAIT_MSEC 100 + +static volatile sig_atomic_t global_signal_val; +bool global_sig_receive; /* default false */ +bool global_run_operation; /* default false*/ + +/* Handle SIGTERM/SIGINT/SIGQUIT to exit */ +static void signal_handler(int sig) +{ + global_signal_val = sig; +} + +int rw_ctl_init(const char *ctl_path) +{ + int ctl_fd; + + ctl_fd = open(ctl_path, O_RDONLY); + if (ctl_fd == -1) { + pr_err("Cannot open ctl_fd\n"); + goto error; + } + + return ctl_fd; + +error: + exit(EXIT_FAILURE); +} + +static int wait_order(int ctl_fd) +{ + struct pollfd poll_fd; + int ret = 0; + + while (!global_sig_receive) { + poll_fd.fd = ctl_fd; + poll_fd.events = POLLIN; + + ret = poll(&poll_fd, 1, EVENT_WAIT_MSEC); + + if (global_signal_val) { + global_sig_receive = true; + pr_info("Receive interrupt %d\n", global_signal_val); + + /* Wakes rw-threads when they are sleeping */ + if (!global_run_operation) + pthread_cond_broadcast(&cond_wakeup); + + ret = -1; + break; + } + + if (ret < 0) { + pr_err("Polling error\n"); + goto error; + } + + if (ret) + break; + }; + + return ret; + +error: + exit(EXIT_FAILURE); +} + +/* + * contol read/write threads by handling global_run_operation + */ +void *rw_ctl_loop(int ctl_fd) +{ + ssize_t rlen; + char buf[HOST_MSG_SIZE]; + int ret; + + /* Setup signal handlers */ + signal(SIGTERM, signal_handler); + signal(SIGINT, signal_handler); + signal(SIGQUIT, signal_handler); + + while (!global_sig_receive) { + + ret = wait_order(ctl_fd); + if (ret < 0) + break; + + rlen = read(ctl_fd, buf, sizeof(buf)); + if (rlen < 0) { + pr_err("read data error in ctl thread\n"); + goto error; + } + + if (rlen == 2 && buf[0] == '1') { + /* + * If host writes '1' to a control path, + * this controller wakes all read/write threads. + */ + global_run_operation = true; + pthread_cond_broadcast(&cond_wakeup); + pr_debug("Wake up all read/write threads\n"); + } else if (rlen == 2 && buf[0] == '0') { + /* + * If host writes '0' to a control path, read/write + * threads will wait for notification from Host. + */ + global_run_operation = false; + pr_debug("Stop all read/write threads\n"); + } else + pr_info("Invalid host notification: %s\n", buf); + } + + return NULL; + +error: + exit(EXIT_FAILURE); +} diff --git a/tools/virtio/virtio-trace/trace-agent-rw.c b/tools/virtio/virtio-trace/trace-agent-rw.c new file mode 100644 index 000000000000..3aace5ea4842 --- /dev/null +++ b/tools/virtio/virtio-trace/trace-agent-rw.c @@ -0,0 +1,192 @@ +/* + * Read/write thread of a guest agent for virtio-trace + * + * Copyright (C) 2012 Hitachi, Ltd. + * Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com> + * Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> + * + * Licensed under GPL version 2 only. + * + */ + +#define _GNU_SOURCE +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/syscall.h> +#include "trace-agent.h" + +#define READ_WAIT_USEC 100000 + +void *rw_thread_info_new(void) +{ + struct rw_thread_info *rw_ti; + + rw_ti = zalloc(sizeof(struct rw_thread_info)); + if (rw_ti == NULL) { + pr_err("rw_thread_info zalloc error\n"); + exit(EXIT_FAILURE); + } + + rw_ti->cpu_num = -1; + rw_ti->in_fd = -1; + rw_ti->out_fd = -1; + rw_ti->read_pipe = -1; + rw_ti->write_pipe = -1; + rw_ti->pipe_size = PIPE_INIT; + + return rw_ti; +} + +void *rw_thread_init(int cpu, const char *in_path, const char *out_path, + bool stdout_flag, unsigned long pipe_size, + struct rw_thread_info *rw_ti) +{ + int data_pipe[2]; + + rw_ti->cpu_num = cpu; + + /* set read(input) fd */ + rw_ti->in_fd = open(in_path, O_RDONLY); + if (rw_ti->in_fd == -1) { + pr_err("Could not open in_fd (CPU:%d)\n", cpu); + goto error; + } + + /* set write(output) fd */ + if (!stdout_flag) { + /* virtio-serial output mode */ + rw_ti->out_fd = open(out_path, O_WRONLY); + if (rw_ti->out_fd == -1) { + pr_err("Could not open out_fd (CPU:%d)\n", cpu); + goto error; + } + } else + /* stdout mode */ + rw_ti->out_fd = STDOUT_FILENO; + + if (pipe2(data_pipe, O_NONBLOCK) < 0) { + pr_err("Could not create pipe in rw-thread(%d)\n", cpu); + goto error; + } + + /* + * Size of pipe is 64kB in default based on fs/pipe.c. + * To read/write trace data speedy, pipe size is changed. + */ + if (fcntl(*data_pipe, F_SETPIPE_SZ, pipe_size) < 0) { + pr_err("Could not change pipe size in rw-thread(%d)\n", cpu); + goto error; + } + + rw_ti->read_pipe = data_pipe[1]; + rw_ti->write_pipe = data_pipe[0]; + rw_ti->pipe_size = pipe_size; + + return NULL; + +error: + exit(EXIT_FAILURE); +} + +/* Bind a thread to a cpu */ +static void bind_cpu(int cpu_num) +{ + cpu_set_t mask; + + CPU_ZERO(&mask); + CPU_SET(cpu_num, &mask); + + /* bind my thread to cpu_num by assigning zero to the first argument */ + if (sched_setaffinity(0, sizeof(mask), &mask) == -1) + pr_err("Could not set CPU#%d affinity\n", (int)cpu_num); +} + +static void *rw_thread_main(void *thread_info) +{ + ssize_t rlen, wlen; + ssize_t ret; + struct rw_thread_info *ts = (struct rw_thread_info *)thread_info; + + bind_cpu(ts->cpu_num); + + while (1) { + /* Wait for a read order of trace data by Host OS */ + if (!global_run_operation) { + pthread_mutex_lock(&mutex_notify); + pthread_cond_wait(&cond_wakeup, &mutex_notify); + pthread_mutex_unlock(&mutex_notify); + } + + if (global_sig_receive) + break; + + /* + * Each thread read trace_pipe_raw of each cpu bounding the + * thread, so contention of multi-threads does not occur. + */ + rlen = splice(ts->in_fd, NULL, ts->read_pipe, NULL, + ts->pipe_size, SPLICE_F_MOVE | SPLICE_F_MORE); + + if (rlen < 0) { + pr_err("Splice_read in rw-thread(%d)\n", ts->cpu_num); + goto error; + } else if (rlen == 0) { + /* + * If trace data do not exist or are unreadable not + * for exceeding the page size, splice_read returns + * NULL. Then, this waits for being filled the data in a + * ring-buffer. + */ + usleep(READ_WAIT_USEC); + pr_debug("Read retry(cpu:%d)\n", ts->cpu_num); + continue; + } + + wlen = 0; + + do { + ret = splice(ts->write_pipe, NULL, ts->out_fd, NULL, + rlen - wlen, + SPLICE_F_MOVE | SPLICE_F_MORE); + + if (ret < 0) { + pr_err("Splice_write in rw-thread(%d)\n", + ts->cpu_num); + goto error; + } else if (ret == 0) + /* + * When host reader is not in time for reading + * trace data, guest will be stopped. This is + * because char dev in QEMU is not supported + * non-blocking mode. Then, writer might be + * sleep in that case. + * This sleep will be removed by supporting + * non-blocking mode. + */ + sleep(1); + wlen += ret; + } while (wlen < rlen); + } + + return NULL; + +error: + exit(EXIT_FAILURE); +} + + +pthread_t rw_thread_run(struct rw_thread_info *rw_ti) +{ + int ret; + pthread_t rw_thread_per_cpu; + + ret = pthread_create(&rw_thread_per_cpu, NULL, rw_thread_main, rw_ti); + if (ret != 0) { + pr_err("Could not create a rw thread(%d)\n", rw_ti->cpu_num); + exit(EXIT_FAILURE); + } + + return rw_thread_per_cpu; +} diff --git a/tools/virtio/virtio-trace/trace-agent.c b/tools/virtio/virtio-trace/trace-agent.c new file mode 100644 index 000000000000..0a0a7dd4eff7 --- /dev/null +++ b/tools/virtio/virtio-trace/trace-agent.c @@ -0,0 +1,270 @@ +/* + * Guest agent for virtio-trace + * + * Copyright (C) 2012 Hitachi, Ltd. + * Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com> + * Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> + * + * Licensed under GPL version 2 only. + * + */ + +#define _GNU_SOURCE +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "trace-agent.h" + +#define PAGE_SIZE (sysconf(_SC_PAGE_SIZE)) +#define PIPE_DEF_BUFS 16 +#define PIPE_MIN_SIZE (PAGE_SIZE*PIPE_DEF_BUFS) +#define PIPE_MAX_SIZE (1024*1024) +#define READ_PATH_FMT \ + "/sys/kernel/debug/tracing/per_cpu/cpu%d/trace_pipe_raw" +#define WRITE_PATH_FMT "/dev/virtio-ports/trace-path-cpu%d" +#define CTL_PATH "/dev/virtio-ports/agent-ctl-path" + +pthread_mutex_t mutex_notify = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t cond_wakeup = PTHREAD_COND_INITIALIZER; + +static int get_total_cpus(void) +{ + int nr_cpus = (int)sysconf(_SC_NPROCESSORS_CONF); + + if (nr_cpus <= 0) { + pr_err("Could not read cpus\n"); + goto error; + } else if (nr_cpus > MAX_CPUS) { + pr_err("Exceed max cpus(%d)\n", (int)MAX_CPUS); + goto error; + } + + return nr_cpus; + +error: + exit(EXIT_FAILURE); +} + +static void *agent_info_new(void) +{ + struct agent_info *s; + int i; + + s = zalloc(sizeof(struct agent_info)); + if (s == NULL) { + pr_err("agent_info zalloc error\n"); + exit(EXIT_FAILURE); + } + + s->pipe_size = PIPE_INIT; + s->use_stdout = false; + s->cpus = get_total_cpus(); + s->ctl_fd = -1; + + /* read/write threads init */ + for (i = 0; i < s->cpus; i++) + s->rw_ti[i] = rw_thread_info_new(); + + return s; +} + +static unsigned long parse_size(const char *arg) +{ + unsigned long value, round; + char *ptr; + + value = strtoul(arg, &ptr, 10); + switch (*ptr) { + case 'K': case 'k': + value <<= 10; + break; + case 'M': case 'm': + value <<= 20; + break; + default: + break; + } + + if (value > PIPE_MAX_SIZE) { + pr_err("Pipe size must be less than 1MB\n"); + goto error; + } else if (value < PIPE_MIN_SIZE) { + pr_err("Pipe size must be over 64KB\n"); + goto error; + } + + /* Align buffer size with page unit */ + round = value & (PAGE_SIZE - 1); + value = value - round; + + return value; +error: + return 0; +} + +static void usage(char const *prg) +{ + pr_err("usage: %s [-h] [-o] [-s <size of pipe>]\n", prg); +} + +static const char *make_path(int cpu_num, bool this_is_write_path) +{ + int ret; + char *buf; + + buf = zalloc(PATH_MAX); + if (buf == NULL) { + pr_err("Could not allocate buffer\n"); + goto error; + } + + if (this_is_write_path) + /* write(output) path */ + ret = snprintf(buf, PATH_MAX, WRITE_PATH_FMT, cpu_num); + else + /* read(input) path */ + ret = snprintf(buf, PATH_MAX, READ_PATH_FMT, cpu_num); + + if (ret <= 0) { + pr_err("Failed to generate %s path(CPU#%d):%d\n", + this_is_write_path ? "read" : "write", cpu_num, ret); + goto error; + } + + return buf; + +error: + free(buf); + return NULL; +} + +static const char *make_input_path(int cpu_num) +{ + return make_path(cpu_num, false); +} + +static const char *make_output_path(int cpu_num) +{ + return make_path(cpu_num, true); +} + +static void *agent_info_init(struct agent_info *s) +{ + int cpu; + const char *in_path = NULL; + const char *out_path = NULL; + + /* init read/write threads */ + for (cpu = 0; cpu < s->cpus; cpu++) { + /* set read(input) path per read/write thread */ + in_path = make_input_path(cpu); + if (in_path == NULL) + goto error; + + /* set write(output) path per read/write thread*/ + if (!s->use_stdout) { + out_path = make_output_path(cpu); + if (out_path == NULL) + goto error; + } else + /* stdout mode */ + pr_debug("stdout mode\n"); + + rw_thread_init(cpu, in_path, out_path, s->use_stdout, + s->pipe_size, s->rw_ti[cpu]); + } + + /* init controller of read/write threads */ + s->ctl_fd = rw_ctl_init((const char *)CTL_PATH); + + return NULL; + +error: + exit(EXIT_FAILURE); +} + +static void *parse_args(int argc, char *argv[], struct agent_info *s) +{ + int cmd; + unsigned long size; + + while ((cmd = getopt(argc, argv, "hos:")) != -1) { + switch (cmd) { + /* stdout mode */ + case 'o': + s->use_stdout = true; + break; + /* size of pipe */ + case 's': + size = parse_size(optarg); + if (size == 0) + goto error; + s->pipe_size = size; + break; + case 'h': + default: + usage(argv[0]); + goto error; + } + } + + agent_info_init(s); + + return NULL; + +error: + exit(EXIT_FAILURE); +} + +static void agent_main_loop(struct agent_info *s) +{ + int cpu; + pthread_t rw_thread_per_cpu[MAX_CPUS]; + + /* Start all read/write threads */ + for (cpu = 0; cpu < s->cpus; cpu++) + rw_thread_per_cpu[cpu] = rw_thread_run(s->rw_ti[cpu]); + + rw_ctl_loop(s->ctl_fd); + + /* Finish all read/write threads */ + for (cpu = 0; cpu < s->cpus; cpu++) { + int ret; + + ret = pthread_join(rw_thread_per_cpu[cpu], NULL); + if (ret != 0) { + pr_err("pthread_join() error:%d (cpu %d)\n", ret, cpu); + exit(EXIT_FAILURE); + } + } +} + +static void agent_info_free(struct agent_info *s) +{ + int i; + + close(s->ctl_fd); + for (i = 0; i < s->cpus; i++) { + close(s->rw_ti[i]->in_fd); + close(s->rw_ti[i]->out_fd); + close(s->rw_ti[i]->read_pipe); + close(s->rw_ti[i]->write_pipe); + free(s->rw_ti[i]); + } + free(s); +} + +int main(int argc, char *argv[]) +{ + struct agent_info *s = NULL; + + s = agent_info_new(); + parse_args(argc, argv, s); + + agent_main_loop(s); + + agent_info_free(s); + + return 0; +} diff --git a/tools/virtio/virtio-trace/trace-agent.h b/tools/virtio/virtio-trace/trace-agent.h new file mode 100644 index 000000000000..8de79bfeaa73 --- /dev/null +++ b/tools/virtio/virtio-trace/trace-agent.h @@ -0,0 +1,75 @@ +#ifndef __TRACE_AGENT_H__ +#define __TRACE_AGENT_H__ +#include <pthread.h> +#include <stdbool.h> + +#define MAX_CPUS 256 +#define PIPE_INIT (1024*1024) + +/* + * agent_info - structure managing total information of guest agent + * @pipe_size: size of pipe (default 1MB) + * @use_stdout: set to true when o option is added (default false) + * @cpus: total number of CPUs + * @ctl_fd: fd of control path, /dev/virtio-ports/agent-ctl-path + * @rw_ti: structure managing information of read/write threads + */ +struct agent_info { + unsigned long pipe_size; + bool use_stdout; + int cpus; + int ctl_fd; + struct rw_thread_info *rw_ti[MAX_CPUS]; +}; + +/* + * rw_thread_info - structure managing a read/write thread a cpu + * @cpu_num: cpu number operating this read/write thread + * @in_fd: fd of reading trace data path in cpu_num + * @out_fd: fd of writing trace data path in cpu_num + * @read_pipe: fd of read pipe + * @write_pipe: fd of write pipe + * @pipe_size: size of pipe (default 1MB) + */ +struct rw_thread_info { + int cpu_num; + int in_fd; + int out_fd; + int read_pipe; + int write_pipe; + unsigned long pipe_size; +}; + +/* use for stopping rw threads */ +extern bool global_sig_receive; + +/* use for notification */ +extern bool global_run_operation; +extern pthread_mutex_t mutex_notify; +extern pthread_cond_t cond_wakeup; + +/* for controller of read/write threads */ +extern int rw_ctl_init(const char *ctl_path); +extern void *rw_ctl_loop(int ctl_fd); + +/* for trace read/write thread */ +extern void *rw_thread_info_new(void); +extern void *rw_thread_init(int cpu, const char *in_path, const char *out_path, + bool stdout_flag, unsigned long pipe_size, + struct rw_thread_info *rw_ti); +extern pthread_t rw_thread_run(struct rw_thread_info *rw_ti); + +static inline void *zalloc(size_t size) +{ + return calloc(1, size); +} + +#define pr_err(format, ...) fprintf(stderr, format, ## __VA_ARGS__) +#define pr_info(format, ...) fprintf(stdout, format, ## __VA_ARGS__) +#ifdef DEBUG +#define pr_debug(format, ...) fprintf(stderr, format, ## __VA_ARGS__) +#else +#define pr_debug(format, ...) do {} while (0) +#endif + +#endif /*__TRACE_AGENT_H__*/ diff --git a/tools/vm/page-types.c b/tools/vm/page-types.c index f576971f6556..b76edf2f8333 100644 --- a/tools/vm/page-types.c +++ b/tools/vm/page-types.c @@ -34,8 +34,8 @@ #include <sys/fcntl.h> #include <sys/mount.h> #include <sys/statfs.h> -#include "../../include/linux/magic.h" -#include "../../include/linux/kernel-page-flags.h" +#include "../../include/uapi/linux/magic.h" +#include "../../include/uapi/linux/kernel-page-flags.h" #ifndef MAX_PATH |