Commit b29c6574 authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo-20160713' of...

Merge tag 'perf-core-for-mingo-20160713' 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:

- Finish merging initial SDT (Statically Defined Traces) support, see
  cset comments for details about how it all works (Masami Hiramatsu)

- Support attaching eBPF programs to tracepoints (Wang Nan)

Infrastructure changes:

- Fix up BITS_PER_LONG setting (Arnaldo Carvalho de Melo)

- Add fallback from ELF_C_READ_MMAP to ELF_C_READ in objtool, fixing
  the build in libelf implementations lacking that elf_begin() cmd,
  such as Alpine Linux's (Arnaldo Carvalho de Melo)

- Avoid checking code drift on busybox's diff in objtool (Arnaldo Carvalho de Melo)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 7b39cafb 8e5dc848
...@@ -62,7 +62,8 @@ FEATURE_TESTS_BASIC := \ ...@@ -62,7 +62,8 @@ FEATURE_TESTS_BASIC := \
zlib \ zlib \
lzma \ lzma \
get_cpuid \ get_cpuid \
bpf bpf \
sdt
# FEATURE_TESTS_BASIC + FEATURE_TESTS_EXTRA is the complete list # FEATURE_TESTS_BASIC + FEATURE_TESTS_EXTRA is the complete list
# of all feature tests # of all feature tests
......
...@@ -45,7 +45,8 @@ FILES= \ ...@@ -45,7 +45,8 @@ FILES= \
test-zlib.bin \ test-zlib.bin \
test-lzma.bin \ test-lzma.bin \
test-bpf.bin \ test-bpf.bin \
test-get_cpuid.bin test-get_cpuid.bin \
test-sdt.bin
FILES := $(addprefix $(OUTPUT),$(FILES)) FILES := $(addprefix $(OUTPUT),$(FILES))
...@@ -213,6 +214,9 @@ $(OUTPUT)test-get_cpuid.bin: ...@@ -213,6 +214,9 @@ $(OUTPUT)test-get_cpuid.bin:
$(OUTPUT)test-bpf.bin: $(OUTPUT)test-bpf.bin:
$(BUILD) $(BUILD)
$(OUTPUT)test-sdt.bin:
$(BUILD)
-include $(OUTPUT)*.d -include $(OUTPUT)*.d
############################### ###############################
......
...@@ -145,6 +145,10 @@ ...@@ -145,6 +145,10 @@
# include "test-libcrypto.c" # include "test-libcrypto.c"
#undef main #undef main
#define main main_test_sdt
# include "test-sdt.c"
#undef main
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
main_test_libpython(); main_test_libpython();
...@@ -178,6 +182,7 @@ int main(int argc, char *argv[]) ...@@ -178,6 +182,7 @@ int main(int argc, char *argv[])
main_test_get_cpuid(); main_test_get_cpuid();
main_test_bpf(); main_test_bpf();
main_test_libcrypto(); main_test_libcrypto();
main_test_sdt();
return 0; return 0;
} }
#include <sys/sdt.h>
int main(void)
{
DTRACE_PROBE(provider, name);
return 0;
}
...@@ -3,6 +3,24 @@ ...@@ -3,6 +3,24 @@
#include <uapi/asm-generic/bitsperlong.h> #include <uapi/asm-generic/bitsperlong.h>
/*
* In the kernel, where this file comes from, we can rely on CONFIG_64BIT,
* here we have to make amends with what the various compilers provides us
* to figure out if we're on a 64-bit machine...
*/
#ifdef __SIZEOF_LONG__
# if __SIZEOF_LONG__ == 8
# define CONFIG_64BIT
# endif
#else
# ifdef __WORDSIZE
# if __WORDSIZE == 64
# define CONFIG_64BIT
# endif
# else
# error Failed to determine BITS_PER_LONG value
# endif
#endif
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
#define BITS_PER_LONG 64 #define BITS_PER_LONG 64
...@@ -10,11 +28,7 @@ ...@@ -10,11 +28,7 @@
#define BITS_PER_LONG 32 #define BITS_PER_LONG 32
#endif /* CONFIG_64BIT */ #endif /* CONFIG_64BIT */
/* #if BITS_PER_LONG != __BITS_PER_LONG
* FIXME: The check currently breaks x86-64 build, so it's
* temporarily disabled. Please fix x86-64 and reenable
*/
#if 0 && BITS_PER_LONG != __BITS_PER_LONG
#error Inconsistent word size. Check asm/bitsperlong.h #error Inconsistent word size. Check asm/bitsperlong.h
#endif #endif
......
...@@ -90,6 +90,7 @@ static const char *libbpf_strerror_table[NR_ERRNO] = { ...@@ -90,6 +90,7 @@ static const char *libbpf_strerror_table[NR_ERRNO] = {
[ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading", [ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading",
[ERRCODE_OFFSET(PROG2BIG)] = "Program too big", [ERRCODE_OFFSET(PROG2BIG)] = "Program too big",
[ERRCODE_OFFSET(KVER)] = "Incorrect kernel version", [ERRCODE_OFFSET(KVER)] = "Incorrect kernel version",
[ERRCODE_OFFSET(PROGTYPE)] = "Kernel doesn't support this program type",
}; };
int libbpf_strerror(int err, char *buf, size_t size) int libbpf_strerror(int err, char *buf, size_t size)
...@@ -158,6 +159,7 @@ struct bpf_program { ...@@ -158,6 +159,7 @@ struct bpf_program {
char *section_name; char *section_name;
struct bpf_insn *insns; struct bpf_insn *insns;
size_t insns_cnt; size_t insns_cnt;
enum bpf_prog_type type;
struct { struct {
int insn_idx; int insn_idx;
...@@ -299,6 +301,7 @@ bpf_program__init(void *data, size_t size, char *name, int idx, ...@@ -299,6 +301,7 @@ bpf_program__init(void *data, size_t size, char *name, int idx,
prog->idx = idx; prog->idx = idx;
prog->instances.fds = NULL; prog->instances.fds = NULL;
prog->instances.nr = -1; prog->instances.nr = -1;
prog->type = BPF_PROG_TYPE_KPROBE;
return 0; return 0;
errout: errout:
...@@ -894,8 +897,8 @@ static int bpf_object__collect_reloc(struct bpf_object *obj) ...@@ -894,8 +897,8 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
} }
static int static int
load_program(struct bpf_insn *insns, int insns_cnt, load_program(enum bpf_prog_type type, struct bpf_insn *insns,
char *license, u32 kern_version, int *pfd) int insns_cnt, char *license, u32 kern_version, int *pfd)
{ {
int ret; int ret;
char *log_buf; char *log_buf;
...@@ -907,9 +910,8 @@ load_program(struct bpf_insn *insns, int insns_cnt, ...@@ -907,9 +910,8 @@ load_program(struct bpf_insn *insns, int insns_cnt,
if (!log_buf) if (!log_buf)
pr_warning("Alloc log buffer for bpf loader error, continue without log\n"); pr_warning("Alloc log buffer for bpf loader error, continue without log\n");
ret = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns, ret = bpf_load_program(type, insns, insns_cnt, license,
insns_cnt, license, kern_version, kern_version, log_buf, BPF_LOG_BUF_SIZE);
log_buf, BPF_LOG_BUF_SIZE);
if (ret >= 0) { if (ret >= 0) {
*pfd = ret; *pfd = ret;
...@@ -925,15 +927,27 @@ load_program(struct bpf_insn *insns, int insns_cnt, ...@@ -925,15 +927,27 @@ load_program(struct bpf_insn *insns, int insns_cnt,
pr_warning("-- BEGIN DUMP LOG ---\n"); pr_warning("-- BEGIN DUMP LOG ---\n");
pr_warning("\n%s\n", log_buf); pr_warning("\n%s\n", log_buf);
pr_warning("-- END LOG --\n"); pr_warning("-- END LOG --\n");
} else if (insns_cnt >= BPF_MAXINSNS) {
pr_warning("Program too large (%d insns), at most %d insns\n",
insns_cnt, BPF_MAXINSNS);
ret = -LIBBPF_ERRNO__PROG2BIG;
} else { } else {
if (insns_cnt >= BPF_MAXINSNS) { /* Wrong program type? */
pr_warning("Program too large (%d insns), at most %d insns\n", if (type != BPF_PROG_TYPE_KPROBE) {
insns_cnt, BPF_MAXINSNS); int fd;
ret = -LIBBPF_ERRNO__PROG2BIG;
} else if (log_buf) { fd = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns,
pr_warning("log buffer is empty\n"); insns_cnt, license, kern_version,
ret = -LIBBPF_ERRNO__KVER; NULL, 0);
if (fd >= 0) {
close(fd);
ret = -LIBBPF_ERRNO__PROGTYPE;
goto out;
}
} }
if (log_buf)
ret = -LIBBPF_ERRNO__KVER;
} }
out: out:
...@@ -968,7 +982,7 @@ bpf_program__load(struct bpf_program *prog, ...@@ -968,7 +982,7 @@ bpf_program__load(struct bpf_program *prog,
pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n", pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n",
prog->section_name, prog->instances.nr); prog->section_name, prog->instances.nr);
} }
err = load_program(prog->insns, prog->insns_cnt, err = load_program(prog->type, prog->insns, prog->insns_cnt,
license, kern_version, &fd); license, kern_version, &fd);
if (!err) if (!err)
prog->instances.fds[0] = fd; prog->instances.fds[0] = fd;
...@@ -997,7 +1011,7 @@ bpf_program__load(struct bpf_program *prog, ...@@ -997,7 +1011,7 @@ bpf_program__load(struct bpf_program *prog,
continue; continue;
} }
err = load_program(result.new_insn_ptr, err = load_program(prog->type, result.new_insn_ptr,
result.new_insn_cnt, result.new_insn_cnt,
license, kern_version, &fd); license, kern_version, &fd);
...@@ -1316,6 +1330,44 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n) ...@@ -1316,6 +1330,44 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n)
return fd; return fd;
} }
static void bpf_program__set_type(struct bpf_program *prog,
enum bpf_prog_type type)
{
prog->type = type;
}
int bpf_program__set_tracepoint(struct bpf_program *prog)
{
if (!prog)
return -EINVAL;
bpf_program__set_type(prog, BPF_PROG_TYPE_TRACEPOINT);
return 0;
}
int bpf_program__set_kprobe(struct bpf_program *prog)
{
if (!prog)
return -EINVAL;
bpf_program__set_type(prog, BPF_PROG_TYPE_KPROBE);
return 0;
}
static bool bpf_program__is_type(struct bpf_program *prog,
enum bpf_prog_type type)
{
return prog ? (prog->type == type) : false;
}
bool bpf_program__is_tracepoint(struct bpf_program *prog)
{
return bpf_program__is_type(prog, BPF_PROG_TYPE_TRACEPOINT);
}
bool bpf_program__is_kprobe(struct bpf_program *prog)
{
return bpf_program__is_type(prog, BPF_PROG_TYPE_KPROBE);
}
int bpf_map__fd(struct bpf_map *map) int bpf_map__fd(struct bpf_map *map)
{ {
return map ? map->fd : -EINVAL; return map ? map->fd : -EINVAL;
......
...@@ -39,6 +39,7 @@ enum libbpf_errno { ...@@ -39,6 +39,7 @@ enum libbpf_errno {
LIBBPF_ERRNO__VERIFY, /* Kernel verifier blocks program loading */ LIBBPF_ERRNO__VERIFY, /* Kernel verifier blocks program loading */
LIBBPF_ERRNO__PROG2BIG, /* Program too big */ LIBBPF_ERRNO__PROG2BIG, /* Program too big */
LIBBPF_ERRNO__KVER, /* Incorrect kernel version */ LIBBPF_ERRNO__KVER, /* Incorrect kernel version */
LIBBPF_ERRNO__PROGTYPE, /* Kernel doesn't support this program type */
__LIBBPF_ERRNO__END, __LIBBPF_ERRNO__END,
}; };
...@@ -164,6 +165,15 @@ int bpf_program__set_prep(struct bpf_program *prog, int nr_instance, ...@@ -164,6 +165,15 @@ int bpf_program__set_prep(struct bpf_program *prog, int nr_instance,
int bpf_program__nth_fd(struct bpf_program *prog, int n); int bpf_program__nth_fd(struct bpf_program *prog, int n);
/*
* Adjust type of bpf program. Default is kprobe.
*/
int bpf_program__set_tracepoint(struct bpf_program *prog);
int bpf_program__set_kprobe(struct bpf_program *prog);
bool bpf_program__is_tracepoint(struct bpf_program *prog);
bool bpf_program__is_kprobe(struct bpf_program *prog);
/* /*
* We don't need __attribute__((packed)) now since it is * We don't need __attribute__((packed)) now since it is
* unnecessary for 'bpf_map_def' because they are all aligned. * unnecessary for 'bpf_map_def' because they are all aligned.
......
...@@ -41,8 +41,11 @@ include $(srctree)/tools/build/Makefile.include ...@@ -41,8 +41,11 @@ include $(srctree)/tools/build/Makefile.include
$(OBJTOOL_IN): fixdep FORCE $(OBJTOOL_IN): fixdep FORCE
@$(MAKE) $(build)=objtool @$(MAKE) $(build)=objtool
# Busybox's diff doesn't have -I, avoid warning in that case
#
$(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN) $(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN)
@(test -d ../../kernel -a -d ../../tools -a -d ../objtool && (( \ @(diff -I 2>&1 | grep -q 'option requires an argument' && \
test -d ../../kernel -a -d ../../tools -a -d ../objtool && (( \
diff -I'^#include' arch/x86/insn/insn.c ../../arch/x86/lib/insn.c >/dev/null && \ diff -I'^#include' arch/x86/insn/insn.c ../../arch/x86/lib/insn.c >/dev/null && \
diff -I'^#include' arch/x86/insn/inat.c ../../arch/x86/lib/inat.c >/dev/null && \ diff -I'^#include' arch/x86/insn/inat.c ../../arch/x86/lib/inat.c >/dev/null && \
diff arch/x86/insn/x86-opcode-map.txt ../../arch/x86/lib/x86-opcode-map.txt >/dev/null && \ diff arch/x86/insn/x86-opcode-map.txt ../../arch/x86/lib/x86-opcode-map.txt >/dev/null && \
......
...@@ -30,6 +30,13 @@ ...@@ -30,6 +30,13 @@
#include "elf.h" #include "elf.h"
#include "warn.h" #include "warn.h"
/*
* Fallback for systems without this "read, mmaping if possible" cmd.
*/
#ifndef ELF_C_READ_MMAP
#define ELF_C_READ_MMAP ELF_C_READ
#endif
struct section *find_section_by_name(struct elf *elf, const char *name) struct section *find_section_by_name(struct elf *elf, const char *name)
{ {
struct section *sec; struct section *sec;
......
...@@ -151,6 +151,10 @@ Probe points are defined by following syntax. ...@@ -151,6 +151,10 @@ Probe points are defined by following syntax.
3) Define event based on source file with lazy pattern 3) Define event based on source file with lazy pattern
[[GROUP:]EVENT=]SRC;PTN [ARG ...] [[GROUP:]EVENT=]SRC;PTN [ARG ...]
4) Pre-defined SDT events or cached event with name
%[sdt_PROVIDER:]SDTEVENT
or,
sdt_PROVIDER:SDTEVENT
'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. You can also specify a group name by 'GROUP', if omitted, set 'probe' is used for kprobe and 'probe_<bin>' is used for uprobe. 'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. You can also specify a group name by 'GROUP', if omitted, set 'probe' is used for kprobe and 'probe_<bin>' is used for uprobe.
Note that using existing group name can conflict with other events. Especially, using the group name reserved for kernel modules can hide embedded events in the Note that using existing group name can conflict with other events. Especially, using the group name reserved for kernel modules can hide embedded events in the
...@@ -158,6 +162,11 @@ modules. ...@@ -158,6 +162,11 @@ modules.
'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function. 'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function.
It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern. It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern.
'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT). 'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT).
'SDTEVENT' and 'PROVIDER' is the pre-defined event name which is defined by user SDT (Statically Defined Tracing) or the pre-cached probes with event name.
Note that before using the SDT event, the target binary (on which SDT events are defined) must be scanned by linkperf:perf-buildid-cache[1] to make SDT events as cached events.
For details of the SDT, see below.
https://sourceware.org/gdb/onlinedocs/gdb/Static-Probe-Points.html
PROBE ARGUMENT PROBE ARGUMENT
-------------- --------------
...@@ -237,4 +246,4 @@ Add probes at malloc() function on libc ...@@ -237,4 +246,4 @@ Add probes at malloc() function on libc
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-trace[1], linkperf:perf-record[1] linkperf:perf-trace[1], linkperf:perf-record[1], linkperf:perf-buildid-cache[1]
...@@ -81,6 +81,9 @@ include ../scripts/utilities.mak ...@@ -81,6 +81,9 @@ include ../scripts/utilities.mak
# #
# Define NO_LIBBPF if you do not want BPF support # Define NO_LIBBPF if you do not want BPF support
# #
# Define NO_SDT if you do not want to define SDT event in perf tools,
# note that it doesn't disable SDT scanning support.
#
# Define FEATURES_DUMP to provide features detection dump file # Define FEATURES_DUMP to provide features detection dump file
# and bypass the feature detection # and bypass the feature detection
......
...@@ -25,7 +25,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -25,7 +25,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_END() OPT_END()
}; };
const char * const list_usage[] = { const char * const list_usage[] = {
"perf list [hw|sw|cache|tracepoint|pmu|event_glob]", "perf list [hw|sw|cache|tracepoint|pmu|sdt|event_glob]",
NULL NULL
}; };
...@@ -62,6 +62,8 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -62,6 +62,8 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
print_hwcache_events(NULL, raw_dump); print_hwcache_events(NULL, raw_dump);
else if (strcmp(argv[i], "pmu") == 0) else if (strcmp(argv[i], "pmu") == 0)
print_pmu_events(NULL, raw_dump); print_pmu_events(NULL, raw_dump);
else if (strcmp(argv[i], "sdt") == 0)
print_sdt_events(NULL, NULL, raw_dump);
else if ((sep = strchr(argv[i], ':')) != NULL) { else if ((sep = strchr(argv[i], ':')) != NULL) {
int sep_idx; int sep_idx;
...@@ -76,6 +78,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -76,6 +78,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
s[sep_idx] = '\0'; s[sep_idx] = '\0';
print_tracepoint_events(s, s + sep_idx + 1, raw_dump); print_tracepoint_events(s, s + sep_idx + 1, raw_dump);
print_sdt_events(s, s + sep_idx + 1, raw_dump);
free(s); free(s);
} else { } else {
if (asprintf(&s, "*%s*", argv[i]) < 0) { if (asprintf(&s, "*%s*", argv[i]) < 0) {
...@@ -89,6 +92,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -89,6 +92,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
print_hwcache_events(s, raw_dump); print_hwcache_events(s, raw_dump);
print_pmu_events(s, raw_dump); print_pmu_events(s, raw_dump);
print_tracepoint_events(NULL, s, raw_dump); print_tracepoint_events(NULL, s, raw_dump);
print_sdt_events(NULL, s, raw_dump);
free(s); free(s);
} }
} }
......
...@@ -370,7 +370,7 @@ static int del_perf_probe_caches(struct strfilter *filter) ...@@ -370,7 +370,7 @@ static int del_perf_probe_caches(struct strfilter *filter)
struct str_node *nd; struct str_node *nd;
int ret; int ret;
bidlist = build_id_cache__list_all(); bidlist = build_id_cache__list_all(false);
if (!bidlist) { if (!bidlist) {
ret = -errno; ret = -errno;
pr_debug("Failed to get buildids: %d\n", ret); pr_debug("Failed to get buildids: %d\n", ret);
......
...@@ -355,6 +355,16 @@ ifndef NO_LIBELF ...@@ -355,6 +355,16 @@ ifndef NO_LIBELF
endif # NO_LIBBPF endif # NO_LIBBPF
endif # NO_LIBELF endif # NO_LIBELF
ifndef NO_SDT
ifneq ($(feature-sdt), 1)
msg := $(warning No sys/sdt.h found, no SDT events are defined, please install systemtap-sdt-devel or systemtap-sdt-dev);
NO_SDT := 1;
else
CFLAGS += -DHAVE_SDT_EVENT
$(call detected,CONFIG_SDT_EVENT)
endif
endif
ifdef PERF_HAVE_JITDUMP ifdef PERF_HAVE_JITDUMP
ifndef NO_DWARF ifndef NO_DWARF
$(call detected,CONFIG_JITDUMP) $(call detected,CONFIG_JITDUMP)
......
...@@ -39,6 +39,7 @@ perf-y += stat.o ...@@ -39,6 +39,7 @@ perf-y += stat.o
perf-y += event_update.o perf-y += event_update.o
perf-y += event-times.o perf-y += event-times.o
perf-y += backward-ring-buffer.o perf-y += backward-ring-buffer.o
perf-y += sdt.o
$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
$(call rule_mkdir) $(call rule_mkdir)
......
...@@ -217,6 +217,10 @@ static struct test generic_tests[] = { ...@@ -217,6 +217,10 @@ static struct test generic_tests[] = {
.desc = "Test cpu map print", .desc = "Test cpu map print",
.func = test__cpu_map_print, .func = test__cpu_map_print,
}, },
{
.desc = "Test SDT event probing",
.func = test__sdt_event,
},
{ {
.func = NULL, .func = NULL,
}, },
......
...@@ -82,6 +82,7 @@ make_no_auxtrace := NO_AUXTRACE=1 ...@@ -82,6 +82,7 @@ make_no_auxtrace := NO_AUXTRACE=1
make_no_libbpf := NO_LIBBPF=1 make_no_libbpf := NO_LIBBPF=1
make_no_libcrypto := NO_LIBCRYPTO=1 make_no_libcrypto := NO_LIBCRYPTO=1
make_with_babeltrace:= LIBBABELTRACE=1 make_with_babeltrace:= LIBBABELTRACE=1
make_no_sdt := NO_SDT=1
make_tags := tags make_tags := tags
make_cscope := cscope make_cscope := cscope
make_help := help make_help := help
...@@ -105,7 +106,7 @@ make_minimal := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1 ...@@ -105,7 +106,7 @@ make_minimal := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1
make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1 make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1
make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1 make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1
make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1 make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1
make_minimal += NO_LIBCRYPTO=1 make_minimal += NO_LIBCRYPTO=1 NO_SDT=1
# $(run) contains all available tests # $(run) contains all available tests
run := make_pure run := make_pure
......
#include <stdio.h>
#include <sys/epoll.h>
#include <util/util.h>
#include <util/evlist.h>
#include <linux/filter.h>
#include "tests.h"
#include "debug.h"
#include "probe-file.h"
#include "build-id.h"
/* To test SDT event, we need libelf support to scan elf binary */
#if defined(HAVE_SDT_EVENT) && defined(HAVE_LIBELF_SUPPORT)
#include <sys/sdt.h>
static int target_function(void)
{
DTRACE_PROBE(perf, test_target);
return TEST_OK;
}
/* Copied from builtin-buildid-cache.c */
static int build_id_cache__add_file(const char *filename)
{
char sbuild_id[SBUILD_ID_SIZE];
u8 build_id[BUILD_ID_SIZE];
int err;
err = filename__read_build_id(filename, &build_id, sizeof(build_id));
if (err < 0) {
pr_debug("Failed to read build id of %s\n", filename);
return err;
}
build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
err = build_id_cache__add_s(sbuild_id, filename, false, false);
if (err < 0)
pr_debug("Failed to add build id cache of %s\n", filename);
return err;
}
static char *get_self_path(void)
{
char *buf = calloc(PATH_MAX, sizeof(char));
if (buf && readlink("/proc/self/exe", buf, PATH_MAX) < 0) {
pr_debug("Failed to get correct path of perf\n");
free(buf);
return NULL;
}
return buf;
}
static int search_cached_probe(const char *target,
const char *group, const char *event)
{
struct probe_cache *cache = probe_cache__new(target);
int ret = 0;
if (!cache) {
pr_debug("Failed to open probe cache of %s\n", target);
return -EINVAL;
}
if (!probe_cache__find_by_name(cache, group, event)) {
pr_debug("Failed to find %s:%s in the cache\n", group, event);
ret = -ENOENT;
}
probe_cache__delete(cache);
return ret;
}
int test__sdt_event(int subtests __maybe_unused)
{
int ret = TEST_FAIL;
char __tempdir[] = "./test-buildid-XXXXXX";
char *tempdir = NULL, *myself = get_self_path();
if (myself == NULL || mkdtemp(__tempdir) == NULL) {
pr_debug("Failed to make a tempdir for build-id cache\n");
goto error;
}
/* Note that buildid_dir must be an absolute path */
tempdir = realpath(__tempdir, NULL);
/* At first, scan itself */
set_buildid_dir(tempdir);
if (build_id_cache__add_file(myself) < 0)
goto error_rmdir;
/* Open a cache and make sure the SDT is stored */
if (search_cached_probe(myself, "sdt_perf", "test_target") < 0)
goto error_rmdir;
/* TBD: probing on the SDT event and collect logs */
/* Call the target and get an event */
ret = target_function();
error_rmdir:
/* Cleanup temporary buildid dir */
rm_rf(tempdir);
error:
free(tempdir);
free(myself);
return ret;
}
#else
int test__sdt_event(int subtests __maybe_unused)
{
pr_debug("Skip SDT event test because SDT support is not compiled\n");
return TEST_SKIP;
}
#endif
...@@ -88,6 +88,7 @@ int test__event_update(int subtest); ...@@ -88,6 +88,7 @@ int test__event_update(int subtest);
int test__event_times(int subtest); int test__event_times(int subtest);
int test__backward_ring_buffer(int subtest); int test__backward_ring_buffer(int subtest);
int test__cpu_map_print(int subtest); int test__cpu_map_print(int subtest);
int test__sdt_event(int subtest);
#if defined(__arm__) || defined(__aarch64__) #if defined(__arm__) || defined(__aarch64__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT #ifdef HAVE_DWARF_UNWIND_SUPPORT
......
...@@ -37,6 +37,9 @@ DEFINE_PRINT_FN(info, 1) ...@@ -37,6 +37,9 @@ DEFINE_PRINT_FN(info, 1)
DEFINE_PRINT_FN(debug, 1) DEFINE_PRINT_FN(debug, 1)
struct bpf_prog_priv { struct bpf_prog_priv {
bool is_tp;
char *sys_name;
char *evt_name;
struct perf_probe_event pev; struct perf_probe_event pev;
bool need_prologue; bool need_prologue;
struct bpf_insn *insns_buf; struct bpf_insn *insns_buf;
...@@ -118,6 +121,8 @@ clear_prog_priv(struct bpf_program *prog __maybe_unused, ...@@ -118,6 +121,8 @@ clear_prog_priv(struct bpf_program *prog __maybe_unused,
cleanup_perf_probe_events(&priv->pev, 1); cleanup_perf_probe_events(&priv->pev, 1);
zfree(&priv->insns_buf); zfree(&priv->insns_buf);
zfree(&priv->type_mapping); zfree(&priv->type_mapping);
zfree(&priv->sys_name);
zfree(&priv->evt_name);
free(priv); free(priv);
} }
...@@ -269,7 +274,8 @@ parse_prog_config_kvpair(const char *config_str, struct perf_probe_event *pev) ...@@ -269,7 +274,8 @@ parse_prog_config_kvpair(const char *config_str, struct perf_probe_event *pev)
} }
static int static int
parse_prog_config(const char *config_str, struct perf_probe_event *pev) parse_prog_config(const char *config_str, const char **p_main_str,
bool *is_tp, struct perf_probe_event *pev)
{ {
int err; int err;
const char *main_str = parse_prog_config_kvpair(config_str, pev); const char *main_str = parse_prog_config_kvpair(config_str, pev);
...@@ -277,6 +283,22 @@ parse_prog_config(const char *config_str, struct perf_probe_event *pev) ...@@ -277,6 +283,22 @@ parse_prog_config(const char *config_str, struct perf_probe_event *pev)
if (IS_ERR(main_str)) if (IS_ERR(main_str))
return PTR_ERR(main_str); return PTR_ERR(main_str);
*p_main_str = main_str;
if (!strchr(main_str, '=')) {
/* Is a tracepoint event? */
const char *s = strchr(main_str, ':');
if (!s) {
pr_debug("bpf: '%s' is not a valid tracepoint\n",
config_str);
return -BPF_LOADER_ERRNO__CONFIG;
}
*is_tp = true;
return 0;
}
*is_tp = false;
err = parse_perf_probe_command(main_str, pev); err = parse_perf_probe_command(main_str, pev);
if (err < 0) { if (err < 0) {
pr_debug("bpf: '%s' is not a valid config string\n", pr_debug("bpf: '%s' is not a valid config string\n",
...@@ -292,7 +314,8 @@ config_bpf_program(struct bpf_program *prog) ...@@ -292,7 +314,8 @@ config_bpf_program(struct bpf_program *prog)
{ {
struct perf_probe_event *pev = NULL; struct perf_probe_event *pev = NULL;
struct bpf_prog_priv *priv = NULL; struct bpf_prog_priv *priv = NULL;
const char *config_str; const char *config_str, *main_str;
bool is_tp = false;
int err; int err;
/* Initialize per-program probing setting */ /* Initialize per-program probing setting */
...@@ -313,10 +336,19 @@ config_bpf_program(struct bpf_program *prog) ...@@ -313,10 +336,19 @@ config_bpf_program(struct bpf_program *prog)
pev = &priv->pev; pev = &priv->pev;
pr_debug("bpf: config program '%s'\n", config_str); pr_debug("bpf: config program '%s'\n", config_str);
err = parse_prog_config(config_str, pev); err = parse_prog_config(config_str, &main_str, &is_tp, pev);
if (err) if (err)
goto errout; goto errout;
if (is_tp) {
char *s = strchr(main_str, ':');
priv->is_tp = true;
priv->sys_name = strndup(main_str, s - main_str);
priv->evt_name = strdup(s + 1);
goto set_priv;
}
if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) { if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) {
pr_debug("bpf: '%s': group for event is set and not '%s'.\n", pr_debug("bpf: '%s': group for event is set and not '%s'.\n",
config_str, PERF_BPF_PROBE_GROUP); config_str, PERF_BPF_PROBE_GROUP);
...@@ -339,6 +371,7 @@ config_bpf_program(struct bpf_program *prog) ...@@ -339,6 +371,7 @@ config_bpf_program(struct bpf_program *prog)
} }
pr_debug("bpf: config '%s' is ok\n", config_str); pr_debug("bpf: config '%s' is ok\n", config_str);
set_priv:
err = bpf_program__set_priv(prog, priv, clear_prog_priv); err = bpf_program__set_priv(prog, priv, clear_prog_priv);
if (err) { if (err) {
pr_debug("Failed to set priv for program '%s'\n", config_str); pr_debug("Failed to set priv for program '%s'\n", config_str);
...@@ -387,7 +420,7 @@ preproc_gen_prologue(struct bpf_program *prog, int n, ...@@ -387,7 +420,7 @@ preproc_gen_prologue(struct bpf_program *prog, int n,
size_t prologue_cnt = 0; size_t prologue_cnt = 0;
int i, err; int i, err;
if (IS_ERR(priv) || !priv) if (IS_ERR(priv) || !priv || priv->is_tp)
goto errout; goto errout;
pev = &priv->pev; pev = &priv->pev;
...@@ -544,6 +577,11 @@ static int hook_load_preprocessor(struct bpf_program *prog) ...@@ -544,6 +577,11 @@ static int hook_load_preprocessor(struct bpf_program *prog)
return -BPF_LOADER_ERRNO__INTERNAL; return -BPF_LOADER_ERRNO__INTERNAL;
} }
if (priv->is_tp) {
priv->need_prologue = false;
return 0;
}
pev = &priv->pev; pev = &priv->pev;
for (i = 0; i < pev->ntevs; i++) { for (i = 0; i < pev->ntevs; i++) {
struct probe_trace_event *tev = &pev->tevs[i]; struct probe_trace_event *tev = &pev->tevs[i];
...@@ -610,6 +648,13 @@ int bpf__probe(struct bpf_object *obj) ...@@ -610,6 +648,13 @@ int bpf__probe(struct bpf_object *obj)
err = PTR_ERR(priv); err = PTR_ERR(priv);
goto out; goto out;
} }
if (priv->is_tp) {
bpf_program__set_tracepoint(prog);
continue;
}
bpf_program__set_kprobe(prog);
pev = &priv->pev; pev = &priv->pev;
err = convert_perf_probe_events(pev, 1); err = convert_perf_probe_events(pev, 1);
...@@ -650,7 +695,7 @@ int bpf__unprobe(struct bpf_object *obj) ...@@ -650,7 +695,7 @@ int bpf__unprobe(struct bpf_object *obj)
struct bpf_prog_priv *priv = bpf_program__priv(prog); struct bpf_prog_priv *priv = bpf_program__priv(prog);
int i; int i;
if (IS_ERR(priv) || !priv) if (IS_ERR(priv) || !priv || priv->is_tp)
continue; continue;
for (i = 0; i < priv->pev.ntevs; i++) { for (i = 0; i < priv->pev.ntevs; i++) {
...@@ -693,9 +738,9 @@ int bpf__load(struct bpf_object *obj) ...@@ -693,9 +738,9 @@ int bpf__load(struct bpf_object *obj)
return 0; return 0;
} }
int bpf__foreach_tev(struct bpf_object *obj, int bpf__foreach_event(struct bpf_object *obj,
bpf_prog_iter_callback_t func, bpf_prog_iter_callback_t func,
void *arg) void *arg)
{ {
struct bpf_program *prog; struct bpf_program *prog;
int err; int err;
...@@ -711,6 +756,16 @@ int bpf__foreach_tev(struct bpf_object *obj, ...@@ -711,6 +756,16 @@ int bpf__foreach_tev(struct bpf_object *obj,
return -BPF_LOADER_ERRNO__INTERNAL; return -BPF_LOADER_ERRNO__INTERNAL;
} }
if (priv->is_tp) {
fd = bpf_program__fd(prog);
err = (*func)(priv->sys_name, priv->evt_name, fd, arg);
if (err) {
pr_debug("bpf: tracepoint call back failed, stop iterate\n");
return err;
}
continue;
}
pev = &priv->pev; pev = &priv->pev;
for (i = 0; i < pev->ntevs; i++) { for (i = 0; i < pev->ntevs; i++) {
tev = &pev->tevs[i]; tev = &pev->tevs[i];
...@@ -728,7 +783,7 @@ int bpf__foreach_tev(struct bpf_object *obj, ...@@ -728,7 +783,7 @@ int bpf__foreach_tev(struct bpf_object *obj,
return fd; return fd;
} }
err = (*func)(tev, fd, arg); err = (*func)(tev->group, tev->event, fd, arg);
if (err) { if (err) {
pr_debug("bpf: call back failed, stop iterate\n"); pr_debug("bpf: call back failed, stop iterate\n");
return err; return err;
......
...@@ -46,7 +46,7 @@ struct bpf_object; ...@@ -46,7 +46,7 @@ struct bpf_object;
struct parse_events_term; struct parse_events_term;
#define PERF_BPF_PROBE_GROUP "perf_bpf_probe" #define PERF_BPF_PROBE_GROUP "perf_bpf_probe"
typedef int (*bpf_prog_iter_callback_t)(struct probe_trace_event *tev, typedef int (*bpf_prog_iter_callback_t)(const char *group, const char *event,
int fd, void *arg); int fd, void *arg);
#ifdef HAVE_LIBBPF_SUPPORT #ifdef HAVE_LIBBPF_SUPPORT
...@@ -67,8 +67,8 @@ int bpf__strerror_probe(struct bpf_object *obj, int err, ...@@ -67,8 +67,8 @@ int bpf__strerror_probe(struct bpf_object *obj, int err,
int bpf__load(struct bpf_object *obj); int bpf__load(struct bpf_object *obj);
int bpf__strerror_load(struct bpf_object *obj, int err, int bpf__strerror_load(struct bpf_object *obj, int err,
char *buf, size_t size); char *buf, size_t size);
int bpf__foreach_tev(struct bpf_object *obj, int bpf__foreach_event(struct bpf_object *obj,
bpf_prog_iter_callback_t func, void *arg); bpf_prog_iter_callback_t func, void *arg);
int bpf__config_obj(struct bpf_object *obj, struct parse_events_term *term, int bpf__config_obj(struct bpf_object *obj, struct parse_events_term *term,
struct perf_evlist *evlist, int *error_pos); struct perf_evlist *evlist, int *error_pos);
...@@ -107,9 +107,9 @@ static inline int bpf__unprobe(struct bpf_object *obj __maybe_unused) { return 0 ...@@ -107,9 +107,9 @@ static inline int bpf__unprobe(struct bpf_object *obj __maybe_unused) { return 0
static inline int bpf__load(struct bpf_object *obj __maybe_unused) { return 0; } static inline int bpf__load(struct bpf_object *obj __maybe_unused) { return 0; }
static inline int static inline int
bpf__foreach_tev(struct bpf_object *obj __maybe_unused, bpf__foreach_event(struct bpf_object *obj __maybe_unused,
bpf_prog_iter_callback_t func __maybe_unused, bpf_prog_iter_callback_t func __maybe_unused,
void *arg __maybe_unused) void *arg __maybe_unused)
{ {
return 0; return 0;
} }
......
...@@ -206,6 +206,31 @@ char *build_id_cache__origname(const char *sbuild_id) ...@@ -206,6 +206,31 @@ char *build_id_cache__origname(const char *sbuild_id)
return ret; return ret;
} }
/* Check if the given build_id cache is valid on current running system */
static bool build_id_cache__valid_id(char *sbuild_id)
{
char real_sbuild_id[SBUILD_ID_SIZE] = "";
char *pathname;
int ret = 0;
bool result = false;
pathname = build_id_cache__origname(sbuild_id);
if (!pathname)
return false;
if (!strcmp(pathname, DSO__NAME_KALLSYMS))
ret = sysfs__sprintf_build_id("/", real_sbuild_id);
else if (pathname[0] == '/')
ret = filename__sprintf_build_id(pathname, real_sbuild_id);
else
ret = -EINVAL; /* Should we support other special DSO cache? */
if (ret >= 0)
result = (strcmp(sbuild_id, real_sbuild_id) == 0);
free(pathname);
return result;
}
static const char *build_id_cache__basename(bool is_kallsyms, bool is_vdso) static const char *build_id_cache__basename(bool is_kallsyms, bool is_vdso)
{ {
return is_kallsyms ? "kallsyms" : (is_vdso ? "vdso" : "elf"); return is_kallsyms ? "kallsyms" : (is_vdso ? "vdso" : "elf");
...@@ -433,13 +458,17 @@ static bool lsdir_bid_tail_filter(const char *name __maybe_unused, ...@@ -433,13 +458,17 @@ static bool lsdir_bid_tail_filter(const char *name __maybe_unused,
return (i == SBUILD_ID_SIZE - 3) && (d->d_name[i] == '\0'); return (i == SBUILD_ID_SIZE - 3) && (d->d_name[i] == '\0');
} }
struct strlist *build_id_cache__list_all(void) struct strlist *build_id_cache__list_all(bool validonly)
{ {
struct strlist *toplist, *linklist = NULL, *bidlist; struct strlist *toplist, *linklist = NULL, *bidlist;
struct str_node *nd, *nd2; struct str_node *nd, *nd2;
char *topdir, *linkdir = NULL; char *topdir, *linkdir = NULL;
char sbuild_id[SBUILD_ID_SIZE]; char sbuild_id[SBUILD_ID_SIZE];
/* for filename__ functions */
if (validonly)
symbol__init(NULL);
/* Open the top-level directory */ /* Open the top-level directory */
if (asprintf(&topdir, "%s/.build-id/", buildid_dir) < 0) if (asprintf(&topdir, "%s/.build-id/", buildid_dir) < 0)
return NULL; return NULL;
...@@ -470,6 +499,8 @@ struct strlist *build_id_cache__list_all(void) ...@@ -470,6 +499,8 @@ struct strlist *build_id_cache__list_all(void)
if (snprintf(sbuild_id, SBUILD_ID_SIZE, "%s%s", if (snprintf(sbuild_id, SBUILD_ID_SIZE, "%s%s",
nd->s, nd2->s) != SBUILD_ID_SIZE - 1) nd->s, nd2->s) != SBUILD_ID_SIZE - 1)
goto err_out; goto err_out;
if (validonly && !build_id_cache__valid_id(sbuild_id))
continue;
if (strlist__add(bidlist, sbuild_id) < 0) if (strlist__add(bidlist, sbuild_id) < 0)
goto err_out; goto err_out;
} }
...@@ -492,6 +523,49 @@ struct strlist *build_id_cache__list_all(void) ...@@ -492,6 +523,49 @@ struct strlist *build_id_cache__list_all(void)
goto out_free; goto out_free;
} }
static bool str_is_build_id(const char *maybe_sbuild_id, size_t len)
{
size_t i;
for (i = 0; i < len; i++) {
if (!isxdigit(maybe_sbuild_id[i]))
return false;
}
return true;
}
/* Return the valid complete build-id */
char *build_id_cache__complement(const char *incomplete_sbuild_id)
{
struct strlist *bidlist;
struct str_node *nd, *cand = NULL;
char *sbuild_id = NULL;
size_t len = strlen(incomplete_sbuild_id);
if (len >= SBUILD_ID_SIZE ||
!str_is_build_id(incomplete_sbuild_id, len))
return NULL;
bidlist = build_id_cache__list_all(true);
if (!bidlist)
return NULL;
strlist__for_each_entry(nd, bidlist) {
if (strncmp(nd->s, incomplete_sbuild_id, len) != 0)
continue;
if (cand) { /* Error: There are more than 2 candidates. */
cand = NULL;
break;
}
cand = nd;
}
if (cand)
sbuild_id = strdup(cand->s);
strlist__delete(bidlist);
return sbuild_id;
}
char *build_id_cache__cachedir(const char *sbuild_id, const char *name, char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
bool is_kallsyms, bool is_vdso) bool is_kallsyms, bool is_vdso)
{ {
......
...@@ -34,7 +34,8 @@ char *build_id_cache__origname(const char *sbuild_id); ...@@ -34,7 +34,8 @@ char *build_id_cache__origname(const char *sbuild_id);
char *build_id_cache__linkname(const char *sbuild_id, char *bf, size_t size); char *build_id_cache__linkname(const char *sbuild_id, char *bf, size_t size);
char *build_id_cache__cachedir(const char *sbuild_id, const char *name, char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
bool is_kallsyms, bool is_vdso); bool is_kallsyms, bool is_vdso);
struct strlist *build_id_cache__list_all(void); struct strlist *build_id_cache__list_all(bool validonly);
char *build_id_cache__complement(const char *incomplete_sbuild_id);
int build_id_cache__list_build_ids(const char *pathname, int build_id_cache__list_build_ids(const char *pathname,
struct strlist **result); struct strlist **result);
bool build_id_cache__cached(const char *sbuild_id); bool build_id_cache__cached(const char *sbuild_id);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "pmu.h" #include "pmu.h"
#include "thread_map.h" #include "thread_map.h"
#include "cpumap.h" #include "cpumap.h"
#include "probe-file.h"
#include "asm/bug.h" #include "asm/bug.h"
#define MAX_NAME_LEN 100 #define MAX_NAME_LEN 100
...@@ -436,7 +437,7 @@ int parse_events_add_cache(struct list_head *list, int *idx, ...@@ -436,7 +437,7 @@ int parse_events_add_cache(struct list_head *list, int *idx,
} }
static void tracepoint_error(struct parse_events_error *e, int err, static void tracepoint_error(struct parse_events_error *e, int err,
char *sys, char *name) const char *sys, const char *name)
{ {
char help[BUFSIZ]; char help[BUFSIZ];
...@@ -466,7 +467,7 @@ static void tracepoint_error(struct parse_events_error *e, int err, ...@@ -466,7 +467,7 @@ static void tracepoint_error(struct parse_events_error *e, int err,
} }
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, const char *sys_name, const char *evt_name,
struct parse_events_error *err, struct parse_events_error *err,
struct list_head *head_config) struct list_head *head_config)
{ {
...@@ -491,7 +492,7 @@ static int add_tracepoint(struct list_head *list, int *idx, ...@@ -491,7 +492,7 @@ static int add_tracepoint(struct list_head *list, int *idx,
} }
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, const char *sys_name, const char *evt_name,
struct parse_events_error *err, struct parse_events_error *err,
struct list_head *head_config) struct list_head *head_config)
{ {
...@@ -533,7 +534,7 @@ static int add_tracepoint_multi_event(struct list_head *list, int *idx, ...@@ -533,7 +534,7 @@ 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, const char *sys_name, const char *evt_name,
struct parse_events_error *err, struct parse_events_error *err,
struct list_head *head_config) struct list_head *head_config)
{ {
...@@ -545,7 +546,7 @@ static int add_tracepoint_event(struct list_head *list, int *idx, ...@@ -545,7 +546,7 @@ static int add_tracepoint_event(struct list_head *list, int *idx,
} }
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, const char *sys_name, const char *evt_name,
struct parse_events_error *err, struct parse_events_error *err,
struct list_head *head_config) struct list_head *head_config)
{ {
...@@ -584,7 +585,7 @@ struct __add_bpf_event_param { ...@@ -584,7 +585,7 @@ struct __add_bpf_event_param {
struct list_head *head_config; struct list_head *head_config;
}; };
static int add_bpf_event(struct probe_trace_event *tev, int fd, static int add_bpf_event(const char *group, const char *event, int fd,
void *_param) void *_param)
{ {
LIST_HEAD(new_evsels); LIST_HEAD(new_evsels);
...@@ -595,27 +596,27 @@ static int add_bpf_event(struct probe_trace_event *tev, int fd, ...@@ -595,27 +596,27 @@ static int add_bpf_event(struct probe_trace_event *tev, int fd,
int err; int err;
pr_debug("add bpf event %s:%s and attach bpf program %d\n", pr_debug("add bpf event %s:%s and attach bpf program %d\n",
tev->group, tev->event, fd); group, event, fd);
err = parse_events_add_tracepoint(&new_evsels, &evlist->idx, tev->group, err = parse_events_add_tracepoint(&new_evsels, &evlist->idx, group,
tev->event, evlist->error, event, evlist->error,
param->head_config); param->head_config);
if (err) { if (err) {
struct perf_evsel *evsel, *tmp; struct perf_evsel *evsel, *tmp;
pr_debug("Failed to add BPF event %s:%s\n", pr_debug("Failed to add BPF event %s:%s\n",
tev->group, tev->event); group, event);
list_for_each_entry_safe(evsel, tmp, &new_evsels, node) { list_for_each_entry_safe(evsel, tmp, &new_evsels, node) {
list_del(&evsel->node); list_del(&evsel->node);
perf_evsel__delete(evsel); perf_evsel__delete(evsel);
} }
return err; return err;
} }
pr_debug("adding %s:%s\n", tev->group, tev->event); pr_debug("adding %s:%s\n", group, event);
list_for_each_entry(pos, &new_evsels, node) { list_for_each_entry(pos, &new_evsels, node) {
pr_debug("adding %s:%s to %p\n", pr_debug("adding %s:%s to %p\n",
tev->group, tev->event, pos); group, event, pos);
pos->bpf_fd = fd; pos->bpf_fd = fd;
} }
list_splice(&new_evsels, list); list_splice(&new_evsels, list);
...@@ -661,7 +662,7 @@ int parse_events_load_bpf_obj(struct parse_events_evlist *data, ...@@ -661,7 +662,7 @@ int parse_events_load_bpf_obj(struct parse_events_evlist *data,
goto errout; goto errout;
} }
err = bpf__foreach_tev(obj, add_bpf_event, &param); err = bpf__foreach_event(obj, add_bpf_event, &param);
if (err) { if (err) {
snprintf(errbuf, sizeof(errbuf), snprintf(errbuf, sizeof(errbuf),
"Attach events in BPF object failed"); "Attach events in BPF object failed");
...@@ -1126,7 +1127,7 @@ do { \ ...@@ -1126,7 +1127,7 @@ do { \
} }
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, const char *sys, const char *event,
struct parse_events_error *err, struct parse_events_error *err,
struct list_head *head_config) struct list_head *head_config)
{ {
...@@ -1984,6 +1985,85 @@ static bool is_event_supported(u8 type, unsigned config) ...@@ -1984,6 +1985,85 @@ static bool is_event_supported(u8 type, unsigned config)
return ret; return ret;
} }
void print_sdt_events(const char *subsys_glob, const char *event_glob,
bool name_only)
{
struct probe_cache *pcache;
struct probe_cache_entry *ent;
struct strlist *bidlist, *sdtlist;
struct strlist_config cfg = {.dont_dupstr = true};
struct str_node *nd, *nd2;
char *buf, *path, *ptr = NULL;
bool show_detail = false;
int ret;
sdtlist = strlist__new(NULL, &cfg);
if (!sdtlist) {
pr_debug("Failed to allocate new strlist for SDT\n");
return;
}
bidlist = build_id_cache__list_all(true);
if (!bidlist) {
pr_debug("Failed to get buildids: %d\n", errno);
return;
}
strlist__for_each_entry(nd, bidlist) {
pcache = probe_cache__new(nd->s);
if (!pcache)
continue;
list_for_each_entry(ent, &pcache->entries, node) {
if (!ent->sdt)
continue;
if (subsys_glob &&
!strglobmatch(ent->pev.group, subsys_glob))
continue;
if (event_glob &&
!strglobmatch(ent->pev.event, event_glob))
continue;
ret = asprintf(&buf, "%s:%s@%s", ent->pev.group,
ent->pev.event, nd->s);
if (ret > 0)
strlist__add(sdtlist, buf);
}
probe_cache__delete(pcache);
}
strlist__delete(bidlist);
strlist__for_each_entry(nd, sdtlist) {
buf = strchr(nd->s, '@');
if (buf)
*(buf++) = '\0';
if (name_only) {
printf("%s ", nd->s);
continue;
}
nd2 = strlist__next(nd);
if (nd2) {
ptr = strchr(nd2->s, '@');
if (ptr)
*ptr = '\0';
if (strcmp(nd->s, nd2->s) == 0)
show_detail = true;
}
if (show_detail) {
path = build_id_cache__origname(buf);
ret = asprintf(&buf, "%s@%s(%.12s)", nd->s, path, buf);
if (ret > 0) {
printf(" %-50s [%s]\n", buf, "SDT event");
free(buf);
}
} else
printf(" %-50s [%s]\n", nd->s, "SDT event");
if (nd2) {
if (strcmp(nd->s, nd2->s) != 0)
show_detail = false;
if (ptr)
*ptr = '@';
}
}
strlist__delete(sdtlist);
}
int print_hwcache_events(const char *event_glob, bool name_only) int print_hwcache_events(const char *event_glob, bool name_only)
{ {
unsigned int type, op, i, evt_i = 0, evt_num = 0; unsigned int type, op, i, evt_i = 0, evt_num = 0;
...@@ -2166,6 +2246,8 @@ void print_events(const char *event_glob, bool name_only) ...@@ -2166,6 +2246,8 @@ void print_events(const char *event_glob, bool name_only)
} }
print_tracepoint_events(NULL, NULL, name_only); print_tracepoint_events(NULL, NULL, name_only);
print_sdt_events(NULL, NULL, name_only);
} }
int parse_events__is_hardcoded_term(struct parse_events_term *term) int parse_events__is_hardcoded_term(struct parse_events_term *term)
......
...@@ -134,7 +134,7 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add); ...@@ -134,7 +134,7 @@ 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, const char *sys, const char *event,
struct parse_events_error *error, struct parse_events_error *error,
struct list_head *head_config); struct list_head *head_config);
int parse_events_load_bpf(struct parse_events_evlist *data, int parse_events_load_bpf(struct parse_events_evlist *data,
...@@ -183,6 +183,8 @@ void print_symbol_events(const char *event_glob, unsigned type, ...@@ -183,6 +183,8 @@ void print_symbol_events(const char *event_glob, unsigned type,
void print_tracepoint_events(const char *subsys_glob, const char *event_glob, void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
bool name_only); bool name_only);
int print_hwcache_events(const char *event_glob, bool name_only); int print_hwcache_events(const char *event_glob, bool name_only);
void print_sdt_events(const char *subsys_glob, const char *event_glob,
bool name_only);
int is_valid_tracepoint(const char *event_string); int is_valid_tracepoint(const char *event_string);
int valid_event_mount(const char *eventfs); int valid_event_mount(const char *eventfs);
......
This diff is collapsed.
...@@ -85,6 +85,7 @@ struct perf_probe_event { ...@@ -85,6 +85,7 @@ struct perf_probe_event {
char *group; /* Group name */ char *group; /* Group name */
struct perf_probe_point point; /* Probe point */ struct perf_probe_point point; /* Probe point */
int nargs; /* Number of arguments */ int nargs; /* Number of arguments */
bool sdt; /* SDT/cached event flag */
bool uprobes; /* Uprobe event flag */ bool uprobes; /* Uprobe event flag */
char *target; /* Target binary */ char *target; /* Target binary */
struct perf_probe_arg *args; /* Arguments */ struct perf_probe_arg *args; /* Arguments */
......
...@@ -362,13 +362,38 @@ probe_cache_entry__new(struct perf_probe_event *pev) ...@@ -362,13 +362,38 @@ probe_cache_entry__new(struct perf_probe_event *pev)
return entry; return entry;
} }
/* For the kernel probe caches, pass target = NULL */ int probe_cache_entry__get_event(struct probe_cache_entry *entry,
struct probe_trace_event **tevs)
{
struct probe_trace_event *tev;
struct str_node *node;
int ret, i;
ret = strlist__nr_entries(entry->tevlist);
if (ret > probe_conf.max_probes)
return -E2BIG;
*tevs = zalloc(ret * sizeof(*tev));
if (!*tevs)
return -ENOMEM;
i = 0;
strlist__for_each_entry(node, entry->tevlist) {
tev = &(*tevs)[i++];
ret = parse_probe_trace_command(node->s, tev);
if (ret < 0)
break;
}
return i;
}
/* For the kernel probe caches, pass target = NULL or DSO__NAME_KALLSYMS */
static int probe_cache__open(struct probe_cache *pcache, const char *target) static int probe_cache__open(struct probe_cache *pcache, const char *target)
{ {
char cpath[PATH_MAX]; char cpath[PATH_MAX];
char sbuildid[SBUILD_ID_SIZE]; char sbuildid[SBUILD_ID_SIZE];
char *dir_name = NULL; char *dir_name = NULL;
bool is_kallsyms = !target; bool is_kallsyms = false;
int ret, fd; int ret, fd;
if (target && build_id_cache__cached(target)) { if (target && build_id_cache__cached(target)) {
...@@ -378,12 +403,13 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target) ...@@ -378,12 +403,13 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target)
goto found; goto found;
} }
if (target) if (!target || !strcmp(target, DSO__NAME_KALLSYMS)) {
ret = filename__sprintf_build_id(target, sbuildid);
else {
target = DSO__NAME_KALLSYMS; target = DSO__NAME_KALLSYMS;
is_kallsyms = true;
ret = sysfs__sprintf_build_id("/", sbuildid); ret = sysfs__sprintf_build_id("/", sbuildid);
} } else
ret = filename__sprintf_build_id(target, sbuildid);
if (ret < 0) { if (ret < 0) {
pr_debug("Failed to get build-id from %s.\n", target); pr_debug("Failed to get build-id from %s.\n", target);
return ret; return ret;
...@@ -546,7 +572,16 @@ probe_cache__find(struct probe_cache *pcache, struct perf_probe_event *pev) ...@@ -546,7 +572,16 @@ probe_cache__find(struct probe_cache *pcache, struct perf_probe_event *pev)
if (!cmd) if (!cmd)
return NULL; return NULL;
list_for_each_entry(entry, &pcache->entries, node) { for_each_probe_cache_entry(entry, pcache) {
if (pev->sdt) {
if (entry->pev.event &&
streql(entry->pev.event, pev->event) &&
(!pev->group ||
streql(entry->pev.group, pev->group)))
goto found;
continue;
}
/* Hit if same event name or same command-string */ /* Hit if same event name or same command-string */
if ((pev->event && if ((pev->event &&
(streql(entry->pev.group, pev->group) && (streql(entry->pev.group, pev->group) &&
...@@ -567,7 +602,7 @@ probe_cache__find_by_name(struct probe_cache *pcache, ...@@ -567,7 +602,7 @@ probe_cache__find_by_name(struct probe_cache *pcache,
{ {
struct probe_cache_entry *entry = NULL; struct probe_cache_entry *entry = NULL;
list_for_each_entry(entry, &pcache->entries, node) { for_each_probe_cache_entry(entry, pcache) {
/* Hit if same event name or same command-string */ /* Hit if same event name or same command-string */
if (streql(entry->pev.group, group) && if (streql(entry->pev.group, group) &&
streql(entry->pev.event, event)) streql(entry->pev.event, event))
...@@ -739,7 +774,7 @@ int probe_cache__commit(struct probe_cache *pcache) ...@@ -739,7 +774,7 @@ int probe_cache__commit(struct probe_cache *pcache)
if (ret < 0) if (ret < 0)
goto out; goto out;
list_for_each_entry(entry, &pcache->entries, node) { for_each_probe_cache_entry(entry, pcache) {
ret = probe_cache_entry__write(entry, pcache->fd); ret = probe_cache_entry__write(entry, pcache->fd);
pr_debug("Cache committed: %d\n", ret); pr_debug("Cache committed: %d\n", ret);
if (ret < 0) if (ret < 0)
...@@ -781,7 +816,7 @@ static int probe_cache__show_entries(struct probe_cache *pcache, ...@@ -781,7 +816,7 @@ static int probe_cache__show_entries(struct probe_cache *pcache,
{ {
struct probe_cache_entry *entry; struct probe_cache_entry *entry;
list_for_each_entry(entry, &pcache->entries, node) { for_each_probe_cache_entry(entry, pcache) {
if (probe_cache_entry__compare(entry, filter)) if (probe_cache_entry__compare(entry, filter))
printf("%s\n", entry->spev); printf("%s\n", entry->spev);
} }
...@@ -799,7 +834,7 @@ int probe_cache__show_all_caches(struct strfilter *filter) ...@@ -799,7 +834,7 @@ int probe_cache__show_all_caches(struct strfilter *filter)
pr_debug("list cache with filter: %s\n", buf); pr_debug("list cache with filter: %s\n", buf);
free(buf); free(buf);
bidlist = build_id_cache__list_all(); bidlist = build_id_cache__list_all(true);
if (!bidlist) { if (!bidlist) {
pr_debug("Failed to get buildids: %d\n", errno); pr_debug("Failed to get buildids: %d\n", errno);
return -EINVAL; return -EINVAL;
......
...@@ -21,7 +21,11 @@ struct probe_cache { ...@@ -21,7 +21,11 @@ struct probe_cache {
#define PF_FL_UPROBE 1 #define PF_FL_UPROBE 1
#define PF_FL_RW 2 #define PF_FL_RW 2
#define for_each_probe_cache_entry(entry, pcache) \
list_for_each_entry(entry, &pcache->entries, node)
/* probe-file.c depends on libelf */
#ifdef HAVE_LIBELF_SUPPORT
int probe_file__open(int flag); int probe_file__open(int flag);
int probe_file__open_both(int *kfd, int *ufd, int flag); int probe_file__open_both(int *kfd, int *ufd, int flag);
struct strlist *probe_file__get_namelist(int fd); struct strlist *probe_file__get_namelist(int fd);
...@@ -32,6 +36,9 @@ int probe_file__get_events(int fd, struct strfilter *filter, ...@@ -32,6 +36,9 @@ int probe_file__get_events(int fd, struct strfilter *filter,
struct strlist *plist); struct strlist *plist);
int probe_file__del_strlist(int fd, struct strlist *namelist); int probe_file__del_strlist(int fd, struct strlist *namelist);
int probe_cache_entry__get_event(struct probe_cache_entry *entry,
struct probe_trace_event **tevs);
struct probe_cache *probe_cache__new(const char *target); struct probe_cache *probe_cache__new(const char *target);
int probe_cache__add_entry(struct probe_cache *pcache, int probe_cache__add_entry(struct probe_cache *pcache,
struct perf_probe_event *pev, struct perf_probe_event *pev,
...@@ -47,4 +54,11 @@ struct probe_cache_entry *probe_cache__find(struct probe_cache *pcache, ...@@ -47,4 +54,11 @@ struct probe_cache_entry *probe_cache__find(struct probe_cache *pcache,
struct probe_cache_entry *probe_cache__find_by_name(struct probe_cache *pcache, struct probe_cache_entry *probe_cache__find_by_name(struct probe_cache *pcache,
const char *group, const char *event); const char *group, const char *event);
int probe_cache__show_all_caches(struct strfilter *filter); int probe_cache__show_all_caches(struct strfilter *filter);
#else /* ! HAVE_LIBELF_SUPPORT */
static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused)
{
return NULL;
}
#define probe_cache__delete(pcache) do {} while (0)
#endif
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment