forked from Minki/linux
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:
commit
a4d71093e7
49
tools/include/linux/err.h
Normal file
49
tools/include/linux/err.h
Normal 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 */
|
@ -2,3 +2,4 @@ ifndef NO_DWARF
|
||||
PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
|
||||
|
@ -21,55 +21,109 @@
|
||||
*/
|
||||
|
||||
#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>
|
||||
|
||||
/*
|
||||
* 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 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",
|
||||
#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,
|
||||
};
|
||||
|
||||
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 */
|
||||
#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
|
||||
#define ARCH_MAX_REGS X86_32_MAX_REGS
|
||||
#define arch_regs_table x86_32_regs_table
|
||||
#define regoffset_table x86_32_regoffset_table
|
||||
#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) */
|
||||
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;
|
||||
}
|
||||
|
@ -317,6 +317,10 @@ static int perf_add_probe_events(struct perf_probe_event *pevs, int npevs)
|
||||
int i, k;
|
||||
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);
|
||||
if (ret < 0)
|
||||
goto out_cleanup;
|
||||
@ -354,6 +358,7 @@ static int perf_add_probe_events(struct perf_probe_event *pevs, int npevs)
|
||||
|
||||
out_cleanup:
|
||||
cleanup_perf_probe_events(pevs, npevs);
|
||||
exit_probe_symbol_maps();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <linux/futex.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
/* For older distros: */
|
||||
#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);
|
||||
|
||||
/* older kernel (e.g., RHEL6) use syscalls:{enter,exit} */
|
||||
if (evsel == NULL)
|
||||
if (IS_ERR(evsel))
|
||||
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;
|
||||
|
||||
@ -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);
|
||||
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);
|
||||
sc->tp_format = trace_event__tp_format("syscalls", tp_name);
|
||||
}
|
||||
|
||||
if (sc->tp_format == NULL)
|
||||
if (IS_ERR(sc->tp_format))
|
||||
return -1;
|
||||
|
||||
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)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname");
|
||||
if (evsel == NULL)
|
||||
|
||||
if (IS_ERR(evsel))
|
||||
return false;
|
||||
|
||||
if (perf_evsel__field(evsel, "pathname") == NULL) {
|
||||
|
@ -109,6 +109,10 @@ endif
|
||||
# include ARCH specific config
|
||||
-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
|
||||
|
||||
ifeq ($(call get-executable,$(FLEX)),)
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <linux/err.h>
|
||||
#include <traceevent/event-parse.h>
|
||||
#include "evsel.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");
|
||||
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;
|
||||
}
|
||||
|
||||
@ -66,6 +67,11 @@ int test__perf_evsel__tp_sched_test(void)
|
||||
|
||||
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))
|
||||
ret = -1;
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "thread_map.h"
|
||||
#include "cpumap.h"
|
||||
#include "tests.h"
|
||||
#include <linux/err.h>
|
||||
|
||||
/*
|
||||
* 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]);
|
||||
evsels[i] = perf_evsel__newtp("syscalls", name);
|
||||
if (evsels[i] == NULL) {
|
||||
if (IS_ERR(evsels[i])) {
|
||||
pr_debug("perf_evsel__new\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <api/fs/fs.h>
|
||||
#include <linux/err.h>
|
||||
#include "evsel.h"
|
||||
#include "tests.h"
|
||||
#include "thread_map.h"
|
||||
@ -31,7 +32,7 @@ int test__openat_syscall_event_on_all_cpus(void)
|
||||
CPU_ZERO(&cpu_set);
|
||||
|
||||
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");
|
||||
pr_err("%s\n", errbuf);
|
||||
goto out_thread_map_delete;
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <linux/err.h>
|
||||
#include "perf.h"
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
@ -30,7 +31,7 @@ int test__syscall_openat_tp_fields(void)
|
||||
}
|
||||
|
||||
evsel = perf_evsel__newtp("syscalls", "sys_enter_openat");
|
||||
if (evsel == NULL) {
|
||||
if (IS_ERR(evsel)) {
|
||||
pr_debug("%s: perf_evsel__newtp\n", __func__);
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <api/fs/tracing_path.h>
|
||||
#include <linux/err.h>
|
||||
#include "thread_map.h"
|
||||
#include "evsel.h"
|
||||
#include "debug.h"
|
||||
@ -19,7 +20,7 @@ int test__openat_syscall_event(void)
|
||||
}
|
||||
|
||||
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");
|
||||
pr_err("%s\n", errbuf);
|
||||
goto out_thread_map_delete;
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/hash.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__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);
|
||||
|
||||
if (evsel == NULL)
|
||||
if (IS_ERR(evsel))
|
||||
return -1;
|
||||
|
||||
evsel->handler = handler;
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <traceevent/event-parse.h>
|
||||
#include <linux/hw_breakpoint.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/err.h>
|
||||
#include <sys/resource.h>
|
||||
#include "asm/bug.h"
|
||||
#include "callchain.h"
|
||||
@ -225,11 +226,17 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
|
||||
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 *evsel = zalloc(perf_evsel__object.size);
|
||||
int err = -ENOMEM;
|
||||
|
||||
if (evsel != NULL) {
|
||||
if (evsel == NULL) {
|
||||
goto out_err;
|
||||
} else {
|
||||
struct perf_event_attr attr = {
|
||||
.type = PERF_TYPE_TRACEPOINT,
|
||||
.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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
event_attr_init(&attr);
|
||||
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:
|
||||
zfree(&evsel->name);
|
||||
free(evsel);
|
||||
return NULL;
|
||||
out_err:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = {
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
* Returns pointer with encoded error via <linux/err.h> interface.
|
||||
*/
|
||||
static inline struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name)
|
||||
{
|
||||
return perf_evsel__newtp_idx(sys, name, 0);
|
||||
|
@ -5,4 +5,12 @@
|
||||
const char *get_arch_regstr(unsigned int n);
|
||||
#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
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <linux/hw_breakpoint.h>
|
||||
#include <linux/err.h>
|
||||
#include "util.h"
|
||||
#include "../perf.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);
|
||||
}
|
||||
|
||||
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,
|
||||
char *sys_name, char *evt_name)
|
||||
char *sys_name, char *evt_name,
|
||||
struct parse_events_error *error __maybe_unused)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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];
|
||||
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);
|
||||
evt_dir = opendir(evt_path);
|
||||
if (!evt_dir) {
|
||||
perror("Can't open event dir");
|
||||
tracepoint_error(error, errno, sys_name, evt_name);
|
||||
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))
|
||||
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);
|
||||
@ -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,
|
||||
char *sys_name, char *evt_name)
|
||||
char *sys_name, char *evt_name,
|
||||
struct parse_events_error *error)
|
||||
{
|
||||
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,
|
||||
char *sys_name, char *evt_name)
|
||||
char *sys_name, char *evt_name,
|
||||
struct parse_events_error *error)
|
||||
{
|
||||
struct dirent *events_ent;
|
||||
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);
|
||||
if (!events_dir) {
|
||||
perror("Can't open event dir");
|
||||
tracepoint_error(error, errno, sys_name, evt_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -465,7 +498,7 @@ static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
|
||||
continue;
|
||||
|
||||
ret = add_tracepoint_event(list, idx, events_ent->d_name,
|
||||
evt_name);
|
||||
evt_name, error);
|
||||
}
|
||||
|
||||
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,
|
||||
char *sys, char *event)
|
||||
char *sys, char *event,
|
||||
struct parse_events_error *error)
|
||||
{
|
||||
if (strpbrk(sys, "*?"))
|
||||
return add_tracepoint_multi_sys(list, idx, sys, event);
|
||||
return add_tracepoint_multi_sys(list, idx, sys, event, error);
|
||||
else
|
||||
return add_tracepoint_event(list, idx, sys, event);
|
||||
return add_tracepoint_event(list, idx, sys, event, error);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -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_name(struct list_head *list, char *name);
|
||||
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,
|
||||
struct list_head *list,
|
||||
u32 type, u64 config,
|
||||
|
@ -371,28 +371,30 @@ event_legacy_tracepoint:
|
||||
PE_NAME '-' PE_NAME ':' PE_NAME
|
||||
{
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct parse_events_error *error = data->error;
|
||||
struct list_head *list;
|
||||
char sys_name[128];
|
||||
snprintf(&sys_name, 128, "%s-%s", $1, $3);
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
||||
PE_NAME ':' PE_NAME
|
||||
{
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct parse_events_error *error = data->error;
|
||||
struct list_head *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->str = strdup("unknown tracepoint");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
$$ = list;
|
||||
|
@ -71,7 +71,7 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
|
||||
static struct machine *host_machine;
|
||||
|
||||
/* 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;
|
||||
|
||||
@ -101,7 +101,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void exit_symbol_maps(void)
|
||||
void exit_probe_symbol_maps(void)
|
||||
{
|
||||
if (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;
|
||||
|
||||
ret = init_symbol_maps(user);
|
||||
ret = init_probe_symbol_maps(user);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = __show_line_range(lr, module, user);
|
||||
exit_symbol_maps();
|
||||
exit_probe_symbol_maps();
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -941,7 +941,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
||||
int i, ret = 0;
|
||||
struct debuginfo *dinfo;
|
||||
|
||||
ret = init_symbol_maps(pevs->uprobes);
|
||||
ret = init_probe_symbol_maps(pevs->uprobes);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -958,7 +958,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
|
||||
|
||||
debuginfo__delete(dinfo);
|
||||
out:
|
||||
exit_symbol_maps();
|
||||
exit_probe_symbol_maps();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2262,7 +2262,7 @@ int show_perf_probe_events(struct strfilter *filter)
|
||||
|
||||
setup_pager();
|
||||
|
||||
ret = init_symbol_maps(false);
|
||||
ret = init_probe_symbol_maps(false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -2278,7 +2278,7 @@ int show_perf_probe_events(struct strfilter *filter)
|
||||
close(kp_fd);
|
||||
if (up_fd > 0)
|
||||
close(up_fd);
|
||||
exit_symbol_maps();
|
||||
exit_probe_symbol_maps();
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -2746,10 +2746,6 @@ int convert_perf_probe_events(struct perf_probe_event *pevs, int npevs)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
ret = init_symbol_maps(pevs->uprobes);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Loop 1: convert all events */
|
||||
for (i = 0; i < npevs; i++) {
|
||||
/* 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]);
|
||||
zfree(&pevs[i].tevs);
|
||||
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 ret;
|
||||
|
||||
ret = init_probe_symbol_maps(pevs->uprobes);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = convert_perf_probe_events(pevs, npevs);
|
||||
if (ret == 0)
|
||||
ret = apply_perf_probe_events(pevs, npevs);
|
||||
|
||||
cleanup_perf_probe_events(pevs, npevs);
|
||||
|
||||
exit_probe_symbol_maps();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2866,7 +2866,7 @@ int show_available_funcs(const char *target, struct strfilter *_filter,
|
||||
struct map *map;
|
||||
int ret;
|
||||
|
||||
ret = init_symbol_maps(user);
|
||||
ret = init_probe_symbol_maps(user);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -2896,7 +2896,7 @@ end:
|
||||
if (user) {
|
||||
map__put(map);
|
||||
}
|
||||
exit_symbol_maps();
|
||||
exit_probe_symbol_maps();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -110,6 +110,8 @@ struct variable_list {
|
||||
};
|
||||
|
||||
struct map;
|
||||
int init_probe_symbol_maps(bool user_only);
|
||||
void exit_probe_symbol_maps(void);
|
||||
|
||||
/* Command string to events */
|
||||
extern int parse_perf_probe_command(const char *cmd,
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <traceevent/event-parse.h>
|
||||
#include <api/fs/tracing_path.h>
|
||||
#include "trace-event.h"
|
||||
@ -66,6 +67,9 @@ void trace_event__cleanup(struct trace_event *t)
|
||||
pevent_free(t->pevent);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns pointer with encoded error via <linux/err.h> interface.
|
||||
*/
|
||||
static struct event_format*
|
||||
tp_format(const char *sys, const char *name)
|
||||
{
|
||||
@ -74,12 +78,14 @@ tp_format(const char *sys, const char *name)
|
||||
char path[PATH_MAX];
|
||||
size_t size;
|
||||
char *data;
|
||||
int err;
|
||||
|
||||
scnprintf(path, PATH_MAX, "%s/%s/%s/format",
|
||||
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);
|
||||
|
||||
@ -87,11 +93,14 @@ tp_format(const char *sys, const char *name)
|
||||
return event;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns pointer with encoded error via <linux/err.h> interface.
|
||||
*/
|
||||
struct event_format*
|
||||
trace_event__tp_format(const char *sys, const char *name)
|
||||
{
|
||||
if (!tevent_initialized && trace_event__init2())
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
return tp_format(sys, name);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user