perf/core improvements and fixes:
. 'perf trace' arg formatting improvements to allow masking arguments in syscalls such as futex and open, where the some arguments are ignored and thus should not be printed depending on other args. . Beautify futex open, openat, open_by_handle_at, lseek and futex syscalls. . Add dummy software event to use when wanting just to keep receiving PERF_RECORD_{MMAP,COMM,etc}, add test for it, from Adrian Hunter. . Fix symbol offset computation for some dsos in 'perf script', from David Ahern. . Skip unsupported hardware events in 'perf list', from Namhyung Kim. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.14 (GNU/Linux) iQIcBAABAgAGBQJSJPgXAAoJENZQFvNTUqpAM4kQALnXlDKd0M8M2KPJqnarwNOk 9mSAhOWv+q6NA3ldpKlPB3NHW9wfMZX9RWImq88GUN6Bk5KMP0hoV8iCRTCkRHuB xVzFzqYFBDv4f/iRZG48Adp6jHIV72OXNluUfSO2/WiZxjAS7rxz3KNKh5+he4jN VrPJr5TKuHBrfEMcvnPt2zjF5ywBas/sJfaG28wA78WM7uFwmLOCb7ROuo6AgJhZ ViQd3DzuLDQz06chQqvQ7202rZIGdV6kowAfv2tgz3oaBJMsjcy7eiaCLmjkOTo8 I/EoOjF4kgLi4Fw4BAX39jGcNTbOv1SSlfl/jfjnWg1nGjE4lBTqNK5AWwDRcatA hOtSkfnNONnxLHgv7pXxFtcgEGNCPFtuXRcq2vBbHsu8/VauWhXfhG9ukLkgjCaU TnAOQ7zq9daO6QxK6PZRka9qZ3blbuUf2+NchXWUyCzygKiKoDxVsDkI9tbCpNAn NAR3qixpZBlW+DP7dLJqbB2ifbPoLG2F0E2FcjlKIB56s1O5iabNWHPLBli5w8wm 8B1aTL1n4CcYohV0udAdpopGZ5xfR+fxwa+ZRBEjlHfygIONtBGGkA/pifXldM1O t1vGqUXV2UkslCx3pTIatk44Hc8EPknyqrwySwPdA6nB57ORehJHbL/i+peFReAv acSJ/Iz/ReCaZDaqIEfF =3dVE -----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: * 'perf trace' arg formatting improvements to allow masking arguments in syscalls such as futex and open, where the some arguments are ignored and thus should not be printed depending on other args. * Beautify futex open, openat, open_by_handle_at, lseek and futex syscalls. * Add dummy software event to use when wanting just to keep receiving PERF_RECORD_{MMAP,COMM,etc}, add test for it, from Adrian Hunter. * Fix symbol offset computation for some dsos in 'perf script', from David Ahern. * Skip unsupported hardware events in 'perf list', 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
61bf86ad86
|
@ -109,6 +109,7 @@ enum perf_sw_ids {
|
|||
PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6,
|
||||
PERF_COUNT_SW_ALIGNMENT_FAULTS = 7,
|
||||
PERF_COUNT_SW_EMULATION_FAULTS = 8,
|
||||
PERF_COUNT_SW_DUMMY = 9,
|
||||
|
||||
PERF_COUNT_SW_MAX, /* non-ABI */
|
||||
};
|
||||
|
|
|
@ -465,6 +465,7 @@ endif # NO_LIBELF
|
|||
ifndef NO_LIBUNWIND
|
||||
LIB_OBJS += $(OUTPUT)util/unwind.o
|
||||
endif
|
||||
LIB_OBJS += $(OUTPUT)tests/keep-tracking.o
|
||||
|
||||
ifndef NO_LIBAUDIT
|
||||
BUILTIN_OBJS += $(OUTPUT)builtin-trace.o
|
||||
|
|
|
@ -14,15 +14,49 @@
|
|||
#include <libaudit.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <linux/futex.h>
|
||||
|
||||
static size_t syscall_arg__scnprintf_hex(char *bf, size_t size, unsigned long arg)
|
||||
static size_t syscall_arg__scnprintf_hex(char *bf, size_t size,
|
||||
unsigned long arg,
|
||||
u8 arg_idx __maybe_unused,
|
||||
u8 *arg_mask __maybe_unused)
|
||||
{
|
||||
return scnprintf(bf, size, "%#lx", arg);
|
||||
}
|
||||
|
||||
#define SCA_HEX syscall_arg__scnprintf_hex
|
||||
|
||||
static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, unsigned long arg)
|
||||
static size_t syscall_arg__scnprintf_whence(char *bf, size_t size,
|
||||
unsigned long arg,
|
||||
u8 arg_idx __maybe_unused,
|
||||
u8 *arg_mask __maybe_unused)
|
||||
{
|
||||
int whence = arg;
|
||||
|
||||
switch (whence) {
|
||||
#define P_WHENCE(n) case SEEK_##n: return scnprintf(bf, size, #n)
|
||||
P_WHENCE(SET);
|
||||
P_WHENCE(CUR);
|
||||
P_WHENCE(END);
|
||||
#ifdef SEEK_DATA
|
||||
P_WHENCE(DATA);
|
||||
#endif
|
||||
#ifdef SEEK_HOLE
|
||||
P_WHENCE(HOLE);
|
||||
#endif
|
||||
#undef P_WHENCE
|
||||
default: break;
|
||||
}
|
||||
|
||||
return scnprintf(bf, size, "%#x", whence);
|
||||
}
|
||||
|
||||
#define SCA_WHENCE syscall_arg__scnprintf_whence
|
||||
|
||||
static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size,
|
||||
unsigned long arg,
|
||||
u8 arg_idx __maybe_unused,
|
||||
u8 *arg_mask __maybe_unused)
|
||||
{
|
||||
int printed = 0, prot = arg;
|
||||
|
||||
|
@ -52,7 +86,9 @@ static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, unsigned l
|
|||
|
||||
#define SCA_MMAP_PROT syscall_arg__scnprintf_mmap_prot
|
||||
|
||||
static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, unsigned long arg)
|
||||
static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size,
|
||||
unsigned long arg, u8 arg_idx __maybe_unused,
|
||||
u8 *arg_mask __maybe_unused)
|
||||
{
|
||||
int printed = 0, flags = arg;
|
||||
|
||||
|
@ -92,7 +128,9 @@ static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, unsigned
|
|||
|
||||
#define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags
|
||||
|
||||
static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, unsigned long arg)
|
||||
static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size,
|
||||
unsigned long arg, u8 arg_idx __maybe_unused,
|
||||
u8 *arg_mask __maybe_unused)
|
||||
{
|
||||
int behavior = arg;
|
||||
|
||||
|
@ -133,10 +171,111 @@ static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, uns
|
|||
|
||||
#define SCA_MADV_BHV syscall_arg__scnprintf_madvise_behavior
|
||||
|
||||
static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, unsigned long arg,
|
||||
u8 arg_idx __maybe_unused, u8 *arg_mask)
|
||||
{
|
||||
enum syscall_futex_args {
|
||||
SCF_UADDR = (1 << 0),
|
||||
SCF_OP = (1 << 1),
|
||||
SCF_VAL = (1 << 2),
|
||||
SCF_TIMEOUT = (1 << 3),
|
||||
SCF_UADDR2 = (1 << 4),
|
||||
SCF_VAL3 = (1 << 5),
|
||||
};
|
||||
int op = arg;
|
||||
int cmd = op & FUTEX_CMD_MASK;
|
||||
size_t printed = 0;
|
||||
|
||||
switch (cmd) {
|
||||
#define P_FUTEX_OP(n) case FUTEX_##n: printed = scnprintf(bf, size, #n);
|
||||
P_FUTEX_OP(WAIT); *arg_mask |= SCF_VAL3|SCF_UADDR2; break;
|
||||
P_FUTEX_OP(WAKE); *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break;
|
||||
P_FUTEX_OP(FD); *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break;
|
||||
P_FUTEX_OP(REQUEUE); *arg_mask |= SCF_VAL3|SCF_TIMEOUT; break;
|
||||
P_FUTEX_OP(CMP_REQUEUE); *arg_mask |= SCF_TIMEOUT; break;
|
||||
P_FUTEX_OP(CMP_REQUEUE_PI); *arg_mask |= SCF_TIMEOUT; break;
|
||||
P_FUTEX_OP(WAKE_OP); break;
|
||||
P_FUTEX_OP(LOCK_PI); *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break;
|
||||
P_FUTEX_OP(UNLOCK_PI); *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break;
|
||||
P_FUTEX_OP(TRYLOCK_PI); *arg_mask |= SCF_VAL3|SCF_UADDR2; break;
|
||||
P_FUTEX_OP(WAIT_BITSET); *arg_mask |= SCF_UADDR2; break;
|
||||
P_FUTEX_OP(WAKE_BITSET); *arg_mask |= SCF_UADDR2; break;
|
||||
P_FUTEX_OP(WAIT_REQUEUE_PI); break;
|
||||
default: printed = scnprintf(bf, size, "%#x", cmd); break;
|
||||
}
|
||||
|
||||
if (op & FUTEX_PRIVATE_FLAG)
|
||||
printed += scnprintf(bf + printed, size - printed, "|PRIV");
|
||||
|
||||
if (op & FUTEX_CLOCK_REALTIME)
|
||||
printed += scnprintf(bf + printed, size - printed, "|CLKRT");
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_FUTEX_OP syscall_arg__scnprintf_futex_op
|
||||
|
||||
static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size,
|
||||
unsigned long arg,
|
||||
u8 arg_idx, u8 *arg_mask)
|
||||
{
|
||||
int printed = 0, flags = arg;
|
||||
|
||||
if (!(flags & O_CREAT))
|
||||
*arg_mask |= 1 << (arg_idx + 1); /* Mask the mode parm */
|
||||
|
||||
if (flags == 0)
|
||||
return scnprintf(bf, size, "RDONLY");
|
||||
#define P_FLAG(n) \
|
||||
if (flags & O_##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
|
||||
flags &= ~O_##n; \
|
||||
}
|
||||
|
||||
P_FLAG(APPEND);
|
||||
P_FLAG(ASYNC);
|
||||
P_FLAG(CLOEXEC);
|
||||
P_FLAG(CREAT);
|
||||
P_FLAG(DIRECT);
|
||||
P_FLAG(DIRECTORY);
|
||||
P_FLAG(EXCL);
|
||||
P_FLAG(LARGEFILE);
|
||||
P_FLAG(NOATIME);
|
||||
P_FLAG(NOCTTY);
|
||||
#ifdef O_NONBLOCK
|
||||
P_FLAG(NONBLOCK);
|
||||
#elif O_NDELAY
|
||||
P_FLAG(NDELAY);
|
||||
#endif
|
||||
#ifdef O_PATH
|
||||
P_FLAG(PATH);
|
||||
#endif
|
||||
P_FLAG(RDWR);
|
||||
#ifdef O_DSYNC
|
||||
if ((flags & O_SYNC) == O_SYNC)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", "SYNC");
|
||||
else {
|
||||
P_FLAG(DSYNC);
|
||||
}
|
||||
#else
|
||||
P_FLAG(SYNC);
|
||||
#endif
|
||||
P_FLAG(TRUNC);
|
||||
P_FLAG(WRONLY);
|
||||
#undef P_FLAG
|
||||
|
||||
if (flags)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_OPEN_FLAGS syscall_arg__scnprintf_open_flags
|
||||
|
||||
static struct syscall_fmt {
|
||||
const char *name;
|
||||
const char *alias;
|
||||
size_t (*arg_scnprintf[6])(char *bf, size_t size, unsigned long arg);
|
||||
size_t (*arg_scnprintf[6])(char *bf, size_t size, unsigned long arg, u8 arg_idx, u8 *arg_mask);
|
||||
bool errmsg;
|
||||
bool timeout;
|
||||
bool hexret;
|
||||
|
@ -149,9 +288,12 @@ static struct syscall_fmt {
|
|||
{ .name = "connect", .errmsg = true, },
|
||||
{ .name = "fstat", .errmsg = true, .alias = "newfstat", },
|
||||
{ .name = "fstatat", .errmsg = true, .alias = "newfstatat", },
|
||||
{ .name = "futex", .errmsg = true, },
|
||||
{ .name = "futex", .errmsg = true,
|
||||
.arg_scnprintf = { [1] = SCA_FUTEX_OP, /* op */ }, },
|
||||
{ .name = "ioctl", .errmsg = true,
|
||||
.arg_scnprintf = { [2] = SCA_HEX, /* arg */ }, },
|
||||
{ .name = "lseek", .errmsg = true,
|
||||
.arg_scnprintf = { [2] = SCA_WHENCE, /* whence */ }, },
|
||||
{ .name = "lstat", .errmsg = true, .alias = "newlstat", },
|
||||
{ .name = "madvise", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_HEX, /* start */
|
||||
|
@ -168,7 +310,12 @@ static struct syscall_fmt {
|
|||
[4] = SCA_HEX, /* new_addr */ }, },
|
||||
{ .name = "munmap", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, },
|
||||
{ .name = "open", .errmsg = true, },
|
||||
{ .name = "open", .errmsg = true,
|
||||
.arg_scnprintf = { [1] = SCA_OPEN_FLAGS, /* flags */ }, },
|
||||
{ .name = "open_by_handle_at", .errmsg = true,
|
||||
.arg_scnprintf = { [2] = SCA_OPEN_FLAGS, /* flags */ }, },
|
||||
{ .name = "openat", .errmsg = true,
|
||||
.arg_scnprintf = { [2] = SCA_OPEN_FLAGS, /* flags */ }, },
|
||||
{ .name = "poll", .errmsg = true, .timeout = true, },
|
||||
{ .name = "ppoll", .errmsg = true, .timeout = true, },
|
||||
{ .name = "pread", .errmsg = true, .alias = "pread64", },
|
||||
|
@ -198,7 +345,8 @@ struct syscall {
|
|||
const char *name;
|
||||
bool filtered;
|
||||
struct syscall_fmt *fmt;
|
||||
size_t (**arg_scnprintf)(char *bf, size_t size, unsigned long arg);
|
||||
size_t (**arg_scnprintf)(char *bf, size_t size,
|
||||
unsigned long arg, u8 arg_idx, u8 *args_mask);
|
||||
};
|
||||
|
||||
static size_t fprintf_duration(unsigned long t, FILE *fp)
|
||||
|
@ -443,17 +591,23 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
|
|||
|
||||
if (sc->tp_format != NULL) {
|
||||
struct format_field *field;
|
||||
u8 mask = 0, bit = 1;
|
||||
|
||||
for (field = sc->tp_format->format.fields->next; field;
|
||||
field = field->next, ++i, bit <<= 1) {
|
||||
if (mask & bit)
|
||||
continue;
|
||||
|
||||
for (field = sc->tp_format->format.fields->next; field; field = field->next) {
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
"%s%s: ", printed ? ", " : "", field->name);
|
||||
|
||||
if (sc->arg_scnprintf && sc->arg_scnprintf[i])
|
||||
printed += sc->arg_scnprintf[i](bf + printed, size - printed, args[i]);
|
||||
else
|
||||
if (sc->arg_scnprintf && sc->arg_scnprintf[i]) {
|
||||
printed += sc->arg_scnprintf[i](bf + printed, size - printed,
|
||||
args[i], i, &mask);
|
||||
} else {
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
"%ld", args[i]);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (i < 6) {
|
||||
|
|
|
@ -107,6 +107,10 @@ static struct test {
|
|||
.desc = "Test sample parsing",
|
||||
.func = test__sample_parsing,
|
||||
},
|
||||
{
|
||||
.desc = "Test using a dummy software event to keep tracking",
|
||||
.func = test__keep_tracking,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include "parse-events.h"
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "thread_map.h"
|
||||
#include "cpumap.h"
|
||||
#include "tests.h"
|
||||
|
||||
#define CHECK__(x) { \
|
||||
while ((x) < 0) { \
|
||||
pr_debug(#x " failed!\n"); \
|
||||
goto out_err; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CHECK_NOT_NULL__(x) { \
|
||||
while ((x) == NULL) { \
|
||||
pr_debug(#x " failed!\n"); \
|
||||
goto out_err; \
|
||||
} \
|
||||
}
|
||||
|
||||
static int find_comm(struct perf_evlist *evlist, const char *comm)
|
||||
{
|
||||
union perf_event *event;
|
||||
int i, found;
|
||||
|
||||
found = 0;
|
||||
for (i = 0; i < evlist->nr_mmaps; i++) {
|
||||
while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
|
||||
if (event->header.type == PERF_RECORD_COMM &&
|
||||
(pid_t)event->comm.pid == getpid() &&
|
||||
(pid_t)event->comm.tid == getpid() &&
|
||||
strcmp(event->comm.comm, comm) == 0)
|
||||
found += 1;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* test__keep_tracking - test using a dummy software event to keep tracking.
|
||||
*
|
||||
* This function implements a test that checks that tracking events continue
|
||||
* when an event is disabled but a dummy software event is not disabled. If the
|
||||
* test passes %0 is returned, otherwise %-1 is returned.
|
||||
*/
|
||||
int test__keep_tracking(void)
|
||||
{
|
||||
struct perf_record_opts opts = {
|
||||
.mmap_pages = UINT_MAX,
|
||||
.user_freq = UINT_MAX,
|
||||
.user_interval = ULLONG_MAX,
|
||||
.freq = 4000,
|
||||
.target = {
|
||||
.uses_mmap = true,
|
||||
},
|
||||
};
|
||||
struct thread_map *threads = NULL;
|
||||
struct cpu_map *cpus = NULL;
|
||||
struct perf_evlist *evlist = NULL;
|
||||
struct perf_evsel *evsel = NULL;
|
||||
int found, err = -1;
|
||||
const char *comm;
|
||||
|
||||
threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
CHECK_NOT_NULL__(threads);
|
||||
|
||||
cpus = cpu_map__new(NULL);
|
||||
CHECK_NOT_NULL__(cpus);
|
||||
|
||||
evlist = perf_evlist__new();
|
||||
CHECK_NOT_NULL__(evlist);
|
||||
|
||||
perf_evlist__set_maps(evlist, cpus, threads);
|
||||
|
||||
CHECK__(parse_events(evlist, "dummy:u"));
|
||||
CHECK__(parse_events(evlist, "cycles:u"));
|
||||
|
||||
perf_evlist__config(evlist, &opts);
|
||||
|
||||
evsel = perf_evlist__first(evlist);
|
||||
|
||||
evsel->attr.comm = 1;
|
||||
evsel->attr.disabled = 1;
|
||||
evsel->attr.enable_on_exec = 0;
|
||||
|
||||
if (perf_evlist__open(evlist) < 0) {
|
||||
fprintf(stderr, " (not supported)");
|
||||
err = 0;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
CHECK__(perf_evlist__mmap(evlist, UINT_MAX, false));
|
||||
|
||||
/*
|
||||
* First, test that a 'comm' event can be found when the event is
|
||||
* enabled.
|
||||
*/
|
||||
|
||||
perf_evlist__enable(evlist);
|
||||
|
||||
comm = "Test COMM 1";
|
||||
CHECK__(prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0));
|
||||
|
||||
perf_evlist__disable(evlist);
|
||||
|
||||
found = find_comm(evlist, comm);
|
||||
if (found != 1) {
|
||||
pr_debug("First time, failed to find tracking event.\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Secondly, test that a 'comm' event can be found when the event is
|
||||
* disabled with the dummy event still enabled.
|
||||
*/
|
||||
|
||||
perf_evlist__enable(evlist);
|
||||
|
||||
evsel = perf_evlist__last(evlist);
|
||||
|
||||
CHECK__(perf_evlist__disable_event(evlist, evsel));
|
||||
|
||||
comm = "Test COMM 2";
|
||||
CHECK__(prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0));
|
||||
|
||||
perf_evlist__disable(evlist);
|
||||
|
||||
found = find_comm(evlist, comm);
|
||||
if (found != 1) {
|
||||
pr_debug("Seconf time, failed to find tracking event.\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
out_err:
|
||||
if (evlist) {
|
||||
perf_evlist__disable(evlist);
|
||||
perf_evlist__munmap(evlist);
|
||||
perf_evlist__close(evlist);
|
||||
perf_evlist__delete(evlist);
|
||||
}
|
||||
if (cpus)
|
||||
cpu_map__delete(cpus);
|
||||
if (threads)
|
||||
thread_map__delete(threads);
|
||||
|
||||
return err;
|
||||
}
|
|
@ -38,5 +38,6 @@ int test__sw_clock_freq(void);
|
|||
int test__perf_time_to_tsc(void);
|
||||
int test__code_reading(void);
|
||||
int test__sample_parsing(void);
|
||||
int test__keep_tracking(void);
|
||||
|
||||
#endif /* TESTS_H */
|
||||
|
|
|
@ -246,7 +246,7 @@ void perf_evlist__disable(struct perf_evlist *evlist)
|
|||
|
||||
for (cpu = 0; cpu < nr_cpus; cpu++) {
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
if (!perf_evsel__is_group_leader(pos))
|
||||
if (!perf_evsel__is_group_leader(pos) || !pos->fd)
|
||||
continue;
|
||||
for (thread = 0; thread < nr_threads; thread++)
|
||||
ioctl(FD(pos, cpu, thread),
|
||||
|
@ -264,7 +264,7 @@ void perf_evlist__enable(struct perf_evlist *evlist)
|
|||
|
||||
for (cpu = 0; cpu < nr_cpus; cpu++) {
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
if (!perf_evsel__is_group_leader(pos))
|
||||
if (!perf_evsel__is_group_leader(pos) || !pos->fd)
|
||||
continue;
|
||||
for (thread = 0; thread < nr_threads; thread++)
|
||||
ioctl(FD(pos, cpu, thread),
|
||||
|
@ -273,6 +273,44 @@ void perf_evlist__enable(struct perf_evlist *evlist)
|
|||
}
|
||||
}
|
||||
|
||||
int perf_evlist__disable_event(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
int cpu, thread, err;
|
||||
|
||||
if (!evsel->fd)
|
||||
return 0;
|
||||
|
||||
for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {
|
||||
for (thread = 0; thread < evlist->threads->nr; thread++) {
|
||||
err = ioctl(FD(evsel, cpu, thread),
|
||||
PERF_EVENT_IOC_DISABLE, 0);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_evlist__enable_event(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel)
|
||||
{
|
||||
int cpu, thread, err;
|
||||
|
||||
if (!evsel->fd)
|
||||
return -EINVAL;
|
||||
|
||||
for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {
|
||||
for (thread = 0; thread < evlist->threads->nr; thread++) {
|
||||
err = ioctl(FD(evsel, cpu, thread),
|
||||
PERF_EVENT_IOC_ENABLE, 0);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
|
||||
{
|
||||
int nr_cpus = cpu_map__nr(evlist->cpus);
|
||||
|
|
|
@ -110,6 +110,11 @@ void perf_evlist__munmap(struct perf_evlist *evlist);
|
|||
void perf_evlist__disable(struct perf_evlist *evlist);
|
||||
void perf_evlist__enable(struct perf_evlist *evlist);
|
||||
|
||||
int perf_evlist__disable_event(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel);
|
||||
int perf_evlist__enable_event(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel);
|
||||
|
||||
void perf_evlist__set_selected(struct perf_evlist *evlist,
|
||||
struct perf_evsel *evsel);
|
||||
|
||||
|
|
|
@ -323,6 +323,7 @@ const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = {
|
|||
"major-faults",
|
||||
"alignment-faults",
|
||||
"emulation-faults",
|
||||
"dummy",
|
||||
};
|
||||
|
||||
static const char *__perf_evsel__sw_name(u64 config)
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#define YY_EXTRA_TYPE int
|
||||
#include "parse-events-flex.h"
|
||||
#include "pmu.h"
|
||||
#include "thread_map.h"
|
||||
|
||||
#define MAX_NAME_LEN 100
|
||||
|
||||
|
@ -108,6 +109,10 @@ static struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
|
|||
.symbol = "emulation-faults",
|
||||
.alias = "",
|
||||
},
|
||||
[PERF_COUNT_SW_DUMMY] = {
|
||||
.symbol = "dummy",
|
||||
.alias = "",
|
||||
},
|
||||
};
|
||||
|
||||
#define __PERF_EVENT_FIELD(config, name) \
|
||||
|
@ -1072,6 +1077,33 @@ int is_valid_tracepoint(const char *event_string)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool is_event_supported(u8 type, unsigned config)
|
||||
{
|
||||
bool ret = true;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_event_attr attr = {
|
||||
.type = type,
|
||||
.config = config,
|
||||
.disabled = 1,
|
||||
.exclude_kernel = 1,
|
||||
};
|
||||
struct {
|
||||
struct thread_map map;
|
||||
int threads[1];
|
||||
} tmap = {
|
||||
.map.nr = 1,
|
||||
.threads = { 0 },
|
||||
};
|
||||
|
||||
evsel = perf_evsel__new(&attr, 0);
|
||||
if (evsel) {
|
||||
ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0;
|
||||
perf_evsel__delete(evsel);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __print_events_type(u8 type, struct event_symbol *syms,
|
||||
unsigned max)
|
||||
{
|
||||
|
@ -1079,14 +1111,16 @@ static void __print_events_type(u8 type, struct event_symbol *syms,
|
|||
unsigned i;
|
||||
|
||||
for (i = 0; i < max ; i++, syms++) {
|
||||
if (!is_event_supported(type, i))
|
||||
continue;
|
||||
|
||||
if (strlen(syms->alias))
|
||||
snprintf(name, sizeof(name), "%s OR %s",
|
||||
syms->symbol, syms->alias);
|
||||
else
|
||||
snprintf(name, sizeof(name), "%s", syms->symbol);
|
||||
|
||||
printf(" %-50s [%s]\n", name,
|
||||
event_type_descriptors[type]);
|
||||
printf(" %-50s [%s]\n", name, event_type_descriptors[type]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1115,6 +1149,10 @@ int print_hwcache_events(const char *event_glob, bool name_only)
|
|||
if (event_glob != NULL && !strglobmatch(name, event_glob))
|
||||
continue;
|
||||
|
||||
if (!is_event_supported(PERF_TYPE_HW_CACHE,
|
||||
type | (op << 8) | (i << 16)))
|
||||
continue;
|
||||
|
||||
if (name_only)
|
||||
printf("%s ", name);
|
||||
else
|
||||
|
@ -1144,6 +1182,9 @@ static void print_symbol_events(const char *event_glob, unsigned type,
|
|||
(syms->alias && strglobmatch(syms->alias, event_glob))))
|
||||
continue;
|
||||
|
||||
if (!is_event_supported(type, i))
|
||||
continue;
|
||||
|
||||
if (name_only) {
|
||||
printf("%s ", syms->symbol);
|
||||
continue;
|
||||
|
|
|
@ -145,6 +145,7 @@ context-switches|cs { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW
|
|||
cpu-migrations|migrations { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); }
|
||||
alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); }
|
||||
emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }
|
||||
dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); }
|
||||
|
||||
L1-dcache|l1-d|l1d|L1-data |
|
||||
L1-icache|l1-i|l1i|L1-instruction |
|
||||
|
|
|
@ -987,6 +987,7 @@ static struct {
|
|||
{ "COUNT_SW_PAGE_FAULTS_MAJ", PERF_COUNT_SW_PAGE_FAULTS_MAJ },
|
||||
{ "COUNT_SW_ALIGNMENT_FAULTS", PERF_COUNT_SW_ALIGNMENT_FAULTS },
|
||||
{ "COUNT_SW_EMULATION_FAULTS", PERF_COUNT_SW_EMULATION_FAULTS },
|
||||
{ "COUNT_SW_DUMMY", PERF_COUNT_SW_DUMMY },
|
||||
|
||||
{ "SAMPLE_IP", PERF_SAMPLE_IP },
|
||||
{ "SAMPLE_TID", PERF_SAMPLE_TID },
|
||||
|
|
|
@ -1513,6 +1513,7 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
|
|||
printf(" ");
|
||||
if (print_symoffset) {
|
||||
al.addr = node->ip;
|
||||
al.map = node->map;
|
||||
symbol__fprintf_symname_offs(node->sym, &al, stdout);
|
||||
} else
|
||||
symbol__fprintf_symname(node->sym, stdout);
|
||||
|
|
|
@ -259,7 +259,10 @@ size_t symbol__fprintf_symname_offs(const struct symbol *sym,
|
|||
if (sym && sym->name) {
|
||||
length = fprintf(fp, "%s", sym->name);
|
||||
if (al) {
|
||||
offset = al->addr - sym->start;
|
||||
if (al->addr < sym->end)
|
||||
offset = al->addr - sym->start;
|
||||
else
|
||||
offset = al->addr - al->map->start - sym->start;
|
||||
length += fprintf(fp, "+0x%lx", offset);
|
||||
}
|
||||
return length;
|
||||
|
|
Loading…
Reference in New Issue