perf/core improvements and fixes:

User visible:
 
 - 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:
 
 - 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>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJV+DhgAAoJENZQFvNTUqpAV5cP/Rxs1T3AULEWpAk65kcooRSz
 Z+LEKSKryR52scTIVgF9ZwxwjtiSWjfSDzBlnmeYWO1/QZ4NTUSmsVu3DOEZ3M+p
 EQ8pUPE5ioK/cLnqDd5IMjhPrtJ/jQn2PdXZ9v7/o4qh10LNA/s7ZQUbpDv+kBZl
 3QJHkzy31EvtPrM+OsjUlCwsS68B/i6aFe8Wt7AbsUPMk0A34qsb7Tb5viXcA2u9
 A7krQ1AE9C4OTvioIV+Zh9br+sVUlQ+1PGwTLJeWjcphOseVfSllap8b8nSCOhR3
 TDuBJ/9RdOi115s8Pm+TPHcgbxcHbzQVTSgukY1bStCJ3u9WA9CKuil7Jzt7c3RD
 zyG/K3zKCCiR454Z++QP9BWo7LBVuK/9thSzphmCsF/Ro9S9wx1Zv905ogzQAE3F
 p7nyBaSdA65vx9srhjPBE91i1jyhSR2hr5PxgeTQRbjhNcW3J6GdgCmysA4RWq/1
 QxKrIwAwYTh2C1wQkidHwa0HewT5dRNJ/4/uIXj4FEGSgv1ZHmo3DLlFELeJupmX
 ejUXBajfAelyDJxWBrfzxsxQE1BnwyW4ew/sZkNMmnwhH09Fb8c/ZdV3cRrt9uYU
 X0JEOC/qFWpFrJC7EUEuyK/AXdGCY8nzX1A123JG0JtbeWp2oAImaNkH6fmlWRsM
 y8IWIn7mNx1nNCW9iNJH
 =Pfiv
 -----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:

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>
This commit is contained in:
Ingo Molnar 2015-09-16 09:12:07 +02:00
commit a4d71093e7
21 changed files with 291 additions and 95 deletions

49
tools/include/linux/err.h Normal file
View File

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

View File

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

View File

@ -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 struct pt_regs_offset {
const char *x86_32_regs_table[X86_32_MAX_REGS] = { const char *name;
"%ax", int offset;
"%cx",
"%dx",
"%bx",
"$stack", /* Stack address instead of %sp */
"%bp",
"%si",
"%di",
}; };
#define X86_64_MAX_REGS 16 #define REG_OFFSET_END {.name = NULL, .offset = 0}
const char *x86_64_regs_table[X86_64_MAX_REGS] = {
"%ax", #ifdef __x86_64__
"%dx", # define REG_OFFSET_NAME_64(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)}
"%cx", # define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = -1}
"%bx", #else
"%si", # define REG_OFFSET_NAME_64(n, r) {.name = n, .offset = -1}
"%di", # define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)}
"%bp", #endif
"%sp",
"%r8", static const struct pt_regs_offset x86_32_regoffset_table[] = {
"%r9", REG_OFFSET_NAME_32("%ax", eax),
"%r10", REG_OFFSET_NAME_32("%cx", ecx),
"%r11", REG_OFFSET_NAME_32("%dx", edx),
"%r12", REG_OFFSET_NAME_32("%bx", ebx),
"%r13", REG_OFFSET_NAME_32("$stack", esp), /* Stack address instead of %sp */
"%r14", REG_OFFSET_NAME_32("%bp", ebp),
"%r15", REG_OFFSET_NAME_32("%si", esi),
REG_OFFSET_NAME_32("%di", edi),
REG_OFFSET_END,
};
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 regoffset_table x86_64_regoffset_table
#define arch_regs_table x86_64_regs_table
#else #else
#define ARCH_MAX_REGS X86_32_MAX_REGS #define regoffset_table x86_32_regoffset_table
#define arch_regs_table x86_32_regs_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;
} }

View File

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

View File

@ -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 (IS_ERR(evsel))
if (perf_evsel__init_syscall_tp(evsel, handler)) return NULL;
goto out_delete;
} 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) {

View File

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

View File

@ -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) { if (IS_ERR(evsel)) {
pr_debug("perf_evsel__new\n"); 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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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] = {

View File

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

View File

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

View File

@ -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) if (IS_ERR(evsel)) {
return -ENOMEM; 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_multi_event(list, idx, sys_name, evt_name, error) :
add_tracepoint(list, idx, sys_name, evt_name); 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

View File

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

View File

@ -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)) { if (parse_events_add_tracepoint(list, &data->idx, $1, $3, error)) {
struct parse_events_error *error = data->error; if (error)
if (error) {
error->idx = @1.first_column; error->idx = @1.first_column;
error->str = strdup("unknown tracepoint");
}
return -1; return -1;
} }
$$ = list; $$ = list;

View File

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

View File

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

View File

@ -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)) err = filename__read_str(path, &data, &size);
return NULL; 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);
} }