perf/ebpf improvements and fixes
Subsystems (3)
Arnaldo Carvalho de Melo
maintainerHi Ingo,
This is on top of my last perf-urgent-for-mingo pull req, that is
outstanding, please check if this can get into this merge window.
- Arnaldo
The following changes since commit 092b1f0b5f9f797812da0de927c3aa26acbe8762:
perf probe: Clear probe_trace_event when add_probe_trace_event() fails (2015-11-13 12:28:09 -0300)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git tags/perf-ebpf-for-mingo
for you to fetch changes up to 0fbcd50e1385d3a78ddb3cf52dcdff802bfb835e:
perf test: Mute test cases error messages if verbose == 0 (2015-11-17 11:45:06 -0300)
----------------------------------------------------------------
perf/ebpf improvements and fixes:
User visible:
- Allows BPF scriptlets specify arguments to be fetched using
DWARF info, using a prologue generated at compile/build time (He Kuang, Wang Nan)
- Allow attaching BPF scriptlets to module symbols (Wang Nan)
- Allow attaching BPF scriptlets to userspace code using uprobe (Wang Nan)
- BPF programs now can specify 'perf probe' tunables via its section name, separating key=val values using semicolons (Wang Nan)
Testing some of these new BPF features:
Use case: get callchains when receiving SSL packets, filter then in the
kernel, at arbitrary place.
# cat ssl.bpf.c #define SEC(NAME) __attribute__((section(NAME), used))
struct pt_regs;
SEC("func=__inet_lookup_established hnum")
int func(struct pt_regs *ctx, int err, unsigned short port)
{
return err == 0 && port == 443;
} char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
#
# perf record -a -g -e ssl.bpf.c ^C[ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.787 MB perf.data (3 samples) ] # perf script | head -30 swapper 0 [000] 58783.268118: perf_bpf_probe:func: (ffffffff816a0f60) hnum=0x1bb 8a0f61 __inet_lookup_established (/lib/modules/4.3.0+/build/vmlinux) 896def ip_rcv_finish (/lib/modules/4.3.0+/build/vmlinux) 8976c2 ip_rcv (/lib/modules/4.3.0+/build/vmlinux) 855eba __netif_receive_skb_core (/lib/modules/4.3.0+/build/vmlinux) 8565d8 __netif_receive_skb (/lib/modules/4.3.0+/build/vmlinux) 8572a8 process_backlog (/lib/modules/4.3.0+/build/vmlinux) 856b11 net_rx_action (/lib/modules/4.3.0+/build/vmlinux) 2a284b __do_softirq (/lib/modules/4.3.0+/build/vmlinux) 2a2ba3 irq_exit (/lib/modules/4.3.0+/build/vmlinux) 96b7a4 do_IRQ (/lib/modules/4.3.0+/build/vmlinux) 969807 ret_from_intr (/lib/modules/4.3.0+/build/vmlinux) 2dede5 cpu_startup_entry (/lib/modules/4.3.0+/build/vmlinux) 95d5bc rest_init (/lib/modules/4.3.0+/build/vmlinux) 1163ffa start_kernel ([kernel.vmlinux].init.text) 11634d7 x86_64_start_reservations ([kernel.vmlinux].init.text) 1163623 x86_64_start_kernel ([kernel.vmlinux].init.text)
qemu-system-x86 9178 [003] 58785.792417: perf_bpf_probe:func: (ffffffff816a0f60) hnum=0x1bb
8a0f61 __inet_lookup_established (/lib/modules/4.3.0+/build/vmlinux) 896def ip_rcv_finish (/lib/modules/4.3.0+/build/vmlinux) 8976c2 ip_rcv (/lib/modules/4.3.0+/build/vmlinux) 855eba __netif_receive_skb_core (/lib/modules/4.3.0+/build/vmlinux) 8565d8 __netif_receive_skb (/lib/modules/4.3.0+/build/vmlinux) 856660 netif_receive_skb_internal (/lib/modules/4.3.0+/build/vmlinux) 8566ec netif_receive_skb_sk (/lib/modules/4.3.0+/build/vmlinux) 430a br_handle_frame_finish ([bridge]) 48bc br_handle_frame ([bridge]) 855f44 __netif_receive_skb_core (/lib/modules/4.3.0+/build/vmlinux) 8565d8 __netif_receive_skb (/lib/modules/4.3.0+/build/vmlinux)
#
Use 'perf probe' various options to list functions, see what variables can be collected at any given point, experiment first collecting without a filter, then filter, use it together with 'perf trace', 'perf top', with or without callchains, if it explodes, please tell us!
Infrastructure:
- Add further 'perf test' entries for BPF and LLVM (Wang Nan)
- Improve 'perf test' to suport subtests, so that the series of tests performed in the LLVM and BPF main tests appear in the default 'perf test' output (Wang Nan)
- Move memdup() from tools/perf to tools/lib/string.c (Arnaldo Carvalho de Melo)
- Adopt strtobool() from the kernel into tools/lib/ (Wang Nan)
----------------------------------------------------------------
Arnaldo Carvalho de Melo (1):
tools: Adopt memdup() from tools/perf, moving it to tools/lib/string.c
He Kuang (1):
perf bpf: Add prologue for BPF programs for fetching arguments
Wang Nan (14):
tools: Clone the kernel's strtobool function
bpf tools: Load a program with different instances using preprocessor perf bpf: Add BPF_PROLOGUE config options for further patches perf bpf: Compile dwarf-regs.c if CONFIG_BPF_PROLOGUE is on perf bpf: Allow BPF program attach to uprobe events
perf bpf: Allow attaching BPF programs to modules symbols perf bpf: Allow BPF program config probing options
perf bpf: Generate prologue for BPF programs
perf test: Test the BPF prologue adding infrastructure
perf test: Fix 'perf test BPF' when it fails to find a suitable vmlinux perf bpf: Use same BPF program if arguments are identical perf test: Print result for each LLVM subtest
perf test: Print result for each BPF subtest
perf test: Mute test cases error messages if verbose == 0
tools/include/linux/string.h | 11 + tools/lib/bpf/libbpf.c | 146 ++++++++- tools/lib/bpf/libbpf.h | 64 ++++ tools/lib/string.c | 62 ++++ tools/perf/MANIFEST | 2 + tools/perf/arch/x86/util/Build | 1 + tools/perf/config/Makefile | 12 + tools/perf/tests/.gitignore | 1 + tools/perf/tests/Build | 9 +- tools/perf/tests/bpf-script-test-prologue.c | 35 +++ tools/perf/tests/bpf.c | 79 ++++- tools/perf/tests/builtin-test.c | 134 ++++++-- tools/perf/tests/llvm.c | 67 ++-- tools/perf/tests/llvm.h | 2 + tools/perf/tests/tests.h | 27 +- tools/perf/util/Build | 7 + tools/perf/util/bpf-loader.c | 434 +++++++++++++++++++++++++- tools/perf/util/bpf-loader.h | 4 + tools/perf/util/bpf-prologue.c | 455 ++++++++++++++++++++++++++++ tools/perf/util/bpf-prologue.h | 34 +++ tools/perf/util/include/linux/string.h | 3 - tools/perf/util/probe-event.c | 7 +- tools/perf/util/string.c | 16 - 23 files changed, 1502 insertions(+), 110 deletions(-) create mode 100644 tools/include/linux/string.h create mode 100644 tools/lib/string.c create mode 100644 tools/perf/tests/bpf-script-test-prologue.c create mode 100644 tools/perf/util/bpf-prologue.c create mode 100644 tools/perf/util/bpf-prologue.h delete mode 100644 tools/perf/util/include/linux/string.h
Arnaldo Carvalho de Melo
maintainerPATCH 1/16 tools: Adopt memdup() from tools/perf, moving it to tools/lib/string.c
From: Arnaldo Carvalho de Melo <[email protected]>
That will contain more string functions with counterparts, sometimes verbatim copies, in the kernel.
--- tools/include/linux/string.h | 9 +++++++++ tools/lib/string.c | 19 +++++++++++++++++++ tools/perf/MANIFEST | 2 ++ tools/perf/util/Build | 6 ++++++ tools/perf/util/include/linux/string.h | 3 --- tools/perf/util/string.c | 16 ---------------- 6 files changed, 36 insertions(+), 19 deletions(-) create mode 100644 tools/include/linux/string.h create mode 100644 tools/lib/string.c delete mode 100644 tools/perf/util/include/linux/string.h
tools/include/linux/string.h new file +9 -0
@@ -0,0 +1,9 @@ +#ifndef _TOOLS_LINUX_STRING_H_ +#define _TOOLS_LINUX_STRING_H_ + + +#include <linux/types.h> /* for size_t */ + +void *memdup (const void *src,size_t len); + +#endif /* _LINUX_STRING_H_ */
tools/lib/string.c new file +19 -0
@@ -0,0 +1,19 @@ +#include <stdlib.h> +#include <string.h> +#include <linux/string.h> + +/** +* memdup - duplicate region of memory +* +* @src: memory region to duplicate +* @len: memory region length +*/ +void *memdup (const void *src,size_t len) +{ +void *p =malloc (len); + +if (p) +memcpy (p, src, len); + +return p; +}
tools/perf/MANIFEST +2 -0
@@ -22,6 +22,7 @@ tools/lib/api tools/lib/bpf tools/lib/hweight.c tools/lib/rbtree.c +tools/lib/string.c tools/lib/symbol/kallsyms.c tools/lib/symbol/kallsyms.h tools/lib/util/find_next_bit.c
@@ -50,6 +51,7 @@ tools/include/linux/log2.h tools/include/linux/poison.h tools/include/linux/rbtree.h tools/include/linux/rbtree_augmented.h +tools/include/linux/string.h tools/include/linux/types.h tools/include/linux/err.h include/asm-generic/bitops/arch_hweight.h
tools/perf/util/Build +6 -0
@@ -21,6 +21,7 @@ libperf-y += parse-events.o libperf-y += perf_regs.o libperf-y += path.o libperf-y += rbtree.o +libperf-y += libstring.o libperf-y += bitmap.o libperf-y += hweight.o libperf-y += run-command.o
@@ -138,6 +139,7 @@ $(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c CFLAGS_find_next_bit.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_rbtree.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" +CFLAGS_libstring.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_hweight.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))" CFLAGS_parse-events.o += -Wno-redundant-decls
@@ -153,6 +155,10 @@ $(OUTPUT)util/rbtree.o: ../lib/rbtree.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) +$(OUTPUT)util/libstring.o: ../lib/string.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,cc_o_c) + $(OUTPUT)util/hweight.o: ../lib/hweight.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c)
tools/perf/util/include/linux/string.h deleted +0 -3
@@ -1,3 +0,0 @@ -#include <string.h> - -void *memdup (const void *src,size_t len);
tools/perf/util/string.c +0 -16
@@ -342,22 +342,6 @@ char *rtrim(char *s)return s; } -/** -* memdup - duplicate region of memory -* @src: memory region to duplicate -* @len: memory region length -*/ -void *memdup (const void *src,size_t len) -{ -void *p; - - p =malloc (len); -if (p) -memcpy (p, src, len); - -return p; -} -char *asprintf_expr_inout_ints (const char *var,bool in,size_t nints,int *ints) {/*
Signature · 1 line
2.1.0
Arnaldo Carvalho de Melo
maintainerPATCH 2/16 tools: Clone the kernel's strtobool function
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
Copying it to tools/lib/string.c, the counterpart to the kernel's lib/string.c.
This is preparation for enhancing BPF program configuration, which will allow config string like 'inlines=yes'.
--- tools/include/linux/string.h | 2 ++ tools/lib/string.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+)
tools/include/linux/string.h +2 -0
@@ -6,4 +6,6 @@void *memdup (const void *src,size_t len); +int strtobool (const char *s,bool *res); +#endif /* _LINUX_STRING_H_ */
tools/lib/string.c +43 -0
51 lines not on this page · raw message
Signature · 1 line
2.1.0
Arnaldo Carvalho de Melo
maintainerPATCH 3/16 bpf tools: Load a program with different instances using preprocessor
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
This patch is a preparation for BPF prologue support which allows generating a series of BPF bytecode for fetching kernel data before calling program code. With the newly introduced multiple instances support, perf is able to create different prologues for different kprobe points.
Before this patch, a bpf_program can be loaded into kernel only once, and get the only resulting fd. What this patch does is to allow creating and loading different variants of one bpf_program, then fetching their fds.
Here we describe the basic idea in this patch. The detailed description of the newly introduced APIs can be found in comments in the patch body.
The key of this patch is the new mechanism in bpf_program__load(). Instead of loading BPF program into kernel directly, it calls a 'pre-processor' to generate program instances which would be finally loaded into the kernel based on the original code. To enable the generation of multiple instances, libbpf passes an index to the pre-processor so it know which instance is being loaded.
Pre-processor should be called from libbpf's user (perf) using bpf_program__set_prep(). The number of instances and the relationship between indices and the target instance should be clear when calling bpf_program__set_prep().
To retrieve a fd for a specific instance of a program, bpf_program__nth_fd() is introduced. It returns the resulting fd according to index.
--- tools/lib/bpf/libbpf.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++--- tools/lib/bpf/libbpf.h | 64 ++++++++++++++++++++++ 2 files changed, 201 insertions(+), 9 deletions(-)
tools/lib/bpf/libbpf.c +137 -9
@@ -152,7 +152,11 @@ struct bpf_program { } *reloc_desc;int nr_reloc; -int fd; +struct { +int nr; +int *fds; + } instances; +bpf_program_prep_t preprocessor;struct bpf_object *obj;void *priv;
@@ -206,10 +210,25 @@ struct bpf_object {static void bpf_program__unload (struct bpf_program *prog) { +int i; +if (!prog)return ; -zclose (prog->fd); +/* +* If the object is opened but the program was never loaded, +* it is possible that prog->instances.nr == -1. +*/ +if (prog->instances.nr >0 ) { +for (i =0 ; i < prog->instances.nr; i++) +zclose (prog->instances.fds[i]); + }else if (prog->instances.nr !=-1 ) { +pr_warning ("Internal error: instances.nr is %d\n" , + prog->instances.nr); + } + + prog->instances.nr =-1 ; +zfree (&prog->instances.fds); }static void bpf_program__exit (struct bpf_program *prog)
@@ -260,7 +279,8 @@ bpf_program__init(void *data, size_t size, char *name, int idx,memcpy (prog->insns, data, prog->insns_cnt *sizeof (struct bpf_insn )); prog->idx = idx; - prog->fd =-1 ; + prog->instances.fds =NULL ; + prog->instances.nr =-1 ;return 0 ; errout:
@@ -860,13 +880,73 @@ static int bpf_program__load(struct bpf_program *prog, char *license, u32 kern_version) { - int err, fd; + int err = 0, fd, i; - err =load_program (prog->insns, prog->insns_cnt, - license, kern_version, &fd); -if (!err) - prog->fd = fd; +if (prog->instances.nr <0 || !prog->instances.fds) { +if (prog->preprocessor) { +pr_warning ("Internal error: can't load program '%s'\n" , + prog->section_name); +return -LIBBPF_ERRNO__INTERNAL ; + } + prog->instances.fds =malloc (sizeof (int )); +if (!prog->instances.fds) { +pr_warning ("Not enough memory for BPF fds\n" ); +return -ENOMEM ; + } + prog->instances.nr =1 ; + prog->instances.fds[0 ] =-1 ; + } + +if (!prog->preprocessor) { +if (prog->instances.nr !=1 ) { +pr_warning ("Program '%s' is inconsistent: nr(%d) != 1\n" , + prog->section_name, prog->instances.nr); + } + err =load_program (prog->insns, prog->insns_cnt, + license, kern_version, &fd); +if (!err) + prog->instances.fds[0 ] = fd; + goto out; + } + +for (i =0 ; i < prog->instances.nr; i++) { +struct bpf_prog_prep_result result; +bpf_program_prep_t preprocessor = prog->preprocessor; + +bzero (&result,sizeof (result)); + err =preprocessor (prog, i, prog->insns, + prog->insns_cnt, &result); +if (err) { +pr_warning ("Preprocessing the %dth instance of program '%s' failed\n" , + i, prog->section_name); + goto out; + } + +if (!result.new_insn_ptr || !result.new_insn_cnt) { +pr_debug ("Skip loading the %dth instance of program '%s'\n" , + i, prog->section_name); + prog->instances.fds[i] =-1 ; +if (result.pfd) + *result.pfd =-1 ; +continue ; + } + + err =load_program (result.new_insn_ptr, + result.new_insn_cnt, + license, kern_version, &fd); + +if (err) { +pr_warning ("Loading the %dth instance of program '%s' failed\n" , + i, prog->section_name); + goto out; + } + +if (result.pfd) + *result.pfd = fd; + prog->instances.fds[i] = fd; + } +out:if (err)pr_warning ("failed to load program '%s'\n" , prog->section_name);
@@ -1121,5 +1201,53 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy)int bpf_program__fd (struct bpf_program *prog) { -return prog->fd; +return bpf_program__nth_fd (prog,0 ); +} + +int bpf_program__set_prep (struct bpf_program *prog,int nr_instances, +bpf_program_prep_t prep) +{ +int *instances_fds; + +if (nr_instances <=0 || !prep) +return -EINVAL ; + +if (prog->instances.nr >0 || prog->instances.fds) { +pr_warning ("Can't set pre-processor after loading\n" ); +return -EINVAL ; + } + + instances_fds =malloc (sizeof (int ) * nr_instances); +if (!instances_fds) { +pr_warning ("alloc memory failed for fds\n" ); +return -ENOMEM ; + } + +/* fill all fd with -1 */ +memset (instances_fds,-1 ,sizeof (int ) * nr_instances); + + prog->instances.nr = nr_instances; + prog->instances.fds = instances_fds; + prog->preprocessor = prep; +return 0 ; +} + +int bpf_program__nth_fd (struct bpf_program *prog,int n) +{ +int fd; + +if (n >= prog->instances.nr || n <0 ) { +pr_warning ("Can't get the %dth fd from program %s: only %d instances\n" , + n, prog->section_name, prog->instances.nr); +return -EINVAL ; + } + + fd = prog->instances.fds[n]; +if (fd <0 ) { +pr_warning ("%dth instance of program '%s' is invalid\n" , + n, prog->section_name); +return -ENOENT ; + } + +return fd; }
tools/lib/bpf/libbpf.h +64 -0
70 lines not on this page · raw message
Signature · 1 line
2.1.0
Arnaldo Carvalho de Melo
maintainerPATCH 4/16 perf bpf: Add BPF_PROLOGUE config options for further patches
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
If both LIBBPF and DWARF are detected, it is possible to create prologue for eBPF programs to help them access kernel data. HAVE_BPF_PROLOGUE and CONFIG_BPF_PROLOGUE are added as flags for this feature.
PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET is introduced in commit 63ab024a5b6f295ca17a293ad81b7c728f49a89a ("perf tools:regs_query_register_offset() infrastructure"), which indicates that an architecture supports converting name of a register to its offset in 'struct pt_regs'. Without this support, BPF_PROLOGUE should be turned off.
--- tools/perf/config/Makefile | 12 ++++++++++++ 1 file changed, 12 insertions(+)
tools/perf/config/Makefile +12 -0
@@ -318,6 +318,18 @@ ifndef NO_LIBELF CFLAGS += -DHAVE_LIBBPF_SUPPORT $(call detected,CONFIG_LIBBPF) endif + + ifndef NO_DWARF + ifdef PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET + CFLAGS += -DHAVE_BPF_PROLOGUE + $(call detected,CONFIG_BPF_PROLOGUE) + else + msg := $(warning BPF prologue is not supported by architecture $(ARCH), missing regs_query_register_offset()); + endif + else + msg := $(warning DWARF support is off, BPF prologue is disabled); + endif + endif # NO_LIBBPF endif # NO_LIBELF
Signature · 1 line
2.1.0
Arnaldo Carvalho de Melo
maintainerPATCH 5/16 perf bpf: Compile dwarf-regs.c if CONFIG_BPF_PROLOGUE is on
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
regs_query_register_offset() in dwarf-regs.c is required by BPF prologue. This patch compiles it if CONFIG_BPF_PROLOGUE is on to avoid build failure when CONFIG_BPF_PROLOGUE is on but CONFIG_DWARF is not set.
--- tools/perf/arch/x86/util/Build | 1 + 1 file changed, 1 insertion(+)
tools/perf/arch/x86/util/Build +1 -0
@@ -5,6 +5,7 @@ libperf-y += kvm-stat.o libperf-y += perf_regs.o libperf-$(CONFIG_DWARF) += dwarf-regs.o +libperf-$(CONFIG_BPF_PROLOGUE) += dwarf-regs.o libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
Signature · 1 line
2.1.0
Arnaldo Carvalho de Melo
maintainerPATCH 6/16 perf bpf: Allow BPF program attach to uprobe events
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
This patch adds a new syntax to the BPF object section name to support probing at uprobe event. Now we can use BPF program like this:
SEC(
"exec=/lib64/libc.so.6;"
"libcwrite=__write"
)
int libcwrite(void *ctx)
{
return 1;
}Where, in section name of a program, before the main config string, we can use 'key=value' style options. Now the only option key is "exec", for uprobes.
--- tools/perf/util/bpf-loader.c | 120 ++++++++++++++++++++++++++++++++++++++++--- tools/perf/util/bpf-loader.h | 1 + 2 files changed, 115 insertions(+), 6 deletions(-)
tools/perf/util/bpf-loader.c +114 -6
@@ -110,6 +110,113 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused, }static int +config__exec (const char *value,struct perf_probe_event *pev) +{ + pev->uprobes = true; + pev->target =strdup (value); +if (!pev->target) +return -ENOMEM ; +return 0 ; +} + +static struct { +const char *key; +const char *usage; +const char *desc; +int (*func)(const char *,struct perf_probe_event *); +} bpf_config_terms[] = { + { + .key ="exec" , + .usage ="exec=<full path of file>" , + .desc ="Set uprobe target" , + .func = config__exec, + }, +}; + +static int +do_config (const char *key,const char *value, +struct perf_probe_event *pev) +{ +unsigned int i; + +pr_debug ("config bpf program: %s=%s\n" , key, value); +for (i =0 ; i <ARRAY_SIZE (bpf_config_terms); i++) +if (strcmp (key, bpf_config_terms[i].key) ==0 ) +return bpf_config_terms[i].func (value, pev); + +pr_debug ("BPF: ERROR: invalid config option in object: %s=%s\n" , + key, value); + +pr_debug ("\nHint: Currently valid options are:\n" ); +for (i =0 ; i <ARRAY_SIZE (bpf_config_terms); i++) +pr_debug ("\t%s:\t%s\n" , bpf_config_terms[i].usage, + bpf_config_terms[i].desc); +pr_debug ("\n" ); + +return -BPF_LOADER_ERRNO__CONFIG_TERM ; +} + +static const char * +parse_config_kvpair (const char *config_str,struct perf_probe_event *pev) +{ +char *text =strdup (config_str); +char *sep, *line; +const char *main_str =NULL ; +int err =0 ; + +if (!text) { +pr_debug ("No enough memory: dup config_str failed\n" ); +return ERR_PTR (-ENOMEM ); + } + + line = text; +while ((sep =strchr (line,';' ))) { +char *equ; + + *sep ='\0' ; + equ =strchr (line,'=' ); +if (!equ) { +pr_warning ("WARNING: invalid config in BPF object: %s\n" , + line); +pr_warning ("\tShould be 'key=value'.\n" ); + goto nextline; + } + *equ ='\0' ; + + err =do_config (line, equ +1 , pev); +if (err) +break ; +nextline: + line = sep +1 ; + } + +if (!err) + main_str = config_str + (line - text); +free (text); + +return err ?ERR_PTR (err) : main_str; +} + +static int +parse_config (const char *config_str,struct perf_probe_event *pev) +{ +int err; +const char *main_str =parse_config_kvpair (config_str, pev); + +if (IS_ERR (main_str)) +return PTR_ERR (main_str); + + err =parse_perf_probe_command (main_str, pev); +if (err <0 ) { +pr_debug ("bpf: '%s' is not a valid config string\n" , + config_str); +/* parse failed, don't need clear pev. */ +return -BPF_LOADER_ERRNO__CONFIG ; + } +return 0 ; +} + +static int config_bpf_program (struct bpf_program *prog) {struct perf_probe_event *pev =NULL ;
@@ -131,13 +238,9 @@ config_bpf_program(struct bpf_program *prog) pev = &priv->pev;pr_debug ("bpf: config program '%s'\n" , config_str); - err =parse_perf_probe_command (config_str, pev); -if (err <0 ) { -pr_debug ("bpf: '%s' is not a valid config string\n" , - config_str); - err = -BPF_LOADER_ERRNO__CONFIG ; + err =parse_config (config_str, pev); +if (err) goto errout; - }if (pev->group &&strcmp (pev->group,PERF_BPF_PROBE_GROUP )) { pr_debug("bpf: '%s': group for event is set and not '%s'.\n",
@@ -340,6 +443,7 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = { [ERRCODE_OFFSET(EVENTNAME)] = "No event name found in config string", [ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error", [ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet", + [ERRCODE_OFFSET(CONFIG_TERM)] = "Invalid config term in config string", }; static int
@@ -420,6 +524,10 @@ int bpf__strerror_probe(struct bpf_object *obj __maybe_unused, int err, char *buf, size_t size) {bpf__strerror_head (err ,buf ,size ); +case BPF_LOADER_ERRNO__CONFIG_TERM : { +scnprintf (buf, size,"%s (add -v to see detail)" , emsg); +break ; + }bpf__strerror_entry (EEXIST ,"Probe point exist. Try use 'perf probe -d \"*\"'" );bpf__strerror_entry (EACCES ,"You need to be root" );bpf__strerror_entry (EPERM ,"You need to be root, and /proc/sys/kernel/kptr_restrict should be 0" );
tools/perf/util/bpf-loader.h +1 -0
@@ -20,6 +20,7 @@ enum bpf_loader_errno {BPF_LOADER_ERRNO__EVENTNAME ,/* Event name is missing */ BPF_LOADER_ERRNO__INTERNAL ,/* BPF loader internal error */ BPF_LOADER_ERRNO__COMPILE ,/* Error when compiling BPF scriptlet */ +BPF_LOADER_ERRNO__CONFIG_TERM ,/* Invalid config term in config term */ __BPF_LOADER_ERRNO__END, };
Signature · 1 line
2.1.0
Arnaldo Carvalho de Melo
maintainerPATCH 7/16 perf bpf: Allow attaching BPF programs to modules symbols
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
By extending the syntax of BPF object section names, this patch allows users to attach BPF programs to symbols in modules. For example:
SEC("module=i915;"
"parse_cmds=i915_parse_cmds")
int parse_cmds(void *ctx)
{
return 1;
}The implementation is very simple: like what 'perf probe' does, for module, fill 'uprobe' field in 'struct perf_probe_event'. Other parts will be done automatically.
--- tools/perf/util/bpf-loader.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
tools/perf/util/bpf-loader.c +16 -0
@@ -119,6 +119,16 @@ config__exec(const char *value, struct perf_probe_event *pev)return 0 ; } +static int +config__module (const char *value,struct perf_probe_event *pev) +{ + pev->uprobes = false; + pev->target =strdup (value); +if (!pev->target) +return -ENOMEM ; +return 0 ; +} + static struct { const char *key; const char *usage;
@@ -131,6 +141,12 @@ static struct { .desc = "Set uprobe target", .func = config__exec, }, + { + .key = "module", + .usage = "module=<module name> ", + .desc = "Set kprobe module", + .func = config__module, + } }; static int
Signature · 1 line
2.1.0
Arnaldo Carvalho de Melo
maintainerPATCH 8/16 perf bpf: Allow BPF program config probing options
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
By extending the syntax of BPF object section names, this patch allows users to config probing options like what they can do in 'perf probe'.
The error message in 'perf probe' is also updated.
Test result:
For following BPF file test_probe_glob.c:
# cat test_probe_glob.c
__attribute__((section("inlines=no;func=SyS_dup?"), used)) int func(void *ctx)
{
return 1;}
char _license[] __attribute__((section("license"), used)) = "GPL"; int _version __attribute__((section("version"), used)) = 0x40300; #
# ./perf record -e ./test_probe_glob.c ls / ... [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.013 MB perf.data ] # ./perf evlist perf_bpf_probe:func_1 perf_bpf_probe:func
After changing "inlines=no" to "inlines=yes":
# ./perf record -e ./test_probe_glob.c ls / ... [ perf record: Woken up 2 times to write data ] [ perf record: Captured and wrote 0.013 MB perf.data ] # ./perf evlist perf_bpf_probe:func_3 perf_bpf_probe:func_2 perf_bpf_probe:func_1 perf_bpf_probe:func
Then test 'force':
Use following program:
# cat test_probe_force.c
__attribute__((section("func=sys_write"), used)) int funca(void *ctx)
{
return 1;}
__attribute__((section("force=yes;func=sys_write"), used))
int funcb(void *ctx)
{
return 1;
}
char _license[] __attribute__((section("license"), used)) = "GPL";
int _version __attribute__((section("version"), used)) = 0x40300;
# # perf record -e ./test_probe_force.c usleep 1
Error: event "func" already exists.
Hint: Remove existing event by 'perf probe -d'
or force duplicates by 'perf probe -f'
or set 'force=yes' in BPF source.
event syntax error: './test_probe_force.c'
\___ Probe point exist. Try 'perf probe -d "*"' and set 'force=yes' (add -v to see detail)
...
Then replace 'force=no' to 'force=yes':
# vim test_probe_force.c # perf record -e ./test_probe_force.c usleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.017 MB perf.data ] # perf evlist perf_bpf_probe:func_1 perf_bpf_probe:func #
--- tools/perf/util/bpf-loader.c | 53 +++++++++++++++++++++++++++++++++++++++++-- tools/perf/util/probe-event.c | 7 ++++-- 2 files changed, 56 insertions(+), 4 deletions(-)
tools/perf/util/bpf-loader.c +51 -2
@@ -7,6 +7,7 @@#include <bpf/libbpf.h> #include <linux/err.h> +#include <linux/string.h> #include "perf.h" #include "debug.h" #include "bpf-loader.h"
@@ -129,6 +130,38 @@ config__module(const char *value, struct perf_probe_event *pev)return 0 ; } +static int +config__bool (const char *value, +bool *pbool,bool invert) +{ +int err; +bool bool_value; + +if (!pbool) +return -EINVAL ; + + err =strtobool (value, &bool_value); +if (err) +return err; + + *pbool = invert ? !bool_value : bool_value; +return 0 ; +} + +static int +config__inlines (const char *value, +struct perf_probe_event *pev__maybe_unused ) +{ +return config__bool (value, &probe_conf.no_inlines, true); +} + +static int +config__force (const char *value, +struct perf_probe_event *pev__maybe_unused ) +{ +return config__bool (value, &probe_conf.force_add, false); +} + static struct { const char *key; const char *usage;
@@ -146,7 +179,19 @@ static struct { .usage = "module=<module name> ", .desc = "Set kprobe module", .func = config__module, - } + }, + { + .key = "inlines", + .usage = "inlines=[yes|no] ", + .desc = "Probe at inline symbol", + .func = config__inlines, + }, + { + .key = "force", + .usage = "force=[yes|no] ", + .desc = "Forcibly add events with existing name", + .func = config__force, + }, }; static int
@@ -240,6 +285,10 @@ config_bpf_program(struct bpf_program *prog)const char *config_str;int err; +/* Initialize per-program probing setting */ + probe_conf.no_inlines = false; + probe_conf.force_add = false; + config_str =bpf_program__title (prog, false);if (IS_ERR (config_str)) {pr_debug ("bpf: unable to get title for program\n" );
@@ -544,7 +593,7 @@ int bpf__strerror_probe(struct bpf_object *obj __maybe_unused,scnprintf (buf, size,"%s (add -v to see detail)" , emsg);break ; } -bpf__strerror_entry (EEXIST ,"Probe point exist. Try use 'perf probe -d \"*\"'" ); +bpf__strerror_entry (EEXIST ,"Probe point exist. Try 'perf probe -d \"*\"' and set 'force=yes'" );bpf__strerror_entry (EACCES ,"You need to be root" );bpf__strerror_entry (EPERM ,"You need to be root, and /proc/sys/kernel/kptr_restrict should be 0" );bpf__strerror_entry (ENOENT ,"You need to check probing points in BPF file" );
tools/perf/util/probe-event.c +5 -2
@@ -2326,8 +2326,11 @@ static int get_new_event_name(char *buf, size_t len, const char *base, goto out;if (!allow_suffix) { -pr_warning ("Error: event \"%s\" already exists. " -"(Use -f to force duplicates.)\n" , buf); +pr_warning ("Error: event \"%s\" already exists.\n" +" Hint: Remove existing event by 'perf probe -d'\n" +" or force duplicates by 'perf probe -f'\n" +" or set 'force=yes' in BPF source.\n" , + buf); ret = -EEXIST ; goto out; }
Signature · 1 line
2.1.0
Arnaldo Carvalho de Melo
maintainerPATCH 9/16 perf bpf: Add prologue for BPF programs for fetching arguments
In reply to Arnaldo Carvalho de MeloFrom: He Kuang <[email protected]>
This patch generates a prologue for a BPF program which fetches arguments for it. With this patch, the program can have arguments as follow:
SEC("lock_page=__lock_page page->flags")
int lock_page(struct pt_regs *ctx, int err, unsigned long flags) {
return 1;
}
This patch passes at most 3 arguments from r3, r4 and r5. r1 is still the ctx pointer. r2 is used to indicate if dereferencing was done successfully.
This patch uses r6 to hold ctx (struct pt_regs) and r7 to hold stack pointer for result. Result of each arguments first store on stack:
low address
BPF_REG_FP - 24 ARG3 BPF_REG_FP - 16 ARG2 BPF_REG_FP - 8 ARG1
BPF_REG_FP
high address
Then loaded into r3, r4 and r5.
The output prologue for offn(...off2(off1(reg)))) should be:
r6 <- r1 // save ctx into a callee saved register
r7 <- fp
r7 <- r7 - stack_offset // pointer to result slot
/* load r3 with the offset in pt_regs of 'reg' */
(r7) <- r3 // make slot valid
r3 <- r3 + off1 // prepare to read unsafe pointer
r2 <- 8
r1 <- r7 // result put onto stack
call probe_read // read unsafe pointer
jnei r0, 0, err // error checking
r3 <- (r7) // read result
r3 <- r3 + off2 // prepare to read unsafe pointer
r2 <- 8
r1 <- r7
call probe_read
jnei r0, 0, err
...
/* load r2, r3, r4 from stack */
goto success
err:
r2 <- 1
/* load r3, r4, r5 with 0 */
goto usercode
success:
r2 <- 0
usercode:
r1 <- r6 // restore ctx
// original user codeIf all of arguments reside in register (dereferencing is not required), gen_prologue_fastpath() will be used to create fast prologue:
r3 <- (r1 + offset of reg1)
r4 <- (r1 + offset of reg2)
r5 <- (r1 + offset of reg3)
r2 <- 0P.S.
eBPF calling convention is defined as:
* r0 - return value from in-kernel function, and exit value
for eBPF program
* r1 - r5 - arguments from eBPF program to in-kernel function
* r6 - r9 - callee saved registers that in-kernel function will
preserve
* r10 - read-only frame pointer to access stack
Committer note:
At least testing if it builds and loads:
# cat test_probe_arg.c struct pt_regs;
__attribute__((section("lock_page=__lock_page page->flags"), used))
int func(struct pt_regs *ctx, int err, unsigned long flags)
{
return 1;
} char _license[] __attribute__((section("license"), used)) = "GPL"; int _version __attribute__((section("version"), used)) = 0x40300;
# perf record -e ./test_probe_arg.c usleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.016 MB perf.data ] # perf evlist perf_bpf_probe:lock_page #
--- tools/perf/util/Build | 1 + tools/perf/util/bpf-loader.c | 3 + tools/perf/util/bpf-loader.h | 3 + tools/perf/util/bpf-prologue.c | 455 +++++++++++++++++++++++++++++++++++++++++ tools/perf/util/bpf-prologue.h | 34 +++ 5 files changed, 496 insertions(+) create mode 100644 tools/perf/util/bpf-prologue.c create mode 100644 tools/perf/util/bpf-prologue.h
tools/perf/util/Build +1 -0
@@ -89,6 +89,7 @@ libperf-y += parse-branch-options.o libperf-y += parse-regs-options.o libperf-$(CONFIG_LIBBPF) += bpf-loader.o +libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o libperf-$(CONFIG_LIBELF) += symbol-elf.o libperf-$(CONFIG_LIBELF) += probe-file.o libperf-$(CONFIG_LIBELF) += probe-event.o
tools/perf/util/bpf-loader.c +3 -0
@@ -509,6 +509,9 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = { [ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error", [ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet", [ERRCODE_OFFSET(CONFIG_TERM)] = "Invalid config term in config string", + [ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue", + [ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program", + [ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue", }; static int
tools/perf/util/bpf-loader.h +3 -0
@@ -21,6 +21,9 @@ enum bpf_loader_errno {BPF_LOADER_ERRNO__INTERNAL ,/* BPF loader internal error */ BPF_LOADER_ERRNO__COMPILE ,/* Error when compiling BPF scriptlet */ BPF_LOADER_ERRNO__CONFIG_TERM ,/* Invalid config term in config term */ +BPF_LOADER_ERRNO__PROLOGUE ,/* Failed to generate prologue */ +BPF_LOADER_ERRNO__PROLOGUE2BIG ,/* Prologue too big for program */ +BPF_LOADER_ERRNO__PROLOGUEOOB ,/* Offset out of bound for prologue */ __BPF_LOADER_ERRNO__END, };
tools/perf/util/bpf-prologue.c new file +455 -0
455 lines not on this page · raw message
tools/perf/util/bpf-prologue.h new file +34 -0
@@ -0,0 +1,34 @@ +/* +* Copyright (C) 2015, He Kuang <[email protected]> +* Copyright (C) 2015, Huawei Inc. +*/ +#ifndef __BPF_PROLOGUE_H +#define __BPF_PROLOGUE_H + +#include <linux/compiler.h> +#include <linux/filter.h> +#include "probe-event.h" + +#define BPF_PROLOGUE_MAX_ARGS 3 +#define BPF_PROLOGUE_START_ARG_REG BPF_REG_3 +#define BPF_PROLOGUE_FETCH_RESULT_REG BPF_REG_2 + +#ifdef HAVE_BPF_PROLOGUE +int bpf__gen_prologue (struct probe_trace_arg *args,int nargs, +struct bpf_insn *new_prog,size_t *new_cnt, +size_t cnt_space); +#else +static inline int +bpf__gen_prologue (struct probe_trace_arg *args__maybe_unused , +int nargs__maybe_unused , +struct bpf_insn *new_prog__maybe_unused , +size_t *new_cnt, +size_t cnt_space__maybe_unused ) +{ +if (!new_cnt) +return -EINVAL ; + *new_cnt =0 ; +return -ENOTSUP ; +} +#endif +#endif /* __BPF_PROLOGUE_H */
Signature · 1 line
2.1.0
Arnaldo Carvalho de Melo
maintainerPATCH 10/16 perf bpf: Generate prologue for BPF programs
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
This patch generates a prologue for each 'struct probe_trace_event' for fetching arguments for BPF programs.
After bpf__probe(), iterate over each program to check whether prologues are required. If none of the 'struct perf_probe_event' programs will attach to have at least one argument, simply skip preprocessor hooking. For those who a prologue is required, call bpf__gen_prologue() and paste the original instruction after the prologue.
--- tools/perf/util/bpf-loader.c | 120 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-)
tools/perf/util/bpf-loader.c +119 -1
@@ -5,12 +5,15 @@* Copyright (C) 2015 Huawei Inc. */ +#include <linux/bpf.h> #include <bpf/libbpf.h> #include <linux/err.h> #include <linux/string.h> #include "perf.h" #include "debug.h" #include "bpf-loader.h" +#include "bpf-prologue.h" +#include "llvm-utils.h" #include "probe-event.h" #include "probe-finder.h" // for MAX_PROBES #include "llvm-utils.h"
@@ -33,6 +36,8 @@ DEFINE_PRINT_FN(debug, 1)struct bpf_prog_priv {struct perf_probe_event pev; +bool need_prologue; +struct bpf_insn *insns_buf; };static bool libbpf_initialized;
@@ -107,6 +112,7 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,struct bpf_prog_priv *priv = _priv;cleanup_perf_probe_events (&priv->pev,1 ); +zfree (&priv->insns_buf);free (priv); }
@@ -365,6 +371,102 @@ static int bpf__prepare_probe(void)return err; } +static int +preproc_gen_prologue (struct bpf_program *prog,int n, +struct bpf_insn *orig_insns,int orig_insns_cnt, +struct bpf_prog_prep_result *res) +{ +struct probe_trace_event *tev; +struct perf_probe_event *pev; +struct bpf_prog_priv *priv; +struct bpf_insn *buf; +size_t prologue_cnt =0 ; +int err; + + err =bpf_program__get_private (prog, (void **)&priv); +if (err || !priv) + goto errout; + + pev = &priv->pev; + +if (n <0 || n >= pev->ntevs) + goto errout; + + tev = &pev->tevs[n]; + + buf = priv->insns_buf; + err =bpf__gen_prologue (tev->args, tev->nargs, + buf, &prologue_cnt, +BPF_MAXINSNS - orig_insns_cnt); +if (err) { +const char *title; + + title =bpf_program__title (prog, false); +if (!title) + title ="[unknown]" ; + +pr_debug ("Failed to generate prologue for program %s\n" , + title); +return err; + } + +memcpy (&buf[prologue_cnt], orig_insns, +sizeof (struct bpf_insn ) * orig_insns_cnt); + + res->new_insn_ptr = buf; + res->new_insn_cnt = prologue_cnt + orig_insns_cnt; + res->pfd =NULL ; +return 0 ; + +errout: +pr_debug ("Internal error in preproc_gen_prologue\n" ); +return -BPF_LOADER_ERRNO__PROLOGUE ; +} + +static int hook_load_preprocessor (struct bpf_program *prog) +{ +struct perf_probe_event *pev; +struct bpf_prog_priv *priv; +bool need_prologue = false; +int err, i; + + err =bpf_program__get_private (prog, (void **)&priv); +if (err || !priv) { +pr_debug ("Internal error when hook preprocessor\n" ); +return -BPF_LOADER_ERRNO__INTERNAL ; + } + + pev = &priv->pev; +for (i =0 ; i < pev->ntevs; i++) { +struct probe_trace_event *tev = &pev->tevs[i]; + +if (tev->nargs >0 ) { + need_prologue = true; +break ; + } + } + +/* +* Since all tevs don't have argument, we don't need generate +* prologue. +*/ +if (!need_prologue) { + priv->need_prologue = false; +return 0 ; + } + + priv->need_prologue = true; + priv->insns_buf =malloc (sizeof (struct bpf_insn ) *BPF_MAXINSNS ); +if (!priv->insns_buf) { +pr_debug ("No enough memory: alloc insns_buf failed\n" ); +return -ENOMEM ; + } + + err =bpf_program__set_prep (prog, pev->ntevs, + preproc_gen_prologue); +return err; +} +int bpf__probe (struct bpf_object *obj) {int err =0 ;
@@ -399,6 +501,18 @@ int bpf__probe(struct bpf_object *obj)pr_debug ("bpf_probe: failed to apply perf probe events" ); goto out; } + +/* +* After probing, let's consider prologue, which +* adds program fetcher to BPF programs. +* +* hook_load_preprocessorr() hooks pre-processor +* to bpf_program, let it generate prologue +* dynamically during loading. +*/ + err =hook_load_preprocessor (prog); +if (err) + goto out; } out:return err <0 ? err :0 ;
@@ -482,7 +596,11 @@ int bpf__foreach_tev(struct bpf_object *obj,for (i =0 ; i < pev->ntevs; i++) { tev = &pev->tevs[i]; - fd =bpf_program__fd (prog); +if (priv->need_prologue) + fd =bpf_program__nth_fd (prog, i); +else + fd =bpf_program__fd (prog); +if (fd <0 ) {pr_debug ("bpf: failed to get file descriptor\n" );return fd;
Signature · 1 line
2.1.0
Arnaldo Carvalho de Melo
maintainerPATCH 11/16 perf test: Test the BPF prologue adding infrastructure
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
This patch introduces a new BPF script to test the BPF prologue adding routines. The new script probes at null_lseek, which is the function pointer used when we try to lseek on '/dev/null'.
The null_lseek function is chosen because it is used by function pointers, so we don't need to consider inlining and LTO.
By extracting file->f_mode, bpf-script-test-prologue.c should know whether the file is writable or readonly. According to llseek_loop() and bpf-script-test-prologue.c, one fourth of total lseeks should be collected.
Committer note:
Testing it:
# perf test -v BPF <SNIP> Kernel build dir is set to /lib/modules/4.3.0+/build set env: KBUILD_DIR=/lib/modules/4.3.0+/build unset env: KBUILD_OPTS include option is set to -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.9.2/include -I/home/git/linux/arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated -I/home/git/linux/include -Iinclude -I/home/git/linux/arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I/home/git/linux/include/uapi -Iinclude/generated/uapi -include /home/git/linux/include/linux/kconfig.h set env: NR_CPUS=4 set env: LINUX_VERSION_CODE=0x40300 set env: CLANG_EXEC=/usr/libexec/icecc/bin/clang set env: CLANG_OPTIONS=-xc set env: KERNEL_INC_OPTIONS= -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.9.2/include -I/home/git/linux/arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated -I/home/git/linux/include -Iinclude -I/home/git/linux/arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I/home/git/linux/include/uapi -Iinclude/generated/uapi -include /home/git/linux/include/linux/kconfig.h set env: WORKING_DIR=/lib/modules/4.3.0+/build set env: CLANG_SOURCE=- llvm compiling command template: echo '/* * bpf-script-test-prologue.c * Test BPF prologue */ #ifndef LINUX_VERSION_CODE # error Need LINUX_VERSION_CODE # error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig' #endif #define SEC(NAME) __attribute__((section(NAME), used))
#include <uapi/linux/fs.h> #define FMODE_READ 0x1 #define FMODE_WRITE 0x2
static void (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
(void *) 6;
SEC("func=null_lseek file->f_mode offset orig")
int bpf_func__null_lseek(void *ctx, int err, unsigned long f_mode,
unsigned long offset, unsigned long orig)
{
if (err)
return 0;
if (f_mode & FMODE_WRITE)
return 0;
if (offset & 1)
return 0;
if (orig == SEEK_CUR)
return 0;
return 1;}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
' | $CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS -DLINUX_VERSION_CODE=$LINUX_VERSION_CODE $CLANG_OPTIONS $KERNEL_INC_OPTIONS -Wno-unused-value -Wno-pointer-sign -working-directory $WORKING_DIR -c "$CLANG_SOURCE" -target bpf -O2 -o -
libbpf: loading object '[bpf_prologue_test]' from buffer
libbpf: section .strtab, size 135, link 0, flags 0, type=3
libbpf: section .text, size 0, link 0, flags 6, type=1
libbpf: section .data, size 0, link 0, flags 3, type=1
libbpf: section .bss, size 0, link 0, flags 3, type=8
libbpf: section func=null_lseek file->f_mode offset orig, size 112, link 0, flags 6, type=1
libbpf: found program func=null_lseek file->f_mode offset orig
libbpf: section license, size 4, link 0, flags 3, type=1
libbpf: license of [bpf_prologue_test] is GPL
libbpf: section version, size 4, link 0, flags 3, type=1
libbpf: kernel version of [bpf_prologue_test] is 40300
libbpf: section .symtab, size 168, link 1, flags 0, type=2
bpf: config program 'func=null_lseek file->f_mode offset orig'
symbol:null_lseek file:(null) line:0 offset:0 return:0 lazy:(null)
parsing arg: file->f_mode into file, f_mode(1)
parsing arg: offset into offset
parsing arg: orig into orig
bpf: config 'func=null_lseek file->f_mode offset orig' is ok
Looking at the vmlinux_path (7 entries long)
Using /lib/modules/4.3.0+/build/vmlinux for symbols
Open Debuginfo file: /lib/modules/4.3.0+/build/vmlinux
Try to find probe point from debuginfo.
Matched function: null_lseek
Probe point found: null_lseek+0
Searching 'file' variable in context.
Converting variable file into trace event.
converting f_mode in file
f_mode type is unsigned int.
Searching 'offset' variable in context.
Converting variable offset into trace event.
offset type is long long int.
Searching 'orig' variable in context.
Converting variable orig into trace event.
orig type is int.
Found 1 probe_trace_events.
Opening /sys/kernel/debug/tracing//kprobe_events write=1
Writing event: p:perf_bpf_probe/func _text+4840528 f_mode=+68(%di):u32 offset=%si:s64 orig=%dx:s32
libbpf: don't need create maps for [bpf_prologue_test]
prologue: pass validation
prologue: slow path
prologue: fetch arg 0, base reg is %di
prologue: arg 0: offset 68
prologue: fetch arg 1, base reg is %si
prologue: fetch arg 2, base reg is %dx
add bpf event perf_bpf_probe:func and attach bpf program 3
adding perf_bpf_probe:func
adding perf_bpf_probe:func to 0x51672c0
mmap size 1052672B
Opening /sys/kernel/debug/tracing//kprobe_events write=1
Opening /sys/kernel/debug/tracing//uprobe_events write=1
Parsing probe_events: p:perf_bpf_probe/func _text+4840528 f_mode=+68(%di):u32 offset=%si:s64 orig=%dx:s32
Group:perf_bpf_probe Event:func probe:p
Writing event: -:perf_bpf_probe/func
test child finished with 0
---- end ----
Test BPF filter: Ok
#
--- tools/perf/tests/.gitignore | 1 + tools/perf/tests/Build | 9 +++++++- tools/perf/tests/bpf-script-test-prologue.c | 35 +++++++++++++++++++++++++++++ tools/perf/tests/bpf.c | 34 ++++++++++++++++++++++++++++ tools/perf/tests/llvm.c | 4 ++++ tools/perf/tests/llvm.h | 2 ++ 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 tools/perf/tests/bpf-script-test-prologue.c
tools/perf/tests/.gitignore +1 -0
@@ -1,2 +1,3 @@ llvm-src-base.c llvm-src-kbuild.c +llvm-src-prologue.c
tools/perf/tests/Build +8 -1
@@ -31,7 +31,7 @@ perf-y += sample-parsing.o perf-y += parse-no-sample-id-all.o perf-y += kmod-path.o perf-y += thread-map.o -perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o +perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o perf-y += bpf.o perf-y += topology.o
@@ -49,6 +49,13 @@ $(OUTPUT)tests/llvm-src-kbuild.c: tests/bpf-script-test-kbuild.c $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ $(Q)echo ';' >> $@ +$(OUTPUT)tests/llvm-src-prologue.c: tests/bpf-script-test-prologue.c + $(call rule_mkdir) + $(Q)echo '#include <tests/llvm.h>' > $@ + $(Q)echo 'const char test_llvm__bpf_test_prologue_prog[] =' >> $@ + $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ + $(Q)echo ';' >> $@ + ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64)) perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o endif
tools/perf/tests/bpf-script-test-prologue.c new file +35 -0
@@ -0,0 +1,35 @@ +/* +* bpf-script-test-prologue.c +* Test BPF prologue +*/ +#ifndef LINUX_VERSION_CODE +# error Need LINUX_VERSION_CODE +# error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig' +#endif +#define SEC (NAME ) __attribute__((section(NAME), used)) + +#include <uapi/linux/fs.h> + +#define FMODE_READ 0x1 +#define FMODE_WRITE 0x2 + +static void (*bpf_trace_printk)(const char *fmt,int fmt_size, ...) = + (void *)6 ; + +SEC("func=null_lseek file->f_mode offset orig") +int bpf_func__null_lseek (void *ctx,int err,unsigned long f_mode, +unsigned long offset,unsigned long orig) +{ +if (err) +return 0 ; +if (f_mode &FMODE_WRITE ) +return 0 ; +if (offset &1 ) +return 0 ; +if (orig ==SEEK_CUR ) +return 0 ; +return 1 ; +} + +char _license[] SEC("license") = "GPL"; +int _version SEC("version") = LINUX_VERSION_CODE;
tools/perf/tests/bpf.c +34 -0
@@ -19,6 +19,29 @@ static int epoll_pwait_loop(void)return 0 ; } +#ifdef HAVE_BPF_PROLOGUE + +static int llseek_loop (void ) +{ +int fds[2 ], i; + + fds[0 ] =open ("/dev/null" ,O_RDONLY ); + fds[1 ] =open ("/dev/null" ,O_RDWR ); + +if (fds[0 ] <0 || fds[1 ] <0 ) +return -1 ; + +for (i =0 ; i <NR_ITERS ; i++) { +lseek (fds[i %2 ], i, (i /2 ) %2 ?SEEK_CUR :SEEK_SET ); +lseek (fds[(i +1 ) %2 ], i, (i /2 ) %2 ?SEEK_CUR :SEEK_SET ); + } +close (fds[0 ]); +close (fds[1 ]); +return 0 ; +} + +#endif + static struct { enum test_llvm__testcase prog_id; const char *desc;
@@ -37,6 +60,17 @@ static struct { &epoll_pwait_loop, (NR_ITERS + 1) / 2, }, +#ifdef HAVE_BPF_PROLOGUE + { + LLVM_TESTCASE_BPF_PROLOGUE, + "Test BPF prologue generation", + "[bpf_prologue_test]", + "fix kbuild first", + "check your vmlinux setting?", + &llseek_loop, + (NR_ITERS + 1) / 4, + }, +#endif }; static int do_test(struct bpf_object *obj, int (*func)(void),
tools/perf/tests/llvm.c +4 -0
@@ -44,6 +44,10 @@ static struct { .source = test_llvm__bpf_test_kbuild_prog, .desc = "Test kbuild searching", }, + [LLVM_TESTCASE_BPF_PROLOGUE] = { + .source = test_llvm__bpf_test_prologue_prog, + .desc = "Test BPF prologue generation", + }, };
tools/perf/tests/llvm.h +2 -0
@@ -6,10 +6,12 @@extern const char test_llvm__bpf_base_prog[];extern const char test_llvm__bpf_test_kbuild_prog[]; +extern const char test_llvm__bpf_test_prologue_prog[];enum test_llvm__testcase {LLVM_TESTCASE_BASE ,LLVM_TESTCASE_KBUILD , +LLVM_TESTCASE_BPF_PROLOGUE , __LLVM_TESTCASE_MAX, };
Signature · 1 line
2.1.0
Arnaldo Carvalho de Melo
maintainerPATCH 12/16 perf test: Fix 'perf test BPF' when it fails to find a suitable vmlinux
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
Two bugs in 'perf test BPF' are found when testing BPF prologue without vmlinux:
# mv /lib/modules/4.3.0-rc4+/build/vmlinux{,.bak}
# ./perf test BPF
37: Test BPF filter :Failed to find the path for kernel: No such file or directory
OkTest BPF should fail in this case.
After this patch:
# ./perf test BPF
37: Test BPF filter :Failed to find the path for kernel: No such file or directory
FAILED!
# mv /lib/modules/4.3.0-rc4+/build/vmlinux{.bak,}
# ./perf test BPF
37: Test BPF filter : Ok--- tools/perf/tests/bpf.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
tools/perf/tests/bpf.c +4 -3
@@ -102,8 +102,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void), err =parse_events_load_bpf_obj (&parse_evlist, &parse_evlist.list, obj);if (err ||list_empty (&parse_evlist.list)) {pr_debug ("Failed to add events selected by BPF\n" ); -if (!err) -return TEST_FAIL ; +return TEST_FAIL ; }snprintf (pid,sizeof (pid),"%d" ,getpid ());
@@ -157,8 +156,10 @@ static int do_test(struct bpf_object *obj, int (*func)(void), } } -if (count != expect) +if (count != expect) {pr_debug ("BPF filter result incorrect\n" ); + goto out_delete_evlist; + } ret =TEST_OK ;
Signature · 1 line
2.1.0
Arnaldo Carvalho de Melo
maintainerPATCH 13/16 perf bpf: Use same BPF program if arguments are identical
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
This patch allows creating only one BPF program for different 'probe_trace_event'(tev) entries generated by one 'perf_probe_event'(pev) if their prologues are identical.
This is done by comparing the argument list of different tev instances, and the maps type of prologue and tev using a mapping array. This patch utilizes qsort to sort the tevs. After sorting, tevs with identical argument lists will be grouped together.
Test result:
Sample BPF program:
#define SEC(NAME) __attribute__((section(NAME), used))
SEC("inlines=no;"
"func=SyS_dup? oldfd")
int func(void *ctx)
{
return 1;
}It would probe at SyS_dup2 and SyS_dup3, obtaining oldfd as its argument.
The following cmdline shows a BPF program being loaded into the kernel by perf:
# perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog
Before this patch:
# perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog [1] 24858 lrwx------ 1 root root 64 Nov 14 04:09 3 -> anon_inode:bpf-prog lrwx------ 1 root root 64 Nov 14 04:09 4 -> anon_inode:bpf-prog ...
After this patch:
# perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog [1] 25699 lrwx------ 1 root root 64 Nov 14 04:10 3 -> anon_inode:bpf-prog ...
--- tools/perf/util/bpf-loader.c | 138 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 131 insertions(+), 7 deletions(-)
tools/perf/util/bpf-loader.c +131 -7
@@ -38,6 +38,8 @@ struct bpf_prog_priv {struct perf_probe_event pev;bool need_prologue;struct bpf_insn *insns_buf; +int nr_types; +int *type_mapping; };static bool libbpf_initialized;
@@ -113,6 +115,7 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,cleanup_perf_probe_events (&priv->pev,1 );zfree (&priv->insns_buf); +zfree (&priv->type_mapping);free (priv); }
@@ -381,7 +384,7 @@ preproc_gen_prologue(struct bpf_program *prog, int n,struct bpf_prog_priv *priv;struct bpf_insn *buf;size_t prologue_cnt =0 ; -int err; +int i, err; err =bpf_program__get_private (prog, (void **)&priv);if (err || !priv)
@@ -389,10 +392,21 @@ preproc_gen_prologue(struct bpf_program *prog, int n, pev = &priv->pev; -if (n <0 || n >= pev->ntevs) +if (n <0 || n >= priv->nr_types) goto errout; - tev = &pev->tevs[n]; +/* Find a tev belongs to that type */ +for (i =0 ; i < pev->ntevs; i++) { +if (priv->type_mapping[i] == n) +break ; + } + +if (i >= pev->ntevs) { +pr_debug ("Internal error: prologue type %d not found\n" , n); +return -BPF_LOADER_ERRNO__PROLOGUE ; + } + + tev = &pev->tevs[i]; buf = priv->insns_buf; err = bpf__gen_prologue(tev->args, tev->nargs,
@@ -423,6 +437,101 @@ errout:return -BPF_LOADER_ERRNO__PROLOGUE ; } +/* +* compare_tev_args is reflexive, transitive and antisymmetric. +* I can proof it but this margin is too narrow to contain. +*/ +static int compare_tev_args (const void *ptev1,const void *ptev2) +{ +int i, ret; +const struct probe_trace_event *tev1 = + *(const struct probe_trace_event **)ptev1; +const struct probe_trace_event *tev2 = + *(const struct probe_trace_event **)ptev2; + + ret = tev2->nargs - tev1->nargs; +if (ret) +return ret; + +for (i =0 ; i < tev1->nargs; i++) { +struct probe_trace_arg *arg1, *arg2; +struct probe_trace_arg_ref *ref1, *ref2; + + arg1 = &tev1->args[i]; + arg2 = &tev2->args[i]; + + ret =strcmp (arg1->value, arg2->value); +if (ret) +return ret; + + ref1 = arg1->ref; + ref2 = arg2->ref; + +while (ref1 && ref2) { + ret = ref2->offset - ref1->offset; +if (ret) +return ret; + + ref1 = ref1->next; + ref2 = ref2->next; + } + +if (ref1 || ref2) +return ref2 ?1 :-1 ; + } + +return 0 ; +} + +/* +* Assign a type number to each tevs in a pev. +* mapping is an array with same slots as tevs in that pev. +* nr_types will be set to number of types. +*/ +static int map_prologue (struct perf_probe_event *pev,int *mapping, +int *nr_types) +{ +int i, type =0 ; +struct probe_trace_event **ptevs; + +size_t array_sz =sizeof (*ptevs) * pev->ntevs; + + ptevs =malloc (array_sz); +if (!ptevs) { +pr_debug ("No ehough memory: alloc ptevs failed\n" ); +return -ENOMEM ; + } + +pr_debug ("In map_prologue, ntevs=%d\n" , pev->ntevs); +for (i =0 ; i < pev->ntevs; i++) + ptevs[i] = &pev->tevs[i]; + +qsort (ptevs, pev->ntevs,sizeof (*ptevs), + compare_tev_args); + +for (i =0 ; i < pev->ntevs; i++) { +int n; + + n = ptevs[i] - pev->tevs; +if (i ==0 ) { + mapping[n] = type; +pr_debug ("mapping[%d]=%d\n" , n, type); +continue ; + } + +if (compare_tev_args (ptevs + i, ptevs + i -1 ) ==0 ) + mapping[n] = type; +else + mapping[n] = ++type; + +pr_debug ("mapping[%d]=%d\n" , n, mapping[n]); + } +free (ptevs); + *nr_types = type +1 ; + +return 0 ; +} +static int hook_load_preprocessor (struct bpf_program *prog) {struct perf_probe_event *pev;
@@ -462,7 +571,19 @@ static int hook_load_preprocessor(struct bpf_program *prog)return -ENOMEM ; } - err =bpf_program__set_prep (prog, pev->ntevs, + priv->type_mapping =malloc (sizeof (int ) * pev->ntevs); +if (!priv->type_mapping) { +pr_debug ("No enough memory: alloc type_mapping failed\n" ); +return -ENOMEM ; + } +memset (priv->type_mapping,-1 , +sizeof (int ) * pev->ntevs); + + err =map_prologue (pev, priv->type_mapping, &priv->nr_types); +if (err) +return err; + + err =bpf_program__set_prep (prog, priv->nr_types, preproc_gen_prologue);return err; }
@@ -596,10 +717,13 @@ int bpf__foreach_tev(struct bpf_object *obj,for (i =0 ; i < pev->ntevs; i++) { tev = &pev->tevs[i]; -if (priv->need_prologue) - fd =bpf_program__nth_fd (prog, i); -else +if (priv->need_prologue) { +int type = priv->type_mapping[i]; + + fd =bpf_program__nth_fd (prog, type); + }else { fd =bpf_program__fd (prog); + }if (fd <0 ) {pr_debug ("bpf: failed to get file descriptor\n" );
Signature · 1 line
2.1.0
Arnaldo Carvalho de Melo
maintainerPATCH 14/16 perf test: Print result for each LLVM subtest
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
Currently 'perf test llvm' and 'perf test BPF' have multiple sub-tests, but the result is provided in only one line:
# perf test LLVM 35: Test LLVM searching and compiling : Ok
This patch introduces sub-tests support, allowing 'perf test' to report result for each sub-tests:
# perf test LLVM 35: Test LLVM searching and compiling : 35.1: Basic BPF llvm compiling test : Ok 35.2: Test kbuild searching : Ok 35.3: Compile source for BPF prologue generation test : Ok
When a failure happens:
# cat ~/.perfconfig
[llvm]
clang-path = "/bin/false"
# perf test LLVM
35: Test LLVM searching and compiling :
35.1: Basic BPF llvm compiling test : FAILED!
35.2: Test kbuild searching : Skip
35.3: Compile source for BPF prologue generation test : SkipAnd:
# rm ~/.perfconfig # ./perf test LLVM 35: Test LLVM searching and compiling : 35.1: Basic BPF llvm compiling test : Skip 35.2: Test kbuild searching : Skip 35.3: Compile source for BPF prologue generation test : Skip
Skip by user:
# ./perf test -s 1,`seq -s , 3 42` 1: vmlinux symtab matches kallsyms : Skip (user override) 2: detect openat syscall event : Ok ... 35: Test LLVM searching and compiling : Skip (user override) ...
--- tools/perf/tests/builtin-test.c | 111 ++++++++++++++++++++++++++++++++-------- tools/perf/tests/llvm.c | 65 +++++++++++------------ tools/perf/tests/tests.h | 22 +++++++- 3 files changed, 141 insertions(+), 57 deletions(-)
tools/perf/tests/builtin-test.c +91 -20
@@ -159,7 +159,13 @@ static struct test generic_tests[] = { }, { .desc = "Test LLVM searching and compiling", - .func = test__llvm, + .need_subtests = true, + .subtest = { + .skip_if_fail = true, + .get_nr = test__llvm_subtest_get_nr, + .get_desc = test__llvm_subtest_get_desc, + .func = test__llvm_subtest, + }, }, { .desc = "Test topology in session",
@@ -203,7 +209,7 @@ static bool perf_test__matches(struct test *test, int curr, int argc, const charreturn false; } -static int run_test (struct test *test) +static int run_test (struct test *test,int subtest) {int status, err =-1 , child =fork ();char sbuf[STRERR_BUFSIZE ];
@@ -216,7 +222,10 @@ static int run_test(struct test *test)if (!child) {pr_debug ("test child forked, pid %d\n" ,getpid ()); - err = test->func (); +if (!test->need_subtests) + err = test->func (); +else + err = test->subtest.func (subtest);exit (err); }
@@ -234,8 +243,48 @@ static int run_test(struct test *test) }#define for_each_test (j, t) \ - for (j = 0; j < ARRAY_SIZE(tests); j++) \ - for (t = &tests[j][0]; t->func; t++) + for (j = 0; j < ARRAY_SIZE(tests); j++) \ + for (t = &tests[j][0]; \ + (!t->need_subtests && t->func) || \ + (t->need_subtests && \ + t->subtest.get_nr && \ + t->subtest.get_desc && \ + t->subtest.func); \ + t++) + +static int test_and_print (struct test *t,bool force_skip,int subtest) +{ +int err; + +if (!force_skip) { +pr_debug ("\n--- start ---\n" ); + err =run_test (t, subtest); +pr_debug ("---- end ----\n" ); + }else { +pr_debug ("\n--- force skipped ---\n" ); + err =TEST_SKIP ; + } + +if (!t->need_subtests) +pr_debug ("%s:" , t->desc); +else +pr_debug ("%s subtest %d:" , t->desc, subtest); + +switch (err) { +case TEST_OK : +pr_info (" Ok\n" ); +break ; +case TEST_SKIP : +color_fprintf (stderr,PERF_COLOR_YELLOW ," Skip\n" ); +break ; +case TEST_FAIL : +default : +color_fprintf (stderr,PERF_COLOR_RED ," FAILED!\n" ); +break ; + } + +return err; +}static int __cmd_test (int argc,const char *argv[],struct intlist *skiplist) {
@@ -264,21 +313,43 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)continue ; } -pr_debug ("\n--- start ---\n" ); - err =run_test (t); -pr_debug ("---- end ----\n%s:" , t->desc); - -switch (err) { -case TEST_OK : -pr_info (" Ok\n" ); -break ; -case TEST_SKIP : -color_fprintf (stderr,PERF_COLOR_YELLOW ," Skip\n" ); -break ; -case TEST_FAIL : -default : -color_fprintf (stderr,PERF_COLOR_RED ," FAILED!\n" ); -break ; +if (!t->need_subtests) { +test_and_print (t, false,-1 ); + }else { +int subn = t->subtest.get_nr (); +/* +* minus 2 to align with normal testcases. +* For subtest we print additional '.x' in number. +* for example: +* +* 35: Test LLVM searching and compiling : +* 35.1: Basic BPF llvm compiling test : Ok +*/ +int subw = width >2 ? width -2 : width; +bool skip = false; +int subi; + +if (subn <=0 ) { +color_fprintf (stderr,PERF_COLOR_YELLOW , +" Skip (not compiled in)\n" ); +continue ; + } +pr_info ("\n" ); + +for (subi =0 ; subi < subn; subi++) { +int len =strlen (t->subtest.get_desc (subi)); + +if (subw < len) + subw = len; + } + +for (subi =0 ; subi < subn; subi++) { +pr_info ("%2d.%1d: %-*s:" , i, subi +1 , subw, + t->subtest.get_desc (subi)); + err =test_and_print (t, skip, subi); +if (err !=TEST_OK && t->subtest.skip_if_fail) + skip = true; + } } }
tools/perf/tests/llvm.c +30 -35
81 lines not on this page · raw message
tools/perf/tests/tests.h +20 -2
@@ -1,6 +1,8 @@#ifndef TESTS_H #define TESTS_H +#include <stdbool.h> +#define TEST_ASSERT_VAL (text, cond) \ do { \ if (!(cond)) { \
@@ -26,7 +28,19 @@ enum {struct test {const char *desc; -int (*func)(void ); + +bool need_subtests; + +union { +int (*func)(void ); + +struct { +bool skip_if_fail; +int (*get_nr)(void ); +const char *(*get_desc)(int i); +int (*func)(int i); + } subtest; + }; };/* Tests */
@@ -65,7 +79,11 @@ int test__fdarray__filter(void);int test__fdarray__add (void );int test__kmod_path__parse (void );int test__thread_map (void ); -int test__llvm (void ); + +const char *test__llvm_subtest_get_desc (int i); +int test__llvm_subtest_get_nr (void ); +int test__llvm_subtest (int i); +int test__bpf (void );int test_session_topology (void );
Signature · 1 line
2.1.0
Arnaldo Carvalho de Melo
maintainerPATCH 15/16 perf test: Print result for each BPF subtest
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
This patch prints each sub-tests results for BPF testcases.
Before:
# ./perf test BPF 37: Test BPF filter : Ok
After:
# ./perf test BPF 37: Test BPF filter : 37.1: Test basic BPF filtering : Ok 37.2: Test BPF prologue generation : Ok
When a failure happens:
# cat ~/.perfconfig
[llvm]
clang-path = "/bin/false"
# ./perf test BPF
37: Test BPF filter :
37.1: Test basic BPF filtering : Skip
37.2: Test BPF prologue generation : Skip--- tools/perf/tests/bpf.c | 38 ++++++++++++++++++++++++++++---------- tools/perf/tests/builtin-test.c | 8 +++++++- tools/perf/tests/tests.h | 5 ++++- 3 files changed, 39 insertions(+), 12 deletions(-)
tools/perf/tests/bpf.c +28 -10
@@ -215,28 +215,46 @@ out:return ret; } -int test__bpf (void ) +int test__bpf_subtest_get_nr (void ) +{ +return (int )ARRAY_SIZE (bpf_testcase_table); +} + +const char *test__bpf_subtest_get_desc (int i) +{ +if (i <0 || i >= (int )ARRAY_SIZE (bpf_testcase_table)) +return NULL ; +return bpf_testcase_table[i].desc; +} + +int test__bpf_subtest (int i) { -unsigned int i;int err; +if (i <0 || i >= (int )ARRAY_SIZE (bpf_testcase_table)) +return TEST_FAIL ; +if (geteuid () !=0 ) {pr_debug ("Only root can run BPF test\n" );return TEST_SKIP ; } -for (i =0 ; i <ARRAY_SIZE (bpf_testcase_table); i++) { - err =__test__bpf (i); + err =__test__bpf (i); +return err; +} -if (err !=TEST_OK ) -return err; - } +#else +int test__bpf_subtest_get_nr (void ) +{ +return 0 ; +} -return TEST_OK ; +const char *test__bpf_subtest_get_desc (int i__maybe_unused ) +{ +return NULL ; } -#else -int test__bpf (void ) +int test__bpf_subtest (int i__maybe_unused ) {pr_debug ("Skip BPF test because BPF support is not compiled\n" );return TEST_SKIP ;
tools/perf/tests/builtin-test.c +7 -1
@@ -173,7 +173,13 @@ static struct test generic_tests[] = { }, { .desc = "Test BPF filter", - .func = test__bpf, + .need_subtests = true, + .subtest = { + .skip_if_fail = true, + .get_nr = test__bpf_subtest_get_nr, + .get_desc = test__bpf_subtest_get_desc, + .func = test__bpf_subtest, + }, }, { .func = NULL,
tools/perf/tests/tests.h +4 -1
@@ -84,7 +84,10 @@ const char *test__llvm_subtest_get_desc(int i);int test__llvm_subtest_get_nr (void );int test__llvm_subtest (int i); -int test__bpf (void ); +const char *test__bpf_subtest_get_desc (int i); +int test__bpf_subtest_get_nr (void ); +int test__bpf_subtest (int i); +int test_session_topology (void );#if defined(__arm__) || defined(__aarch64__)
Signature · 1 line
2.1.0
Arnaldo Carvalho de Melo
maintainerPATCH 16/16 perf test: Mute test cases error messages if verbose == 0
In reply to Arnaldo Carvalho de MeloFrom: Wang Nan <[email protected]>
Sometimes error messages in breaks the pretty output of 'perf test'. For example:
# mv /lib/modules/4.3.0-rc4+/build/vmlinux{,.bak}
# perf test LLVM BPF
35: Test LLVM searching and compiling :
35.1: Basic BPF llvm compiling test : Ok
35.2: Test kbuild searching : Ok
35.3: Compile source for BPF prologue generation test : Ok
37: Test BPF filter :
37.1: Test basic BPF filtering : Ok
37.2: Test BPF prologue generation :Failed to find the path for kernel: No such file or directory FAILED!This patch mute test cases thoroughly by redirect their stdout and stderr to /dev/null when verbose == 0. After applying this patch:
# ./perf test LLVM BPF 35: Test LLVM searching and compiling : 35.1: Basic BPF llvm compiling test : Ok 35.2: Test kbuild searching : Ok 35.3: Compile source for BPF prologue generation test : Ok 37: Test BPF filter : 37.1: Test basic BPF filtering : Ok 37.2: Test BPF prologue generation : FAILED!
# ./perf test -v LLVM BPF 35: Test LLVM searching and compiling : 35.1: Basic BPF llvm compiling test : --- start --- test child forked, pid 13183 Kernel build dir is set to /lib/modules/4.3.0-rc4+/build set env: KBUILD_DIR=/lib/modules/4.3.0-rc4+/build ... bpf: config 'func=null_lseek file->f_mode offset orig' is ok Looking at the vmlinux_path (7 entries long) Failed to find the path for kernel: No such file or directory bpf_probe: failed to convert perf probe eventsFailed to add events selected by BPF test child finished with -1 ---- end ---- Test BPF filter subtest 1: FAILED!
--- tools/perf/tests/builtin-test.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
tools/perf/tests/builtin-test.c +15 -0
@@ -227,7 +227,22 @@ static int run_test(struct test *test, int subtest) }if (!child) { +int nullfd; +pr_debug ("test child forked, pid %d\n" ,getpid ()); + +if (!verbose) { + nullfd =open ("/dev/null" ,O_WRONLY ); +if (nullfd >=0 ) { +close (STDERR_FILENO ); +close (STDOUT_FILENO ); + +dup2 (nullfd,STDOUT_FILENO ); +dup2 (STDOUT_FILENO ,STDERR_FILENO ); +close (nullfd); + } + } +if (!test->need_subtests) err = test->func ();else
Signature · 1 line
2.1.0
Quoted 153 lines · Arnaldo Carvalho de Melo
Hi Ingo,
This is on top of my last perf-urgent-for-mingo pull req, that is
outstanding, please check if this can get into this merge window.- Arnaldo
The following changes since commit
092b1f0b5f9f797812da0de927c3aa26acbe8762:perf probe: Clear
probe_trace_eventwhenadd_probe_trace_event()fails (2015-11-13 12:28:09 -0300)are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.gittags/perf-ebpf-for-mingofor you to fetch changes up to
0fbcd50e1385d3a78ddb3cf52dcdff802bfb835e:perf test: Mute test cases error messages if verbose == 0 (2015-11-17 11:45:06 -0300)
----------------------------------------------------------------
perf/ebpf improvements and fixes:User visible:
- Allows BPF scriptlets specify arguments to be fetched using
DWARF info, using a prologue generated at compile/build time (He Kuang, Wang Nan)- Allow attaching BPF scriptlets to module symbols (Wang Nan)
- Allow attaching BPF scriptlets to userspace code using uprobe (Wang Nan)
- BPF programs now can specify 'perf probe' tunables via its section name, separating key=val values using semicolons (Wang Nan)
Testing some of these new BPF features:
Use case: get callchains when receiving SSL packets, filter then in the
kernel, at arbitrary place.# cat ssl.bpf.c #define SEC(NAME) __attribute__((section(NAME), used))struct
pt_regs;SEC("func=__inet_lookup_established hnum") int func(struct pt_regs *ctx, int err, unsigned short port) { return err == 0 && port == 443; }char _license[]
SEC("license")= "GPL";
int_versionSEC("version")=LINUX_VERSION_CODE;
## perf record -a -g -e ssl.bpf.c ^C[ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.787 MB perf.data (3 samples) ] # perf script | head -30 swapper 0 [000] 58783.268118: perf_bpf_probe:func: (ffffffff816a0f60) hnum=0x1bb 8a0f61 __inet_lookup_established (/lib/modules/4.3.0+/build/vmlinux) 896def ip_rcv_finish (/lib/modules/4.3.0+/build/vmlinux) 8976c2 ip_rcv (/lib/modules/4.3.0+/build/vmlinux) 855eba __netif_receive_skb_core (/lib/modules/4.3.0+/build/vmlinux) 8565d8 __netif_receive_skb (/lib/modules/4.3.0+/build/vmlinux) 8572a8 process_backlog (/lib/modules/4.3.0+/build/vmlinux) 856b11 net_rx_action (/lib/modules/4.3.0+/build/vmlinux) 2a284b __do_softirq (/lib/modules/4.3.0+/build/vmlinux) 2a2ba3 irq_exit (/lib/modules/4.3.0+/build/vmlinux) 96b7a4 do_IRQ (/lib/modules/4.3.0+/build/vmlinux) 969807 ret_from_intr (/lib/modules/4.3.0+/build/vmlinux) 2dede5 cpu_startup_entry (/lib/modules/4.3.0+/build/vmlinux) 95d5bc rest_init (/lib/modules/4.3.0+/build/vmlinux) 1163ffa start_kernel ([kernel.vmlinux].init.text) 11634d7 x86_64_start_reservations ([kernel.vmlinux].init.text) 1163623 x86_64_start_kernel ([kernel.vmlinux].init.text)qemu-system-x86 9178 [003] 58785.792417: perf_bpf_probe:func: (
ffffffff816a0f60) hnum=0x1bb8a0f61 __inet_lookup_established (/lib/modules/4.3.0+/build/vmlinux) 896def ip_rcv_finish (/lib/modules/4.3.0+/build/vmlinux) 8976c2 ip_rcv (/lib/modules/4.3.0+/build/vmlinux) 855eba __netif_receive_skb_core (/lib/modules/4.3.0+/build/vmlinux) 8565d8 __netif_receive_skb (/lib/modules/4.3.0+/build/vmlinux) 856660 netif_receive_skb_internal (/lib/modules/4.3.0+/build/vmlinux) 8566ec netif_receive_skb_sk (/lib/modules/4.3.0+/build/vmlinux) 430a br_handle_frame_finish ([bridge]) 48bc br_handle_frame ([bridge]) 855f44 __netif_receive_skb_core (/lib/modules/4.3.0+/build/vmlinux) 8565d8 __netif_receive_skb (/lib/modules/4.3.0+/build/vmlinux)#
Use 'perf probe' various options to list functions, see what variables can be collected at any given point, experiment first collecting without a filter, then filter, use it together with 'perf trace', 'perf top', with or without callchains, if it explodes, please tell us!
Infrastructure:
- Add further 'perf test' entries for BPF and LLVM (Wang Nan)
- Improve 'perf test' to suport subtests, so that the series of tests performed in the LLVM and BPF main tests appear in the default 'perf test' output (Wang Nan)
- Move
memdup()fromtools/perftotools/lib/string.c(Arnaldo Carvalho de Melo)- Adopt
strtobool()from the kernel intotools/lib/(Wang Nan)Signed-off-by Arnaldo Carvalho de Melo <[email protected]>----------------------------------------------------------------
Arnaldo Carvalho de Melo (1):
tools: Adoptmemdup()fromtools/perf, moving it totools/lib/string.cHe Kuang (1):
perf bpf: Add prologue for BPF programs for fetching argumentsWang Nan (14):
tools: Clone the kernel's strtobool function
bpf tools: Load a program with different instances using preprocessor perf bpf: AddBPF_PROLOGUEconfig options for further patches perf bpf: Compiledwarf-regs.cifCONFIG_BPF_PROLOGUEis on perf bpf: Allow BPF program attach to uprobe events
perf bpf: Allow attaching BPF programs to modules symbols perf bpf: Allow BPF program config probing options
perf bpf: Generate prologue for BPF programs
perf test: Test the BPF prologue adding infrastructure
perf test: Fix 'perf test BPF' when it fails to find a suitable vmlinux perf bpf: Use same BPF program if arguments are identical perf test: Print result for each LLVM subtest
perf test: Print result for each BPF subtest
perf test: Mute test cases error messages if verbose == 0tools/include/linux/string.h | 11 + tools/lib/bpf/libbpf.c | 146 ++++++++- tools/lib/bpf/libbpf.h | 64 ++++ tools/lib/string.c | 62 ++++ tools/perf/MANIFEST | 2 + tools/perf/arch/x86/util/Build | 1 + tools/perf/config/Makefile | 12 + tools/perf/tests/.gitignore | 1 + tools/perf/tests/Build | 9 +- tools/perf/tests/bpf-script-test-prologue.c | 35 +++ tools/perf/tests/bpf.c | 79 ++++- tools/perf/tests/builtin-test.c | 134 ++++++-- tools/perf/tests/llvm.c | 67 ++-- tools/perf/tests/llvm.h | 2 + tools/perf/tests/tests.h | 27 +- tools/perf/util/Build | 7 + tools/perf/util/bpf-loader.c | 434 +++++++++++++++++++++++++- tools/perf/util/bpf-loader.h | 4 + tools/perf/util/bpf-prologue.c | 455 ++++++++++++++++++++++++++++ tools/perf/util/bpf-prologue.h | 34 +++ tools/perf/util/include/linux/string.h | 3 - tools/perf/util/probe-event.c | 7 +-
Quoted 8 lines
tools/perf/util/string.c | 16 - 23 files changed, 1502 insertions(+), 110 deletions(-) create mode 100644 tools/include/linux/string.h create mode 100644 tools/lib/string.c create mode 100644 tools/perf/tests/bpf-script-test-prologue.c create mode 100644 tools/perf/util/bpf-prologue.c create mode 100644 tools/perf/util/bpf-prologue.h delete mode 100644 tools/perf/util/include/linux/string.h
So considering the relatively large diffstat and the fact that we are 3 days after the closing of the merge window, I think we should separate this into minimal fixes for perf/urgent, and more involved changes for v4.5.
Nice progress with eBPF tooling!
Thanks,
Ingo
Arnaldo Carvalho de Melo
maintainerEm Wed, Nov 18, 2015 at 07:32:38AM +0100, Ingo Molnar escreveu:
Quoted 3 lines
So considering the relatively large diffstat and the fact that we are 3 days after the closing of the merge window, I think we should separate this into minimal fixes for perf/urgent, and more involved changes for v4.5.
Ok, I'll do that, separate pure bugfixes into perf/urgent and then move the rest to perf/core.
Quoted 1 line
Nice progress with eBPF tooling!
Indeed!
- Arnaldo
发自我的 iPhone
Quoted 9 lines · 在 2015年11月18日,下午8:44,Arnaldo Carvalho de Melo <[email protected]> 写道:
Em Wed, Nov 18, 2015 at 07:32:38AM +0100, Ingo Molnar escreveu:
Quoted 3 lines
So considering the relatively large diffstat and the fact that we are 3 days after the closing of the merge window, I think we should separate this into minimal fixes for perf/urgent, and more involved changes for v4.5.
Ok, I'll do that, separate pure bugfixes into perf/urgent and then move the rest to perf/core.
So you won't maintain perf/ebpf and will move all eBPF related commits to perf/core? Then let me wait for your action, and rebase my local tree to perf/core again.
I hope I can start sending other patches in my local tree to you soon. They are almost ready.
Thank you.
Quoted 6 lines
Quoted 2 lines
Nice progress with eBPF tooling!
Indeed!
- Arnaldo
Arnaldo Carvalho de Melo
maintainerEm Wed, Nov 18, 2015 at 09:06:58PM +0800, pi3orama escreveu:
Quoted 11 lines
发自我的 iPhone
Quoted 7 lines · 在 2015年11月18日,下午8:44,Arnaldo Carvalho de Melo <[email protected]> 写道:
Em Wed, Nov 18, 2015 at 07:32:38AM +0100, Ingo Molnar escreveu:
Quoted 3 lines
So considering the relatively large diffstat and the fact that we are 3 days after the closing of the merge window, I think we should separate this into minimal fixes for perf/urgent, and more involved changes for v4.5.
Quoted 6 lines
Quoted 2 lines
Ok, I'll do that, separate pure bugfixes into perf/urgent and then move the rest to perf/core.
So you won't maintain perf/ebpf and will move all eBPF related commits to perf/core? Then let me wait for your action, and rebase my local tree to perf/core again.
I think you can work on perf/ebpf, I can't envision any major problems arising from that, what I'll do is basically cherry-pick pure fixes that are there, IIRC there are several (like the perf test emitting errors in non verbose mode, etc), and then rebase perf/ebpf on top of that, just some patch reordering should take place.
At that point I'll just rename perf/ebpf to perf/core and collect more non-pure-bug-fixes on top of it.
Quoted 2 lines
I hope I can start sending other patches in my local tree to you soon. They are almost ready.
Ok
Quoted 7 lines
Thank you.
Quoted 6 lines
Quoted 2 lines
Nice progress with eBPF tooling!
Indeed!
- Arnaldo