Commit 1d787d37 authored by Ingo Molnar's avatar Ingo Molnar

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

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

 * 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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents ac2ba2b3 aec1930b
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
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
......
......@@ -320,7 +320,7 @@ static int perf_record__open(struct perf_record *rec)
}
}
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;
......
......@@ -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;
......
......@@ -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);
......@@ -1339,6 +1451,10 @@ static struct test {
.desc = "Check parsing of sched tracepoints fields",
.func = perf_evsel__tp_sched_test,
},
{
.desc = "Generate and check syscalls:sys_enter_open event fields",
.func = test__syscall_open_tp_fields,
},
{
.func = NULL,
},
......
......@@ -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;
......
#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_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);
......
......@@ -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
......
......@@ -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
......@@ -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 },
};
......
......@@ -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 */
......@@ -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;
......
......@@ -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)
......
......@@ -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);
......
......@@ -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;
}
......@@ -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)
{
......
......@@ -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().
......
......@@ -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,
......
......@@ -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,
......
......@@ -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);
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment