perf/core improvements and fixes
. Improve listing of accessible enum perf probe variables, from Hyeoncheol Lee. . Don't stop the build if the audit libraries are not installed, fix from Namhyung Kim. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iQIcBAABAgAGBQJQZLyTAAoJENZQFvNTUqpAfgMQAIHFPCA2AHLzQae1kr6ApoXi 4hwhT8MyvI6Rnaj19vlOjthxk8Ynjkx+5ZiNtYxy+K6aJvQ7ZgsmS6q9//oRdJx0 //7zgRz29pmvq0ZOkd3lktyi77mrL6euVIIt53J2c1pjoExxN4KRiJ2QIYUsyBvL YsG7RXjuh3cACEZ+KMytxFFdbdXTGSicMmF4a5py5kuRydkuCZjmujt25N4Bzlrs jDxjLhXDBi14Dk0OXy2yUeTthAAa96kYIkJAetbduzbOricmfMHhMRgIGfQk2/K1 QirrcbTZApZ78YBjbq1EAdNf6lSaTEA5YIG/NZBS6N2tTeHXi4meXzM6ke+Lb2J1 42Gpy3xVDOkcG93Wgd4m1EO5x5E2kYhdJuYqUKf4jFl5b4qXCkX3qrllu2Tu4D46 rr/V3FcItyzmuEclCZFcKrVefoy8vC5Kg1/szQ7C+c1anuO6cNTLMy68lTqb8wx/ bnhuPmLS79dYdtDuKNwS9uTsFJD98U7GrIOW074GYV5o2mqdC/VyGWIIxL/vMlxo 6AgWKmDTKvNa63n4fwkM+CDucJTk8pqFbrtAnq+yvr0JlypDEJqpM/0u5tZizrGc dScjB4YwtC17s3VjWGszwExtjxC39j+YXF0aFKkbxgYAUEHMefZuJ7Pxu9TDZ2BF P/A/ggGw5nUIDNn2FHmL =98/I -----END PGP SIGNATURE----- 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: * Improve listing of accessible enum perf probe variables, from Hyeoncheol Lee. * Don't stop the build if the audit libraries are not installed, fix from Namhyung Kim. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
1d787d37c8
53
tools/perf/Documentation/perf-trace.txt
Normal file
53
tools/perf/Documentation/perf-trace.txt
Normal file
@ -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]
|
@ -559,6 +559,19 @@ else
|
|||||||
LIB_OBJS += $(OUTPUT)util/unwind.o
|
LIB_OBJS += $(OUTPUT)util/unwind.o
|
||||||
endif
|
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
|
ifdef NO_NEWT
|
||||||
BASIC_CFLAGS += -DNO_NEWT_SUPPORT
|
BASIC_CFLAGS += -DNO_NEWT_SUPPORT
|
||||||
else
|
else
|
||||||
|
@ -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,
|
error("failed to set filter with %d (%s)\n", errno,
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
rc = -1;
|
rc = -1;
|
||||||
|
@ -478,7 +478,7 @@ static int run_perf_stat(int argc __maybe_unused, const char **argv)
|
|||||||
counter->supported = true;
|
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,
|
error("failed to set filter with %d (%s)\n", errno,
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -564,7 +564,7 @@ static int test__basic_mmap(void)
|
|||||||
goto out_munmap;
|
goto out_munmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = perf_evlist__parse_sample(evlist, event, &sample, false);
|
err = perf_evlist__parse_sample(evlist, event, &sample);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_err("Can't parse sample, err = %d\n", err);
|
pr_err("Can't parse sample, err = %d\n", err);
|
||||||
goto out_munmap;
|
goto out_munmap;
|
||||||
@ -781,7 +781,7 @@ static int test__PERF_RECORD(void)
|
|||||||
if (type < PERF_RECORD_MAX)
|
if (type < PERF_RECORD_MAX)
|
||||||
nr_events[type]++;
|
nr_events[type]++;
|
||||||
|
|
||||||
err = perf_evlist__parse_sample(evlist, event, &sample, false);
|
err = perf_evlist__parse_sample(evlist, event, &sample);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
perf_event__fprintf(event, stderr);
|
perf_event__fprintf(event, stderr);
|
||||||
@ -1289,6 +1289,118 @@ static int perf_evsel__tp_sched_test(void)
|
|||||||
return ret;
|
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 {
|
static struct test {
|
||||||
const char *desc;
|
const char *desc;
|
||||||
int (*func)(void);
|
int (*func)(void);
|
||||||
@ -1339,6 +1451,10 @@ static struct test {
|
|||||||
.desc = "Check parsing of sched tracepoints fields",
|
.desc = "Check parsing of sched tracepoints fields",
|
||||||
.func = perf_evsel__tp_sched_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,
|
.func = NULL,
|
||||||
},
|
},
|
||||||
|
@ -823,7 +823,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
while ((event = perf_evlist__mmap_read(top->evlist, idx)) != NULL) {
|
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) {
|
if (ret) {
|
||||||
pr_err("Can't parse sample, err = %d\n", ret);
|
pr_err("Can't parse sample, err = %d\n", ret);
|
||||||
continue;
|
continue;
|
||||||
|
310
tools/perf/builtin-trace.c
Normal file
310
tools/perf/builtin-trace.c
Normal file
@ -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);
|
||||||
|
}
|
@ -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_lock(int argc, const char **argv, const char *prefix);
|
||||||
extern int cmd_kvm(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_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 cmd_inject(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);
|
||||||
|
@ -17,6 +17,7 @@ perf-report mainporcelain common
|
|||||||
perf-stat mainporcelain common
|
perf-stat mainporcelain common
|
||||||
perf-timechart mainporcelain common
|
perf-timechart mainporcelain common
|
||||||
perf-top mainporcelain common
|
perf-top mainporcelain common
|
||||||
|
perf-trace mainporcelain common
|
||||||
perf-script mainporcelain common
|
perf-script mainporcelain common
|
||||||
perf-probe mainporcelain full
|
perf-probe mainporcelain full
|
||||||
perf-kmem mainporcelain common
|
perf-kmem mainporcelain common
|
||||||
|
@ -193,3 +193,14 @@ int main(void)
|
|||||||
}
|
}
|
||||||
endef
|
endef
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifndef NO_LIBAUDIT
|
||||||
|
define SOURCE_LIBAUDIT
|
||||||
|
#include <libaudit.h>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
return audit_open();
|
||||||
|
}
|
||||||
|
endef
|
||||||
|
endif
|
@ -55,6 +55,9 @@ static struct cmd_struct commands[] = {
|
|||||||
{ "lock", cmd_lock, 0 },
|
{ "lock", cmd_lock, 0 },
|
||||||
{ "kvm", cmd_kvm, 0 },
|
{ "kvm", cmd_kvm, 0 },
|
||||||
{ "test", cmd_test, 0 },
|
{ "test", cmd_test, 0 },
|
||||||
|
#ifndef NO_LIBAUDIT_SUPPORT
|
||||||
|
{ "trace", cmd_trace, 0 },
|
||||||
|
#endif
|
||||||
{ "inject", cmd_inject, 0 },
|
{ "inject", cmd_inject, 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define __PERF_CPUMAP_H
|
#define __PERF_CPUMAP_H
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
struct cpu_map {
|
struct cpu_map {
|
||||||
int nr;
|
int nr;
|
||||||
@ -14,4 +15,14 @@ void cpu_map__delete(struct cpu_map *map);
|
|||||||
struct cpu_map *cpu_map__read(FILE *file);
|
struct cpu_map *cpu_map__read(FILE *file);
|
||||||
size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
|
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 */
|
#endif /* __PERF_CPUMAP_H */
|
||||||
|
@ -804,6 +804,8 @@ int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
|
|||||||
tmp = "union ";
|
tmp = "union ";
|
||||||
else if (tag == DW_TAG_structure_type)
|
else if (tag == DW_TAG_structure_type)
|
||||||
tmp = "struct ";
|
tmp = "struct ";
|
||||||
|
else if (tag == DW_TAG_enumeration_type)
|
||||||
|
tmp = "enum ";
|
||||||
/* Write a base name */
|
/* Write a base name */
|
||||||
ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
|
ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
|
||||||
return (ret >= len) ? -E2BIG : ret;
|
return (ret >= len) ? -E2BIG : ret;
|
||||||
|
@ -304,7 +304,7 @@ void perf_evlist__enable(struct perf_evlist *evlist)
|
|||||||
int cpu, thread;
|
int cpu, thread;
|
||||||
struct perf_evsel *pos;
|
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) {
|
list_for_each_entry(pos, &evlist->entries, node) {
|
||||||
for (thread = 0; thread < evlist->threads->nr; thread++)
|
for (thread = 0; thread < evlist->threads->nr; thread++)
|
||||||
ioctl(FD(pos, cpu, 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)
|
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);
|
evlist->pollfd = malloc(sizeof(struct pollfd) * nfds);
|
||||||
return evlist->pollfd != NULL ? 0 : -ENOMEM;
|
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)
|
static int perf_evlist__alloc_mmap(struct perf_evlist *evlist)
|
||||||
{
|
{
|
||||||
evlist->nr_mmaps = evlist->cpus->nr;
|
evlist->nr_mmaps = cpu_map__nr(evlist->cpus);
|
||||||
if (evlist->cpus->map[0] == -1)
|
if (cpu_map__all(evlist->cpus))
|
||||||
evlist->nr_mmaps = evlist->threads->nr;
|
evlist->nr_mmaps = evlist->threads->nr;
|
||||||
evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap));
|
evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap));
|
||||||
return evlist->mmap != NULL ? 0 : -ENOMEM;
|
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) {
|
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||||
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
|
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
|
||||||
evsel->sample_id == NULL &&
|
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;
|
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_thread(evlist, prot, mask);
|
||||||
|
|
||||||
return perf_evlist__mmap_per_cpu(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;
|
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;
|
struct perf_evsel *evsel;
|
||||||
char *filter;
|
int err = 0;
|
||||||
int thread;
|
const int ncpus = cpu_map__nr(evlist->cpus),
|
||||||
int cpu;
|
nthreads = evlist->threads->nr;
|
||||||
int err;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||||
filter = evsel->filter;
|
if (evsel->filter == NULL)
|
||||||
if (!filter)
|
|
||||||
continue;
|
continue;
|
||||||
for (cpu = 0; cpu < cpus->nr; cpu++) {
|
|
||||||
for (thread = 0; thread < threads->nr; thread++) {
|
err = perf_evsel__set_filter(evsel, ncpus, nthreads, evsel->filter);
|
||||||
fd = FD(evsel, cpu, thread);
|
if (err)
|
||||||
err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter);
|
break;
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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,
|
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);
|
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)
|
size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp)
|
||||||
|
@ -72,6 +72,8 @@ int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist,
|
|||||||
#define perf_evlist__set_tracepoints_handlers_array(evlist, array) \
|
#define perf_evlist__set_tracepoints_handlers_array(evlist, array) \
|
||||||
perf_evlist__set_tracepoints_handlers(evlist, array, ARRAY_SIZE(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 *
|
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);
|
||||||
|
|
||||||
@ -115,7 +117,7 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist,
|
|||||||
int perf_evlist__create_maps(struct perf_evlist *evlist,
|
int perf_evlist__create_maps(struct perf_evlist *evlist,
|
||||||
struct perf_target *target);
|
struct perf_target *target);
|
||||||
void perf_evlist__delete_maps(struct perf_evlist *evlist);
|
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 list_head *list);
|
||||||
void perf_evlist__set_leader(struct perf_evlist *evlist);
|
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);
|
u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist);
|
||||||
|
|
||||||
int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event,
|
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_type(struct perf_evlist *evlist);
|
||||||
bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist);
|
bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist);
|
||||||
|
@ -70,7 +70,7 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
|
|||||||
return evsel;
|
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;
|
int fd, n;
|
||||||
char *filename;
|
char *filename;
|
||||||
@ -117,21 +117,28 @@ struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx)
|
|||||||
|
|
||||||
if (evsel != NULL) {
|
if (evsel != NULL) {
|
||||||
struct perf_event_attr attr = {
|
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);
|
evsel->tp_format = event_format__new(sys, name);
|
||||||
if (evsel->tp_format == NULL)
|
if (evsel->tp_format == NULL)
|
||||||
goto out_free;
|
goto out_free;
|
||||||
|
|
||||||
|
event_attr_init(&attr);
|
||||||
attr.config = evsel->tp_format->id;
|
attr.config = evsel->tp_format->id;
|
||||||
|
attr.sample_period = 1;
|
||||||
perf_evsel__init(evsel, &attr, idx);
|
perf_evsel__init(evsel, &attr, idx);
|
||||||
evsel->name = evsel->tp_format->name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return evsel;
|
return evsel;
|
||||||
|
|
||||||
out_free:
|
out_free:
|
||||||
|
free(evsel->name);
|
||||||
free(evsel);
|
free(evsel);
|
||||||
return NULL;
|
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;
|
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)
|
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));
|
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);
|
perf_evsel__exit(evsel);
|
||||||
close_cgroup(evsel->cgrp);
|
close_cgroup(evsel->cgrp);
|
||||||
free(evsel->group_name);
|
free(evsel->group_name);
|
||||||
if (evsel->tp_format && evsel->name == evsel->tp_format->name) {
|
if (evsel->tp_format)
|
||||||
evsel->name = NULL;
|
|
||||||
pevent_free_format(evsel->tp_format);
|
pevent_free_format(evsel->tp_format);
|
||||||
}
|
|
||||||
free(evsel->name);
|
free(evsel->name);
|
||||||
free(evsel);
|
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);
|
return __perf_evsel__open(evsel, &empty_cpu_map.map, threads);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int perf_event__parse_id_sample(const union perf_event *event, u64 type,
|
static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel,
|
||||||
struct perf_sample *sample,
|
const union perf_event *event,
|
||||||
bool swapped)
|
struct perf_sample *sample)
|
||||||
{
|
{
|
||||||
|
u64 type = evsel->attr.sample_type;
|
||||||
const u64 *array = event->sample.array;
|
const u64 *array = event->sample.array;
|
||||||
|
bool swapped = evsel->needs_swap;
|
||||||
union u64_swap u;
|
union u64_swap u;
|
||||||
|
|
||||||
array += ((event->header.size -
|
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,
|
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 type = evsel->attr.sample_type;
|
||||||
u64 regs_user = evsel->attr.sample_regs_user;
|
u64 regs_user = evsel->attr.sample_regs_user;
|
||||||
|
bool swapped = evsel->needs_swap;
|
||||||
const u64 *array;
|
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 (event->header.type != PERF_RECORD_SAMPLE) {
|
||||||
if (!evsel->attr.sample_id_all)
|
if (!evsel->attr.sample_id_all)
|
||||||
return 0;
|
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;
|
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);
|
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)
|
const char *name)
|
||||||
{
|
{
|
||||||
struct format_field *field = perf_evsel__field(evsel, 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)
|
const char *name)
|
||||||
{
|
{
|
||||||
struct format_field *field = perf_evsel__field(evsel, name);
|
struct format_field *field = perf_evsel__field(evsel, name);
|
||||||
u64 val;
|
void *ptr;
|
||||||
|
u64 value;
|
||||||
|
|
||||||
if (!field)
|
if (!field)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
val = pevent_read_number(evsel->tp_format->pevent,
|
ptr = sample->raw_data + field->offset;
|
||||||
sample->raw_data + field->offset, field->size);
|
|
||||||
return val;
|
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,7 @@ struct perf_evsel {
|
|||||||
struct cpu_map *cpus;
|
struct cpu_map *cpus;
|
||||||
unsigned int sample_size;
|
unsigned int sample_size;
|
||||||
bool supported;
|
bool supported;
|
||||||
|
bool needs_swap;
|
||||||
/* parse modifier helper */
|
/* parse modifier helper */
|
||||||
int exclude_GH;
|
int exclude_GH;
|
||||||
struct perf_evsel *leader;
|
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__new(struct perf_event_attr *attr, int idx);
|
||||||
struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, 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,
|
void perf_evsel__init(struct perf_evsel *evsel,
|
||||||
struct perf_event_attr *attr, int idx);
|
struct perf_event_attr *attr, int idx);
|
||||||
void perf_evsel__exit(struct perf_evsel *evsel);
|
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__free_id(struct perf_evsel *evsel);
|
||||||
void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
|
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,
|
int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
|
||||||
struct cpu_map *cpus);
|
struct cpu_map *cpus);
|
||||||
int perf_evsel__open_per_thread(struct perf_evsel *evsel,
|
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;
|
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);
|
const char *name);
|
||||||
u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample,
|
u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample,
|
||||||
const char *name);
|
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;
|
||||||
|
|
||||||
struct format_field *perf_evsel__field(struct perf_evsel *evsel, const char *name);
|
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);
|
void hists__init(struct hists *hists);
|
||||||
|
|
||||||
int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
|
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)
|
static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel)
|
||||||
{
|
{
|
||||||
|
@ -1256,8 +1256,10 @@ read_event_desc(struct perf_header *ph, int fd)
|
|||||||
if (ret != (ssize_t)sizeof(nr))
|
if (ret != (ssize_t)sizeof(nr))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (ph->needs_swap)
|
if (ph->needs_swap) {
|
||||||
nr = bswap_32(nr);
|
nr = bswap_32(nr);
|
||||||
|
evsel->needs_swap = true;
|
||||||
|
}
|
||||||
|
|
||||||
evsel->name = do_read_string(fd, ph);
|
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)
|
if (evsel == NULL)
|
||||||
goto out_delete_evlist;
|
goto out_delete_evlist;
|
||||||
|
|
||||||
|
evsel->needs_swap = header->needs_swap;
|
||||||
/*
|
/*
|
||||||
* Do it before so that if perf_evsel__alloc_id fails, this
|
* Do it before so that if perf_evsel__alloc_id fails, this
|
||||||
* entry gets purged too at perf_evlist__delete().
|
* entry gets purged too at perf_evlist__delete().
|
||||||
|
@ -410,8 +410,13 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
|
|||||||
cmp = hist_entry__collapse(iter, he);
|
cmp = hist_entry__collapse(iter, he);
|
||||||
|
|
||||||
if (!cmp) {
|
if (!cmp) {
|
||||||
iter->period += he->period;
|
iter->period += he->period;
|
||||||
iter->nr_events += he->nr_events;
|
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) {
|
if (symbol_conf.use_callchain) {
|
||||||
callchain_cursor_reset(&callchain_cursor);
|
callchain_cursor_reset(&callchain_cursor);
|
||||||
callchain_merge(&callchain_cursor,
|
callchain_merge(&callchain_cursor,
|
||||||
|
@ -356,42 +356,28 @@ int parse_events_add_cache(struct list_head **list, int *idx,
|
|||||||
return add_event(list, idx, &attr, name);
|
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)
|
char *sys_name, char *evt_name)
|
||||||
{
|
{
|
||||||
struct perf_event_attr attr;
|
struct perf_evsel *evsel;
|
||||||
char name[MAX_NAME_LEN];
|
struct list_head *list = *listp;
|
||||||
char evt_path[MAXPATHLEN];
|
|
||||||
char id_buf[4];
|
|
||||||
u64 id;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", tracing_events_path,
|
if (!list) {
|
||||||
sys_name, evt_name);
|
list = malloc(sizeof(*list));
|
||||||
|
if (!list)
|
||||||
fd = open(evt_path, O_RDONLY);
|
return -ENOMEM;
|
||||||
if (fd < 0)
|
INIT_LIST_HEAD(list);
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (read(fd, id_buf, sizeof(id_buf)) < 0) {
|
|
||||||
close(fd);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close(fd);
|
evsel = perf_evsel__newtp(sys_name, evt_name, (*idx)++);
|
||||||
id = atoll(id_buf);
|
if (!evsel) {
|
||||||
|
free(list);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
memset(&attr, 0, sizeof(attr));
|
list_add_tail(&evsel->node, list);
|
||||||
attr.config = id;
|
*listp = list;
|
||||||
attr.type = PERF_TYPE_TRACEPOINT;
|
return 0;
|
||||||
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;
|
|
||||||
|
|
||||||
snprintf(name, MAX_NAME_LEN, "%s:%s", sys_name, evt_name);
|
|
||||||
return add_event(list, idx, &attr, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add_tracepoint_multi(struct list_head **list, int *idx,
|
static int add_tracepoint_multi(struct list_head **list, int *idx,
|
||||||
|
@ -805,7 +805,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,
|
|||||||
if (pyevent == NULL)
|
if (pyevent == NULL)
|
||||||
return PyErr_NoMemory();
|
return PyErr_NoMemory();
|
||||||
|
|
||||||
err = perf_evlist__parse_sample(evlist, event, &pevent->sample, false);
|
err = perf_evlist__parse_sample(evlist, event, &pevent->sample);
|
||||||
if (err)
|
if (err)
|
||||||
return PyErr_Format(PyExc_OSError,
|
return PyErr_Format(PyExc_OSError,
|
||||||
"perf: can't parse sample, err=%d", err);
|
"perf: can't parse sample, err=%d", err);
|
||||||
|
@ -722,8 +722,7 @@ static int flush_sample_queue(struct perf_session *s,
|
|||||||
if (iter->timestamp > limit)
|
if (iter->timestamp > limit)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample,
|
ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample);
|
||||||
s->header.needs_swap);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
pr_err("Can't parse sample, err = %d\n", ret);
|
pr_err("Can't parse sample, err = %d\n", ret);
|
||||||
else {
|
else {
|
||||||
@ -1174,8 +1173,7 @@ static int 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(session->evlist, event, &sample);
|
||||||
session->header.needs_swap);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user