Browse Source

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:

User visible changes:

- Enhance the error reporting of tracepoint event parsing, e.g.:

    $ oldperf record -e sched:sched_switc usleep 1
    event syntax error: 'sched:sched_switc'
                         \___ unknown tracepoint
    Run 'perf list' for a list of valid events

  Now we get the much nicer:

    $ perf record -e sched:sched_switc ls
    event syntax error: 'sched:sched_switc'
                         \___ can't access trace events

    Error: No permissions to read /sys/kernel/debug/tracing/events/sched/sched_switc
    Hint:  Try 'sudo mount -o remount,mode=755 /sys/kernel/debug'

  And after we have those mount point permissions fixed:

    $ perf record -e sched:sched_switc ls
    event syntax error: 'sched:sched_switc'
                         \___ unknown tracepoint

    Error: File /sys/kernel/debug/tracing/events/sched/sched_switc not found.
    Hint:  Perhaps this kernel misses some CONFIG_ setting to enable this feature?.

  Now its just a matter of using what git uses to suggest alternatives when we
  make a typo, i.e. that it is just an 'h' missing :-)

  I.e. basically now the event parsing routing uses the strerror_open()
  routines introduced by and used in 'perf trace' work. (Jiri Olsa)

Infrastructure changes:

- Export init/exit_probe_symbol_maps() from 'perf probe' for use in eBPF.
  (Namhyung Kim)

- Free perf_probe_event in cleanup_perf_probe_events(). (Namhyung Kim)

- regs_query_register_offset() infrastructure + implementation for x86.
  First user will be the perf/eBPF code. (Wang Nan)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Ingo Molnar 10 years ago
parent
commit
a4d71093e7

+ 49 - 0
tools/include/linux/err.h

@@ -0,0 +1,49 @@
+#ifndef __TOOLS_LINUX_ERR_H
+#define __TOOLS_LINUX_ERR_H
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+#include <asm/errno.h>
+
+/*
+ * Original kernel header comment:
+ *
+ * Kernel pointers have redundant information, so we can use a
+ * scheme where we can return either an error code or a normal
+ * pointer with the same return value.
+ *
+ * This should be a per-architecture thing, to allow different
+ * error and pointer decisions.
+ *
+ * Userspace note:
+ * The same principle works for userspace, because 'error' pointers
+ * fall down to the unused hole far from user space, as described
+ * in Documentation/x86/x86_64/mm.txt for x86_64 arch:
+ *
+ * 0000000000000000 - 00007fffffffffff (=47 bits) user space, different per mm hole caused by [48:63] sign extension
+ * ffffffffffe00000 - ffffffffffffffff (=2 MB) unused hole
+ *
+ * It should be the same case for other architectures, because
+ * this code is used in generic kernel code.
+ */
+#define MAX_ERRNO	4095
+
+#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
+
+static inline void * __must_check ERR_PTR(long error)
+{
+	return (void *) error;
+}
+
+static inline long __must_check PTR_ERR(__force const void *ptr)
+{
+	return (long) ptr;
+}
+
+static inline bool __must_check IS_ERR(__force const void *ptr)
+{
+	return IS_ERR_VALUE((unsigned long)ptr);
+}
+
+#endif /* _LINUX_ERR_H */

+ 1 - 0
tools/perf/arch/x86/Makefile

@@ -2,3 +2,4 @@ ifndef NO_DWARF
 PERF_HAVE_DWARF_REGS := 1
 PERF_HAVE_DWARF_REGS := 1
 endif
 endif
 HAVE_KVM_STAT_SUPPORT := 1
 HAVE_KVM_STAT_SUPPORT := 1
+PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1

+ 88 - 34
tools/perf/arch/x86/util/dwarf-regs.c

@@ -21,55 +21,109 @@
  */
  */
 
 
 #include <stddef.h>
 #include <stddef.h>
+#include <errno.h> /* for EINVAL */
+#include <string.h> /* for strcmp */
+#include <linux/ptrace.h> /* for struct pt_regs */
+#include <linux/kernel.h> /* for offsetof */
 #include <dwarf-regs.h>
 #include <dwarf-regs.h>
 
 
 /*
 /*
- * Generic dwarf analysis helpers
+ * See arch/x86/kernel/ptrace.c.
+ * Different from it:
+ *
+ *  - Since struct pt_regs is defined differently for user and kernel,
+ *    but we want to use 'ax, bx' instead of 'rax, rbx' (which is struct
+ *    field name of user's pt_regs), we make REG_OFFSET_NAME to accept
+ *    both string name and reg field name.
+ *
+ *  - Since accessing x86_32's pt_regs from x86_64 building is difficult
+ *    and vise versa, we simply fill offset with -1, so
+ *    get_arch_regstr() still works but regs_query_register_offset()
+ *    returns error.
+ *    The only inconvenience caused by it now is that we are not allowed
+ *    to generate BPF prologue for a x86_64 kernel if perf is built for
+ *    x86_32. This is really a rare usecase.
+ *
+ *  - Order is different from kernel's ptrace.c for get_arch_regstr(). Use
+ *    the order defined by dwarf.
  */
  */
 
 
-#define X86_32_MAX_REGS 8
-const char *x86_32_regs_table[X86_32_MAX_REGS] = {
-	"%ax",
-	"%cx",
-	"%dx",
-	"%bx",
-	"$stack",	/* Stack address instead of %sp */
-	"%bp",
-	"%si",
-	"%di",
+struct pt_regs_offset {
+	const char *name;
+	int offset;
+};
+
+#define REG_OFFSET_END {.name = NULL, .offset = 0}
+
+#ifdef __x86_64__
+# define REG_OFFSET_NAME_64(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)}
+# define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = -1}
+#else
+# define REG_OFFSET_NAME_64(n, r) {.name = n, .offset = -1}
+# define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)}
+#endif
+
+static const struct pt_regs_offset x86_32_regoffset_table[] = {
+	REG_OFFSET_NAME_32("%ax",	eax),
+	REG_OFFSET_NAME_32("%cx",	ecx),
+	REG_OFFSET_NAME_32("%dx",	edx),
+	REG_OFFSET_NAME_32("%bx",	ebx),
+	REG_OFFSET_NAME_32("$stack",	esp),	/* Stack address instead of %sp */
+	REG_OFFSET_NAME_32("%bp",	ebp),
+	REG_OFFSET_NAME_32("%si",	esi),
+	REG_OFFSET_NAME_32("%di",	edi),
+	REG_OFFSET_END,
 };
 };
 
 
-#define X86_64_MAX_REGS 16
-const char *x86_64_regs_table[X86_64_MAX_REGS] = {
-	"%ax",
-	"%dx",
-	"%cx",
-	"%bx",
-	"%si",
-	"%di",
-	"%bp",
-	"%sp",
-	"%r8",
-	"%r9",
-	"%r10",
-	"%r11",
-	"%r12",
-	"%r13",
-	"%r14",
-	"%r15",
+static const struct pt_regs_offset x86_64_regoffset_table[] = {
+	REG_OFFSET_NAME_64("%ax",	rax),
+	REG_OFFSET_NAME_64("%dx",	rdx),
+	REG_OFFSET_NAME_64("%cx",	rcx),
+	REG_OFFSET_NAME_64("%bx",	rbx),
+	REG_OFFSET_NAME_64("%si",	rsi),
+	REG_OFFSET_NAME_64("%di",	rdi),
+	REG_OFFSET_NAME_64("%bp",	rbp),
+	REG_OFFSET_NAME_64("%sp",	rsp),
+	REG_OFFSET_NAME_64("%r8",	r8),
+	REG_OFFSET_NAME_64("%r9",	r9),
+	REG_OFFSET_NAME_64("%r10",	r10),
+	REG_OFFSET_NAME_64("%r11",	r11),
+	REG_OFFSET_NAME_64("%r12",	r12),
+	REG_OFFSET_NAME_64("%r13",	r13),
+	REG_OFFSET_NAME_64("%r14",	r14),
+	REG_OFFSET_NAME_64("%r15",	r15),
+	REG_OFFSET_END,
 };
 };
 
 
 /* TODO: switching by dwarf address size */
 /* TODO: switching by dwarf address size */
 #ifdef __x86_64__
 #ifdef __x86_64__
-#define ARCH_MAX_REGS X86_64_MAX_REGS
-#define arch_regs_table x86_64_regs_table
+#define regoffset_table x86_64_regoffset_table
 #else
 #else
-#define ARCH_MAX_REGS X86_32_MAX_REGS
-#define arch_regs_table x86_32_regs_table
+#define regoffset_table x86_32_regoffset_table
 #endif
 #endif
 
 
+/* Minus 1 for the ending REG_OFFSET_END */
+#define ARCH_MAX_REGS ((sizeof(regoffset_table) / sizeof(regoffset_table[0])) - 1)
+
 /* Return architecture dependent register string (for kprobe-tracer) */
 /* Return architecture dependent register string (for kprobe-tracer) */
 const char *get_arch_regstr(unsigned int n)
 const char *get_arch_regstr(unsigned int n)
 {
 {
-	return (n < ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
+	return (n < ARCH_MAX_REGS) ? regoffset_table[n].name : NULL;
+}
+
+/* Reuse code from arch/x86/kernel/ptrace.c */
+/**
+ * regs_query_register_offset() - query register offset from its name
+ * @name:	the name of a register
+ *
+ * regs_query_register_offset() returns the offset of a register in struct
+ * pt_regs from its name. If the name is invalid, this returns -EINVAL;
+ */
+int regs_query_register_offset(const char *name)
+{
+	const struct pt_regs_offset *roff;
+	for (roff = regoffset_table; roff->name != NULL; roff++)
+		if (!strcmp(roff->name, name))
+			return roff->offset;
+	return -EINVAL;
 }
 }

+ 5 - 0
tools/perf/builtin-probe.c

@@ -317,6 +317,10 @@ static int perf_add_probe_events(struct perf_probe_event *pevs, int npevs)
 	int i, k;
 	int i, k;
 	const char *event = NULL, *group = NULL;
 	const char *event = NULL, *group = NULL;
 
 
+	ret = init_probe_symbol_maps(pevs->uprobes);
+	if (ret < 0)
+		return ret;
+
 	ret = convert_perf_probe_events(pevs, npevs);
 	ret = convert_perf_probe_events(pevs, npevs);
 	if (ret < 0)
 	if (ret < 0)
 		goto out_cleanup;
 		goto out_cleanup;
@@ -354,6 +358,7 @@ static int perf_add_probe_events(struct perf_probe_event *pevs, int npevs)
 
 
 out_cleanup:
 out_cleanup:
 	cleanup_perf_probe_events(pevs, npevs);
 	cleanup_perf_probe_events(pevs, npevs);
+	exit_probe_symbol_maps();
 	return ret;
 	return ret;
 }
 }
 
 

