Commit fa6e8e5f authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo' of...

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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 9450d14f f23b24f1
......@@ -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:
......
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
#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__ */
......@@ -1361,10 +1361,12 @@ 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);
if (test) {
printf(" '%s: %s'\n", event->event->name, test);
free(test);
}
}
}
free_events(events);
......@@ -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)
......
......@@ -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
--------------
......
......@@ -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)
#
......
......@@ -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"
......
......@@ -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"
......
......@@ -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[] = {
......
......@@ -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[] =
......
......@@ -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>
......
......@@ -28,7 +28,8 @@ 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,
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];
......@@ -137,18 +138,17 @@ 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,
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;
......
......@@ -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,7 +128,7 @@ 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,
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);
......@@ -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);
......
......@@ -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"
......
......@@ -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>
......
......@@ -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"
......
......@@ -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"
......
......@@ -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',
......
......@@ -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,8 +44,13 @@ static double cpu2slot(int cpu)
return 2 * cpu + 1;
}
static int *topology_map;
static double cpu2y(int cpu)
{
if (topology_map)
return cpu2slot(topology_map[cpu]) * SLOT_MULT;
else
return cpu2slot(cpu) * SLOT_MULT;
}
......@@ -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;
}
......@@ -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 */
......@@ -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,7 +1258,7 @@ 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,
if (dso__read_binary_type_filename(dso, symtab_type,
root_dir, name, PATH_MAX))
continue;
......@@ -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);
......
......@@ -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"
......
......@@ -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>
......
......@@ -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
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment