[v3,26/30] perf clang: Link BPF functions declaration into perf

Message ID 20161126070354.141764-27-wangnan0@huawei.com
State New
Headers show

Commit Message

Wang Nan Nov. 26, 2016, 7:03 a.m.
Use a shell script to generate BPF functions declarations from kernel
source code, embed the generated header into a C string. Following
commits will utilizes clang's virtual file system to automatically
include this header to all BPF scripts.

The generated header is wrapped by a BUILTIN_CLANG_NO_DEFAULT_INCLUDE.
This macro will be used by following commits to allow user disable this
and other builtin includes.

Signed-off-by: Wang Nan <wangnan0@huawei.com>

Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
---
 tools/perf/util/c++/Build                |   1 +
 tools/perf/util/c++/bpf-funcs-str.c      | 228 +++++++++++++++++++++++++++++++
 tools/perf/util/c++/clang-bpf-includes.h |  12 ++
 3 files changed, 241 insertions(+)
 create mode 100644 tools/perf/util/c++/bpf-funcs-str.c
 create mode 100644 tools/perf/util/c++/clang-bpf-includes.h

-- 
2.10.1

Comments

Alexei Starovoitov Nov. 26, 2016, 5:40 p.m. | #1
On Sat, Nov 26, 2016 at 07:03:50AM +0000, Wang Nan wrote:
> Use a shell script to generate BPF functions declarations from kernel

> source code, embed the generated header into a C string. Following

> commits will utilizes clang's virtual file system to automatically

> include this header to all BPF scripts.

> 

> The generated header is wrapped by a BUILTIN_CLANG_NO_DEFAULT_INCLUDE.

> This macro will be used by following commits to allow user disable this

> and other builtin includes.

> 

> Signed-off-by: Wang Nan <wangnan0@huawei.com>

> Cc: Arnaldo Carvalho de Melo <acme@redhat.com>

> Cc: Alexei Starovoitov <ast@fb.com>

> Cc: He Kuang <hekuang@huawei.com>

> Cc: Jiri Olsa <jolsa@kernel.org>

> Cc: Zefan Li <lizefan@huawei.com>

> Cc: pi3orama@163.com

> ---

>  tools/perf/util/c++/Build                |   1 +

>  tools/perf/util/c++/bpf-funcs-str.c      | 228 +++++++++++++++++++++++++++++++

>  tools/perf/util/c++/clang-bpf-includes.h |  12 ++

>  3 files changed, 241 insertions(+)

>  create mode 100644 tools/perf/util/c++/bpf-funcs-str.c

>  create mode 100644 tools/perf/util/c++/clang-bpf-includes.h

> 

> diff --git a/tools/perf/util/c++/Build b/tools/perf/util/c++/Build

> index 988fef1..bd71abf 100644

> --- a/tools/perf/util/c++/Build

> +++ b/tools/perf/util/c++/Build

> @@ -1,2 +1,3 @@

>  libperf-$(CONFIG_CLANGLLVM) += clang.o

>  libperf-$(CONFIG_CLANGLLVM) += clang-test.o

> +libperf-$(CONFIG_CLANGLLVM) += bpf-funcs-str.o

> diff --git a/tools/perf/util/c++/bpf-funcs-str.c b/tools/perf/util/c++/bpf-funcs-str.c

> new file mode 100644

> index 0000000..f6bcf76

> --- /dev/null

> +++ b/tools/perf/util/c++/bpf-funcs-str.c

> @@ -0,0 +1,228 @@