+ 11 - 8
tools/perf/builtin-trace.c

@@ -38,6 +38,7 @@
 #include <stdlib.h>
 #include <stdlib.h>
 #include <sys/mman.h>
 #include <sys/mman.h>
 #include <linux/futex.h>
 #include <linux/futex.h>
+#include <linux/err.h>
 
 
 /* For older distros: */
 /* For older distros: */
 #ifndef MAP_STACK
 #ifndef MAP_STACK
@@ -245,13 +246,14 @@ static struct perf_evsel *perf_evsel__syscall_newtp(const char *direction, void
 	struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction);
 	struct perf_evsel *evsel = perf_evsel__newtp("raw_syscalls", direction);
 
 
 	/* older kernel (e.g., RHEL6) use syscalls:{enter,exit} */
 	/* older kernel (e.g., RHEL6) use syscalls:{enter,exit} */
-	if (evsel == NULL)
+	if (IS_ERR(evsel))
 		evsel = perf_evsel__newtp("syscalls", direction);
 		evsel = perf_evsel__newtp("syscalls", direction);
 
 
-	if (evsel) {
-		if (perf_evsel__init_syscall_tp(evsel, handler))
-			goto out_delete;
-	}
+	if (IS_ERR(evsel))
+		return NULL;
+
+	if (perf_evsel__init_syscall_tp(evsel, handler))
+		goto out_delete;
 
 
 	return evsel;
 	return evsel;
 
 
@@ -1705,12 +1707,12 @@ static int trace__read_syscall_info(struct trace *trace, int id)
 	snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
 	snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
 	sc->tp_format = trace_event__tp_format("syscalls", tp_name);
 	sc->tp_format = trace_event__tp_format("syscalls", tp_name);
 
 
-	if (sc->tp_format == NULL && sc->fmt && sc->fmt->alias) {
+	if (IS_ERR(sc->tp_format) && sc->fmt && sc->fmt->alias) {
 		snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->fmt->alias);
 		snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->fmt->alias);
 		sc->tp_format = trace_event__tp_format("syscalls", tp_name);
 		sc->tp_format = trace_event__tp_format("syscalls", tp_name);
 	}
 	}
 
 
-	if (sc->tp_format == NULL)
+	if (IS_ERR(sc->tp_format))
 		return -1;
 		return -1;
 
 
 	sc->args = sc->tp_format->format.fields;
 	sc->args = sc->tp_format->format.fields;
