summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/perf/Documentation/perf-trace.txt53
-rw-r--r--tools/perf/Makefile13
-rw-r--r--tools/perf/builtin-record.c2
-rw-r--r--tools/perf/builtin-stat.c2
-rw-r--r--tools/perf/builtin-test.c120
-rw-r--r--tools/perf/builtin-top.c2
-rw-r--r--tools/perf/builtin-trace.c310
-rw-r--r--tools/perf/builtin.h1
-rw-r--r--tools/perf/command-list.txt1
-rw-r--r--tools/perf/config/feature-tests.mak11
-rw-r--r--tools/perf/perf.c3
-rw-r--r--tools/perf/util/cpumap.h11
-rw-r--r--tools/perf/util/dwarf-aux.c2
-rw-r--r--tools/perf/util/evlist.c61
-rw-r--r--tools/perf/util/evlist.h6
-rw-r--r--tools/perf/util/evsel.c88
-rw-r--r--tools/perf/util/evsel.h18
-rw-r--r--tools/perf/util/header.c6
-rw-r--r--tools/perf/util/hist.c9
-rw-r--r--tools/perf/util/parse-events.c46
-rw-r--r--tools/perf/util/python.c2
-rw-r--r--tools/perf/util/session.c6
22 files changed, 683 insertions, 90 deletions
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt
new file mode 100644
index 000000000000..3a2ae37310a9
--- /dev/null
+++ b/tools/perf/Documentation/perf-trace.txt
@@ -0,0 +1,53 @@
+perf-trace(1)
+=============
+
+NAME
+----
+perf-trace - strace inspired tool
+
+SYNOPSIS
+--------
+[verse]
+'perf trace'
+
+DESCRIPTION
+-----------
+This command will show the events associated with the target, initially
+syscalls, but other system events like pagefaults, task lifetime events,
+scheduling events, etc.
+
+Initially this is a live mode only tool, but eventually will work with
+perf.data files like the other tools, allowing a detached 'record' from
+analysis phases.
+
+OPTIONS
+-------
+
+--all-cpus::
+ System-wide collection from all CPUs.
+
+-p::
+--pid=::
+ Record events on existing process ID (comma separated list).
+
+--tid=::
+ Record events on existing thread ID (comma separated list).
+
+--uid=::
+ Record events in threads owned by uid. Name or number.
+
+--no-inherit::
+ Child tasks do not inherit counters.
+
+--mmap-pages=::
+ Number of mmap data pages. Must be a power of two.
+
+--cpu::
+Collect samples only on the list of CPUs provided. Multiple CPUs can be provided as a
+comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
+In per-thread mode with inheritance mode on (default), Events are captured only when
+the thread executes on the designated CPUs. Default is to monitor all CPUs.
+
+SEE ALSO
+--------
+linkperf:perf-record[1], linkperf:perf-script[1]
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 251dcd7fb5ac..e5e71e7d95a0 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -559,6 +559,19 @@ else
LIB_OBJS += $(OUTPUT)util/unwind.o
endif
+ifdef NO_LIBAUDIT
+ BASIC_CFLAGS += -DNO_LIBAUDIT_SUPPORT
+else
+ FLAGS_LIBAUDIT = $(ALL_CFLAGS) $(ALL_LDFLAGS) -laudit
+ ifneq ($(call try-cc,$(SOURCE_LIBAUDIT),$(FLAGS_LIBAUDIT)),y)
+ msg := $(warning No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev);
+ BASIC_CFLAGS += -DNO_LIBAUDIT_SUPPORT
+ else
+ BUILTIN_OBJS += $(OUTPUT)builtin-trace.o
+ EXTLIBS += -laudit
+ endif
+endif
+
ifdef NO_NEWT
BASIC_CFLAGS += -DNO_NEWT_SUPPORT
else
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 2cb74343de3e..f14cb5fdb91f 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -320,7 +320,7 @@ try_again:
}
}
- if (perf_evlist__set_filters(evlist)) {
+ if (perf_evlist__apply_filters(evlist)) {
error("failed to set filter with %d (%s)\n", errno,
strerror(errno));
rc = -1;
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index e0f65fe65944..e8cd4d81b06e 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -478,7 +478,7 @@ static int run_perf_stat(int argc __maybe_unused, const char **argv)
counter->supported = true;
}
- if (perf_evlist__set_filters(evsel_list)) {
+ if (perf_evlist__apply_filters(evsel_list)) {
error("failed to set filter with %d (%s)\n", errno,
strerror(errno));
return -1;
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c
index 78b47a75a7c9..484f26cc0c00 100644
--- a/tools/perf/builtin-test.c
+++ b/tools/perf/builtin-test.c
@@ -564,7 +564,7 @@ static int test__basic_mmap(void)
goto out_munmap;
}
- err = perf_evlist__parse_sample(evlist, event, &sample, false);
+ err = perf_evlist__parse_sample(evlist, event, &sample);
if (err) {
pr_err("Can't parse sample, err = %d\n", err);
goto out_munmap;
@@ -781,7 +781,7 @@ static int test__PERF_RECORD(void)
if (type < PERF_RECORD_MAX)
nr_events[type]++;
- err = perf_evlist__parse_sample(evlist, event, &sample, false);
+ err = perf_evlist__parse_sample(evlist, event, &sample);
if (err < 0) {
if (verbose)
perf_event__fprintf(event, stderr);
@@ -1289,6 +1289,118 @@ static int perf_evsel__tp_sched_test(void)
return ret;
}
+static int test__syscall_open_tp_fields(void)
+{
+ struct perf_record_opts opts = {
+ .target = {
+ .uid = UINT_MAX,
+ .uses_mmap = true,
+ },
+ .no_delay = true,
+ .freq = 1,
+ .mmap_pages = 256,
+ .raw_samples = true,
+ };
+ const char *filename = "/etc/passwd";
+ int flags = O_RDONLY | O_DIRECTORY;
+ struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
+ struct perf_evsel *evsel;
+ int err = -1, i, nr_events = 0, nr_polls = 0;
+
+ if (evlist == NULL) {
+ pr_debug("%s: perf_evlist__new\n", __func__);
+ goto out;
+ }
+
+ evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0);
+ if (evsel == NULL) {
+ pr_debug("%s: perf_evsel__newtp\n", __func__);
+ goto out_delete_evlist;
+ }
+
+ perf_evlist__add(evlist, evsel);
+
+ err = perf_evlist__create_maps(evlist, &opts.target);
+ if (err < 0) {
+ pr_debug("%s: perf_evlist__create_maps\n", __func__);
+ goto out_delete_evlist;
+ }
+
+ perf_evsel__config(evsel, &opts, evsel);
+
+ evlist->threads->map[0] = getpid();
+
+ err = perf_evlist__open(evlist);
+ if (err < 0) {
+ pr_debug("perf_evlist__open: %s\n", strerror(errno));
+ goto out_delete_evlist;
+ }
+
+ err = perf_evlist__mmap(evlist, UINT_MAX, false);
+ if (err < 0) {
+ pr_debug("perf_evlist__mmap: %s\n", strerror(errno));
+ goto out_delete_evlist;
+ }
+
+ perf_evlist__enable(evlist);
+
+ /*
+ * Generate the event:
+ */
+ open(filename, flags);
+
+ while (1) {
+ int before = nr_events;
+
+ for (i = 0; i < evlist->nr_mmaps; i++) {
+ union perf_event *event;
+
+ while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
+ const u32 type = event->header.type;
+ int tp_flags;
+ struct perf_sample sample;
+
+ ++nr_events;
+
+ if (type != PERF_RECORD_SAMPLE)
+ continue;
+
+ err = perf_evsel__parse_sample(evsel, event, &sample);
+ if (err) {
+ pr_err("Can't parse sample, err = %d\n", err);
+ goto out_munmap;
+ }
+
+ tp_flags = perf_evsel__intval(evsel, &sample, "flags");
+
+ if (flags != tp_flags) {
+ pr_debug("%s: Expected flags=%#x, got %#x\n",
+ __func__, flags, tp_flags);
+ goto out_munmap;
+ }
+
+ goto out_ok;
+ }
+ }
+
+ if (nr_events == before)
+ poll(evlist->pollfd, evlist->nr_fds, 10);
+
+ if (++nr_polls > 5) {
+ pr_debug("%s: no events!\n", __func__);
+ goto out_munmap;
+ }
+ }
+out_ok:
+ err = 0;
+out_munmap:
+ perf_evlist__munmap(evlist);
+out_delete_evlist:
+ perf_evlist__delete(evlist);
+out:
+ return err;
+}
+
static struct test {
const char *desc;
int (*func)(void);
@@ -1340,6 +1452,10 @@ static struct test {
.func = perf_evsel__tp_sched_test,
},
{
+ .desc = "Generate and check syscalls:sys_enter_open event fields",
+ .func = test__syscall_open_tp_fields,
+ },
+ {
.func = NULL,
},
};
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 5550754c05f2..e434a16bb5ac 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -823,7 +823,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
int ret;
while ((event = perf_evlist__mmap_read(top->evlist, idx)) != NULL) {
- ret = perf_evlist__parse_sample(top->evlist, event, &sample, false);
+ ret = perf_evlist__parse_sample(top->evlist, event, &sample);
if (ret) {
pr_err("Can't parse sample, err = %d\n", ret);
continue;
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
new file mode 100644
index 000000000000..8f113dab8bf1
--- /dev/null
+++ b/tools/perf/builtin-trace.c
@@ -0,0 +1,310 @@
+#include "builtin.h"
+#include "util/evlist.h"
+#include "util/parse-options.h"
+#include "util/thread_map.h"
+#include "event-parse.h"
+
+#include <libaudit.h>
+#include <stdlib.h>
+
+static struct syscall_fmt {
+ const char *name;
+ const char *alias;
+ bool errmsg;
+ bool timeout;
+} syscall_fmts[] = {
+ { .name = "arch_prctl", .errmsg = true, .alias = "prctl", },
+ { .name = "fstat", .errmsg = true, .alias = "newfstat", },
+ { .name = "fstatat", .errmsg = true, .alias = "newfstatat", },
+ { .name = "futex", .errmsg = true, },
+ { .name = "poll", .errmsg = true, .timeout = true, },
+ { .name = "ppoll", .errmsg = true, .timeout = true, },
+ { .name = "read", .errmsg = true, },
+ { .name = "recvfrom", .errmsg = true, },
+ { .name = "select", .errmsg = true, .timeout = true, },
+ { .name = "stat", .errmsg = true, .alias = "newstat", },
+};
+
+static int syscall_fmt__cmp(const void *name, const void *fmtp)
+{
+ const struct syscall_fmt *fmt = fmtp;
+ return strcmp(name, fmt->name);
+}
+
+static struct syscall_fmt *syscall_fmt__find(const char *name)
+{
+ const int nmemb = ARRAY_SIZE(syscall_fmts);
+ return bsearch(name, syscall_fmts, nmemb, sizeof(struct syscall_fmt), syscall_fmt__cmp);
+}
+
+struct syscall {
+ struct event_format *tp_format;
+ const char *name;
+ struct syscall_fmt *fmt;
+};
+
+struct trace {
+ int audit_machine;
+ struct {
+ int max;
+ struct syscall *table;
+ } syscalls;
+ struct perf_record_opts opts;
+};
+
+static int trace__read_syscall_info(struct trace *trace, int id)
+{
+ char tp_name[128];
+ struct syscall *sc;
+
+ if (id > trace->syscalls.max) {
+ struct syscall *nsyscalls = realloc(trace->syscalls.table, (id + 1) * sizeof(*sc));
+
+ if (nsyscalls == NULL)
+ return -1;
+
+ if (trace->syscalls.max != -1) {
+ memset(nsyscalls + trace->syscalls.max + 1, 0,
+ (id - trace->syscalls.max) * sizeof(*sc));
+ } else {
+ memset(nsyscalls, 0, (id + 1) * sizeof(*sc));
+ }
+
+ trace->syscalls.table = nsyscalls;
+ trace->syscalls.max = id;
+ }
+
+ sc = trace->syscalls.table + id;
+ sc->name = audit_syscall_to_name(id, trace->audit_machine);
+ if (sc->name == NULL)
+ return -1;
+
+ sc->fmt = syscall_fmt__find(sc->name);
+
+ snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
+ sc->tp_format = event_format__new("syscalls", tp_name);
+
+ if (sc->tp_format == NULL && sc->fmt && sc->fmt->alias) {
+ snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->fmt->alias);
+ sc->tp_format = event_format__new("syscalls", tp_name);
+ }
+
+ return sc->tp_format != NULL ? 0 : -1;
+}
+
+static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FILE *fp)
+{
+ int i = 0;
+ size_t printed = 0;
+
+ if (sc->tp_format != NULL) {
+ struct format_field *field;
+
+ for (field = sc->tp_format->format.fields->next; field; field = field->next) {
+ printed += fprintf(fp, "%s%s: %ld", printed ? ", " : "",
+ field->name, args[i++]);
+ }
+ } else {
+ while (i < 6) {
+ printed += fprintf(fp, "%sarg%d: %ld", printed ? ", " : "", i, args[i]);
+ ++i;
+ }
+ }
+
+ return printed;
+}
+
+static int trace__run(struct trace *trace)
+{
+ struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
+ struct perf_evsel *evsel, *evsel_enter, *evsel_exit;
+ int err = -1, i, nr_events = 0, before;
+
+ if (evlist == NULL) {
+ printf("Not enough memory to run!\n");
+ goto out;
+ }
+
+ evsel_enter = perf_evsel__newtp("raw_syscalls", "sys_enter", 0);
+ if (evsel_enter == NULL) {
+ printf("Couldn't read the raw_syscalls:sys_enter tracepoint information!\n");
+ goto out_delete_evlist;
+ }
+
+ perf_evlist__add(evlist, evsel_enter);
+
+ evsel_exit = perf_evsel__newtp("raw_syscalls", "sys_exit", 1);
+ if (evsel_exit == NULL) {
+ printf("Couldn't read the raw_syscalls:sys_exit tracepoint information!\n");
+ goto out_delete_evlist;
+ }
+
+ perf_evlist__add(evlist, evsel_exit);
+
+ err = perf_evlist__create_maps(evlist, &trace->opts.target);
+ if (err < 0) {
+ printf("Problems parsing the target to trace, check your options!\n");
+ goto out_delete_evlist;
+ }
+
+ perf_evlist__config_attrs(evlist, &trace->opts);
+
+ err = perf_evlist__open(evlist);
+ if (err < 0) {
+ printf("Couldn't create the events: %s\n", strerror(errno));
+ goto out_delete_evlist;
+ }
+
+ err = perf_evlist__mmap(evlist, UINT_MAX, false);
+ if (err < 0) {
+ printf("Couldn't mmap the events: %s\n", strerror(errno));
+ goto out_delete_evlist;
+ }
+
+ perf_evlist__enable(evlist);
+again:
+ before = nr_events;
+
+ for (i = 0; i < evlist->nr_mmaps; i++) {
+ union perf_event *event;
+
+ while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
+ const u32 type = event->header.type;
+ struct syscall *sc;
+ struct perf_sample sample;
+ int id;
+
+ ++nr_events;
+
+ switch (type) {
+ case PERF_RECORD_SAMPLE:
+ break;
+ case PERF_RECORD_LOST:
+ printf("LOST %" PRIu64 " events!\n", event->lost.lost);
+ continue;
+ default:
+ printf("Unexpected %s event, skipping...\n",
+ perf_event__name(type));
+ continue;
+ }
+
+ err = perf_evlist__parse_sample(evlist, event, &sample);
+ if (err) {
+ printf("Can't parse sample, err = %d, skipping...\n", err);
+ continue;
+ }
+
+ evsel = perf_evlist__id2evsel(evlist, sample.id);
+ if (evsel == NULL) {
+ printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id);
+ continue;
+ }
+
+ id = perf_evsel__intval(evsel, &sample, "id");
+ if (id < 0) {
+ printf("Invalid syscall %d id, skipping...\n", id);
+ continue;
+ }
+
+ if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) &&
+ trace__read_syscall_info(trace, id))
+ continue;
+
+ if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL))
+ continue;
+
+ sc = &trace->syscalls.table[id];
+
+ if (evlist->threads->map[0] == -1 || evlist->threads->nr > 1)
+ printf("%d ", sample.tid);
+
+ if (evsel == evsel_enter) {
+ void *args = perf_evsel__rawptr(evsel, &sample, "args");
+
+ printf("%s(", sc->name);
+ syscall__fprintf_args(sc, args, stdout);
+ } else if (evsel == evsel_exit) {
+ int ret = perf_evsel__intval(evsel, &sample, "ret");
+
+ if (ret < 0 && sc->fmt && sc->fmt->errmsg) {
+ char bf[256];
+ const char *emsg = strerror_r(-ret, bf, sizeof(bf)),
+ *e = audit_errno_to_name(-ret);
+
+ printf(") = -1 %s %s", e, emsg);
+ } else if (ret == 0 && sc->fmt && sc->fmt->timeout)
+ printf(") = 0 Timeout");
+ else
+ printf(") = %d", ret);
+
+ putchar('\n');
+ }
+ }
+ }
+
+ if (nr_events == before)
+ poll(evlist->pollfd, evlist->nr_fds, -1);
+
+ goto again;
+
+out_delete_evlist:
+ perf_evlist__delete(evlist);
+out:
+ return err;
+}
+
+int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
+{
+ const char * const trace_usage[] = {
+ "perf trace [<options>]",
+ NULL
+ };
+ struct trace trace = {
+ .audit_machine = audit_detect_machine(),
+ .syscalls = {
+ . max = -1,
+ },
+ .opts = {
+ .target = {
+ .uid = UINT_MAX,
+ .uses_mmap = true,
+ },
+ .user_freq = UINT_MAX,
+ .user_interval = ULLONG_MAX,
+ .no_delay = true,
+ .mmap_pages = 1024,
+ },
+ };
+ const struct option trace_options[] = {
+ OPT_STRING('p', "pid", &trace.opts.target.pid, "pid",
+ "trace events on existing process id"),
+ OPT_STRING(0, "tid", &trace.opts.target.tid, "tid",
+ "trace events on existing thread id"),
+ OPT_BOOLEAN(0, "all-cpus", &trace.opts.target.system_wide,
+ "system-wide collection from all CPUs"),
+ OPT_STRING(0, "cpu", &trace.opts.target.cpu_list, "cpu",
+ "list of cpus to monitor"),
+ OPT_BOOLEAN(0, "no-inherit", &trace.opts.no_inherit,
+ "child tasks do not inherit counters"),
+ OPT_UINTEGER(0, "mmap-pages", &trace.opts.mmap_pages,
+ "number of mmap data pages"),
+ OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user",
+ "user to profile"),
+ OPT_END()
+ };
+ int err;
+
+ argc = parse_options(argc, argv, trace_options, trace_usage, 0);
+ if (argc)
+ usage_with_options(trace_usage, trace_options);
+
+ err = perf_target__parse_uid(&trace.opts.target);
+ if (err) {
+ char bf[BUFSIZ];
+ perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf));
+ printf("%s", bf);
+ return err;
+ }
+
+ return trace__run(&trace);
+}
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
index 3ea74ed1b26b..08143bd854c7 100644
--- a/tools/perf/builtin.h
+++ b/tools/perf/builtin.h
@@ -34,6 +34,7 @@ extern int cmd_kmem(int argc, const char **argv, const char *prefix);
extern int cmd_lock(int argc, const char **argv, const char *prefix);
extern int cmd_kvm(int argc, const char **argv, const char *prefix);
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_inject(int argc, const char **argv, const char *prefix);
extern int find_scripts(char **scripts_array, char **scripts_path_array);
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt
index 0303ec692274..3e86bbd8c2d5 100644
--- a/tools/perf/command-list.txt
+++ b/tools/perf/command-list.txt
@@ -17,6 +17,7 @@ perf-report mainporcelain common
perf-stat mainporcelain common
perf-timechart mainporcelain common
perf-top mainporcelain common
+perf-trace mainporcelain common
perf-script mainporcelain common
perf-probe mainporcelain full
perf-kmem mainporcelain common
diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak
index 116690a669d2..4add41bb0c7e 100644
--- a/tools/perf/config/feature-tests.mak
+++ b/tools/perf/config/feature-tests.mak
@@ -193,3 +193,14 @@ int main(void)
}
endef
endif
+
+ifndef NO_LIBAUDIT
+define SOURCE_LIBAUDIT
+#include <libaudit.h>
+
+int main(void)
+{
+ return audit_open();
+}
+endef
+endif \ No newline at end of file
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index fb8578cfa03c..fc2f770e3027 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -55,6 +55,9 @@ static struct cmd_struct commands[] = {
{ "lock", cmd_lock, 0 },
{ "kvm", cmd_kvm, 0 },
{ "test", cmd_test, 0 },
+#ifndef NO_LIBAUDIT_SUPPORT
+ { "trace", cmd_trace, 0 },
+#endif
{ "inject", cmd_inject, 0 },
};
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 17b5264f6436..2f68a3b8c285 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -2,6 +2,7 @@
#define __PERF_CPUMAP_H
#include <stdio.h>
+#include <stdbool.h>
struct cpu_map {
int nr;
@@ -14,4 +15,14 @@ void cpu_map__delete(struct cpu_map *map);
struct cpu_map *cpu_map__read(FILE *file);
size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
+static inline int cpu_map__nr(const struct cpu_map *map)
+{
+ return map ? map->nr : 1;
+}
+
+static inline bool cpu_map__all(const struct cpu_map *map)
+{
+ return map ? map->map[0] == -1 : true;
+}
+
#endif /* __PERF_CPUMAP_H */
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index ee51e9b4dc09..3e5f5430a28a 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -804,6 +804,8 @@ int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
tmp = "union ";
else if (tag == DW_TAG_structure_type)
tmp = "struct ";
+ else if (tag == DW_TAG_enumeration_type)
+ tmp = "enum ";
/* Write a base name */
ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
return (ret >= len) ? -E2BIG : ret;
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 892353729c7a..ae89686102f4 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -304,7 +304,7 @@ void perf_evlist__enable(struct perf_evlist *evlist)
int cpu, thread;
struct perf_evsel *pos;
- for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {
+ for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) {
list_for_each_entry(pos, &evlist->entries, node) {
for (thread = 0; thread < evlist->threads->nr; thread++)
ioctl(FD(pos, cpu, thread),
@@ -315,7 +315,7 @@ void perf_evlist__enable(struct perf_evlist *evlist)
static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
{
- int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries;
+ int nfds = cpu_map__nr(evlist->cpus) * evlist->threads->nr * evlist->nr_entries;
evlist->pollfd = malloc(sizeof(struct pollfd) * nfds);
return evlist->pollfd != NULL ? 0 : -ENOMEM;
}
@@ -475,8 +475,8 @@ void perf_evlist__munmap(struct perf_evlist *evlist)
static int perf_evlist__alloc_mmap(struct perf_evlist *evlist)
{
- evlist->nr_mmaps = evlist->cpus->nr;
- if (evlist->cpus->map[0] == -1)
+ evlist->nr_mmaps = cpu_map__nr(evlist->cpus);
+ if (cpu_map__all(evlist->cpus))
evlist->nr_mmaps = evlist->threads->nr;
evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap));
return evlist->mmap != NULL ? 0 : -ENOMEM;
@@ -622,11 +622,11 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
list_for_each_entry(evsel, &evlist->entries, node) {
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
evsel->sample_id == NULL &&
- perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0)
+ perf_evsel__alloc_id(evsel, cpu_map__nr(cpus), threads->nr) < 0)
return -ENOMEM;
}
- if (evlist->cpus->map[0] == -1)
+ if (cpu_map__all(cpus))
return perf_evlist__mmap_per_thread(evlist, prot, mask);
return perf_evlist__mmap_per_cpu(evlist, prot, mask);
@@ -666,32 +666,39 @@ void perf_evlist__delete_maps(struct perf_evlist *evlist)
evlist->threads = NULL;
}
-int perf_evlist__set_filters(struct perf_evlist *evlist)
+int perf_evlist__apply_filters(struct perf_evlist *evlist)
{
- const struct thread_map *threads = evlist->threads;
- const struct cpu_map *cpus = evlist->cpus;
struct perf_evsel *evsel;
- char *filter;
- int thread;
- int cpu;
- int err;
- int fd;
+ int err = 0;
+ const int ncpus = cpu_map__nr(evlist->cpus),
+ nthreads = evlist->threads->nr;
list_for_each_entry(evsel, &evlist->entries, node) {
- filter = evsel->filter;
- if (!filter)
+ if (evsel->filter == NULL)
continue;
- for (cpu = 0; cpu < cpus->nr; cpu++) {
- for (thread = 0; thread < threads->nr; thread++) {
- fd = FD(evsel, cpu, thread);
- err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter);
- if (err)
- return err;
- }
- }
+
+ err = perf_evsel__set_filter(evsel, ncpus, nthreads, evsel->filter);
+ if (err)
+ break;
}
- return 0;
+ return err;
+}
+
+int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter)
+{
+ struct perf_evsel *evsel;
+ int err = 0;
+ const int ncpus = cpu_map__nr(evlist->cpus),
+ nthreads = evlist->threads->nr;
+
+ list_for_each_entry(evsel, &evlist->entries, node) {
+ err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter);
+ if (err)
+ break;
+ }
+
+ return err;
}
bool perf_evlist__valid_sample_type(struct perf_evlist *evlist)
@@ -884,10 +891,10 @@ int perf_evlist__start_workload(struct perf_evlist *evlist)
}
int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event,
- struct perf_sample *sample, bool swapped)
+ struct perf_sample *sample)
{
struct perf_evsel *evsel = perf_evlist__first(evlist);
- return perf_evsel__parse_sample(evsel, event, sample, swapped);
+ return perf_evsel__parse_sample(evsel, event, sample);
}
size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp)
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 3f2e1e4ccdd5..3f1fb66be022 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -72,6 +72,8 @@ int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist,
#define perf_evlist__set_tracepoints_handlers_array(evlist, array) \
perf_evlist__set_tracepoints_handlers(evlist, array, ARRAY_SIZE(array))
+int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter);
+
struct perf_evsel *
perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id);
@@ -115,7 +117,7 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist,
int perf_evlist__create_maps(struct perf_evlist *evlist,
struct perf_target *target);
void perf_evlist__delete_maps(struct perf_evlist *evlist);
-int perf_evlist__set_filters(struct perf_evlist *evlist);
+int perf_evlist__apply_filters(struct perf_evlist *evlist);
void __perf_evlist__set_leader(struct list_head *list);
void perf_evlist__set_leader(struct perf_evlist *evlist);
@@ -125,7 +127,7 @@ bool perf_evlist__sample_id_all(struct perf_evlist *evlist);
u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist);
int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event,
- struct perf_sample *sample, bool swapped);
+ struct perf_sample *sample);
bool perf_evlist__valid_sample_type(struct perf_evlist *evlist);
bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 00936ad29ff2..ffdd94e9c9c3 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -70,7 +70,7 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
return evsel;
}
-static struct event_format *event_format__new(const char *sys, const char *name)
+struct event_format *event_format__new(const char *sys, const char *name)
{
int fd, n;
char *filename;
@@ -117,21 +117,28 @@ struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx)
if (evsel != NULL) {
struct perf_event_attr attr = {
- .type = PERF_TYPE_TRACEPOINT,
+ .type = PERF_TYPE_TRACEPOINT,
+ .sample_type = (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
+ PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD),
};
+ if (asprintf(&evsel->name, "%s:%s", sys, name) < 0)
+ goto out_free;
+
evsel->tp_format = event_format__new(sys, name);
if (evsel->tp_format == NULL)
goto out_free;
+ event_attr_init(&attr);
attr.config = evsel->tp_format->id;
+ attr.sample_period = 1;
perf_evsel__init(evsel, &attr, idx);
- evsel->name = evsel->tp_format->name;
}
return evsel;
out_free:
+ free(evsel->name);
free(evsel);
return NULL;
}
@@ -501,6 +508,24 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
return evsel->fd != NULL ? 0 : -ENOMEM;
}
+int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
+ const char *filter)
+{
+ int cpu, thread;
+
+ for (cpu = 0; cpu < ncpus; cpu++) {
+ for (thread = 0; thread < nthreads; thread++) {
+ int fd = FD(evsel, cpu, thread),
+ err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter);
+
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
{
evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id));
@@ -562,10 +587,8 @@ void perf_evsel__delete(struct perf_evsel *evsel)
perf_evsel__exit(evsel);
close_cgroup(evsel->cgrp);
free(evsel->group_name);
- if (evsel->tp_format && evsel->name == evsel->tp_format->name) {
- evsel->name = NULL;
+ if (evsel->tp_format)
pevent_free_format(evsel->tp_format);
- }
free(evsel->name);
free(evsel);
}
@@ -763,11 +786,13 @@ int perf_evsel__open_per_thread(struct perf_evsel *evsel,
return __perf_evsel__open(evsel, &empty_cpu_map.map, threads);
}
-static int perf_event__parse_id_sample(const union perf_event *event, u64 type,
- struct perf_sample *sample,
- bool swapped)
+static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel,
+ const union perf_event *event,
+ struct perf_sample *sample)
{
+ u64 type = evsel->attr.sample_type;
const u64 *array = event->sample.array;
+ bool swapped = evsel->needs_swap;
union u64_swap u;
array += ((event->header.size -
@@ -828,10 +853,11 @@ static bool sample_overlap(const union perf_event *event,
}
int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
- struct perf_sample *data, bool swapped)
+ struct perf_sample *data)
{
u64 type = evsel->attr.sample_type;
u64 regs_user = evsel->attr.sample_regs_user;
+ bool swapped = evsel->needs_swap;
const u64 *array;
/*
@@ -848,7 +874,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
if (event->header.type != PERF_RECORD_SAMPLE) {
if (!evsel->attr.sample_id_all)
return 0;
- return perf_event__parse_id_sample(event, type, data, swapped);
+ return perf_evsel__parse_id_sample(evsel, event, data);
}
array = event->sample.array;
@@ -1078,7 +1104,7 @@ struct format_field *perf_evsel__field(struct perf_evsel *evsel, const char *nam
return pevent_find_field(evsel->tp_format, name);
}
-char *perf_evsel__strval(struct perf_evsel *evsel, struct perf_sample *sample,
+void *perf_evsel__rawptr(struct perf_evsel *evsel, struct perf_sample *sample,
const char *name)
{
struct format_field *field = perf_evsel__field(evsel, name);
@@ -1101,13 +1127,43 @@ u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample,
const char *name)
{
struct format_field *field = perf_evsel__field(evsel, name);
- u64 val;
+ void *ptr;
+ u64 value;
if (!field)
return 0;
- val = pevent_read_number(evsel->tp_format->pevent,
- sample->raw_data + field->offset, field->size);
- return val;
+ ptr = sample->raw_data + field->offset;
+ switch (field->size) {
+ case 1:
+ return *(u8 *)ptr;
+ case 2:
+ value = *(u16 *)ptr;
+ break;
+ case 4:
+ value = *(u32 *)ptr;
+ break;
+ case 8:
+ value = *(u64 *)ptr;
+ break;
+ default:
+ return 0;
+ }
+
+ if (!evsel->needs_swap)
+ return value;
+
+ switch (field->size) {
+ case 2:
+ return bswap_16(value);
+ case 4:
+ return bswap_32(value);
+ case 8:
+ return bswap_64(value);
+ default:
+ return 0;
+ }
+
+ return 0;
}
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index bb445d1cbc7b..3ead0d59c03d 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -69,6 +69,7 @@ struct perf_evsel {
struct cpu_map *cpus;
unsigned int sample_size;
bool supported;
+ bool needs_swap;
/* parse modifier helper */
int exclude_GH;
struct perf_evsel *leader;
@@ -82,6 +83,9 @@ struct perf_record_opts;
struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx);
struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx);
+
+struct event_format *event_format__new(const char *sys, const char *name);
+
void perf_evsel__init(struct perf_evsel *evsel,
struct perf_event_attr *attr, int idx);
void perf_evsel__exit(struct perf_evsel *evsel);
@@ -114,6 +118,9 @@ void perf_evsel__free_fd(struct perf_evsel *evsel);
void perf_evsel__free_id(struct perf_evsel *evsel);
void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
+int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
+ const char *filter);
+
int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
struct cpu_map *cpus);
int perf_evsel__open_per_thread(struct perf_evsel *evsel,
@@ -124,11 +131,18 @@ void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads);
struct perf_sample;
-char *perf_evsel__strval(struct perf_evsel *evsel, struct perf_sample *sample,
+void *perf_evsel__rawptr(struct perf_evsel *evsel, struct perf_sample *sample,
const char *name);
u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample,
const char *name);
+static inline char *perf_evsel__strval(struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ const char *name)
+{
+ return perf_evsel__rawptr(evsel, sample, name);
+}
+
struct format_field;
struct format_field *perf_evsel__field(struct perf_evsel *evsel, const char *name);
@@ -205,7 +219,7 @@ static inline int perf_evsel__read_scaled(struct perf_evsel *evsel,
void hists__init(struct hists *hists);
int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
- struct perf_sample *sample, bool swapped);
+ struct perf_sample *sample);
static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel)
{
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 6aae3290358e..7daad237dea5 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1256,8 +1256,10 @@ read_event_desc(struct perf_header *ph, int fd)
if (ret != (ssize_t)sizeof(nr))
goto error;
- if (ph->needs_swap)
+ if (ph->needs_swap) {
nr = bswap_32(nr);
+ evsel->needs_swap = true;
+ }
evsel->name = do_read_string(fd, ph);
@@ -2626,6 +2628,8 @@ int perf_session__read_header(struct perf_session *session, int fd)
if (evsel == NULL)
goto out_delete_evlist;
+
+ evsel->needs_swap = header->needs_swap;
/*
* Do it before so that if perf_evsel__alloc_id fails, this
* entry gets purged too at perf_evlist__delete().
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 6ec5398de89d..236bc9d98ff2 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -410,8 +410,13 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
cmp = hist_entry__collapse(iter, he);
if (!cmp) {
- iter->period += he->period;
- iter->nr_events += he->nr_events;
+ iter->period += he->period;
+ iter->period_sys += he->period_sys;
+ iter->period_us += he->period_us;
+ iter->period_guest_sys += he->period_guest_sys;
+ iter->period_guest_us += he->period_guest_us;
+ iter->nr_events += he->nr_events;
+
if (symbol_conf.use_callchain) {
callchain_cursor_reset(&callchain_cursor);
callchain_merge(&callchain_cursor,
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index bf5d033ee1b4..aed38e4b9dfa 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -356,42 +356,28 @@ int parse_events_add_cache(struct list_head **list, int *idx,
return add_event(list, idx, &attr, name);
}
-static int add_tracepoint(struct list_head **list, int *idx,
+static int add_tracepoint(struct list_head **listp, int *idx,
char *sys_name, char *evt_name)
{
- struct perf_event_attr attr;
- char name[MAX_NAME_LEN];
- char evt_path[MAXPATHLEN];
- char id_buf[4];
- u64 id;
- int fd;
-
- snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", tracing_events_path,
- sys_name, evt_name);
-
- fd = open(evt_path, O_RDONLY);
- if (fd < 0)
- return -1;
+ struct perf_evsel *evsel;
+ struct list_head *list = *listp;
- if (read(fd, id_buf, sizeof(id_buf)) < 0) {
- close(fd);
- return -1;
+ if (!list) {
+ list = malloc(sizeof(*list));
+ if (!list)
+ return -ENOMEM;
+ INIT_LIST_HEAD(list);
}
- close(fd);
- id = atoll(id_buf);
-
- memset(&attr, 0, sizeof(attr));
- attr.config = id;
- attr.type = PERF_TYPE_TRACEPOINT;
- attr.sample_type |= PERF_SAMPLE_RAW;
- attr.sample_type |= PERF_SAMPLE_TIME;
- attr.sample_type |= PERF_SAMPLE_CPU;
- attr.sample_type |= PERF_SAMPLE_PERIOD;
- attr.sample_period = 1;
+ evsel = perf_evsel__newtp(sys_name, evt_name, (*idx)++);
+ if (!evsel) {
+ free(list);
+ return -ENOMEM;
+ }
- snprintf(name, MAX_NAME_LEN, "%s:%s", sys_name, evt_name);
- return add_event(list, idx, &attr, name);
+ list_add_tail(&evsel->node, list);
+ *listp = list;
+ return 0;
}
static int add_tracepoint_multi(struct list_head **list, int *idx,
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index ca85444bcfbf..9181bf212fb9 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -805,7 +805,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,
if (pyevent == NULL)
return PyErr_NoMemory();
- err = perf_evlist__parse_sample(evlist, event, &pevent->sample, false);
+ err = perf_evlist__parse_sample(evlist, event, &pevent->sample);
if (err)
return PyErr_Format(PyExc_OSError,
"perf: can't parse sample, err=%d", err);
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 3049b0ae7003..8cdd23239c90 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -722,8 +722,7 @@ static int flush_sample_queue(struct perf_session *s,
if (iter->timestamp > limit)
break;
- ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample,
- s->header.needs_swap);
+ ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample);
if (ret)
pr_err("Can't parse sample, err = %d\n", ret);
else {
@@ -1174,8 +1173,7 @@ static int perf_session__process_event(struct perf_session *session,
/*
* For all kernel events we get the sample data
*/
- ret = perf_evlist__parse_sample(session->evlist, event, &sample,
- session->header.needs_swap);
+ ret = perf_evlist__parse_sample(session->evlist, event, &sample);
if (ret)
return ret;