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:
Ingo Molnar 2012-09-28 09:46:43 +02:00
commit 1d787d37c8
22 changed files with 683 additions and 90 deletions

View 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]

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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,
}, },

View file

@ -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
View 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);
}

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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 },
}; };

View file

@ -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 */

View file

@ -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;

View file

@ -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);
err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter);
if (err) if (err)
return 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) 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)

View file

@ -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);

View file

@ -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;
@ -118,20 +118,27 @@ 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;
} }

View file

@ -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)
{ {

View file

@ -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().

View file

@ -411,7 +411,12 @@ static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
if (!cmp) { if (!cmp) {
iter->period += he->period; 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; 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,

View file

@ -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,

View file

@ -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);

View file

@ -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;