@@ -2390,7 +2392,8 @@ static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp);
 static bool perf_evlist__add_vfs_getname(struct perf_evlist *evlist)
 static bool perf_evlist__add_vfs_getname(struct perf_evlist *evlist)
 {
 {
 	struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname");
 	struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname");
-	if (evsel == NULL)
+
+	if (IS_ERR(evsel))
 		return false;
 		return false;
 
 
 	if (perf_evsel__field(evsel, "pathname") == NULL) {
 	if (perf_evsel__field(evsel, "pathname") == NULL) {

+ 4 - 0
tools/perf/config/Makefile

@@ -109,6 +109,10 @@ endif
 # include ARCH specific config
 # include ARCH specific config
 -include $(src-perf)/arch/$(ARCH)/Makefile
 -include $(src-perf)/arch/$(ARCH)/Makefile
 
 
+ifdef PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET
+  CFLAGS += -DHAVE_ARCH_REGS_QUERY_REGISTER_OFFSET
+endif
+
 include $(src-perf)/config/utilities.mak
 include $(src-perf)/config/utilities.mak
 
 
 ifeq ($(call get-executable,$(FLEX)),)
 ifeq ($(call get-executable,$(FLEX)),)

+ 8 - 2
tools/perf/tests/evsel-tp-sched.c

@@ -1,3 +1,4 @@
+#include <linux/err.h>
 #include <traceevent/event-parse.h>
 #include <traceevent/event-parse.h>
 #include "evsel.h"
 #include "evsel.h"
 #include "tests.h"
 #include "tests.h"
@@ -36,8 +37,8 @@ int test__perf_evsel__tp_sched_test(void)
 	struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch");
 	struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch");
 	int ret = 0;
 	int ret = 0;
 
 
-	if (evsel == NULL) {
-		pr_debug("perf_evsel__new\n");
+	if (IS_ERR(evsel)) {
+		pr_debug("perf_evsel__newtp failed with %ld\n", PTR_ERR(evsel));
 		return -1;
 		return -1;
 	}
 	}
 
 
@@ -66,6 +67,11 @@ int test__perf_evsel__tp_sched_test(void)
 
 
 	evsel = perf_evsel__newtp("sched", "sched_wakeup");
 	evsel = perf_evsel__newtp("sched", "sched_wakeup");
 
 
+	if (IS_ERR(evsel)) {
+		pr_debug("perf_evsel__newtp failed with %ld\n", PTR_ERR(evsel));
+		return -1;
+	}
+
 	if (perf_evsel__test_field(evsel, "comm", 16, true))
 	if (perf_evsel__test_field(evsel, "comm", 16, true))
 		ret = -1;
 		ret = -1;
 
 

+ 2 - 1
tools/perf/tests/mmap-basic.c

@@ -3,6 +3,7 @@
 #include "thread_map.h"
 #include "thread_map.h"
 #include "cpumap.h"
 #include "cpumap.h"
 #include "tests.h"
 #include "tests.h"
+#include <linux/err.h>
 
 
 /*
 /*
  * This test will generate random numbers of calls to some getpid syscalls,
  * This test will generate random numbers of calls to some getpid syscalls,
@@ -65,7 +66,7 @@ int test__basic_mmap(void)
 
 
 		snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]);
 		snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]);
 		evsels[i] = perf_evsel__newtp("syscalls", name);
 		evsels[i] = perf_evsel__newtp("syscalls", name);
-		if (evsels[i] == NULL) {
+		if (IS_ERR(evsels[i])) {
 			pr_debug("perf_evsel__new\n");
 			pr_debug("perf_evsel__new\n");
 			goto out_delete_evlist;
 			goto out_delete_evlist;
 		}
 		}

+ 2 - 1
tools/perf/tests/openat-syscall-all-cpus.c

@@ -1,4 +1,5 @@
 #include <api/fs/fs.h>
 #include <api/fs/fs.h>
+#include <linux/err.h>
 #include "evsel.h"
 #include "evsel.h"
 #include "tests.h"
 #include "tests.h"
 #include "thread_map.h"
 #include "thread_map.h"
@@ -31,7 +32,7 @@ int test__openat_syscall_event_on_all_cpus(void)
 	CPU_ZERO(&cpu_set);
 	CPU_ZERO(&cpu_set);
 
 
 	evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
 	evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
-	if (evsel == NULL) {
+	if (IS_ERR(evsel)) {
 		tracing_path__strerror_open_tp(errno, errbuf, sizeof(errbuf), "syscalls", "sys_enter_openat");
 		tracing_path__strerror_open_tp(errno, errbuf, sizeof(errbuf), "syscalls", "sys_enter_openat");
 		pr_err("%s\n", errbuf);
 		pr_err("%s\n", errbuf);
 		goto out_thread_map_delete;
 		goto out_thread_map_delete;

+ 2 - 1
tools/perf/tests/openat-syscall-tp-fields.c

@@ -1,3 +1,4 @@
+#include <linux/err.h>
 #include "perf.h"
 #include "perf.h"
 #include "evlist.h"
 #include "evlist.h"
 #include "evsel.h"
 #include "evsel.h"
@@ -30,7 +31,7 @@ int test__syscall_openat_tp_fields(void)
 	}
 	}
 
 
 	evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
 	evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
-	if (evsel == NULL) {
+	if (IS_ERR(evsel)) {
 		pr_debug("%s: perf_evsel__newtp\n", __func__);
 		pr_debug("%s: perf_evsel__newtp\n", __func__);
 		goto out_delete_evlist;
 		goto out_delete_evlist;
 	}
 	}

+ 2 - 1
tools/perf/tests/openat-syscall.c

@@ -1,4 +1,5 @@
 #include <api/fs/tracing_path.h>
 #include <api/fs/tracing_path.h>
+#include <linux/err.h>
 #include "thread_map.h"
 #include "thread_map.h"
 #include "evsel.h"
 #include "evsel.h"
 #include "debug.h"
 #include "debug.h"
@@ -19,7 +20,7 @@ int test__openat_syscall_event(void)
 	}
 	}
 
 
 	evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
 	evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
-	if (evsel == NULL) {
+	if (IS_ERR(evsel)) {
 		tracing_path__strerror_open_tp(errno, errbuf, sizeof(errbuf), "syscalls", "sys_enter_openat");
 		tracing_path__strerror_open_tp(errno, errbuf, sizeof(errbuf), "syscalls", "sys_enter_openat");
 		pr_err("%s\n", errbuf);
 		pr_err("%s\n", errbuf);
 		goto out_thread_map_delete;
 		goto out_thread_map_delete;

+ 2 - 1
tools/perf/util/evlist.c

@@ -25,6 +25,7 @@
 #include <linux/bitops.h>
 #include <linux/bitops.h>
 #include <linux/hash.h>
 #include <linux/hash.h>
 #include <linux/log2.h>
 #include <linux/log2.h>
+#include <linux/err.h>
 
 
 static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx);
 static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx);
 static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx);
 static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx);
@@ -265,7 +266,7 @@ int perf_evlist__add_newtp(struct perf_evlist *evlist,
 {
 {
 	struct perf_evsel *evsel = perf_evsel__newtp(sys, name);
 	struct perf_evsel *evsel = perf_evsel__newtp(sys, name);
 
 
-	if (evsel == NULL)
+	if (IS_ERR(evsel))
 		return -1;
 		return -1;
 
 
 	evsel->handler = handler;
 	evsel->handler = handler;

+ 13 - 3
tools/perf/util/evsel.c

@@ -13,6 +13,7 @@
 #include <traceevent/event-parse.h>
 #include <traceevent/event-parse.h>
 #include <linux/hw_breakpoint.h>
 #include <linux/hw_breakpoint.h>
 #include <linux/perf_event.h>
 #include <linux/perf_event.h>
+#include <linux/err.h>
 #include <sys/resource.h>
 #include <sys/resource.h>
 #include "asm/bug.h"
 #include "asm/bug.h"
 #include "callchain.h"
 #include "callchain.h"
@@ -225,11 +226,17 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
 	return evsel;
 	return evsel;
 }
 }
 
 
+/*
+ * Returns pointer with encoded error via <linux/err.h> interface.
+ */
 struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx)
 struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx)
 {
 {
 	struct perf_evsel *evsel = zalloc(perf_evsel__object.size);
 	struct perf_evsel *evsel = zalloc(perf_evsel__object.size);
+	int err = -ENOMEM;
 
 
-	if (evsel != NULL) {
+	if (evsel == NULL) {
+		goto out_err;
+	} else {
 		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 |
 			.sample_type   = (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
@@ -240,8 +247,10 @@ struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int
 			goto out_free;
 			goto out_free;
 
 
 		evsel->tp_format = trace_event__tp_format(sys, name);
 		evsel->tp_format = trace_event__tp_format(sys, name);
-		if (evsel->tp_format == NULL)
+		if (IS_ERR(evsel->tp_format)) {
+			err = PTR_ERR(evsel->tp_format);
 			goto out_free;
 			goto out_free;
+		}
 
 
 		event_attr_init(&attr);
 		event_attr_init(&attr);
 		attr.config = evsel->tp_format->id;
 		attr.config = evsel->tp_format->id;
@@ -254,7 +263,8 @@ struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int
 out_free:
 out_free:
 	zfree(&evsel->name);
 	zfree(&evsel->name);
 	free(evsel);
 	free(evsel);
-	return NULL;
+out_err:
+	return ERR_PTR(err);
 }
 }
 
 
 const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = {
 const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = {

+ 3 - 0
tools/perf/util/evsel.h

@@ -160,6 +160,9 @@ static inline struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr)
 
 
 struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx);
 struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx);
 
 
+/*
+ * Returns pointer with encoded error via <linux/err.h> interface.
+ */
 static inline struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name)
 static inline struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name)
 {
 {
 	return perf_evsel__newtp_idx(sys, name, 0);
 	return perf_evsel__newtp_idx(sys, name, 0);

+ 8 - 0
tools/perf/util/include/dwarf-regs.h

@@ -5,4 +5,12 @@
 const char *get_arch_regstr(unsigned int n);
 const char *get_arch_regstr(unsigned int n);
 #endif
 #endif
 
 
+#ifdef HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET
+/*
+ * Arch should support fetching the offset of a register in pt_regs
+ * by its name. See kernel's regs_query_register_offset in
+ * arch/xxx/kernel/ptrace.c.
+ */
+int regs_query_register_offset(const char *name);
+#endif
 #endif
 #endif

+ 50 - 16
tools/perf/util/parse-events.c

@@ -1,4 +1,5 @@
 #include <linux/hw_breakpoint.h>
 #include <linux/hw_breakpoint.h>
+#include <linux/err.h>
 #include "util.h"
 #include "util.h"
 #include "../perf.h"
 #include "../perf.h"
 #include "evlist.h"
 #include "evlist.h"
@@ -386,22 +387,52 @@ int parse_events_add_cache(struct list_head *list, int *idx,
 	return add_event(list, idx, &attr, name, NULL);
 	return add_event(list, idx, &attr, name, NULL);
 }
 }
 
 
+static void tracepoint_error(struct parse_events_error *error, int err,
+			     char *sys, char *name)
+{
+	char help[BUFSIZ];
+
+	/*
+	 * We get error directly from syscall errno ( > 0),
+	 * or from encoded pointer's error ( < 0).
+	 */
+	err = abs(err);
+
+	switch (err) {
+	case EACCES:
+		error->str = strdup("can't access trace events");
+		break;
+	case ENOENT:
+		error->str = strdup("unknown tracepoint");
+		break;
+	default:
+		error->str = strdup("failed to add tracepoint");
+		break;
+	}
+
+	tracing_path__strerror_open_tp(err, help, sizeof(help), sys, name);
+	error->help = strdup(help);
+}
+
 static int add_tracepoint(struct list_head *list, int *idx,
 static int add_tracepoint(struct list_head *list, int *idx,
-			  char *sys_name, char *evt_name)
+			  char *sys_name, char *evt_name,
+			  struct parse_events_error *error __maybe_unused)
 {
 {
 	struct perf_evsel *evsel;
 	struct perf_evsel *evsel;
 
 
 	evsel = perf_evsel__newtp_idx(sys_name, evt_name, (*idx)++);
 	evsel = perf_evsel__newtp_idx(sys_name, evt_name, (*idx)++);
-	if (!evsel)
-		return -ENOMEM;
+	if (IS_ERR(evsel)) {
+		tracepoint_error(error, PTR_ERR(evsel), sys_name, evt_name);
+		return PTR_ERR(evsel);
+	}
 
 
 	list_add_tail(&evsel->node, list);
 	list_add_tail(&evsel->node, list);
-
 	return 0;
 	return 0;
 }
 }
 
 
 static int add_tracepoint_multi_event(struct list_head *list, int *idx,
 static int add_tracepoint_multi_event(struct list_head *list, int *idx,
-				      char *sys_name, char *evt_name)
+				      char *sys_name, char *evt_name,
+				      struct parse_events_error *error)
 {
 {
 	char evt_path[MAXPATHLEN];
 	char evt_path[MAXPATHLEN];
 	struct dirent *evt_ent;
 	struct dirent *evt_ent;
@@ -411,7 +442,7 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx,
 	snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name);
 	snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name);
 	evt_dir = opendir(evt_path);
 	evt_dir = opendir(evt_path);
 	if (!evt_dir) {
 	if (!evt_dir) {
-		perror("Can't open event dir");
+		tracepoint_error(error, errno, sys_name, evt_name);
 		return -1;
 		return -1;
 	}
 	}
 
 
@@ -425,7 +456,7 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx,
 		if (!strglobmatch(evt_ent->d_name, evt_name))
 		if (!strglobmatch(evt_ent->d_name, evt_name))
 			continue;
 			continue;
 
 
-		ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name);
+		ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name, error);
 	}
 	}
 
 
 	closedir(evt_dir);
 	closedir(evt_dir);
