diff options
author | Ingo Molnar <mingo@kernel.org> | 2013-12-18 11:10:03 +0100 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2013-12-18 14:07:26 +0100 |
commit | fa6e8e5f7cbf85f364ebd5a90525dbbe9de2083b (patch) | |
tree | a85962bf1567292ac58f8340edc16f6cc35ea918 | |
parent | 9450d14fb959336803e5209119eb422b667b96aa (diff) | |
parent | f23b24f1bf90b56cfaeb2a1c9b77c46efe8916a6 (diff) |
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and refactorings from Arnaldo Carvalho de Melo:
New features:
. In perf timechart:
- Add backtrace support to CPU info
. Print pid along the name
. Add support for CPU topology
. Add new option --highlight'ing threads, be it by name or,
if a numeric value is provided, that run more than given duration.
From Stanislav Fomichev.
Refactorings:
. Rename some struct DSO binary_type related members and methods,
to clarify its purpose and need for differentiation from symtab_type,
i.e. one is about the files .text, CFI, etc, i.e. its binary contents,
and the other is about where the symbol table came from.
. Convert to new topic libraries, starting with an API one (sysfs, debugfs,
etc), renaming liblk in the process, from Borislav Petkov.
. Get rid of some more panic() like error handling in libtraceevent,
from Namhyung Kim.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
26 files changed, 344 insertions, 142 deletions
diff --git a/tools/Makefile b/tools/Makefile index a9b02008443..927cd46d36d 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -39,10 +39,10 @@ cpupower: FORCE cgroup firewire guest usb virtio vm net: FORCE $(call descend,$@) -liblk: FORCE - $(call descend,lib/lk) +libapikfs: FORCE + $(call descend,lib/api) -perf: liblk FORCE +perf: libapikfs FORCE $(call descend,$@) selftests: FORCE @@ -80,10 +80,10 @@ cpupower_clean: cgroup_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean: $(call descend,$(@:_clean=),clean) -liblk_clean: - $(call descend,lib/lk,clean) +libapikfs_clean: + $(call descend,lib/api,clean) -perf_clean: liblk_clean +perf_clean: libapikfs_clean $(call descend,$(@:_clean=),clean) selftests_clean: diff --git a/tools/lib/lk/Makefile b/tools/lib/api/Makefile index 3dba0a4aebb..ed2f51e11b8 100644 --- a/tools/lib/lk/Makefile +++ b/tools/lib/api/Makefile @@ -1,4 +1,5 @@ include ../../scripts/Makefile.include +include ../../perf/config/utilities.mak # QUIET_CLEAN CC = $(CROSS_COMPILE)gcc AR = $(CROSS_COMPILE)ar @@ -7,11 +8,11 @@ AR = $(CROSS_COMPILE)ar LIB_H= LIB_OBJS= -LIB_H += debugfs.h +LIB_H += fs/debugfs.h -LIB_OBJS += $(OUTPUT)debugfs.o +LIB_OBJS += $(OUTPUT)fs/debugfs.o -LIBFILE = liblk.a +LIBFILE = libapikfs.a CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) -fPIC EXTLIBS = -lelf -lpthread -lrt -lm @@ -25,14 +26,17 @@ $(LIBFILE): $(LIB_OBJS) $(LIB_OBJS): $(LIB_H) -$(OUTPUT)%.o: %.c +libapi_dirs: + $(QUIET_MKDIR)mkdir -p $(OUTPUT)fs/ + +$(OUTPUT)%.o: %.c libapi_dirs $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< -$(OUTPUT)%.s: %.c +$(OUTPUT)%.s: %.c libapi_dirs $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $< -$(OUTPUT)%.o: %.S +$(OUTPUT)%.o: %.S libapi_dirs $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< clean: - $(RM) $(LIB_OBJS) $(LIBFILE) + $(call QUIET_CLEAN, libapi) $(RM) $(LIB_OBJS) $(LIBFILE) .PHONY: clean diff --git a/tools/lib/lk/debugfs.c b/tools/lib/api/fs/debugfs.c index 7c434796235..7c434796235 100644 --- a/tools/lib/lk/debugfs.c +++ b/tools/lib/api/fs/debugfs.c diff --git a/tools/lib/lk/debugfs.h b/tools/lib/api/fs/debugfs.h index 935c59bdb44..f19d3df9609 100644 --- a/tools/lib/lk/debugfs.h +++ b/tools/lib/api/fs/debugfs.h @@ -1,5 +1,5 @@ -#ifndef __LK_DEBUGFS_H__ -#define __LK_DEBUGFS_H__ +#ifndef __API_DEBUGFS_H__ +#define __API_DEBUGFS_H__ #define _STR(x) #x #define STR(x) _STR(x) @@ -26,4 +26,4 @@ char *debugfs_mount(const char *mountpoint); extern char debugfs_mountpoint[]; -#endif /* __LK_DEBUGFS_H__ */ +#endif /* __API_DEBUGFS_H__ */ diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c index 9303c55128d..e2842b92675 100644 --- a/tools/lib/traceevent/parse-filter.c +++ b/tools/lib/traceevent/parse-filter.c @@ -1361,8 +1361,10 @@ enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter, if (ret >= 0 && pevent->test_filters) { char *test; test = pevent_filter_make_string(filter, event->event->id); - printf(" '%s: %s'\n", event->event->name, test); - free(test); + if (test) { + printf(" '%s: %s'\n", event->event->name, test); + free(test); + } } } @@ -2050,7 +2052,6 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) int left_val = -1; int right_val = -1; int val; - int len; switch (arg->op.type) { case FILTER_OP_AND: @@ -2097,11 +2098,7 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) default: break; } - str = malloc_or_die(6); - if (val) - strcpy(str, "TRUE"); - else - strcpy(str, "FALSE"); + asprintf(&str, val ? "TRUE" : "FALSE"); break; } } @@ -2119,10 +2116,7 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) break; } - len = strlen(left) + strlen(right) + strlen(op) + 10; - str = malloc_or_die(len); - snprintf(str, len, "(%s) %s (%s)", - left, op, right); + asprintf(&str, "(%s) %s (%s)", left, op, right); break; case FILTER_OP_NOT: @@ -2138,16 +2132,10 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) right_val = 0; if (right_val >= 0) { /* just return the opposite */ - str = malloc_or_die(6); - if (right_val) - strcpy(str, "FALSE"); - else - strcpy(str, "TRUE"); + asprintf(&str, right_val ? "FALSE" : "TRUE"); break; } - len = strlen(right) + strlen(op) + 3; - str = malloc_or_die(len); - snprintf(str, len, "%s(%s)", op, right); + asprintf(&str, "%s(%s)", op, right); break; default: @@ -2161,11 +2149,9 @@ static char *op_to_str(struct event_filter *filter, struct filter_arg *arg) static char *val_to_str(struct event_filter *filter, struct filter_arg *arg) { - char *str; - - str = malloc_or_die(30); + char *str = NULL; - snprintf(str, 30, "%lld", arg->value.val); + asprintf(&str, "%lld", arg->value.val); return str; } @@ -2181,7 +2167,6 @@ static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg) char *rstr; char *op; char *str = NULL; - int len; lstr = arg_to_str(filter, arg->exp.left); rstr = arg_to_str(filter, arg->exp.right); @@ -2220,12 +2205,11 @@ static char *exp_to_str(struct event_filter *filter, struct filter_arg *arg) op = "^"; break; default: - die("oops in exp"); + op = "[ERROR IN EXPRESSION TYPE]"; + break; } - len = strlen(op) + strlen(lstr) + strlen(rstr) + 4; - str = malloc_or_die(len); - snprintf(str, len, "%s %s %s", lstr, op, rstr); + asprintf(&str, "%s %s %s", lstr, op, rstr); out: free(lstr); free(rstr); @@ -2239,7 +2223,6 @@ static char *num_to_str(struct event_filter *filter, struct filter_arg *arg) char *rstr; char *str = NULL; char *op = NULL; - int len; lstr = arg_to_str(filter, arg->num.left); rstr = arg_to_str(filter, arg->num.right); @@ -2270,10 +2253,7 @@ static char *num_to_str(struct event_filter *filter, struct filter_arg *arg) if (!op) op = "<="; - len = strlen(lstr) + strlen(op) + strlen(rstr) + 4; - str = malloc_or_die(len); - sprintf(str, "%s %s %s", lstr, op, rstr); - + asprintf(&str, "%s %s %s", lstr, op, rstr); break; default: @@ -2291,7 +2271,6 @@ static char *str_to_str(struct event_filter *filter, struct filter_arg *arg) { char *str = NULL; char *op = NULL; - int len; switch (arg->str.type) { case FILTER_CMP_MATCH: @@ -2309,12 +2288,8 @@ static char *str_to_str(struct event_filter *filter, struct filter_arg *arg) if (!op) op = "!~"; - len = strlen(arg->str.field->name) + strlen(op) + - strlen(arg->str.val) + 6; - str = malloc_or_die(len); - snprintf(str, len, "%s %s \"%s\"", - arg->str.field->name, - op, arg->str.val); + asprintf(&str, "%s %s \"%s\"", + arg->str.field->name, op, arg->str.val); break; default: @@ -2326,15 +2301,11 @@ static char *str_to_str(struct event_filter *filter, struct filter_arg *arg) static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg) { - char *str; + char *str = NULL; switch (arg->type) { case FILTER_ARG_BOOLEAN: - str = malloc_or_die(6); - if (arg->boolean.value) - strcpy(str, "TRUE"); - else - strcpy(str, "FALSE"); + asprintf(&str, arg->boolean.value ? "TRUE" : "FALSE"); return str; case FILTER_ARG_OP: @@ -2369,7 +2340,7 @@ static char *arg_to_str(struct event_filter *filter, struct filter_arg *arg) * * Returns a string that displays the filter contents. * This string must be freed with free(str). - * NULL is returned if no filter is found. + * NULL is returned if no filter is found or allocation failed. */ char * pevent_filter_make_string(struct event_filter *filter, int event_id) diff --git a/tools/perf/Documentation/perf-timechart.txt b/tools/perf/Documentation/perf-timechart.txt index 271dd4ed5b0..bc5990c33dc 100644 --- a/tools/perf/Documentation/perf-timechart.txt +++ b/tools/perf/Documentation/perf-timechart.txt @@ -56,9 +56,25 @@ $ perf timechart Written 10.2 seconds of trace to output.svg. +Record system-wide timechart: + + $ perf timechart record + + then generate timechart and highlight 'gcc' tasks: + + $ perf timechart --highlight gcc + -n:: --proc-num:: Print task info for at least given number of tasks. +-t:: +--topology:: + Sort CPUs according to topology. +--highlight=<duration_nsecs|task_name>:: + Highlight tasks (using different color) that run more than given + duration or tasks with given name. If number is given it's interpreted + as number of nanoseconds. If non-numeric string is given it's + interpreted as task name. RECORD OPTIONS -------------- diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index fad61079e79..97a2145e4cc 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -86,7 +86,7 @@ FLEX = flex BISON = bison STRIP = strip -LK_DIR = $(srctree)/tools/lib/lk/ +LIB_DIR = $(srctree)/tools/lib/api/ TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/ # include config/Makefile by default and rule out @@ -127,20 +127,20 @@ strip-libs = $(filter-out -l%,$(1)) ifneq ($(OUTPUT),) TE_PATH=$(OUTPUT) ifneq ($(subdir),) - LK_PATH=$(OUTPUT)/../lib/lk/ + LIB_PATH=$(OUTPUT)/../lib/api/ else - LK_PATH=$(OUTPUT) + LIB_PATH=$(OUTPUT) endif else TE_PATH=$(TRACE_EVENT_DIR) - LK_PATH=$(LK_DIR) + LIB_PATH=$(LIB_DIR) endif LIBTRACEEVENT = $(TE_PATH)libtraceevent.a export LIBTRACEEVENT -LIBLK = $(LK_PATH)liblk.a -export LIBLK +LIBAPIKFS = $(LIB_PATH)libapikfs.a +export LIBAPIKFS # python extension build directories PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/ @@ -151,7 +151,7 @@ export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP python-clean := $(call QUIET_CLEAN, python) $(RM) -r $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) -PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(LIBLK) +PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py $(LIBTRACEEVENT) $(LIBAPIKFS) $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) $(QUIET_GEN)CFLAGS='$(CFLAGS)' $(PYTHON_WORD) util/setup.py \ @@ -441,7 +441,7 @@ BUILTIN_OBJS += $(OUTPUT)builtin-inject.o BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o BUILTIN_OBJS += $(OUTPUT)builtin-mem.o -PERFLIBS = $(LIB_FILE) $(LIBLK) $(LIBTRACEEVENT) +PERFLIBS = $(LIB_FILE) $(LIBAPIKFS) $(LIBTRACEEVENT) # We choose to avoid "if .. else if .. else .. endif endif" # because maintaining the nesting to match is a pain. If @@ -730,19 +730,19 @@ $(LIBTRACEEVENT)-clean: install-traceevent-plugins: $(LIBTRACEEVENT) $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(LIBTRACEEVENT_FLAGS) install_plugins -LIBLK_SOURCES = $(wildcard $(LK_PATH)*.[ch]) +LIBAPIKFS_SOURCES = $(wildcard $(LIB_PATH)fs/*.[ch]) # if subdir is set, we've been called from above so target has been built # already -$(LIBLK): $(LIBLK_SOURCES) +$(LIBAPIKFS): $(LIBAPIKFS_SOURCES) ifeq ($(subdir),) - $(QUIET_SUBDIR0)$(LK_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) liblk.a + $(QUIET_SUBDIR0)$(LIB_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) libapikfs.a endif -$(LIBLK)-clean: +$(LIBAPIKFS)-clean: ifeq ($(subdir),) - $(call QUIET_CLEAN, liblk) - @$(MAKE) -C $(LK_DIR) O=$(OUTPUT) clean >/dev/null + $(call QUIET_CLEAN, libapikfs) + @$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) clean >/dev/null endif help: @@ -881,12 +881,11 @@ config-clean: $(call QUIET_CLEAN, config) @$(MAKE) -C config/feature-checks clean >/dev/null -clean: $(LIBTRACEEVENT)-clean $(LIBLK)-clean config-clean +clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS) $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* - $(call QUIET_CLEAN, Documentation) - @$(MAKE) -C Documentation O=$(OUTPUT) clean >/dev/null + $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean $(python-clean) # diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index c6fa3cbd45a..154b397a5d2 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -13,7 +13,7 @@ #include "util/parse-options.h" #include "util/trace-event.h" #include "util/debug.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include "util/tool.h" #include "util/stat.h" #include "util/top.h" diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 6ea9e85bdc0..c98ccb57050 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -37,7 +37,7 @@ #include "util/strfilter.h" #include "util/symbol.h" #include "util/debug.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include "util/parse-options.h" #include "util/probe-finder.h" #include "util/probe-event.h" diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 0bda620a717..20d4212fa33 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -58,7 +58,8 @@ struct timechart { first_time, last_time; bool power_only, tasks_only, - with_backtrace; + with_backtrace, + topology; }; struct per_pidcomm; @@ -531,12 +532,10 @@ static int process_sample_event(struct perf_tool *tool, tchart->last_time = sample->time; } - if (sample->cpu > tchart->numcpus) - tchart->numcpus = sample->cpu; - if (evsel->handler != NULL) { tracepoint_handler f = evsel->handler; - return f(tchart, evsel, sample, cat_backtrace(event, sample, machine)); + return f(tchart, evsel, sample, + cat_backtrace(event, sample, machine)); } return 0; @@ -837,8 +836,14 @@ static void draw_cpu_usage(struct timechart *tchart) while (c) { sample = c->samples; while (sample) { - if (sample->type == TYPE_RUNNING) - svg_process(sample->cpu, sample->start_time, sample->end_time, "sample", c->comm); + if (sample->type == TYPE_RUNNING) { + svg_process(sample->cpu, + sample->start_time, + sample->end_time, + p->pid, + c->comm, + sample->backtrace); + } sample = sample->next; } @@ -1031,8 +1036,6 @@ static void write_svg_file(struct timechart *tchart, const char *filename) int count; int thresh = TIME_THRESH; - tchart->numcpus++; - if (tchart->power_only) tchart->proc_num = 0; @@ -1062,6 +1065,37 @@ static void write_svg_file(struct timechart *tchart, const char *filename) svg_close(); } +static int process_header(struct perf_file_section *section __maybe_unused, + struct perf_header *ph, + int feat, + int fd __maybe_unused, + void *data) +{ + struct timechart *tchart = data; + + switch (feat) { + case HEADER_NRCPUS: + tchart->numcpus = ph->env.nr_cpus_avail; + break; + + case HEADER_CPU_TOPOLOGY: + if (!tchart->topology) + break; + + if (svg_build_topology_map(ph->env.sibling_cores, + ph->env.nr_sibling_cores, + ph->env.sibling_threads, + ph->env.nr_sibling_threads)) + fprintf(stderr, "problem building topology\n"); + break; + + default: + break; + } + + return 0; +} + static int __cmd_timechart(struct timechart *tchart, const char *output_name) { const struct perf_evsel_str_handler power_tracepoints[] = { @@ -1087,6 +1121,11 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name) if (session == NULL) return -ENOMEM; + (void)perf_header__process_sections(&session->header, + perf_data_file__fd(session->file), + tchart, + process_header); + if (!perf_session__has_traces(session, "timechart record")) goto out_delete; @@ -1212,6 +1251,23 @@ parse_process(const struct option *opt __maybe_unused, const char *arg, return 0; } +static int +parse_highlight(const struct option *opt __maybe_unused, const char *arg, + int __maybe_unused unset) +{ + unsigned long duration = strtoul(arg, NULL, 0); + + if (svg_highlight || svg_highlight_name) + return -1; + + if (duration) + svg_highlight = duration; + else + svg_highlight_name = strdup(arg); + + return 0; +} + int cmd_timechart(int argc, const char **argv, const char *prefix __maybe_unused) { @@ -1230,6 +1286,9 @@ int cmd_timechart(int argc, const char **argv, 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_CALLBACK(0, "highlight", NULL, "duration or task name", + "highlight tasks. Pass duration in ns or process name.", + parse_highlight), OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, "output processes data only"), @@ -1240,6 +1299,8 @@ int cmd_timechart(int argc, const char **argv, "Look for files with symbols relative to this directory"), OPT_INTEGER('n', "proc-num", &tchart.proc_num, "min. number of tasks to print"), + OPT_BOOLEAN('t', "topology", &tchart.topology, + "sort CPUs according to topology"), OPT_END() }; const char * const timechart_usage[] = { diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 8b38b4e80ec..431798a4110 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -13,7 +13,7 @@ #include "util/quote.h" #include "util/run-command.h" #include "util/parse-events.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include <pthread.h> const char perf_usage_string[] = diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 3cbd1049608..e4ce8aed29d 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -3,7 +3,7 @@ #include "evsel.h" #include "evlist.h" #include "fs.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include "tests.h" #include <linux/hw_breakpoint.h> diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 436922f1f9d..4ddeecb9ff8 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -28,8 +28,9 @@ char dso__symtab_origin(const struct dso *dso) return origin[dso->symtab_type]; } -int dso__binary_type_file(const struct dso *dso, enum dso_binary_type type, - char *root_dir, char *filename, size_t size) +int dso__read_binary_type_filename(const struct dso *dso, + enum dso_binary_type type, + char *root_dir, char *filename, size_t size) { char build_id_hex[BUILD_ID_SIZE * 2 + 1]; int ret = 0; @@ -137,19 +138,18 @@ int dso__binary_type_file(const struct dso *dso, enum dso_binary_type type, static int open_dso(struct dso *dso, struct machine *machine) { - char *root_dir = (char *) ""; - char *name; int fd; + char *root_dir = (char *)""; + char *name = malloc(PATH_MAX); - name = malloc(PATH_MAX); if (!name) return -ENOMEM; if (machine) root_dir = machine->root_dir; - if (dso__binary_type_file(dso, dso->data_type, - root_dir, name, PATH_MAX)) { + if (dso__read_binary_type_filename(dso, dso->binary_type, + root_dir, name, PATH_MAX)) { free(name); return -EINVAL; } @@ -161,26 +161,26 @@ static int open_dso(struct dso *dso, struct machine *machine) int dso__data_fd(struct dso *dso, struct machine *machine) { - static enum dso_binary_type binary_type_data[] = { + enum dso_binary_type binary_type_data[] = { DSO_BINARY_TYPE__BUILD_ID_CACHE, DSO_BINARY_TYPE__SYSTEM_PATH_DSO, DSO_BINARY_TYPE__NOT_FOUND, }; int i = 0; - if (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND) + if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) return open_dso(dso, machine); do { int fd; - dso->data_type = binary_type_data[i++]; + dso->binary_type = binary_type_data[i++]; fd = open_dso(dso, machine); if (fd >= 0) return fd; - } while (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND); + } while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND); return -EINVAL; } @@ -475,7 +475,7 @@ struct dso *dso__new(const char *name) dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; dso->cache = RB_ROOT; dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; - dso->data_type = DSO_BINARY_TYPE__NOT_FOUND; + dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND; dso->loaded = 0; dso->rel = 0; dso->sorted_by_name = 0; diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index e1cc5069813..cd7d6f078cd 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -83,7 +83,7 @@ struct dso { enum dso_kernel_type kernel; enum dso_swap_type needs_swap; enum dso_binary_type symtab_type; - enum dso_binary_type data_type; + enum dso_binary_type binary_type; u8 adjust_symbols:1; u8 has_build_id:1; u8 has_srcline:1; @@ -128,8 +128,8 @@ void dso__read_running_kernel_build_id(struct dso *dso, int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir); char dso__symtab_origin(const struct dso *dso); -int dso__binary_type_file(const struct dso *dso, enum dso_binary_type type, - char *root_dir, char *filename, size_t size); +int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, + char *root_dir, char *filename, size_t size); int dso__data_fd(struct dso *dso, struct machine *machine); ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, @@ -159,14 +159,14 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp); static inline bool dso__is_vmlinux(struct dso *dso) { - return dso->data_type == DSO_BINARY_TYPE__VMLINUX || - dso->data_type == DSO_BINARY_TYPE__GUEST_VMLINUX; + return dso->binary_type == DSO_BINARY_TYPE__VMLINUX || + dso->binary_type == DSO_BINARY_TYPE__GUEST_VMLINUX; } static inline bool dso__is_kcore(struct dso *dso) { - return dso->data_type == DSO_BINARY_TYPE__KCORE || - dso->data_type == DSO_BINARY_TYPE__GUEST_KCORE; + return dso->binary_type == DSO_BINARY_TYPE__KCORE || + dso->binary_type == DSO_BINARY_TYPE__GUEST_KCORE; } void dso__free_a2l(struct dso *dso); diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 0b31cee3487..da318291498 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -7,7 +7,7 @@ * Released under the GPL v2. (and only v2, not any later version) */ #include "util.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include <poll.h> #include "cpumap.h" #include "thread_map.h" diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 7b510fd1f08..01ff4cfde1f 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -9,7 +9,7 @@ #include <byteswap.h> #include <linux/bitops.h> -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include <traceevent/event-parse.h> #include <linux/hw_breakpoint.h> #include <linux/perf_event.h> diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 969cb8f0d88..094c28ba2fa 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -10,7 +10,7 @@ #include "symbol.h" #include "cache.h" #include "header.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include "parse-events-bison.h" #define YY_EXTRA_TYPE int #include "parse-events-flex.h" diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index d7cff57945c..544ac1898a9 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -40,7 +40,7 @@ #include "color.h" #include "symbol.h" #include "thread.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include "trace-event.h" /* For __maybe_unused */ #include "probe-event.h" #include "probe-finder.h" diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 58ea5ca6c25..d0aee4b9dfd 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py @@ -25,7 +25,7 @@ cflags += ['-fno-strict-aliasing', '-Wno-write-strings', '-Wno-unused-parameter' build_lib = getenv('PYTHON_EXTBUILD_LIB') build_tmp = getenv('PYTHON_EXTBUILD_TMP') libtraceevent = getenv('LIBTRACEEVENT') -liblk = getenv('LIBLK') +libapikfs = getenv('LIBAPIKFS') ext_sources = [f.strip() for f in file('util/python-ext-sources') if len(f.strip()) > 0 and f[0] != '#'] @@ -34,7 +34,7 @@ perf = Extension('perf', sources = ext_sources, include_dirs = ['util/include'], extra_compile_args = cflags, - extra_objects = [libtraceevent, liblk], + extra_objects = [libtraceevent, libapikfs], ) setup(name='perf', diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index 8b79d3ad124..56a84f2cc46 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c @@ -17,8 +17,11 @@ #include <stdlib.h> #include <unistd.h> #include <string.h> +#include <linux/bitops.h> +#include "perf.h" #include "svghelper.h" +#include "cpumap.h" static u64 first_time, last_time; static u64 turbo_frequency, max_freq; @@ -28,6 +31,8 @@ static u64 turbo_frequency, max_freq; #define SLOT_HEIGHT 25.0 int svg_page_width = 1000; +u64 svg_highlight; +const char *svg_highlight_name; #define MIN_TEXT_SIZE 0.01 @@ -39,9 +44,14 @@ static double cpu2slot(int cpu) return 2 * cpu + 1; } +static int *topology_map; + static double cpu2y(int cpu) { - return cpu2slot(cpu) * SLOT_MULT; + if (topology_map) + return cpu2slot(topology_map[cpu]) * SLOT_MULT; + else + return cpu2slot(cpu) * SLOT_MULT; } static double time2pixels(u64 __time) @@ -104,6 +114,7 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end) fprintf(svgfile, " rect.process { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:1; stroke:rgb( 0, 0, 0); } \n"); fprintf(svgfile, " rect.process2 { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); fprintf(svgfile, " rect.sample { fill:rgb( 0, 0,255); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); + fprintf(svgfile, " rect.sample_hi{ fill:rgb(255,128, 0); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); fprintf(svgfile, " rect.blocked { fill:rgb(255, 0, 0); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); fprintf(svgfile, " rect.waiting { fill:rgb(224,214, 0); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); fprintf(svgfile, " rect.WAITING { fill:rgb(255,214, 48); fill-opacity:0.6; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); @@ -147,17 +158,24 @@ void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace) void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace) { double text_size; + const char *type; + if (!svgfile) return; + if (svg_highlight && end - start > svg_highlight) + type = "sample_hi"; + else + type = "sample"; fprintf(svgfile, "<g>\n"); fprintf(svgfile, "<title>#%d running %s</title>\n", cpu, time_to_string(end - start)); if (backtrace) fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace); - fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"sample\"/>\n", - time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT); + fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"%s\"/>\n", + time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, + type); text_size = (time2pixels(end)-time2pixels(start)); if (cpu > 9) @@ -275,7 +293,7 @@ void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq) time2pixels(last_time)-time2pixels(first_time), cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT); - sprintf(cpu_string, "CPU %i", (int)cpu+1); + sprintf(cpu_string, "CPU %i", (int)cpu); fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\">%s</text>\n", 10+time2pixels(first_time), cpu2y(cpu) + SLOT_HEIGHT/2, cpu_string); @@ -285,16 +303,25 @@ void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq) fprintf(svgfile, "</g>\n"); } -void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name) +void svg_process(int cpu, u64 start, u64 end, int pid, const char *name, const char *backtrace) { double width; + const char *type; if (!svgfile) return; + if (svg_highlight && end - start >= svg_highlight) + type = "sample_hi"; + else if (svg_highlight_name && strstr(name, svg_highlight_name)) + type = "sample_hi"; + else + type = "sample"; fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), cpu2y(cpu)); - fprintf(svgfile, "<title>%s %s</title>\n", name, time_to_string(end - start)); + fprintf(svgfile, "<title>%d %s running %s</title>\n", pid, name, time_to_string(end - start)); + if (backtrace) + fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace); fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n", time2pixels(end)-time2pixels(start), SLOT_MULT+SLOT_HEIGHT, type); width = time2pixels(end)-time2pixels(start); @@ -566,3 +593,123 @@ void svg_close(void) svgfile = NULL; } } + +#define cpumask_bits(maskp) ((maskp)->bits) +typedef struct { DECLARE_BITMAP(bits, MAX_NR_CPUS); } cpumask_t; + +struct topology { + cpumask_t *sib_core; + int sib_core_nr; + cpumask_t *sib_thr; + int sib_thr_nr; +}; + +static void scan_thread_topology(int *map, struct topology *t, int cpu, int *pos) +{ + int i; + int thr; + + for (i = 0; i < t->sib_thr_nr; i++) { + if (!test_bit(cpu, cpumask_bits(&t->sib_thr[i]))) + continue; + + for_each_set_bit(thr, + cpumask_bits(&t->sib_thr[i]), + MAX_NR_CPUS) + if (map[thr] == -1) + map[thr] = (*pos)++; + } +} + +static void scan_core_topology(int *map, struct topology *t) +{ + int pos = 0; + int i; + int cpu; + + for (i = 0; i < t->sib_core_nr; i++) + for_each_set_bit(cpu, + cpumask_bits(&t->sib_core[i]), + MAX_NR_CPUS) + scan_thread_topology(map, t, cpu, &pos); +} + +static int str_to_bitmap(char *s, cpumask_t *b) +{ + int i; + int ret = 0; + struct cpu_map *m; + int c; + + m = cpu_map__new(s); + if (!m) + return -1; + + for (i = 0; i < m->nr; i++) { + c = m->map[i]; + if (c >= MAX_NR_CPUS) { + ret = -1; + break; + } + + set_bit(c, cpumask_bits(b)); + } + + cpu_map__delete(m); + + return ret; +} + +int svg_build_topology_map(char *sib_core, int sib_core_nr, + char *sib_thr, int sib_thr_nr) +{ + int i; + struct topology t; + + t.sib_core_nr = sib_core_nr; + t.sib_thr_nr = sib_thr_nr; + t.sib_core = calloc(sib_core_nr, sizeof(cpumask_t)); + t.sib_thr = calloc(sib_thr_nr, sizeof(cpumask_t)); + + if (!t.sib_core || !t.sib_thr) { + fprintf(stderr, "topology: no memory\n"); + goto exit; + } + + for (i = 0; i < sib_core_nr; i++) { + if (str_to_bitmap(sib_core, &t.sib_core[i])) { + fprintf(stderr, "topology: can't parse siblings map\n"); + goto exit; + } + + sib_core += strlen(sib_core) + 1; + } + + for (i = 0; i < sib_thr_nr; i++) { + if (str_to_bitmap(sib_thr, &t.sib_thr[i])) { + fprintf(stderr, "topology: can't parse siblings map\n"); + goto exit; + } + + sib_thr += strlen(sib_thr) + 1; + } + + topology_map = malloc(sizeof(int) * MAX_NR_CPUS); + if (!topology_map) { + fprintf(stderr, "topology: no memory\n"); + goto exit; + } + + for (i = 0; i < MAX_NR_CPUS; i++) + topology_map[i] = -1; + + scan_core_topology(topology_map, &t); + + return 0; + +exit: + free(t.sib_core); + free(t.sib_thr); + + return -1; +} diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h index fad79ceeac1..f7b4d6e699e 100644 --- a/tools/perf/util/svghelper.h +++ b/tools/perf/util/svghelper.h @@ -11,7 +11,7 @@ extern void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *back extern void svg_cpu_box(int cpu, u64 max_frequency, u64 turbo_frequency); -extern void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name); +extern void svg_process(int cpu, u64 start, u64 end, int pid, const char *name, const char *backtrace); extern void svg_cstate(int cpu, u64 start, u64 end, int type); extern void svg_pstate(int cpu, u64 start, u64 end, u64 freq); @@ -23,7 +23,11 @@ extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, cha extern void svg_interrupt(u64 start, int row, const char *backtrace); extern void svg_text(int Yslot, u64 start, const char *text); extern void svg_close(void); +extern int svg_build_topology_map(char *sib_core, int sib_core_nr, + char *sib_thr, int sib_thr_nr); extern int svg_page_width; +extern u64 svg_highlight; +extern const char *svg_highlight_name; #endif /* __PERF_SVGHELPER_H */ diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 61eb1cddf01..923d00040bb 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1089,9 +1089,9 @@ static int dso__load_kcore(struct dso *dso, struct map *map, * dso__data_read_addr(). */ if (dso->kernel == DSO_TYPE_GUEST_KERNEL) - dso->data_type = DSO_BINARY_TYPE__GUEST_KCORE; + dso->binary_type = DSO_BINARY_TYPE__GUEST_KCORE; else - dso->data_type = DSO_BINARY_TYPE__KCORE; + dso->binary_type = DSO_BINARY_TYPE__KCORE; dso__set_long_name(dso, strdup(kcore_filename), true); close(fd); @@ -1258,8 +1258,8 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) enum dso_binary_type symtab_type = binary_type_symtab[i]; - if (dso__binary_type_file(dso, symtab_type, - root_dir, name, PATH_MAX)) + if (dso__read_binary_type_filename(dso, symtab_type, + root_dir, name, PATH_MAX)) continue; /* Name is now the name of the next image to try */ @@ -1368,9 +1368,9 @@ int dso__load_vmlinux(struct dso *dso, struct map *map, if (err > 0) { if (dso->kernel == DSO_TYPE_GUEST_KERNEL) - dso->data_type = DSO_BINARY_TYPE__GUEST_VMLINUX; + dso->binary_type = DSO_BINARY_TYPE__GUEST_VMLINUX; else - dso->data_type = DSO_BINARY_TYPE__VMLINUX; + dso->binary_type = DSO_BINARY_TYPE__VMLINUX; dso__set_long_name(dso, vmlinux, vmlinux_allocated); dso__set_loaded(dso, map->type); pr_debug("Using %s for symbols\n", symfs_vmlinux); diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index f3c9e551bd3..c354b95a234 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -38,7 +38,7 @@ #include "../perf.h" #include "trace-event.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include "evsel.h" #define VERSION "0.5" diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index a1eea3e809a..9a2c268ad71 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -71,7 +71,7 @@ #include <linux/magic.h> #include "types.h" #include <sys/ttydefaults.h> -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include <termios.h> #include <linux/bitops.h> diff --git a/tools/vm/Makefile b/tools/vm/Makefile index 24e9ddd93fa..3d907dacf2a 100644 --- a/tools/vm/Makefile +++ b/tools/vm/Makefile @@ -2,21 +2,21 @@ # TARGETS=page-types slabinfo -LK_DIR = ../lib/lk -LIBLK = $(LK_DIR)/liblk.a +LIB_DIR = ../lib/api +LIBS = $(LIB_DIR)/libapikfs.a CC = $(CROSS_COMPILE)gcc CFLAGS = -Wall -Wextra -I../lib/ -LDFLAGS = $(LIBLK) +LDFLAGS = $(LIBS) -$(TARGETS): liblk +$(TARGETS): $(LIBS) -liblk: - make -C $(LK_DIR) +$(LIBS): + make -C $(LIB_DIR) %: %.c $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) clean: $(RM) page-types slabinfo - make -C ../lib/lk clean + make -C $(LIB_DIR) clean diff --git a/tools/vm/page-types.c b/tools/vm/page-types.c index d5e9d6d185c..f9be24d9efa 100644 --- a/tools/vm/page-types.c +++ b/tools/vm/page-types.c @@ -36,7 +36,7 @@ #include <sys/statfs.h> #include "../../include/uapi/linux/magic.h" #include "../../include/uapi/linux/kernel-page-flags.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #ifndef MAX_PATH # define MAX_PATH 256 |