Commit 0afb1704 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 fixes from Arnaldo Carvalho de Melo:

New user selectable features:

  - Support recording running/enabled time in 'perf record' (Andi Kleen)

  - New tool: 'perf data' for converting perf.data to other formats,
    initially for the CTF (Common Trace Format) from LTTng (Jiri Olsa, Sebastian Siewior)

User visible changes:

  - Only insert blank duration bracket when tracing syscalls in 'perf trace' (Arnaldo Carvalho de Melo)

  - Filter out the trace pid when no threads are specified in 'perf trace' (Arnaldo Carvalho de Melo)

  - Add 'perf trace' man page entry for --event (Arnaldo Carvalho de Melo)

  - Dump stack on segfaults in 'perf trace' (Arnaldo Carvalho de Melo)

Infrastructure changes:

  - Introduce set_filter_pid and set_filter_pids methods in the evlist class (Arnaldo Carvalho de Melo)

  - Some perf_session untanglement patches, removing the need to pass a
    perf_session instance for things that are related to evlists, so that
    tools that don't deal with perf.data files like trace in live mode can
    make use of the ordered_events class (Arnaldo Carvalho de Melo)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents e9e4e443 54cf776a
...@@ -18,6 +18,7 @@ perf-y += builtin-lock.o ...@@ -18,6 +18,7 @@ perf-y += builtin-lock.o
perf-y += builtin-kvm.o perf-y += builtin-kvm.o
perf-y += builtin-inject.o perf-y += builtin-inject.o
perf-y += builtin-mem.o perf-y += builtin-mem.o
perf-y += builtin-data.o
perf-$(CONFIG_AUDIT) += builtin-trace.o perf-$(CONFIG_AUDIT) += builtin-trace.o
perf-$(CONFIG_LIBELF) += builtin-probe.o perf-$(CONFIG_LIBELF) += builtin-probe.o
......
perf-data(1)
==============
NAME
----
perf-data - Data file related processing
SYNOPSIS
--------
[verse]
'perf data' [<common options>] <command> [<options>]",
DESCRIPTION
-----------
Data file related processing.
COMMANDS
--------
convert::
Converts perf data file into another format (only CTF [1] format is support by now).
It's possible to set data-convert debug variable to get debug messages from conversion,
like:
perf --debug data-convert data convert ...
OPTIONS for 'convert'
---------------------
--to-ctf::
Triggers the CTF conversion, specify the path of CTF data directory.
-i::
Specify input perf data file path.
-v::
--verbose::
Be more verbose (show counter open errors, etc).
SEE ALSO
--------
linkperf:perf[1]
[1] Common Trace Format - http://www.efficios.com/ctf
...@@ -241,6 +241,9 @@ Capture machine state (registers) at interrupt, i.e., on counter overflows for ...@@ -241,6 +241,9 @@ Capture machine state (registers) at interrupt, i.e., on counter overflows for
each sample. List of captured registers depends on the architecture. This option each sample. List of captured registers depends on the architecture. This option
is off by default. is off by default.
--running-time::
Record running and enabled time for read events (:S)
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-stat[1], linkperf:perf-list[1] linkperf:perf-stat[1], linkperf:perf-list[1]
...@@ -55,6 +55,9 @@ OPTIONS ...@@ -55,6 +55,9 @@ OPTIONS
--uid=:: --uid=::
Record events in threads owned by uid. Name or number. Record events in threads owned by uid. Name or number.
--filter-pids=::
Filter out events for these pids and for 'trace' itself (comma separated list).
-v:: -v::
--verbose=:: --verbose=::
Verbosity level. Verbosity level.
...@@ -115,6 +118,9 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. ...@@ -115,6 +118,9 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.
--syscalls:: --syscalls::
Trace system calls. This options is enabled by default. Trace system calls. This options is enabled by default.
--event::
Trace other events, see 'perf list' for a complete list.
PAGEFAULTS PAGEFAULTS
---------- ----------
......
...@@ -13,11 +13,16 @@ SYNOPSIS ...@@ -13,11 +13,16 @@ SYNOPSIS
OPTIONS OPTIONS
------- -------
--debug:: --debug::
Setup debug variable (just verbose for now) in value Setup debug variable (see list below) in value
range (0, 10). Use like: range (0, 10). Use like:
--debug verbose # sets verbose = 1 --debug verbose # sets verbose = 1
--debug verbose=2 # sets verbose = 2 --debug verbose=2 # sets verbose = 2
List of debug variables allowed to set:
verbose - general debug messages
ordered-events - ordered events object debug messages
data-convert - data convert command debug messages
--buildid-dir:: --buildid-dir::
Setup buildid cache directory. It has higher priority than Setup buildid cache directory. It has higher priority than
buildid.dir config file option. buildid.dir config file option.
......
...@@ -68,7 +68,9 @@ include config/utilities.mak ...@@ -68,7 +68,9 @@ include config/utilities.mak
# for reading the x32 mode 32-bit compatibility VDSO in 64-bit mode # for reading the x32 mode 32-bit compatibility VDSO in 64-bit mode
# #
# Define NO_ZLIB if you do not want to support compressed kernel modules # Define NO_ZLIB if you do not want to support compressed kernel modules
#
# Define NO_LIBBABELTRACE if you do not want libbabeltrace support
# for CTF data format.
ifeq ($(srctree),) ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(shell pwd))) srctree := $(patsubst %/,%,$(dir $(shell pwd)))
......
#include <linux/compiler.h>
#include "builtin.h"
#include "perf.h"
#include "debug.h"
#include "parse-options.h"
#include "data-convert-bt.h"
typedef int (*data_cmd_fn_t)(int argc, const char **argv, const char *prefix);
struct data_cmd {
const char *name;
const char *summary;
data_cmd_fn_t fn;
};
static struct data_cmd data_cmds[];
#define for_each_cmd(cmd) \
for (cmd = data_cmds; cmd && cmd->name; cmd++)
static const struct option data_options[] = {
OPT_END()
};
static const char * const data_usage[] = {
"perf data [<common options>] <command> [<options>]",
NULL
};
static void print_usage(void)
{
struct data_cmd *cmd;
printf("Usage:\n");
printf("\t%s\n\n", data_usage[0]);
printf("\tAvailable commands:\n");
for_each_cmd(cmd) {
printf("\t %s\t- %s\n", cmd->name, cmd->summary);
}
printf("\n");
}
static const char * const data_convert_usage[] = {
"perf data convert [<options>]",
NULL
};
static int cmd_data_convert(int argc, const char **argv,
const char *prefix __maybe_unused)
{
const char *to_ctf = NULL;
const struct option options[] = {
OPT_INCR('v', "verbose", &verbose, "be more verbose"),
OPT_STRING('i', "input", &input_name, "file", "input file name"),
#ifdef HAVE_LIBBABELTRACE_SUPPORT
OPT_STRING(0, "to-ctf", &to_ctf, NULL, "Convert to CTF format"),
#endif
OPT_END()
};
#ifndef HAVE_LIBBABELTRACE_SUPPORT
pr_err("No conversion support compiled in.\n");
return -1;
#endif
argc = parse_options(argc, argv, options,
data_convert_usage, 0);
if (argc) {
usage_with_options(data_convert_usage, options);
return -1;
}
if (to_ctf) {
#ifdef HAVE_LIBBABELTRACE_SUPPORT
return bt_convert__perf2ctf(input_name, to_ctf);
#else
pr_err("The libbabeltrace support is not compiled in.\n");
return -1;
#endif
}
return 0;
}
static struct data_cmd data_cmds[] = {
{ "convert", "converts data file between formats", cmd_data_convert },
{ NULL },
};
int cmd_data(int argc, const char **argv, const char *prefix)
{
struct data_cmd *cmd;
const char *cmdstr;
/* No command specified. */
if (argc < 2)
goto usage;
argc = parse_options(argc, argv, data_options, data_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (argc < 1)
goto usage;
cmdstr = argv[0];
for_each_cmd(cmd) {
if (strcmp(cmd->name, cmdstr))
continue;
return cmd->fn(argc, argv, prefix);
}
pr_err("Unknown command: %s\n", cmdstr);
usage:
print_usage();
return -1;
}
...@@ -839,6 +839,8 @@ struct option __record_options[] = { ...@@ -839,6 +839,8 @@ struct option __record_options[] = {
"use per-thread mmaps"), "use per-thread mmaps"),
OPT_BOOLEAN('I', "intr-regs", &record.opts.sample_intr_regs, OPT_BOOLEAN('I', "intr-regs", &record.opts.sample_intr_regs,
"Sample machine registers on interrupt"), "Sample machine registers on interrupt"),
OPT_BOOLEAN(0, "running-time", &record.opts.running_time,
"Record running/enabled time of read (:S) events"),
OPT_END() OPT_END()
}; };
......
...@@ -1473,9 +1473,9 @@ static int perf_sched__read_events(struct perf_sched *sched, ...@@ -1473,9 +1473,9 @@ static int perf_sched__read_events(struct perf_sched *sched,
goto out_delete; goto out_delete;
} }
sched->nr_events = session->stats.nr_events[0]; sched->nr_events = session->evlist->stats.nr_events[0];
sched->nr_lost_events = session->stats.total_lost; sched->nr_lost_events = session->evlist->stats.total_lost;
sched->nr_lost_chunks = session->stats.nr_events[PERF_RECORD_LOST]; sched->nr_lost_chunks = session->evlist->stats.nr_events[PERF_RECORD_LOST];
} }
if (psession) if (psession)
......
...@@ -716,7 +716,7 @@ static void perf_event__process_sample(struct perf_tool *tool, ...@@ -716,7 +716,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
if (!machine) { if (!machine) {
pr_err("%u unprocessable samples recorded.\r", pr_err("%u unprocessable samples recorded.\r",
top->session->stats.nr_unprocessable_samples++); top->session->evlist->stats.nr_unprocessable_samples++);
return; return;
} }
...@@ -856,7 +856,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) ...@@ -856,7 +856,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
hists__inc_nr_events(evsel__hists(evsel), event->header.type); hists__inc_nr_events(evsel__hists(evsel), event->header.type);
machine__process_event(machine, event, &sample); machine__process_event(machine, event, &sample);
} else } else
++session->stats.nr_unknown_events; ++session->evlist->stats.nr_unknown_events;
next_event: next_event:
perf_evlist__mmap_consume(top->evlist, idx); perf_evlist__mmap_consume(top->evlist, idx);
} }
......
...@@ -1229,6 +1229,10 @@ struct trace { ...@@ -1229,6 +1229,10 @@ struct trace {
const char *last_vfs_getname; const char *last_vfs_getname;
struct intlist *tid_list; struct intlist *tid_list;
struct intlist *pid_list; struct intlist *pid_list;
struct {
size_t nr;
pid_t *entries;
} filter_pids;
double duration_filter; double duration_filter;
double runtime_ms; double runtime_ms;
struct { struct {
...@@ -1840,7 +1844,11 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, ...@@ -1840,7 +1844,11 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel,
{ {
trace__printf_interrupted_entry(trace, sample); trace__printf_interrupted_entry(trace, sample);
trace__fprintf_tstamp(trace, sample->time, trace->output); trace__fprintf_tstamp(trace, sample->time, trace->output);
fprintf(trace->output, "(%9.9s): %s:", " ", evsel->name);
if (trace->trace_syscalls)
fprintf(trace->output, "( ): ");
fprintf(trace->output, "%s:", evsel->name);
if (evsel->tp_format) { if (evsel->tp_format) {
event_format__fprintf(evsel->tp_format, sample->cpu, event_format__fprintf(evsel->tp_format, sample->cpu,
...@@ -2084,10 +2092,39 @@ static int perf_evlist__add_pgfault(struct perf_evlist *evlist, ...@@ -2084,10 +2092,39 @@ static int perf_evlist__add_pgfault(struct perf_evlist *evlist,
return 0; return 0;
} }
static void trace__handle_event(struct trace *trace, union perf_event *event, struct perf_sample *sample)
{
const u32 type = event->header.type;
struct perf_evsel *evsel;
if (!trace->full_time && trace->base_time == 0)
trace->base_time = sample->time;
if (type != PERF_RECORD_SAMPLE) {
trace__process_event(trace, trace->host, event, sample);
return;
}
evsel = perf_evlist__id2evsel(trace->evlist, sample->id);
if (evsel == NULL) {
fprintf(trace->output, "Unknown tp ID %" PRIu64 ", skipping...\n", sample->id);
return;
}
if (evsel->attr.type == PERF_TYPE_TRACEPOINT &&
sample->raw_data == NULL) {
fprintf(trace->output, "%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);
} else {
tracepoint_handler handler = evsel->handler;
handler(trace, evsel, event, sample);
}
}
static int trace__run(struct trace *trace, int argc, const char **argv) static int trace__run(struct trace *trace, int argc, const char **argv)
{ {
struct perf_evlist *evlist = trace->evlist; struct perf_evlist *evlist = trace->evlist;
struct perf_evsel *evsel;
int err = -1, i; int err = -1, i;
unsigned long before; unsigned long before;
const bool forks = argc > 0; const bool forks = argc > 0;
...@@ -2147,6 +2184,22 @@ static int trace__run(struct trace *trace, int argc, const char **argv) ...@@ -2147,6 +2184,22 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
if (err < 0) if (err < 0)
goto out_error_open; goto out_error_open;
/*
* Better not use !target__has_task() here because we need to cover the
* case where no threads were specified in the command line, but a
* workload was, and in that case we will fill in the thread_map when
* we fork the workload in perf_evlist__prepare_workload.
*/
if (trace->filter_pids.nr > 0)
err = perf_evlist__set_filter_pids(evlist, trace->filter_pids.nr, trace->filter_pids.entries);
else if (evlist->threads->map[0] == -1)
err = perf_evlist__set_filter_pid(evlist, getpid());
if (err < 0) {
printf("err=%d,%s\n", -err, strerror(-err));
exit(1);
}
err = perf_evlist__mmap(evlist, trace->opts.mmap_pages, false); err = perf_evlist__mmap(evlist, trace->opts.mmap_pages, false);
if (err < 0) if (err < 0)
goto out_error_mmap; goto out_error_mmap;
...@@ -2166,8 +2219,6 @@ static int trace__run(struct trace *trace, int argc, const char **argv) ...@@ -2166,8 +2219,6 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
union perf_event *event; union perf_event *event;
while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
const u32 type = event->header.type;
tracepoint_handler handler;
struct perf_sample sample; struct perf_sample sample;
++trace->nr_events; ++trace->nr_events;
...@@ -2178,30 +2229,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv) ...@@ -2178,30 +2229,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
goto next_event; goto next_event;
} }
if (!trace->full_time && trace->base_time == 0) trace__handle_event(trace, event, &sample);
trace->base_time = sample.time;
if (type != PERF_RECORD_SAMPLE) {
trace__process_event(trace, trace->host, event, &sample);
continue;
}
evsel = perf_evlist__id2evsel(evlist, sample.id);
if (evsel == NULL) {
fprintf(trace->output, "Unknown tp ID %" PRIu64 ", skipping...\n", sample.id);
goto next_event;
}
if (evsel->attr.type == PERF_TYPE_TRACEPOINT &&
sample.raw_data == NULL) {
fprintf(trace->output, "%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);
goto next_event;
}
handler = evsel->handler;
handler(trace, evsel, event, &sample);
next_event: next_event:
perf_evlist__mmap_consume(evlist, i); perf_evlist__mmap_consume(evlist, i);
...@@ -2478,6 +2506,38 @@ static int trace__set_duration(const struct option *opt, const char *str, ...@@ -2478,6 +2506,38 @@ static int trace__set_duration(const struct option *opt, const char *str,
return 0; return 0;
} }
static int trace__set_filter_pids(const struct option *opt, const char *str,
int unset __maybe_unused)
{
int ret = -1;
size_t i;
struct trace *trace = opt->value;
/*
* FIXME: introduce a intarray class, plain parse csv and create a
* { int nr, int entries[] } struct...
*/
struct intlist *list = intlist__new(str);
if (list == NULL)
return -1;
i = trace->filter_pids.nr = intlist__nr_entries(list) + 1;
trace->filter_pids.entries = calloc(i, sizeof(pid_t));
if (trace->filter_pids.entries == NULL)
goto out;
trace->filter_pids.entries[0] = getpid();
for (i = 1; i < trace->filter_pids.nr; ++i)
trace->filter_pids.entries[i] = intlist__entry(list, i - 1)->i;
intlist__delete(list);
ret = 0;
out:
return ret;
}
static int trace__open_output(struct trace *trace, const char *filename) static int trace__open_output(struct trace *trace, const char *filename)
{ {
struct stat st; struct stat st;
...@@ -2568,6 +2628,8 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -2568,6 +2628,8 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
"trace events on existing process id"), "trace events on existing process id"),
OPT_STRING('t', "tid", &trace.opts.target.tid, "tid", OPT_STRING('t', "tid", &trace.opts.target.tid, "tid",
"trace events on existing thread id"), "trace events on existing thread id"),
OPT_CALLBACK(0, "filter-pids", &trace, "float",
"show only events with duration > N.M ms", trace__set_filter_pids),
OPT_BOOLEAN('a', "all-cpus", &trace.opts.target.system_wide, OPT_BOOLEAN('a', "all-cpus", &trace.opts.target.system_wide,
"system-wide collection from all CPUs"), "system-wide collection from all CPUs"),
OPT_STRING('C', "cpu", &trace.opts.target.cpu_list, "cpu", OPT_STRING('C', "cpu", &trace.opts.target.cpu_list, "cpu",
...@@ -2598,6 +2660,9 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -2598,6 +2660,9 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
int err; int err;
char bf[BUFSIZ]; char bf[BUFSIZ];
signal(SIGSEGV, sighandler_dump_stack);
signal(SIGFPE, sighandler_dump_stack);
trace.evlist = perf_evlist__new(); trace.evlist = perf_evlist__new();
if (trace.evlist == NULL) if (trace.evlist == NULL)
return -ENOMEM; return -ENOMEM;
......
...@@ -37,6 +37,7 @@ extern int cmd_test(int argc, const char **argv, const char *prefix); ...@@ -37,6 +37,7 @@ extern int cmd_test(int argc, const char **argv, const char *prefix);
extern int cmd_trace(int argc, const char **argv, const char *prefix); extern int cmd_trace(int argc, const char **argv, const char *prefix);
extern int cmd_inject(int argc, const char **argv, const char *prefix); extern int cmd_inject(int argc, const char **argv, const char *prefix);
extern int cmd_mem(int argc, const char **argv, const char *prefix); extern int cmd_mem(int argc, const char **argv, const char *prefix);
extern int cmd_data(int argc, const char **argv, const char *prefix);
extern int find_scripts(char **scripts_array, char **scripts_path_array); extern int find_scripts(char **scripts_array, char **scripts_path_array);
#endif #endif
...@@ -7,6 +7,7 @@ perf-archive mainporcelain common ...@@ -7,6 +7,7 @@ perf-archive mainporcelain common
perf-bench mainporcelain common perf-bench mainporcelain common
perf-buildid-cache mainporcelain common perf-buildid-cache mainporcelain common
perf-buildid-list mainporcelain common perf-buildid-list mainporcelain common
perf-data mainporcelain common
perf-diff mainporcelain common perf-diff mainporcelain common
perf-evlist mainporcelain common perf-evlist mainporcelain common
perf-inject mainporcelain common perf-inject mainporcelain common
......
...@@ -96,6 +96,17 @@ ifndef NO_LIBELF ...@@ -96,6 +96,17 @@ ifndef NO_LIBELF
FEATURE_CHECK_LDFLAGS-libdw-dwarf-unwind := $(LIBDW_LDFLAGS) -ldw FEATURE_CHECK_LDFLAGS-libdw-dwarf-unwind := $(LIBDW_LDFLAGS) -ldw
endif endif
ifndef NO_LIBBABELTRACE
# for linking with debug library, run like:
# make DEBUG=1 LIBBABELTRACE_DIR=/opt/libbabeltrace/
ifdef LIBBABELTRACE_DIR
LIBBABELTRACE_CFLAGS := -I$(LIBBABELTRACE_DIR)/include
LIBBABELTRACE_LDFLAGS := -L$(LIBBABELTRACE_DIR)/lib
endif
FEATURE_CHECK_CFLAGS-libbabeltrace := $(LIBBABELTRACE_CFLAGS)
FEATURE_CHECK_LDFLAGS-libbabeltrace := $(LIBBABELTRACE_LDFLAGS) -lbabeltrace-ctf
endif
# include ARCH specific config # include ARCH specific config
-include $(src-perf)/arch/$(ARCH)/Makefile -include $(src-perf)/arch/$(ARCH)/Makefile
...@@ -216,6 +227,7 @@ CORE_FEATURE_TESTS = \ ...@@ -216,6 +227,7 @@ CORE_FEATURE_TESTS = \
stackprotector-all \ stackprotector-all \
timerfd \ timerfd \
libdw-dwarf-unwind \ libdw-dwarf-unwind \
libbabeltrace \
zlib zlib
LIB_FEATURE_TESTS = \ LIB_FEATURE_TESTS = \
...@@ -231,6 +243,7 @@ LIB_FEATURE_TESTS = \ ...@@ -231,6 +243,7 @@ LIB_FEATURE_TESTS = \
libslang \ libslang \
libunwind \ libunwind \
libdw-dwarf-unwind \ libdw-dwarf-unwind \
libbabeltrace \
zlib zlib
VF_FEATURE_TESTS = \ VF_FEATURE_TESTS = \
...@@ -692,6 +705,18 @@ else ...@@ -692,6 +705,18 @@ else
NO_PERF_READ_VDSOX32 := 1 NO_PERF_READ_VDSOX32 := 1
endif endif
ifndef NO_LIBBABELTRACE
ifeq ($(feature-libbabeltrace), 0)
msg := $(warning No libbabeltrace found, disables 'perf data' CTF format support, please install libbabeltrace-devel/libbabeltrace-ctf-dev);
NO_LIBBABELTRACE := 1
else
CFLAGS += -DHAVE_LIBBABELTRACE_SUPPORT $(LIBBABELTRACE_CFLAGS)
LDFLAGS += $(LIBBABELTRACE_LDFLAGS)
EXTLIBS += -lbabeltrace-ctf
$(call detected,CONFIG_LIBBABELTRACE)
endif
endif
# Among the variables below, these: # Among the variables below, these:
# perfexecdir # perfexecdir
# template_dir # template_dir
......
...@@ -29,6 +29,7 @@ FILES= \ ...@@ -29,6 +29,7 @@ FILES= \
test-stackprotector-all.bin \ test-stackprotector-all.bin \
test-timerfd.bin \ test-timerfd.bin \
test-libdw-dwarf-unwind.bin \ test-libdw-dwarf-unwind.bin \
test-libbabeltrace.bin \
test-compile-32.bin \ test-compile-32.bin \
test-compile-x32.bin \ test-compile-x32.bin \
test-zlib.bin test-zlib.bin
...@@ -43,7 +44,7 @@ BUILD = $(CC) $(CFLAGS) -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@) $(LDFLAGS) ...@@ -43,7 +44,7 @@ BUILD = $(CC) $(CFLAGS) -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@) $(LDFLAGS)
############################### ###############################
test-all.bin: test-all.bin:
$(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz $(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz -lbabeltrace
test-hello.bin: test-hello.bin:
$(BUILD) $(BUILD)
...@@ -133,7 +134,10 @@ test-timerfd.bin: ...@@ -133,7 +134,10 @@ test-timerfd.bin:
$(BUILD) $(BUILD)
test-libdw-dwarf-unwind.bin: test-libdw-dwarf-unwind.bin:
$(BUILD) $(BUILD) # -ldw provided by $(FEATURE_CHECK_LDFLAGS-libdw-dwarf-unwind)
test-libbabeltrace.bin:
$(BUILD) # -lbabeltrace provided by $(FEATURE_CHECK_LDFLAGS-libbabeltrace)
test-sync-compare-and-swap.bin: test-sync-compare-and-swap.bin:
$(BUILD) -Werror $(BUILD) -Werror
......
...@@ -101,6 +101,10 @@ ...@@ -101,6 +101,10 @@
# include "test-pthread_attr_setaffinity_np.c" # include "test-pthread_attr_setaffinity_np.c"
#undef main #undef main
#define main main_test_libbabeltrace
# include "test-libbabeltrace.c"
#undef main
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
main_test_libpython(); main_test_libpython();
...@@ -126,6 +130,7 @@ int main(int argc, char *argv[]) ...@@ -126,6 +130,7 @@ int main(int argc, char *argv[])
main_test_sync_compare_and_swap(argc, argv); main_test_sync_compare_and_swap(argc, argv);
main_test_zlib(); main_test_zlib();
main_test_pthread_attr_setaffinity_np(); main_test_pthread_attr_setaffinity_np();
main_test_libbabeltrace();
return 0; return 0;
} }
#include <babeltrace/ctf-writer/writer.h>
int main(void)
{
bt_ctf_stream_class_get_packet_context_type((void *) 0);
return 0;
}
...@@ -62,6 +62,7 @@ static struct cmd_struct commands[] = { ...@@ -62,6 +62,7 @@ static struct cmd_struct commands[] = {
#endif #endif
{ "inject", cmd_inject, 0 }, { "inject", cmd_inject, 0 },
{ "mem", cmd_mem, 0 }, { "mem", cmd_mem, 0 },
{ "data", cmd_data, 0 },
}; };
struct pager_config { struct pager_config {
......
...@@ -53,6 +53,7 @@ struct record_opts { ...@@ -53,6 +53,7 @@ struct record_opts {
bool sample_time; bool sample_time;
bool period; bool period;
bool sample_intr_regs; bool sample_intr_regs;
bool running_time;
unsigned int freq; unsigned int freq;
unsigned int mmap_pages; unsigned int mmap_pages;
unsigned int user_freq; unsigned int user_freq;
......
...@@ -88,6 +88,8 @@ libperf-$(CONFIG_DWARF) += dwarf-aux.o ...@@ -88,6 +88,8 @@ libperf-$(CONFIG_DWARF) += dwarf-aux.o
libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
libperf-$(CONFIG_LIBBABELTRACE) += data-convert-bt.o
libperf-y += scripting-engines/ libperf-y += scripting-engines/
libperf-$(CONFIG_PERF_REGS) += perf_regs.o libperf-$(CONFIG_PERF_REGS) += perf_regs.o
......
/*
* CTF writing support via babeltrace.
*
* Copyright (C) 2014, Jiri Olsa <jolsa@redhat.com>
* Copyright (C) 2014, Sebastian Andrzej Siewior <bigeasy@linutronix.de>
*
* Released under the GPL v2. (and only v2, not any later version)
*/
#include <linux/compiler.h>
#include <babeltrace/ctf-writer/writer.h>
#include <babeltrace/ctf-writer/clock.h>
#include <babeltrace/ctf-writer/stream.h>
#include <babeltrace/ctf-writer/event.h>
#include <babeltrace/ctf-writer/event-types.h>
#include <babeltrace/ctf-writer/event-fields.h>
#include <babeltrace/ctf/events.h>
#include <traceevent/event-parse.h>
#include "asm/bug.h"
#include "data-convert-bt.h"
#include "session.h"
#include "util.h"
#include "debug.h"
#include "tool.h"
#include "evlist.h"
#include "evsel.h"
#include "machine.h"
#define pr_N(n, fmt, ...) \
eprintf(n, debug_data_convert, fmt, ##__VA_ARGS__)
#define pr(fmt, ...) pr_N(1, pr_fmt(fmt), ##__VA_ARGS__)
#define pr2(fmt, ...) pr_N(2, pr_fmt(fmt), ##__VA_ARGS__)
#define pr_time2(t, fmt, ...) pr_time_N(2, debug_data_convert, t, pr_fmt(fmt), ##__VA_ARGS__)
struct evsel_priv {
struct bt_ctf_event_class *event_class;
};
struct ctf_writer {
/* writer primitives */
struct bt_ctf_writer *writer;
struct bt_ctf_stream *stream;
struct bt_ctf_stream_class *stream_class;
struct bt_ctf_clock *clock;
/* data types */
union {
struct {
struct bt_ctf_field_type *s64;
struct bt_ctf_field_type *u64;
struct bt_ctf_field_type *s32;
struct bt_ctf_field_type *u32;
struct bt_ctf_field_type *string;
struct bt_ctf_field_type *u64_hex;
};
struct bt_ctf_field_type *array[6];
} data;
};
struct convert {
struct perf_tool tool;
struct ctf_writer writer;
u64 events_size;
u64 events_count;
};
static int value_set(struct bt_ctf_field_type *type,
struct bt_ctf_event *event,
const char *name, u64 val)
{
struct bt_ctf_field *field;
bool sign = bt_ctf_field_type_integer_get_signed(type);
int ret;
field = bt_ctf_field_create(type);
if (!field) {
pr_err("failed to create a field %s\n", name);
return -1;
}
if (sign) {
ret = bt_ctf_field_signed_integer_set_value(field, val);
if (ret) {
pr_err("failed to set field value %s\n", name);
goto err;
}
} else {
ret = bt_ctf_field_unsigned_integer_set_value(field, val);
if (ret) {
pr_err("failed to set field value %s\n", name);
goto err;
}
}
ret = bt_ctf_event_set_payload(event, name, field);
if (ret) {
pr_err("failed to set payload %s\n", name);
goto err;
}
pr2(" SET [%s = %" PRIu64 "]\n", name, val);
err:
bt_ctf_field_put(field);
return ret;
}
#define __FUNC_VALUE_SET(_name, _val_type) \
static __maybe_unused int value_set_##_name(struct ctf_writer *cw, \
struct bt_ctf_event *event, \
const char *name, \
_val_type val) \
{ \
struct bt_ctf_field_type *type = cw->data._name; \
return value_set(type, event, name, (u64) val); \
}
#define FUNC_VALUE_SET(_name) __FUNC_VALUE_SET(_name, _name)
FUNC_VALUE_SET(s32)
FUNC_VALUE_SET(u32)
FUNC_VALUE_SET(s64)
FUNC_VALUE_SET(u64)
__FUNC_VALUE_SET(u64_hex, u64)
static int add_generic_values(struct ctf_writer *cw,
struct bt_ctf_event *event,
struct perf_evsel *evsel,
struct perf_sample *sample)
{
u64 type = evsel->attr.sample_type;
int ret;
/*
* missing:
* PERF_SAMPLE_TIME - not needed as we have it in
* ctf event header
* PERF_SAMPLE_READ - TODO
* PERF_SAMPLE_CALLCHAIN - TODO
* PERF_SAMPLE_RAW - tracepoint fields are handled separately
* PERF_SAMPLE_BRANCH_STACK - TODO
* PERF_SAMPLE_REGS_USER - TODO
* PERF_SAMPLE_STACK_USER - TODO
*/
if (type & PERF_SAMPLE_IP) {
ret = value_set_u64_hex(cw, event, "perf_ip", sample->ip);
if (ret)
return -1;
}
if (type & PERF_SAMPLE_TID) {
ret = value_set_s32(cw, event, "perf_tid", sample->tid);
if (ret)
return -1;
ret = value_set_s32(cw, event, "perf_pid", sample->pid);
if (ret)
return -1;
}
if ((type & PERF_SAMPLE_ID) ||
(type & PERF_SAMPLE_IDENTIFIER)) {
ret = value_set_u64(cw, event, "perf_id", sample->id);
if (ret)
return -1;
}
if (type & PERF_SAMPLE_STREAM_ID) {
ret = value_set_u64(cw, event, "perf_stream_id", sample->stream_id);
if (ret)
return -1;
}
if (type & PERF_SAMPLE_CPU) {
ret = value_set_u32(cw, event, "perf_cpu", sample->cpu);
if (ret)
return -1;
}
if (type & PERF_SAMPLE_PERIOD) {
ret = value_set_u64(cw, event, "perf_period", sample->period);
if (ret)
return -1;
}
if (type & PERF_SAMPLE_WEIGHT) {
ret = value_set_u64(cw, event, "perf_weight", sample->weight);
if (ret)
return -1;
}
if (type & PERF_SAMPLE_DATA_SRC) {
ret = value_set_u64(cw, event, "perf_data_src",
sample->data_src);
if (ret)
return -1;
}
if (type & PERF_SAMPLE_TRANSACTION) {
ret = value_set_u64(cw, event, "perf_transaction",
sample->transaction);
if (ret)
return -1;
}
return 0;
}
static int process_sample_event(struct perf_tool *tool,
union perf_event *_event __maybe_unused,
struct perf_sample *sample,
struct perf_evsel *evsel,
struct machine *machine __maybe_unused)
{
struct convert *c = container_of(tool, struct convert, tool);
struct evsel_priv *priv = evsel->priv;
struct ctf_writer *cw = &c->writer;
struct bt_ctf_event_class *event_class;
struct bt_ctf_event *event;
int ret;
if (WARN_ONCE(!priv, "Failed to setup all events.\n"))
return 0;
event_class = priv->event_class;
/* update stats */
c->events_count++;
c->events_size += _event->header.size;
pr_time2(sample->time, "sample %" PRIu64 "\n", c->events_count);
event = bt_ctf_event_create(event_class);
if (!event) {
pr_err("Failed to create an CTF event\n");
return -1;
}
bt_ctf_clock_set_time(cw->clock, sample->time);
ret = add_generic_values(cw, event, evsel, sample);
if (ret)
return -1;
bt_ctf_stream_append_event(cw->stream, event);
bt_ctf_event_put(event);
return 0;
}
static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel,
struct bt_ctf_event_class *event_class)
{
u64 type = evsel->attr.sample_type;
/*
* missing:
* PERF_SAMPLE_TIME - not needed as we have it in
* ctf event header
* PERF_SAMPLE_READ - TODO
* PERF_SAMPLE_CALLCHAIN - TODO
* PERF_SAMPLE_RAW - tracepoint fields are handled separately
* PERF_SAMPLE_BRANCH_STACK - TODO
* PERF_SAMPLE_REGS_USER - TODO
* PERF_SAMPLE_STACK_USER - TODO
*/
#define ADD_FIELD(cl, t, n) \
do { \
pr2(" field '%s'\n", n); \
if (bt_ctf_event_class_add_field(cl, t, n)) { \
pr_err("Failed to add field '%s;\n", n); \
return -1; \
} \
} while (0)
if (type & PERF_SAMPLE_IP)
ADD_FIELD(event_class, cw->data.u64_hex, "perf_ip");
if (type & PERF_SAMPLE_TID) {
ADD_FIELD(event_class, cw->data.s32, "perf_tid");
ADD_FIELD(event_class, cw->data.s32, "perf_pid");
}
if ((type & PERF_SAMPLE_ID) ||
(type & PERF_SAMPLE_IDENTIFIER))
ADD_FIELD(event_class, cw->data.u64, "perf_id");
if (type & PERF_SAMPLE_STREAM_ID)
ADD_FIELD(event_class, cw->data.u64, "perf_stream_id");
if (type & PERF_SAMPLE_CPU)
ADD_FIELD(event_class, cw->data.u32, "perf_cpu");
if (type & PERF_SAMPLE_PERIOD)
ADD_FIELD(event_class, cw->data.u64, "perf_period");
if (type & PERF_SAMPLE_WEIGHT)
ADD_FIELD(event_class, cw->data.u64, "perf_weight");
if (type & PERF_SAMPLE_DATA_SRC)
ADD_FIELD(event_class, cw->data.u64, "perf_data_src");
if (type & PERF_SAMPLE_TRANSACTION)
ADD_FIELD(event_class, cw->data.u64, "perf_transaction");
#undef ADD_FIELD
return 0;
}
static int add_event(struct ctf_writer *cw, struct perf_evsel *evsel)
{
struct bt_ctf_event_class *event_class;
struct evsel_priv *priv;
const char *name = perf_evsel__name(evsel);
int ret;
pr("Adding event '%s' (type %d)\n", name, evsel->attr.type);
event_class = bt_ctf_event_class_create(name);
if (!event_class)
return -1;
ret = add_generic_types(cw, evsel, event_class);
if (ret)
goto err;
ret = bt_ctf_stream_class_add_event_class(cw->stream_class, event_class);
if (ret) {
pr("Failed to add event class into stream.\n");
goto err;
}
priv = malloc(sizeof(*priv));
if (!priv)
goto err;
priv->event_class = event_class;
evsel->priv = priv;
return 0;
err:
bt_ctf_event_class_put(event_class);
pr_err("Failed to add event '%s'.\n", name);
return -1;
}
static int setup_events(struct ctf_writer *cw, struct perf_session *session)
{
struct perf_evlist *evlist = session->evlist;
struct perf_evsel *evsel;
int ret;
evlist__for_each(evlist, evsel) {
ret = add_event(cw, evsel);
if (ret)
return ret;
}
return 0;
}
static int ctf_writer__setup_env(struct ctf_writer *cw,
struct perf_session *session)
{
struct perf_header *header = &session->header;
struct bt_ctf_writer *writer = cw->writer;
#define ADD(__n, __v) \
do { \
if (bt_ctf_writer_add_environment_field(writer, __n, __v)) \
return -1; \
} while (0)
ADD("host", header->env.hostname);
ADD("sysname", "Linux");
ADD("release", header->env.os_release);
ADD("version", header->env.version);
ADD("machine", header->env.arch);
ADD("domain", "kernel");
ADD("tracer_name", "perf");
#undef ADD
return 0;
}
static int ctf_writer__setup_clock(struct ctf_writer *cw)
{
struct bt_ctf_clock *clock = cw->clock;
bt_ctf_clock_set_description(clock, "perf clock");
#define SET(__n, __v) \
do { \
if (bt_ctf_clock_set_##__n(clock, __v)) \
return -1; \
} while (0)
SET(frequency, 1000000000);
SET(offset_s, 0);
SET(offset, 0);
SET(precision, 10);
SET(is_absolute, 0);
#undef SET
return 0;
}
static struct bt_ctf_field_type *create_int_type(int size, bool sign, bool hex)
{
struct bt_ctf_field_type *type;
type = bt_ctf_field_type_integer_create(size);
if (!type)
return NULL;
if (sign &&
bt_ctf_field_type_integer_set_signed(type, 1))
goto err;
if (hex &&
bt_ctf_field_type_integer_set_base(type, BT_CTF_INTEGER_BASE_HEXADECIMAL))
goto err;
pr2("Created type: INTEGER %d-bit %ssigned %s\n",
size, sign ? "un" : "", hex ? "hex" : "");
return type;
err:
bt_ctf_field_type_put(type);
return NULL;
}
static void ctf_writer__cleanup_data(struct ctf_writer *cw)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(cw->data.array); i++)
bt_ctf_field_type_put(cw->data.array[i]);
}
static int ctf_writer__init_data(struct ctf_writer *cw)
{
#define CREATE_INT_TYPE(type, size, sign, hex) \
do { \
(type) = create_int_type(size, sign, hex); \
if (!(type)) \
goto err; \
} while (0)
CREATE_INT_TYPE(cw->data.s64, 64, true, false);
CREATE_INT_TYPE(cw->data.u64, 64, false, false);
CREATE_INT_TYPE(cw->data.s32, 32, true, false);
CREATE_INT_TYPE(cw->data.u32, 32, false, false);
CREATE_INT_TYPE(cw->data.u64_hex, 64, false, true);
cw->data.string = bt_ctf_field_type_string_create();
if (cw->data.string)
return 0;
err:
ctf_writer__cleanup_data(cw);
pr_err("Failed to create data types.\n");
return -1;
}
static void ctf_writer__cleanup(struct ctf_writer *cw)
{
ctf_writer__cleanup_data(cw);
bt_ctf_clock_put(cw->clock);
bt_ctf_stream_put(cw->stream);
bt_ctf_stream_class_put(cw->stream_class);
bt_ctf_writer_put(cw->writer);
/* and NULL all the pointers */
memset(cw, 0, sizeof(*cw));
}
static int ctf_writer__init(struct ctf_writer *cw, const char *path)
{
struct bt_ctf_writer *writer;
struct bt_ctf_stream_class *stream_class;
struct bt_ctf_stream *stream;
struct bt_ctf_clock *clock;
/* CTF writer */
writer = bt_ctf_writer_create(path);
if (!writer)
goto err;
cw->writer = writer;
/* CTF clock */
clock = bt_ctf_clock_create("perf_clock");
if (!clock) {
pr("Failed to create CTF clock.\n");
goto err_cleanup;
}
cw->clock = clock;
if (ctf_writer__setup_clock(cw)) {
pr("Failed to setup CTF clock.\n");
goto err_cleanup;
}
/* CTF stream class */
stream_class = bt_ctf_stream_class_create("perf_stream");
if (!stream_class) {
pr("Failed to create CTF stream class.\n");
goto err_cleanup;
}
cw->stream_class = stream_class;
/* CTF clock stream setup */
if (bt_ctf_stream_class_set_clock(stream_class, clock)) {
pr("Failed to assign CTF clock to stream class.\n");
goto err_cleanup;
}
if (ctf_writer__init_data(cw))
goto err_cleanup;
/* CTF stream instance */
stream = bt_ctf_writer_create_stream(writer, stream_class);
if (!stream) {
pr("Failed to create CTF stream.\n");
goto err_cleanup;
}
cw->stream = stream;
/* CTF clock writer setup */
if (bt_ctf_writer_add_clock(writer, clock)) {
pr("Failed to assign CTF clock to writer.\n");
goto err_cleanup;
}
return 0;
err_cleanup:
ctf_writer__cleanup(cw);
err:
pr_err("Failed to setup CTF writer.\n");
return -1;
}
int bt_convert__perf2ctf(const char *input, const char *path)
{
struct perf_session *session;
struct perf_data_file file = {
.path = input,
.mode = PERF_DATA_MODE_READ,
};
struct convert c = {
.tool = {
.sample = process_sample_event,
.mmap = perf_event__process_mmap,
.mmap2 = perf_event__process_mmap2,
.comm = perf_event__process_comm,
.exit = perf_event__process_exit,
.fork = perf_event__process_fork,
.lost = perf_event__process_lost,
.tracing_data = perf_event__process_tracing_data,
.build_id = perf_event__process_build_id,
.ordered_events = true,
.ordering_requires_timestamps = true,
},
};
struct ctf_writer *cw = &c.writer;
int err = -1;
/* CTF writer */
if (ctf_writer__init(cw, path))
return -1;
/* perf.data session */
session = perf_session__new(&file, 0, NULL);
if (!session)
goto free_writer;
/* CTF writer env/clock setup */
if (ctf_writer__setup_env(cw, session))
goto free_session;
/* CTF events setup */
if (setup_events(cw, session))
goto free_session;
err = perf_session__process_events(session, &c.tool);
if (!err)
err = bt_ctf_stream_flush(cw->stream);
fprintf(stderr,
"[ perf data convert: Converted '%s' into CTF data '%s' ]\n",
file.path, path);
fprintf(stderr,
"[ perf data convert: Converted and wrote %.3f MB (%" PRIu64 " samples) ]\n",
(double) c.events_size / 1024.0 / 1024.0,
c.events_count);
/* its all good */
free_session:
perf_session__delete(session);
free_writer:
ctf_writer__cleanup(cw);
return err;
}
#ifndef __DATA_CONVERT_BT_H
#define __DATA_CONVERT_BT_H
#ifdef HAVE_LIBBABELTRACE_SUPPORT
int bt_convert__perf2ctf(const char *input_name, const char *to_ctf);
#endif /* HAVE_LIBBABELTRACE_SUPPORT */
#endif /* __DATA_CONVERT_BT_H */
...@@ -20,6 +20,7 @@ int verbose; ...@@ -20,6 +20,7 @@ int verbose;
bool dump_trace = false, quiet = false; bool dump_trace = false, quiet = false;
int debug_ordered_events; int debug_ordered_events;
static int redirect_to_stderr; static int redirect_to_stderr;
int debug_data_convert;
static int _eprintf(int level, int var, const char *fmt, va_list args) static int _eprintf(int level, int var, const char *fmt, va_list args)
{ {
...@@ -147,6 +148,7 @@ static struct debug_variable { ...@@ -147,6 +148,7 @@ static struct debug_variable {
{ .name = "verbose", .ptr = &verbose }, { .name = "verbose", .ptr = &verbose },
{ .name = "ordered-events", .ptr = &debug_ordered_events}, { .name = "ordered-events", .ptr = &debug_ordered_events},
{ .name = "stderr", .ptr = &redirect_to_stderr}, { .name = "stderr", .ptr = &redirect_to_stderr},
{ .name = "data-convert", .ptr = &debug_data_convert },
{ .name = NULL, } { .name = NULL, }
}; };
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
extern int verbose; extern int verbose;
extern bool quiet, dump_trace; extern bool quiet, dump_trace;
extern int debug_ordered_events; extern int debug_ordered_events;
extern int debug_data_convert;
#ifndef pr_fmt #ifndef pr_fmt
#define pr_fmt(fmt) fmt #define pr_fmt(fmt) fmt
......
...@@ -615,7 +615,7 @@ size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) ...@@ -615,7 +615,7 @@ size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp)
else else
s = ""; s = "";
return fprintf(fp, "%s: %s:%d\n", s, event->comm.comm, event->comm.tid); return fprintf(fp, "%s: %s:%d/%d\n", s, event->comm.comm, event->comm.pid, event->comm.tid);
} }
int perf_event__process_comm(struct perf_tool *tool __maybe_unused, int perf_event__process_comm(struct perf_tool *tool __maybe_unused,
......
...@@ -1085,6 +1085,38 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter) ...@@ -1085,6 +1085,38 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter)
return err; return err;
} }
int perf_evlist__set_filter_pids(struct perf_evlist *evlist, size_t npids, pid_t *pids)
{
char *filter;
int ret = -1;
size_t i;
for (i = 0; i < npids; ++i) {
if (i == 0) {
if (asprintf(&filter, "common_pid != %d", pids[i]) < 0)
return -1;
} else {
char *tmp;
if (asprintf(&tmp, "%s && common_pid != %d", filter, pids[i]) < 0)
goto out_free;
free(filter);
filter = tmp;
}
}
ret = perf_evlist__set_filter(evlist, filter);
out_free:
free(filter);
return ret;
}
int perf_evlist__set_filter_pid(struct perf_evlist *evlist, pid_t pid)
{
return perf_evlist__set_filter_pids(evlist, 1, &pid);
}
bool perf_evlist__valid_sample_type(struct perf_evlist *evlist) bool perf_evlist__valid_sample_type(struct perf_evlist *evlist)
{ {
struct perf_evsel *pos; struct perf_evsel *pos;
......
...@@ -51,6 +51,7 @@ struct perf_evlist { ...@@ -51,6 +51,7 @@ struct perf_evlist {
struct thread_map *threads; struct thread_map *threads;
struct cpu_map *cpus; struct cpu_map *cpus;
struct perf_evsel *selected; struct perf_evsel *selected;
struct events_stats stats;
}; };
struct perf_evsel_str_handler { struct perf_evsel_str_handler {
...@@ -77,6 +78,8 @@ int perf_evlist__add_newtp(struct perf_evlist *evlist, ...@@ -77,6 +78,8 @@ int perf_evlist__add_newtp(struct perf_evlist *evlist,
const char *sys, const char *name, void *handler); const char *sys, const char *name, void *handler);
int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter); int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter);
int perf_evlist__set_filter_pid(struct perf_evlist *evlist, pid_t pid);
int perf_evlist__set_filter_pids(struct perf_evlist *evlist, size_t npids, pid_t *pids);
struct perf_evsel * struct perf_evsel *
perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id); perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id);
......
...@@ -734,6 +734,12 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) ...@@ -734,6 +734,12 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
if (opts->sample_transaction) if (opts->sample_transaction)
perf_evsel__set_sample_bit(evsel, TRANSACTION); perf_evsel__set_sample_bit(evsel, TRANSACTION);
if (opts->running_time) {
evsel->attr.read_format |=
PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING;
}
/* /*
* XXX see the function comment above * XXX see the function comment above
* *
......
...@@ -166,7 +166,7 @@ static int __ordered_events__flush(struct perf_session *s, ...@@ -166,7 +166,7 @@ static int __ordered_events__flush(struct perf_session *s,
struct ui_progress prog; struct ui_progress prog;
int ret; int ret;
if (!tool->ordered_events || !limit) if (!limit)
return 0; return 0;
if (show_progress) if (show_progress)
...@@ -216,6 +216,9 @@ int ordered_events__flush(struct perf_session *s, struct perf_tool *tool, ...@@ -216,6 +216,9 @@ int ordered_events__flush(struct perf_session *s, struct perf_tool *tool,
}; };
int err; int err;
if (oe->nr_events == 0)
return 0;
switch (how) { switch (how) {
case OE_FLUSH__FINAL: case OE_FLUSH__FINAL:
oe->next_flush = ULLONG_MAX; oe->next_flush = ULLONG_MAX;
......
...@@ -537,7 +537,7 @@ int perf_session_queue_event(struct perf_session *s, union perf_event *event, ...@@ -537,7 +537,7 @@ int perf_session_queue_event(struct perf_session *s, union perf_event *event,
pr_oe_time(oe->last_flush, "last flush, last_flush_type %d\n", pr_oe_time(oe->last_flush, "last flush, last_flush_type %d\n",
oe->last_flush_type); oe->last_flush_type);
s->stats.nr_unordered_events++; s->evlist->stats.nr_unordered_events++;
} }
new = ordered_events__new(oe, timestamp, event); new = ordered_events__new(oe, timestamp, event);
...@@ -688,14 +688,14 @@ static void stack_user__printf(struct stack_dump *dump) ...@@ -688,14 +688,14 @@ static void stack_user__printf(struct stack_dump *dump)
dump->size, dump->offset); dump->size, dump->offset);
} }
static void perf_session__print_tstamp(struct perf_session *session, static void perf_evlist__print_tstamp(struct perf_evlist *evlist,
union perf_event *event, union perf_event *event,
struct perf_sample *sample) struct perf_sample *sample)
{ {
u64 sample_type = __perf_evlist__combined_sample_type(session->evlist); u64 sample_type = __perf_evlist__combined_sample_type(evlist);
if (event->header.type != PERF_RECORD_SAMPLE && if (event->header.type != PERF_RECORD_SAMPLE &&
!perf_evlist__sample_id_all(session->evlist)) { !perf_evlist__sample_id_all(evlist)) {
fputs("-1 -1 ", stdout); fputs("-1 -1 ", stdout);
return; return;
} }
...@@ -737,7 +737,7 @@ static void sample_read__printf(struct perf_sample *sample, u64 read_format) ...@@ -737,7 +737,7 @@ static void sample_read__printf(struct perf_sample *sample, u64 read_format)
sample->read.one.id, sample->read.one.value); sample->read.one.id, sample->read.one.value);
} }
static void dump_event(struct perf_session *session, union perf_event *event, static void dump_event(struct perf_evlist *evlist, union perf_event *event,
u64 file_offset, struct perf_sample *sample) u64 file_offset, struct perf_sample *sample)
{ {
if (!dump_trace) if (!dump_trace)
...@@ -749,7 +749,7 @@ static void dump_event(struct perf_session *session, union perf_event *event, ...@@ -749,7 +749,7 @@ static void dump_event(struct perf_session *session, union perf_event *event,
trace_event(event); trace_event(event);
if (sample) if (sample)
perf_session__print_tstamp(session, event, sample); perf_evlist__print_tstamp(evlist, event, sample);
printf("%#" PRIx64 " [%#x]: PERF_RECORD_%s", file_offset, printf("%#" PRIx64 " [%#x]: PERF_RECORD_%s", file_offset,
event->header.size, perf_event__name(event->header.type)); event->header.size, perf_event__name(event->header.type));
...@@ -797,8 +797,7 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event, ...@@ -797,8 +797,7 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
sample_read__printf(sample, evsel->attr.read_format); sample_read__printf(sample, evsel->attr.read_format);
} }
static struct machine * static struct machine *machines__find_for_cpumode(struct machines *machines,
perf_session__find_machine_for_cpumode(struct perf_session *session,
union perf_event *event, union perf_event *event,
struct perf_sample *sample) struct perf_sample *sample)
{ {
...@@ -816,26 +815,24 @@ static struct machine * ...@@ -816,26 +815,24 @@ static struct machine *
else else
pid = sample->pid; pid = sample->pid;
machine = perf_session__find_machine(session, pid); machine = machines__find(machines, pid);
if (!machine) if (!machine)
machine = perf_session__findnew_machine(session, machine = machines__find(machines, DEFAULT_GUEST_KERNEL_ID);
DEFAULT_GUEST_KERNEL_ID);
return machine; return machine;
} }
return &session->machines.host; return &machines->host;
} }
static int deliver_sample_value(struct perf_session *session, static int deliver_sample_value(struct perf_evlist *evlist,
struct perf_tool *tool, struct perf_tool *tool,
union perf_event *event, union perf_event *event,
struct perf_sample *sample, struct perf_sample *sample,
struct sample_read_value *v, struct sample_read_value *v,
struct machine *machine) struct machine *machine)
{ {
struct perf_sample_id *sid; struct perf_sample_id *sid = perf_evlist__id2sid(evlist, v->id);
sid = perf_evlist__id2sid(session->evlist, v->id);
if (sid) { if (sid) {
sample->id = v->id; sample->id = v->id;
sample->period = v->value - sid->period; sample->period = v->value - sid->period;
...@@ -843,14 +840,14 @@ static int deliver_sample_value(struct perf_session *session, ...@@ -843,14 +840,14 @@ static int deliver_sample_value(struct perf_session *session,
} }
if (!sid || sid->evsel == NULL) { if (!sid || sid->evsel == NULL) {
++session->stats.nr_unknown_id; ++evlist->stats.nr_unknown_id;
return 0; return 0;
} }
return tool->sample(tool, event, sample, sid->evsel, machine); return tool->sample(tool, event, sample, sid->evsel, machine);
} }
static int deliver_sample_group(struct perf_session *session, static int deliver_sample_group(struct perf_evlist *evlist,
struct perf_tool *tool, struct perf_tool *tool,
union perf_event *event, union perf_event *event,
struct perf_sample *sample, struct perf_sample *sample,
...@@ -860,7 +857,7 @@ static int deliver_sample_group(struct perf_session *session, ...@@ -860,7 +857,7 @@ static int deliver_sample_group(struct perf_session *session,
u64 i; u64 i;
for (i = 0; i < sample->read.group.nr; i++) { for (i = 0; i < sample->read.group.nr; i++) {
ret = deliver_sample_value(session, tool, event, sample, ret = deliver_sample_value(evlist, tool, event, sample,
&sample->read.group.values[i], &sample->read.group.values[i],
machine); machine);
if (ret) if (ret)
...@@ -871,7 +868,7 @@ static int deliver_sample_group(struct perf_session *session, ...@@ -871,7 +868,7 @@ static int deliver_sample_group(struct perf_session *session,
} }
static int static int
perf_session__deliver_sample(struct perf_session *session, perf_evlist__deliver_sample(struct perf_evlist *evlist,
struct perf_tool *tool, struct perf_tool *tool,
union perf_event *event, union perf_event *event,
struct perf_sample *sample, struct perf_sample *sample,
...@@ -888,10 +885,10 @@ perf_session__deliver_sample(struct perf_session *session, ...@@ -888,10 +885,10 @@ perf_session__deliver_sample(struct perf_session *session,
/* For PERF_SAMPLE_READ we have either single or group mode. */ /* For PERF_SAMPLE_READ we have either single or group mode. */
if (read_format & PERF_FORMAT_GROUP) if (read_format & PERF_FORMAT_GROUP)
return deliver_sample_group(session, tool, event, sample, return deliver_sample_group(evlist, tool, event, sample,
machine); machine);
else else
return deliver_sample_value(session, tool, event, sample, return deliver_sample_value(evlist, tool, event, sample,
&sample->read.one, machine); &sample->read.one, machine);
} }
...@@ -900,29 +897,28 @@ int perf_session__deliver_event(struct perf_session *session, ...@@ -900,29 +897,28 @@ int perf_session__deliver_event(struct perf_session *session,
struct perf_sample *sample, struct perf_sample *sample,
struct perf_tool *tool, u64 file_offset) struct perf_tool *tool, u64 file_offset)
{ {
struct perf_evlist *evlist = session->evlist;
struct perf_evsel *evsel; struct perf_evsel *evsel;
struct machine *machine; struct machine *machine;
dump_event(session, event, file_offset, sample); dump_event(evlist, event, file_offset, sample);
evsel = perf_evlist__id2evsel(session->evlist, sample->id); evsel = perf_evlist__id2evsel(evlist, sample->id);
machine = perf_session__find_machine_for_cpumode(session, event, machine = machines__find_for_cpumode(&session->machines, event, sample);
sample);
switch (event->header.type) { switch (event->header.type) {
case PERF_RECORD_SAMPLE: case PERF_RECORD_SAMPLE:
dump_sample(evsel, event, sample); dump_sample(evsel, event, sample);
if (evsel == NULL) { if (evsel == NULL) {
++session->stats.nr_unknown_id; ++evlist->stats.nr_unknown_id;
return 0; return 0;
} }
if (machine == NULL) { if (machine == NULL) {
++session->stats.nr_unprocessable_samples; ++evlist->stats.nr_unprocessable_samples;
return 0; return 0;
} }
return perf_session__deliver_sample(session, tool, event, return perf_evlist__deliver_sample(evlist, tool, event, sample, evsel, machine);
sample, evsel, machine);
case PERF_RECORD_MMAP: case PERF_RECORD_MMAP:
return tool->mmap(tool, event, sample, machine); return tool->mmap(tool, event, sample, machine);
case PERF_RECORD_MMAP2: case PERF_RECORD_MMAP2:
...@@ -935,7 +931,7 @@ int perf_session__deliver_event(struct perf_session *session, ...@@ -935,7 +931,7 @@ int perf_session__deliver_event(struct perf_session *session,
return tool->exit(tool, event, sample, machine); return tool->exit(tool, event, sample, machine);
case PERF_RECORD_LOST: case PERF_RECORD_LOST:
if (tool->lost == perf_event__process_lost) if (tool->lost == perf_event__process_lost)
session->stats.total_lost += event->lost.lost; evlist->stats.total_lost += event->lost.lost;
return tool->lost(tool, event, sample, machine); return tool->lost(tool, event, sample, machine);
case PERF_RECORD_READ: case PERF_RECORD_READ:
return tool->read(tool, event, sample, evsel, machine); return tool->read(tool, event, sample, evsel, machine);
...@@ -944,7 +940,7 @@ int perf_session__deliver_event(struct perf_session *session, ...@@ -944,7 +940,7 @@ int perf_session__deliver_event(struct perf_session *session,
case PERF_RECORD_UNTHROTTLE: case PERF_RECORD_UNTHROTTLE:
return tool->unthrottle(tool, event, sample, machine); return tool->unthrottle(tool, event, sample, machine);
default: default:
++session->stats.nr_unknown_events; ++evlist->stats.nr_unknown_events;
return -1; return -1;
} }
} }
...@@ -957,7 +953,7 @@ static s64 perf_session__process_user_event(struct perf_session *session, ...@@ -957,7 +953,7 @@ static s64 perf_session__process_user_event(struct perf_session *session,
int fd = perf_data_file__fd(session->file); int fd = perf_data_file__fd(session->file);
int err; int err;
dump_event(session, event, file_offset, NULL); dump_event(session->evlist, event, file_offset, NULL);
/* These events are processed right away */ /* These events are processed right away */
switch (event->header.type) { switch (event->header.type) {
...@@ -994,7 +990,7 @@ int perf_session__deliver_synth_event(struct perf_session *session, ...@@ -994,7 +990,7 @@ int perf_session__deliver_synth_event(struct perf_session *session,
struct perf_sample *sample, struct perf_sample *sample,
struct perf_tool *tool) struct perf_tool *tool)
{ {
events_stats__inc(&session->stats, event->header.type); events_stats__inc(&session->evlist->stats, event->header.type);
if (event->header.type >= PERF_RECORD_USER_TYPE_START) if (event->header.type >= PERF_RECORD_USER_TYPE_START)
return perf_session__process_user_event(session, event, tool, 0); return perf_session__process_user_event(session, event, tool, 0);
...@@ -1071,16 +1067,17 @@ static s64 perf_session__process_event(struct perf_session *session, ...@@ -1071,16 +1067,17 @@ static s64 perf_session__process_event(struct perf_session *session,
struct perf_tool *tool, struct perf_tool *tool,
u64 file_offset) u64 file_offset)
{ {
struct perf_evlist *evlist = session->evlist;
struct perf_sample sample; struct perf_sample sample;
int ret; int ret;
if (session->header.needs_swap) if (session->header.needs_swap)
event_swap(event, perf_evlist__sample_id_all(session->evlist)); event_swap(event, perf_evlist__sample_id_all(evlist));
if (event->header.type >= PERF_RECORD_HEADER_MAX) if (event->header.type >= PERF_RECORD_HEADER_MAX)
return -EINVAL; return -EINVAL;
events_stats__inc(&session->stats, event->header.type); events_stats__inc(&evlist->stats, event->header.type);
if (event->header.type >= PERF_RECORD_USER_TYPE_START) if (event->header.type >= PERF_RECORD_USER_TYPE_START)
return perf_session__process_user_event(session, event, tool, file_offset); return perf_session__process_user_event(session, event, tool, file_offset);
...@@ -1088,7 +1085,7 @@ static s64 perf_session__process_event(struct perf_session *session, ...@@ -1088,7 +1085,7 @@ static s64 perf_session__process_event(struct perf_session *session,
/* /*
* For all kernel events we get the sample data * For all kernel events we get the sample data
*/ */
ret = perf_evlist__parse_sample(session->evlist, event, &sample); ret = perf_evlist__parse_sample(evlist, event, &sample);
if (ret) if (ret)
return ret; return ret;
...@@ -1128,47 +1125,47 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se ...@@ -1128,47 +1125,47 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se
return thread; return thread;
} }
static void perf_session__warn_about_errors(const struct perf_session *session, static void perf_tool__warn_about_errors(const struct perf_tool *tool,
const struct perf_tool *tool) const struct events_stats *stats)
{ {
if (tool->lost == perf_event__process_lost && if (tool->lost == perf_event__process_lost &&
session->stats.nr_events[PERF_RECORD_LOST] != 0) { stats->nr_events[PERF_RECORD_LOST] != 0) {
ui__warning("Processed %d events and lost %d chunks!\n\n" ui__warning("Processed %d events and lost %d chunks!\n\n"
"Check IO/CPU overload!\n\n", "Check IO/CPU overload!\n\n",
session->stats.nr_events[0], stats->nr_events[0],
session->stats.nr_events[PERF_RECORD_LOST]); stats->nr_events[PERF_RECORD_LOST]);
} }
if (session->stats.nr_unknown_events != 0) { if (stats->nr_unknown_events != 0) {
ui__warning("Found %u unknown events!\n\n" ui__warning("Found %u unknown events!\n\n"
"Is this an older tool processing a perf.data " "Is this an older tool processing a perf.data "
"file generated by a more recent tool?\n\n" "file generated by a more recent tool?\n\n"
"If that is not the case, consider " "If that is not the case, consider "
"reporting to linux-kernel@vger.kernel.org.\n\n", "reporting to linux-kernel@vger.kernel.org.\n\n",
session->stats.nr_unknown_events); stats->nr_unknown_events);
} }
if (session->stats.nr_unknown_id != 0) { if (stats->nr_unknown_id != 0) {
ui__warning("%u samples with id not present in the header\n", ui__warning("%u samples with id not present in the header\n",
session->stats.nr_unknown_id); stats->nr_unknown_id);
} }
if (session->stats.nr_invalid_chains != 0) { if (stats->nr_invalid_chains != 0) {
ui__warning("Found invalid callchains!\n\n" ui__warning("Found invalid callchains!\n\n"
"%u out of %u events were discarded for this reason.\n\n" "%u out of %u events were discarded for this reason.\n\n"
"Consider reporting to linux-kernel@vger.kernel.org.\n\n", "Consider reporting to linux-kernel@vger.kernel.org.\n\n",
session->stats.nr_invalid_chains, stats->nr_invalid_chains,
session->stats.nr_events[PERF_RECORD_SAMPLE]); stats->nr_events[PERF_RECORD_SAMPLE]);
} }
if (session->stats.nr_unprocessable_samples != 0) { if (stats->nr_unprocessable_samples != 0) {
ui__warning("%u unprocessable samples recorded.\n" ui__warning("%u unprocessable samples recorded.\n"
"Do you have a KVM guest running and not using 'perf kvm'?\n", "Do you have a KVM guest running and not using 'perf kvm'?\n",
session->stats.nr_unprocessable_samples); stats->nr_unprocessable_samples);
} }
if (session->stats.nr_unordered_events != 0) if (stats->nr_unordered_events != 0)
ui__warning("%u out of order events recorded.\n", session->stats.nr_unordered_events); ui__warning("%u out of order events recorded.\n", stats->nr_unordered_events);
} }
volatile int session_done; volatile int session_done;
...@@ -1258,7 +1255,7 @@ static int __perf_session__process_pipe_events(struct perf_session *session, ...@@ -1258,7 +1255,7 @@ static int __perf_session__process_pipe_events(struct perf_session *session,
err = ordered_events__flush(session, tool, OE_FLUSH__FINAL); err = ordered_events__flush(session, tool, OE_FLUSH__FINAL);
out_err: out_err:
free(buf); free(buf);
perf_session__warn_about_errors(session, tool); perf_tool__warn_about_errors(tool, &session->evlist->stats);
ordered_events__free(&session->ordered_events); ordered_events__free(&session->ordered_events);
return err; return err;
} }
...@@ -1403,7 +1400,7 @@ static int __perf_session__process_events(struct perf_session *session, ...@@ -1403,7 +1400,7 @@ static int __perf_session__process_events(struct perf_session *session,
err = ordered_events__flush(session, tool, OE_FLUSH__FINAL); err = ordered_events__flush(session, tool, OE_FLUSH__FINAL);
out_err: out_err:
ui_progress__finish(); ui_progress__finish();
perf_session__warn_about_errors(session, tool); perf_tool__warn_about_errors(tool, &session->evlist->stats);
ordered_events__free(&session->ordered_events); ordered_events__free(&session->ordered_events);
session->one_mmap = false; session->one_mmap = false;
return err; return err;
...@@ -1488,7 +1485,7 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) ...@@ -1488,7 +1485,7 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
{ {
size_t ret = fprintf(fp, "Aggregated stats:\n"); size_t ret = fprintf(fp, "Aggregated stats:\n");
ret += events_stats__fprintf(&session->stats, fp); ret += events_stats__fprintf(&session->evlist->stats, fp);
return ret; return ret;
} }
......
...@@ -20,7 +20,6 @@ struct perf_session { ...@@ -20,7 +20,6 @@ struct perf_session {
struct machines machines; struct machines machines;
struct perf_evlist *evlist; struct perf_evlist *evlist;
struct trace_event tevent; struct trace_event tevent;
struct events_stats stats;
bool repipe; bool repipe;
bool one_mmap; bool one_mmap;
void *one_mmap_addr; void *one_mmap_addr;
......
...@@ -269,6 +269,13 @@ void dump_stack(void) ...@@ -269,6 +269,13 @@ void dump_stack(void)
void dump_stack(void) {} void dump_stack(void) {}
#endif #endif
void sighandler_dump_stack(int sig)
{
psignal(sig, "perf");
dump_stack();
exit(sig);
}
void get_term_dimensions(struct winsize *ws) void get_term_dimensions(struct winsize *ws)
{ {
char *s = getenv("LINES"); char *s = getenv("LINES");
......
...@@ -277,6 +277,7 @@ char *ltrim(char *s); ...@@ -277,6 +277,7 @@ char *ltrim(char *s);
char *rtrim(char *s); char *rtrim(char *s);
void dump_stack(void); void dump_stack(void);
void sighandler_dump_stack(int sig);
extern unsigned int page_size; extern unsigned int page_size;
extern int cacheline_size; extern int cacheline_size;
......
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