@@ -433,15 +464,17 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx,
 }
 }
 
 
 static int add_tracepoint_event(struct list_head *list, int *idx,
 static int add_tracepoint_event(struct list_head *list, int *idx,
-				char *sys_name, char *evt_name)
+				char *sys_name, char *evt_name,
+				struct parse_events_error *error)
 {
 {
 	return strpbrk(evt_name, "*?") ?
 	return strpbrk(evt_name, "*?") ?
-	       add_tracepoint_multi_event(list, idx, sys_name, evt_name) :
-	       add_tracepoint(list, idx, sys_name, evt_name);
+	       add_tracepoint_multi_event(list, idx, sys_name, evt_name, error) :
+	       add_tracepoint(list, idx, sys_name, evt_name, error);
 }
 }
 
 
 static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
 static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
-				    char *sys_name, char *evt_name)
+				    char *sys_name, char *evt_name,
+				    struct parse_events_error *error)
 {
 {
 	struct dirent *events_ent;
 	struct dirent *events_ent;
 	DIR *events_dir;
 	DIR *events_dir;
@@ -449,7 +482,7 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
 
 
 	events_dir = opendir(tracing_events_path);
 	events_dir = opendir(tracing_events_path);
 	if (!events_dir) {
 	if (!events_dir) {
-		perror("Can't open event dir");
+		tracepoint_error(error, errno, sys_name, evt_name);
 		return -1;
 		return -1;
 	}
 	}
 
 
@@ -465,7 +498,7 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
 			continue;
 			continue;
 
 
 		ret = add_tracepoint_event(list, idx, events_ent->d_name,
 		ret = add_tracepoint_event(list, idx, events_ent->d_name,
-					   evt_name);
+					   evt_name, error);
 	}
 	}
 
 
 	closedir(events_dir);
 	closedir(events_dir);
