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

From: Wang Nan
Date: Sat Nov 26 2016 - 02:10:03 EST


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@xxxxxxxxxx>
Cc: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
Cc: Alexei Starovoitov <ast@xxxxxx>
Cc: He Kuang <hekuang@xxxxxxxxxx>
Cc: Jiri Olsa <jolsa@xxxxxxxxxx>
Cc: Zefan Li <lizefan@xxxxxxxxxx>
Cc: pi3orama@xxxxxxx
---
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
+ * {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
--
2.10.1