Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 61bf86ad authored by Ingo Molnar's avatar Ingo Molnar
Browse files

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:

 * '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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 7bfb7e6b 31cd3855
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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 */
};
+1 −0
Original line number Diff line number Diff line
@@ -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
+167 −13
Original line number Diff line number Diff line
@@ -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) {
+4 −0
Original line number Diff line number Diff line
@@ -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,
	},
+154 −0
Original line number Diff line number Diff line
#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;
}
Loading