> +/*

> + * This file is generated by following script:

> + *

> + * #!/bin/bash

> + * TEMP_KBUILD=$(mktemp -d)

> + * KERNEL_DIR=$(pwd)

> + * OUTPUT=tools/perf/util/c++/bpf-funcs-str.c

> + * rm -rf $OUTPUT

> + * echo "Use temp dir: $TEMP_KBUILD"

> + * function finish()

> + * {

> + * 	rm -rf $TEMP_KBUILD

> + * }

> + * trap finish EXIT

> + * SRCLIST=$(find -name "*.c" | xargs grep bpf_func_proto -l)

> + * cd $TEMP_KBUILD

> + * yes '' | make -C $KERNEL_DIR O=`pwd` oldconfig

> + * cat << EOF >> ./.config

> + * CONFIG_BPF=y

> + * CONFIG_BPF_SYSCALL=y

> + * CONFIG_PERF_EVENTS=y

> + * CONFIG_SOCK_CGROUP_DATA=y

> + * EOF

> + * yes '' | make -C $KERNEL_DIR O=`pwd` oldconfig

> + * FIXOBJLIST=""

> + * for src in ${SRCLIST}

> + * do

> + * 	mkdir -p $(dirname $src)

> + * 	cat << EOF > "${src}-fix.c"

> + * #include <linux/init.h>

> + * #undef __init

> + * #define __init __attribute__((constructor))

> + * #include "`basename $src`"

> + * EOF

> + * 	if [ $(basename $src) == "syscall.c" ]

> + * 	then

> + * 		cat << EOF >> "${src}-fix.c"

> + * const struct bpf_verifier_ops *

> + * find_prog_type_export(enum bpf_prog_type type)

> + * {

> + * 	struct bpf_prog_aux aux;

> + * 	struct bpf_prog p = {.aux = &aux };

> + * 	if (find_prog_type(type, &p))

> + * 		return NULL;

> + * 	return p.aux->ops;

> + * }

> + * EOF

> + * 	fi

> + * 	FIXOBJLIST="$FIXOBJLIST ${src}-fix.o"

> + * done

> + * function dolink()

> + * {

> + * 	touch ./syms.c

> + * 	echo gcc kernel/bpf/main.o ./syms.c $FIXOBJLIST -o ./gen

> + * 	gcc kernel/bpf/main.o ./syms.c $FIXOBJLIST -o ./gen

> + * }

> + * MAIN=kernel/bpf/main.c

> + * cat << EOF > $MAIN

> + * #include <uapi/linux/bpf.h>

> + * #include <linux/bpf.h>

> + * struct bpf_func {

> + *   const char *name;

> + *   int id;

> + * } bpf_funcs[] = {

> + * EOF

> + * grep '^[[:space:]]BPF_FUNC_[^ ]*,' $KERNEL_DIR/include/uapi/linux/bpf.h | \

> + * 	sed -e 's/.*BPF_FUNC_\([^,]*\),.*$/\1/g' | \

> + * 	xargs -n 1 sh -c 'echo {.name = \"$1\", .id = BPF_FUNC_$1}, >> '"$MAIN" sh

> + * cat << EOF >> $MAIN


This is pretty fragile and already broken.
Please just hardcode them as-is. There is no need to be so fancy.

> +"static void *(*bpf_map_lookup_elem)(void *, void *) = (void *)1;\n"

> +"static long (*bpf_map_update_elem)(void *, void *, void *, unsigned long) = (void *)2;\n"

> +"static long (*bpf_map_delete_elem)(void *, void *) = (void *)3;\n"


just hard code this way.
Arnaldo Carvalho de Melo Nov. 30, 2016, 4:12 p.m. | #2
Em Sat, Nov 26, 2016 at 09:40:41AM -0800, Alexei Starovoitov escreveu:
> On Sat, Nov 26, 2016 at 07:03:50AM +0000, Wang Nan wrote:

> > + * } bpf_funcs[] = {

> > + * EOF

> > + * grep '^[[:space:]]BPF_FUNC_[^ ]*,' $KERNEL_DIR/include/uapi/linux/bpf.h | \

> > + * 	sed -e 's/.*BPF_FUNC_\([^,]*\),.*$/\1/g' | \

> > + * 	xargs -n 1 sh -c 'echo {.name = \"$1\", .id = BPF_FUNC_$1}, >> '"$MAIN" sh

> > + * cat << EOF >> $MAIN

> 

> This is pretty fragile and already broken.

> Please just hardcode them as-is. There is no need to be so fancy.

> 

> > +"static void *(*bpf_map_lookup_elem)(void *, void *) = (void *)1;\n"

> > +"static long (*bpf_map_update_elem)(void *, void *, void *, unsigned long) = (void *)2;\n"

> > +"static long (*bpf_map_delete_elem)(void *, void *) = (void *)3;\n"

> 

> just hard code this way.


Wang, can you please resend this? I'm trying to process this patchkit,
but will take some time to get to this 26th patch.

- Arnaldo

Patch

diff --git a/tools/perf/util/c++/Build b/tools/perf/util/c++/Build
index 988fef1..bd71abf 100644
--- a/tools/perf/util/c++/Build
+++ b/tools/perf/util/c++/Build
@@ -1,2 +1,3 @@ 
 libperf-$(CONFIG_CLANGLLVM) += clang.o
 libperf-$(CONFIG_CLANGLLVM) += clang-test.o
+libperf-$(CONFIG_CLANGLLVM) += bpf-funcs-str.o
diff --git a/tools/perf/util/c++/bpf-funcs-str.c b/tools/perf/util/c++/bpf-funcs-str.c
new file mode 100644
index 0000000..f6bcf76
--- /dev/null
+++ b/tools/perf/util/c++/bpf-funcs-str.c
@@ -0,0 +1,228 @@ 
+/*
+ * This file is generated by following script:
+ *
+ * #!/bin/bash
+ * TEMP_KBUILD=$(mktemp -d)
+ * KERNEL_DIR=$(pwd)
+ * OUTPUT=tools/perf/util/c++/bpf-funcs-str.c
+ * rm -rf $OUTPUT
+ * echo "Use temp dir: $TEMP_KBUILD"
+ * function finish()
+ * {
+ * 	rm -rf $TEMP_KBUILD
+ * }
+ * trap finish EXIT
+ * SRCLIST=$(find -name "*.c" | xargs grep bpf_func_proto -l)
+ * cd $TEMP_KBUILD
+ * yes '' | make -C $KERNEL_DIR O=`pwd` oldconfig
+ * cat << EOF >> ./.config
+ * CONFIG_BPF=y
+ * CONFIG_BPF_SYSCALL=y
+ * CONFIG_PERF_EVENTS=y
+ * CONFIG_SOCK_CGROUP_DATA=y
+ * EOF
+ * yes '' | make -C $KERNEL_DIR O=`pwd` oldconfig
+ * FIXOBJLIST=""
+ * for src in ${SRCLIST}
+ * do
+ * 	mkdir -p $(dirname $src)
+ * 	cat << EOF > "${src}-fix.c"
+ * #include <linux/init.h>
+ * #undef __init
+ * #define __init __attribute__((constructor))
+ * #include "`basename $src`"
+ * EOF
+ * 	if [ $(basename $src) == "syscall.c" ]
+ * 	then
+ * 		cat << EOF >> "${src}-fix.c"
+ * const struct bpf_verifier_ops *
+ * find_prog_type_export(enum bpf_prog_type type)
+ * {
+ * 	struct bpf_prog_aux aux;
+ * 	struct bpf_prog p = {.aux = &aux };
+ * 	if (find_prog_type(type, &p))
+ * 		return NULL;
+ * 	return p.aux->ops;
+ * }
+ * EOF
+ * 	fi
+ * 	FIXOBJLIST="$FIXOBJLIST ${src}-fix.o"
+ * done
+ * function dolink()
+ * {
+ * 	touch ./syms.c
+ * 	echo gcc kernel/bpf/main.o ./syms.c $FIXOBJLIST -o ./gen
+ * 	gcc kernel/bpf/main.o ./syms.c $FIXOBJLIST -o ./gen
+ * }
+ * MAIN=kernel/bpf/main.c
+ * cat << EOF > $MAIN
+ * #include <uapi/linux/bpf.h>
+ * #include <linux/bpf.h>
+ * struct bpf_func {
+ *   const char *name;
+ *   int id;
+ * } bpf_funcs[] = {
+ * EOF
+ * grep '^[[:space:]]BPF_FUNC_[^ ]*,' $KERNEL_DIR/include/uapi/linux/bpf.h | \
+ * 	sed -e 's/.*BPF_FUNC_\([^,]*\),.*$/\1/g' | \
+ * 	xargs -n 1 sh -c 'echo {.name = \"$1\", .id = BPF_FUNC_$1}, >> '"$MAIN" sh
+ * cat << EOF >> $MAIN
+ * {NULL, -1},
+ * };
+ * int capable(int x) {return 1;}
+ * int trace_printk_init_buffers(void) {return 0;}
+ * static int x;
+ * void *metadata_dst_alloc_percpu(int a, int b) {return &x;}
+ * int ___ratelimit(void *a, const void *func) {return 0;}
+ * extern const struct bpf_verifier_ops *
+ * find_prog_type_export(enum bpf_prog_type type);
+ * extern int printf(const char *fmt, ...);
+ * int main(int argc, char *argv[])
+ * {
+ * 	struct bpf_func *f = &bpf_funcs[0];
+ * 	printf("#ifndef BPF_FUNCS_DEFINED\n");
+ * 	printf("#define BPF_FUNCS_DEFINED\n");
+ * 	while (f->id != -1) {
+ * 		enum bpf_prog_type t;
+ * 		const enum bpf_arg_type *argt;
+ * 		const struct bpf_verifier_ops *ops = NULL;
+ * 		const struct bpf_func_proto *proto = NULL;
+ * 		if (f->id == 0)
+ * 			goto skip;
+ * 		for (t = BPF_PROG_TYPE_UNSPEC + 1; ; t++) {
+ * 			ops = find_prog_type_export(t);
+ * 			if (!ops)
+ * 				break;
+ * 			proto = ops->get_func_proto(f->id);
+ * 			if (proto)
+ * 				break;
+ * 		}
+ * 		if (!proto) {
+ * 			printf("static void (*%s)(void) = (void *)-1;\n", f->name);
+ * 			continue;
+ * 		}
+ * 		printf("static ");
+ * 		switch (proto->ret_type) {
+ * 		case RET_INTEGER:
+ * 			printf("long ");
+ * 			break;
+ * 		case RET_PTR_TO_MAP_VALUE_OR_NULL:
+ * 			printf("void *");
+ * 			break;
+ * 		default:
+ * 		case RET_VOID:
+ * 			printf("void ");
+ * 			break;
+ * 		}
+ * 		printf("(*bpf_%s)(", f->name);
+ * 		for (argt = &proto->arg1_type; argt <= &proto->arg5_type; argt++) {
+ * 			if (*argt == ARG_DONTCARE) {
+ * 				if (argt == &proto->arg1_type)
+ * 					printf("void");
+ * 				else if (strcmp(f->name, "trace_printk") == 0) {
+ * 					printf(", ...");
+ * 					goto finish;
+ * 				}
+ * 				goto finish;
+ * 			}
+ * 			if (argt != &proto->arg1_type)
+ * 				printf(", ");
+ * 			switch (*argt) {
+ * 			case ARG_CONST_MAP_PTR:
+ * 			case ARG_PTR_TO_MAP_KEY:
+ * 			case ARG_PTR_TO_MAP_VALUE:
+ * 			case ARG_PTR_TO_STACK:
+ * 			case ARG_PTR_TO_RAW_STACK:
+ * 			case ARG_PTR_TO_CTX:
+ * 				printf("void *");
+ * 				break;
+ * 			default:
+ * 				printf("unsigned long");
+ * 				break;
+ * 			}
+ * 		}
+ * finish:
+ * 		printf(") = (void *)%d;\n", f->id);
+ * skip:
+ * 		f++;
+ * 	}
+ * 	printf("#endif\n");
+ * 	return 0;
+ * }
+ * EOF
+ * make -j8 KBUILD_CFLAGS_KERNEL=-mpreferred-stack-boundary=4 kernel/bpf/main.o $FIXOBJLIST
+ * rm -f ./syms.c
+ * export LANG=POSIX
+ * export LC_ALL=POSIX
+ * dolink 2>&1 | \
+ *        grep 'undefined reference' | \
+ *        awk -F "\`" '{print $2}' | \
+ *        sed "s/'$//g" | sort | uniq | \
+ *        xargs -n 1 sh -c 'echo "int $1 __attribute__((weak));" >> ./syms.c' sh
+ * dolink
+ * cd $KERNEL_DIR
+ * unset X
+ * cat << EOF > $OUTPUT
+ * /$X*
+ *  * This file is generated by following script:
+ *  *
+ * EOF
+ * cat $0 | awk '$0 != "" {print " * " $0}' >> $OUTPUT
+ * echo ' *''/' >> $OUTPUT
+ * echo '#include "clang-bpf-includes.h"' >> $OUTPUT
+ * echo 'const char clang_builtin_bpf_funcs_str[] =' >> $OUTPUT
+ * echo '"#ifdef BUILTIN_CLANG_DEFAULT_INCLUDE\n"' >> $OUTPUT
+ * $TEMP_KBUILD/gen | awk '{print "\""$0"\\n\""}' >> $OUTPUT
+ * echo '"#endif\n"' >> $OUTPUT
+ * echo ';' >> $OUTPUT
+ * echo "Finish generating " $OUTPUT
+ */
+#include "clang-bpf-includes.h"
+const char clang_builtin_bpf_funcs_str[] =
+"#ifdef BUILTIN_CLANG_DEFAULT_INCLUDE\n"
+"#ifndef BPF_FUNCS_DEFINED\n"
+"#define BPF_FUNCS_DEFINED\n"
+"static void *(*bpf_map_lookup_elem)(void *, void *) = (void *)1;\n"
+"static long (*bpf_map_update_elem)(void *, void *, void *, unsigned long) = (void *)2;\n"
+"static long (*bpf_map_delete_elem)(void *, void *) = (void *)3;\n"
+"static long (*bpf_probe_read)(void *, unsigned long, unsigned long) = (void *)4;\n"
+"static long (*bpf_ktime_get_ns)(void) = (void *)5;\n"
+"static long (*bpf_trace_printk)(void *, unsigned long, ...) = (void *)6;\n"
+"static long (*bpf_get_prandom_u32)(void) = (void *)7;\n"
+"static long (*bpf_get_smp_processor_id)(void) = (void *)8;\n"
+"static long (*bpf_skb_store_bytes)(void *, unsigned long, void *, unsigned long, unsigned long) = (void *)9;\n"
+"static long (*bpf_l3_csum_replace)(void *, unsigned long, unsigned long, unsigned long, unsigned long) = (void *)10;\n"
+"static long (*bpf_l4_csum_replace)(void *, unsigned long, unsigned long, unsigned long, unsigned long) = (void *)11;\n"
+"static void (*bpf_tail_call)(void *, void *, unsigned long) = (void *)12;\n"
+"static long (*bpf_clone_redirect)(void *, unsigned long, unsigned long) = (void *)13;\n"
+"static long (*bpf_get_current_pid_tgid)(void) = (void *)14;\n"
+"static long (*bpf_get_current_uid_gid)(void) = (void *)15;\n"
+"static long (*bpf_get_current_comm)(void *, unsigned long) = (void *)16;\n"
+"static long (*bpf_get_cgroup_classid)(void *) = (void *)17;\n"
+"static long (*bpf_skb_vlan_push)(void *, unsigned long, unsigned long) = (void *)18;\n"
+"static long (*bpf_skb_vlan_pop)(void *) = (void *)19;\n"
+"static long (*bpf_skb_get_tunnel_key)(void *, void *, unsigned long, unsigned long) = (void *)20;\n"
+"static long (*bpf_skb_set_tunnel_key)(void *, void *, unsigned long, unsigned long) = (void *)21;\n"
+"static long (*bpf_perf_event_read)(void *, unsigned long) = (void *)22;\n"
+"static long (*bpf_redirect)(unsigned long, unsigned long) = (void *)23;\n"
+"static long (*bpf_get_route_realm)(void *) = (void *)24;\n"
+"static long (*bpf_perf_event_output)(void *, void *, unsigned long, void *, unsigned long) = (void *)25;\n"
+"static long (*bpf_skb_load_bytes)(void *, unsigned long, void *, unsigned long) = (void *)26;\n"
+"static long (*bpf_get_stackid)(void *, void *, unsigned long) = (void *)27;\n"
+"static long (*bpf_csum_diff)(void *, unsigned long, void *, unsigned long, unsigned long) = (void *)28;\n"
+"static long (*bpf_skb_get_tunnel_opt)(void *, void *, unsigned long) = (void *)29;\n"
+"static long (*bpf_skb_set_tunnel_opt)(void *, void *, unsigned long) = (void *)30;\n"
+"static long (*bpf_skb_change_proto)(void *, unsigned long, unsigned long) = (void *)31;\n"
+"static long (*bpf_skb_change_type)(void *, unsigned long) = (void *)32;\n"
+"static long (*bpf_skb_under_cgroup)(void *, void *, unsigned long) = (void *)33;\n"
+"static long (*bpf_get_hash_recalc)(void *) = (void *)34;\n"
+"static long (*bpf_get_current_task)(void) = (void *)35;\n"
+"static long (*bpf_probe_write_user)(unsigned long, void *, unsigned long) = (void *)36;\n"
+"static long (*bpf_current_task_under_cgroup)(void *, unsigned long) = (void *)37;\n"
+"static long (*bpf_skb_change_tail)(void *, unsigned long, unsigned long) = (void *)38;\n"
+"static long (*bpf_skb_pull_data)(void *, unsigned long) = (void *)39;\n"
+"static long (*bpf_csum_update)(void *, unsigned long) = (void *)40;\n"
+"static long (*bpf_set_hash_invalid)(void *) = (void *)41;\n"
+"#endif\n"
+"#endif\n"
+;
diff --git a/tools/perf/util/c++/clang-bpf-includes.h b/tools/perf/util/c++/clang-bpf-includes.h
new file mode 100644
index 0000000..385a5bb
--- /dev/null
+++ b/tools/perf/util/c++/clang-bpf-includes.h
@@ -0,0 +1,12 @@ 
+#ifndef CLANG_BPF_INCLUDS_H
+#define CLANG_BPF_INCLUDS_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const char clang_builtin_bpf_funcs_str[];
+
+#ifdef __cplusplus
+}
+#endif
+#endif