@@ -473,12 +506,13 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
 }
 }
 
 
 int parse_events_add_tracepoint(struct list_head *list, int *idx,
 int parse_events_add_tracepoint(struct list_head *list, int *idx,
-				char *sys, char *event)
+				char *sys, char *event,
+				struct parse_events_error *error)
 {
 {
 	if (strpbrk(sys, "*?"))
 	if (strpbrk(sys, "*?"))
-		return add_tracepoint_multi_sys(list, idx, sys, event);
+		return add_tracepoint_multi_sys(list, idx, sys, event, error);
 	else
 	else
-		return add_tracepoint_event(list, idx, sys, event);
+		return add_tracepoint_event(list, idx, sys, event, error);
 }
 }
 
 
 static int
 static int

+ 2 - 1
tools/perf/util/parse-events.h

@@ -118,7 +118,8 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add);
 int parse_events__modifier_group(struct list_head *list, char *event_mod);
 int parse_events__modifier_group(struct list_head *list, char *event_mod);
 int parse_events_name(struct list_head *list, char *name);
 int parse_events_name(struct list_head *list, char *name);
 int parse_events_add_tracepoint(struct list_head *list, int *idx,
 int parse_events_add_tracepoint(struct list_head *list, int *idx,
-				char *sys, char *event);
+				char *sys, char *event,
+				struct parse_events_error *error);
 int parse_events_add_numeric(struct parse_events_evlist *data,
 int parse_events_add_numeric(struct parse_events_evlist *data,
 			     struct list_head *list,
 			     struct list_head *list,
 			     u32 type, u64 config,
 			     u32 type, u64 config,

+ 9 - 7
tools/perf/util/parse-events.y

@@ -371,28 +371,30 @@ event_legacy_tracepoint:
 PE_NAME '-' PE_NAME ':' PE_NAME
 PE_NAME '-' PE_NAME ':' PE_NAME
 {
 {
 	struct parse_events_evlist *data = _data;
 	struct parse_events_evlist *data = _data;
+	struct parse_events_error *error = data->error;
 	struct list_head *list;
 	struct list_head *list;
 	char sys_name[128];
 	char sys_name[128];
 	snprintf(&sys_name, 128, "%s-%s", $1, $3);
 	snprintf(&sys_name, 128, "%s-%s", $1, $3);
 
 
 	ALLOC_LIST(list);
 	ALLOC_LIST(list);
-	ABORT_ON(parse_events_add_tracepoint(list, &data->idx, &sys_name, $5));
+	if (parse_events_add_tracepoint(list, &data->idx, &sys_name, $5, error)) {
+		if (error)
+			error->idx = @1.first_column;
+		return -1;
+	}
 	$$ = list;
 	$$ = list;
 }
 }
 |
 |
 PE_NAME ':' PE_NAME
 PE_NAME ':' PE_NAME
 {
 {
 	struct parse_events_evlist *data = _data;
 	struct parse_events_evlist *data = _data;
+	struct parse_events_error *error = data->error;
 	struct list_head *list;
 	struct list_head *list;
 
 
 	ALLOC_LIST(list);
 	ALLOC_LIST(list);
-	if (parse_events_add_tracepoint(list, &data->idx, $1, $3)) {
-		struct parse_events_error *error = data->error;
-
-		if (error) {
+	if (parse_events_add_tracepoint(list, &data->idx, $1, $3, error)) {
+		if (error)
 			error->idx = @1.first_column;
 			error->idx = @1.first_column;
-			error->str = strdup("unknown tracepoint");
-		}
 		return -1;
 		return -1;
 	}
 	}
 	$$ = list;
 	$$ = list;

+ 16 - 16
tools/perf/util/probe-event.c

@@ -71,7 +71,7 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
 static struct machine *host_machine;
 static struct machine *host_machine;
 
 
 /* Initialize symbol maps and path of vmlinux/modules */
 /* Initialize symbol maps and path of vmlinux/modules */
-static int init_symbol_maps(bool user_only)
+int init_probe_symbol_maps(bool user_only)
 {
 {
 	int ret;
 	int ret;
 
 
@@ -101,7 +101,7 @@ out:
 	return ret;
 	return ret;
 }
 }
 
 
-static void exit_symbol_maps(void)
+void exit_probe_symbol_maps(void)
 {
 {
 	if (host_machine) {
 	if (host_machine) {
 		machine__delete(host_machine);
 		machine__delete(host_machine);
@@ -859,11 +859,11 @@ int show_line_range(struct line_range *lr, const char *module, bool user)
 {
 {
 	int ret;
 	int ret;
 
 
-	ret = init_symbol_maps(user);
+	ret = init_probe_symbol_maps(user);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 	ret = __show_line_range(lr, module, user);
 	ret = __show_line_range(lr, module, user);
-	exit_symbol_maps();
+	exit_probe_symbol_maps();
 
 
 	return ret;
 	return ret;
 }
 }
@@ -941,7 +941,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
 	int i, ret = 0;
 	int i, ret = 0;
 	struct debuginfo *dinfo;
 	struct debuginfo *dinfo;
 
 
-	ret = init_symbol_maps(pevs->uprobes);
+	ret = init_probe_symbol_maps(pevs->uprobes);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
@@ -958,7 +958,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
 
 
 	debuginfo__delete(dinfo);
 	debuginfo__delete(dinfo);
 out:
 out:
-	exit_symbol_maps();
+	exit_probe_symbol_maps();
 	return ret;
 	return ret;
 }
 }
 
 
@@ -2262,7 +2262,7 @@ int show_perf_probe_events(struct strfilter *filter)
 
 
 	setup_pager();
 	setup_pager();
 
 
-	ret = init_symbol_maps(false);
+	ret = init_probe_symbol_maps(false);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
@@ -2278,7 +2278,7 @@ int show_perf_probe_events(struct strfilter *filter)
 		close(kp_fd);
 		close(kp_fd);
 	if (up_fd > 0)
 	if (up_fd > 0)
 		close(up_fd);
 		close(up_fd);
-	exit_symbol_maps();
+	exit_probe_symbol_maps();
 
 
 	return ret;
 	return ret;
 }
 }
@@ -2746,10 +2746,6 @@ int convert_perf_probe_events(struct perf_probe_event *pevs, int npevs)
 {
 {
 	int i, ret;
 	int i, ret;
 
 
-	ret = init_symbol_maps(pevs->uprobes);
-	if (ret < 0)
-		return ret;
-
 	/* Loop 1: convert all events */
 	/* Loop 1: convert all events */
 	for (i = 0; i < npevs; i++) {
 	for (i = 0; i < npevs; i++) {
 		/* Init kprobe blacklist if needed */
 		/* Init kprobe blacklist if needed */
@@ -2792,21 +2788,25 @@ void cleanup_perf_probe_events(struct perf_probe_event *pevs, int npevs)
 			clear_probe_trace_event(&pevs[i].tevs[j]);
 			clear_probe_trace_event(&pevs[i].tevs[j]);
 		zfree(&pevs[i].tevs);
 		zfree(&pevs[i].tevs);
 		pevs[i].ntevs = 0;
 		pevs[i].ntevs = 0;
+		clear_perf_probe_event(&pevs[i]);
 	}
 	}
-
-	exit_symbol_maps();
 }
 }
 
 
 int add_perf_probe_events(struct perf_probe_event *pevs, int npevs)
 int add_perf_probe_events(struct perf_probe_event *pevs, int npevs)
 {
 {
 	int ret;
 	int ret;
 
 
+	ret = init_probe_symbol_maps(pevs->uprobes);
+	if (ret < 0)
+		return ret;
+
 	ret = convert_perf_probe_events(pevs, npevs);
 	ret = convert_perf_probe_events(pevs, npevs);
 	if (ret == 0)
 	if (ret == 0)
 		ret = apply_perf_probe_events(pevs, npevs);
 		ret = apply_perf_probe_events(pevs, npevs);
 
 
 	cleanup_perf_probe_events(pevs, npevs);
 	cleanup_perf_probe_events(pevs, npevs);
 
 
+	exit_probe_symbol_maps();
 	return ret;
 	return ret;
 }
 }
 
 
@@ -2866,7 +2866,7 @@ int show_available_funcs(const char *target, struct strfilter *_filter,
 	struct map *map;
 	struct map *map;
 	int ret;
 	int ret;
 
 
-	ret = init_symbol_maps(user);
+	ret = init_probe_symbol_maps(user);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
@@ -2896,7 +2896,7 @@ end:
 	if (user) {
 	if (user) {
 		map__put(map);
 		map__put(map);
 	}
 	}
-	exit_symbol_maps();
+	exit_probe_symbol_maps();
 
 
 	return ret;
 	return ret;
 }
 }

+ 2 - 0
tools/perf/util/probe-event.h

@@ -110,6 +110,8 @@ struct variable_list {
 };
 };
 
 
 struct map;
 struct map;
+int init_probe_symbol_maps(bool user_only);
+void exit_probe_symbol_maps(void);
 
 
 /* Command string to events */
 /* Command string to events */
 extern int parse_perf_probe_command(const char *cmd,
 extern int parse_perf_probe_command(const char *cmd,

+ 12 - 3
tools/perf/util/trace-event.c

@@ -7,6 +7,7 @@
 #include <sys/stat.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <fcntl.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
+#include <linux/err.h>
 #include <traceevent/event-parse.h>
 #include <traceevent/event-parse.h>
 #include <api/fs/tracing_path.h>
 #include <api/fs/tracing_path.h>
 #include "trace-event.h"
 #include "trace-event.h"
@@ -66,6 +67,9 @@ void trace_event__cleanup(struct trace_event *t)
 	pevent_free(t->pevent);
 	pevent_free(t->pevent);
 }
 }
 
 
+/*
+ * Returns pointer with encoded error via <linux/err.h> interface.
+ */
 static struct event_format*
 static struct event_format*
 tp_format(const char *sys, const char *name)
 tp_format(const char *sys, const char *name)
 {
 {
@@ -74,12 +78,14 @@ tp_format(const char *sys, const char *name)
 	char path[PATH_MAX];
 	char path[PATH_MAX];
 	size_t size;
 	size_t size;
 	char *data;
 	char *data;
+	int err;
 
 
 	scnprintf(path, PATH_MAX, "%s/%s/%s/format",
 	scnprintf(path, PATH_MAX, "%s/%s/%s/format",
 		  tracing_events_path, sys, name);
 		  tracing_events_path, sys, name);
 
 
-	if (filename__read_str(path, &data, &size))
-		return NULL;
+	err = filename__read_str(path, &data, &size);
+	if (err)
+		return ERR_PTR(err);
 
 
 	pevent_parse_format(pevent, &event, data, size, sys);
 	pevent_parse_format(pevent, &event, data, size, sys);
 
 
@@ -87,11 +93,14 @@ tp_format(const char *sys, const char *name)
 	return event;
 	return event;
 }
 }
 
 
+/*
+ * Returns pointer with encoded error via <linux/err.h> interface.
+ */
 struct event_format*
 struct event_format*
 trace_event__tp_format(const char *sys, const char *name)
 trace_event__tp_format(const char *sys, const char *name)
 {
 {
 	if (!tevent_initialized && trace_event__init2())
 	if (!tevent_initialized && trace_event__init2())
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 
 
 	return tp_format(sys, name);
 	return tp_format(sys, name);
 }
 }