From aa61fd05ca79666c973d5b15e0f91ecdc7dcfa21 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 21 Jul 2015 11:13:34 +0000 Subject: perf tools: Introduce llvm config options This patch introduces [llvm] config section with 5 options. Following patches will use then to config llvm dynamica compiling. 'llvm-utils.[ch]' is introduced in this patch for holding all llvm/clang related stuffs. Example: [llvm] # Path to clang. If omit, search it from $PATH. clang-path = "/path/to/clang" # Cmdline template. Following line shows its default value. # Environment variable is used to passing options. # # *NOTE*: -D__KERNEL__ MUST appears before $CLANG_OPTIONS, # so user have a chance to use -U__KERNEL__ in $CLANG_OPTIONS # to cancel it. clang-bpf-cmd-template = "$CLANG_EXEC -D__KERNEL__ $CLANG_OPTIONS \ $KERNEL_INC_OPTIONS -Wno-unused-value \ -Wno-pointer-sign -working-directory \ $WORKING_DIR -c $CLANG_SOURCE -target \ bpf -O2 -o -" # Options passed to clang, will be passed to cmdline by # $CLANG_OPTIONS. clang-opt = "-Wno-unused-value -Wno-pointer-sign" # kbuild directory. If not set, use /lib/modules/`uname -r`/build. # If set to "" deliberately, skip kernel header auto-detector. kbuild-dir = "/path/to/kernel/build" # Options passed to 'make' when detecting kernel header options. kbuild-opts = "ARCH=x86_64" Signed-off-by: Wang Nan Acked-by: Alexei Starovoitov Cc: Brendan Gregg Cc: Daniel Borkmann Cc: David Ahern Cc: He Kuang Cc: Jiri Olsa Cc: Kaixu Xia Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1437477214-149684-1-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/llvm-utils.c | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tools/perf/util/llvm-utils.c (limited to 'tools/perf/util/llvm-utils.c') diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c new file mode 100644 index 000000000000..472e8cd69361 --- /dev/null +++ b/tools/perf/util/llvm-utils.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2015, Wang Nan + * Copyright (C) 2015, Huawei Inc. + */ + +#include +#include "util.h" +#include "debug.h" +#include "llvm-utils.h" +#include "cache.h" + +#define CLANG_BPF_CMD_DEFAULT_TEMPLATE \ + "$CLANG_EXEC -D__KERNEL__ $CLANG_OPTIONS " \ + "$KERNEL_INC_OPTIONS -Wno-unused-value " \ + "-Wno-pointer-sign -working-directory " \ + "$WORKING_DIR -c \"$CLANG_SOURCE\" -target bpf -O2 -o -" + +struct llvm_param llvm_param = { + .clang_path = "clang", + .clang_bpf_cmd_template = CLANG_BPF_CMD_DEFAULT_TEMPLATE, + .clang_opt = NULL, + .kbuild_dir = NULL, + .kbuild_opts = NULL, +}; + +int perf_llvm_config(const char *var, const char *value) +{ + if (prefixcmp(var, "llvm.")) + return 0; + var += sizeof("llvm.") - 1; + + if (!strcmp(var, "clang-path")) + llvm_param.clang_path = strdup(value); + else if (!strcmp(var, "clang-bpf-cmd-template")) + llvm_param.clang_bpf_cmd_template = strdup(value); + else if (!strcmp(var, "clang-opt")) + llvm_param.clang_opt = strdup(value); + else if (!strcmp(var, "kbuild-dir")) + llvm_param.kbuild_dir = strdup(value); + else if (!strcmp(var, "kbuild-opts")) + llvm_param.kbuild_opts = strdup(value); + else + return -1; + return 0; +} -- cgit v1.2.3-70-g09d2 From 4cea3a9cb30a962fa759fcb081fb83351113d9c4 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Thu, 11 Jun 2015 10:31:09 +0000 Subject: perf tools: Call clang to compile C source to object code This is the core patch for supporting eBPF on-the-fly compiling, does the following work: 1. Search clang compiler using search_program(). 2. Run command template defined in llvm-bpf-cmd-template option in [llvm] config section using read_from_pipe(). Patch of clang and source code path is injected into shell command using environment variable using force_set_env(). Commiter notice: When building with DEBUG=1 we get a compiler error that gets fixed with the same approach described in commit b236512280fb: perf kmem: Fix compiler warning about may be accessing uninitialized variable The last argument to strtok_r doesn't need to be initialized, its just a placeholder to make this routine reentrant, but gcc doesn't know about that and complains, breaking the build, fix it by setting it to NULL. Signed-off-by: Wang Nan Acked-by: Alexei Starovoitov Cc: Brendan Gregg Cc: Daniel Borkmann Cc: David Ahern Cc: He Kuang Cc: Jiri Olsa Cc: Kaixu Xia Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/n/1436445342-1402-14-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/llvm-utils.c | 226 +++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/llvm-utils.h | 3 + 2 files changed, 229 insertions(+) (limited to 'tools/perf/util/llvm-utils.c') diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index 472e8cd69361..5ae11249baad 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c @@ -43,3 +43,229 @@ int perf_llvm_config(const char *var, const char *value) return -1; return 0; } + +static int +search_program(const char *def, const char *name, + char *output) +{ + char *env, *path, *tmp = NULL; + char buf[PATH_MAX]; + int ret; + + output[0] = '\0'; + if (def && def[0] != '\0') { + if (def[0] == '/') { + if (access(def, F_OK) == 0) { + strlcpy(output, def, PATH_MAX); + return 0; + } + } else if (def[0] != '\0') + name = def; + } + + env = getenv("PATH"); + if (!env) + return -1; + env = strdup(env); + if (!env) + return -1; + + ret = -ENOENT; + path = strtok_r(env, ":", &tmp); + while (path) { + scnprintf(buf, sizeof(buf), "%s/%s", path, name); + if (access(buf, F_OK) == 0) { + strlcpy(output, buf, PATH_MAX); + ret = 0; + break; + } + path = strtok_r(NULL, ":", &tmp); + } + + free(env); + return ret; +} + +#define READ_SIZE 4096 +static int +read_from_pipe(const char *cmd, void **p_buf, size_t *p_read_sz) +{ + int err = 0; + void *buf = NULL; + FILE *file = NULL; + size_t read_sz = 0, buf_sz = 0; + + file = popen(cmd, "r"); + if (!file) { + pr_err("ERROR: unable to popen cmd: %s\n", + strerror(errno)); + return -EINVAL; + } + + while (!feof(file) && !ferror(file)) { + /* + * Make buf_sz always have obe byte extra space so we + * can put '\0' there. + */ + if (buf_sz - read_sz < READ_SIZE + 1) { + void *new_buf; + + buf_sz = read_sz + READ_SIZE + 1; + new_buf = realloc(buf, buf_sz); + + if (!new_buf) { + pr_err("ERROR: failed to realloc memory\n"); + err = -ENOMEM; + goto errout; + } + + buf = new_buf; + } + read_sz += fread(buf + read_sz, 1, READ_SIZE, file); + } + + if (buf_sz - read_sz < 1) { + pr_err("ERROR: internal error\n"); + err = -EINVAL; + goto errout; + } + + if (ferror(file)) { + pr_err("ERROR: error occurred when reading from pipe: %s\n", + strerror(errno)); + err = -EIO; + goto errout; + } + + err = WEXITSTATUS(pclose(file)); + file = NULL; + if (err) { + err = -EINVAL; + goto errout; + } + + /* + * If buf is string, give it terminal '\0' to make our life + * easier. If buf is not string, that '\0' is out of space + * indicated by read_sz so caller won't even notice it. + */ + ((char *)buf)[read_sz] = '\0'; + + if (!p_buf) + free(buf); + else + *p_buf = buf; + + if (p_read_sz) + *p_read_sz = read_sz; + return 0; + +errout: + if (file) + pclose(file); + free(buf); + if (p_buf) + *p_buf = NULL; + if (p_read_sz) + *p_read_sz = 0; + return err; +} + +static inline void +force_set_env(const char *var, const char *value) +{ + if (value) { + setenv(var, value, 1); + pr_debug("set env: %s=%s\n", var, value); + } else { + unsetenv(var); + pr_debug("unset env: %s\n", var); + } +} + +static void +version_notice(void) +{ + pr_err( +" \tLLVM 3.7 or newer is required. Which can be found from http://llvm.org\n" +" \tYou may want to try git trunk:\n" +" \t\tgit clone http://llvm.org/git/llvm.git\n" +" \t\t and\n" +" \t\tgit clone http://llvm.org/git/clang.git\n\n" +" \tOr fetch the latest clang/llvm 3.7 from pre-built llvm packages for\n" +" \tdebian/ubuntu:\n" +" \t\thttp://llvm.org/apt\n\n" +" \tIf you are using old version of clang, change 'clang-bpf-cmd-template'\n" +" \toption in [llvm] section of ~/.perfconfig to:\n\n" +" \t \"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS \\\n" +" \t -working-directory $WORKING_DIR -c $CLANG_SOURCE \\\n" +" \t -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -\"\n" +" \t(Replace /path/to/llc with path to your llc)\n\n" +); +} + +int llvm__compile_bpf(const char *path, void **p_obj_buf, + size_t *p_obj_buf_sz) +{ + int err; + char clang_path[PATH_MAX]; + const char *clang_opt = llvm_param.clang_opt; + const char *template = llvm_param.clang_bpf_cmd_template; + void *obj_buf = NULL; + size_t obj_buf_sz; + + if (!template) + template = CLANG_BPF_CMD_DEFAULT_TEMPLATE; + + err = search_program(llvm_param.clang_path, + "clang", clang_path); + if (err) { + pr_err( +"ERROR:\tunable to find clang.\n" +"Hint:\tTry to install latest clang/llvm to support BPF. Check your $PATH\n" +" \tand 'clang-path' option in [llvm] section of ~/.perfconfig.\n"); + version_notice(); + return -ENOENT; + } + + force_set_env("CLANG_EXEC", clang_path); + force_set_env("CLANG_OPTIONS", clang_opt); + force_set_env("KERNEL_INC_OPTIONS", NULL); + force_set_env("WORKING_DIR", "."); + + /* + * Since we may reset clang's working dir, path of source file + * should be transferred into absolute path, except we want + * stdin to be source file (testing). + */ + force_set_env("CLANG_SOURCE", + (path[0] == '-') ? path : + make_nonrelative_path(path)); + + pr_debug("llvm compiling command template: %s\n", template); + err = read_from_pipe(template, &obj_buf, &obj_buf_sz); + if (err) { + pr_err("ERROR:\tunable to compile %s\n", path); + pr_err("Hint:\tCheck error message shown above.\n"); + pr_err("Hint:\tYou can also pre-compile it into .o using:\n"); + pr_err(" \t\tclang -target bpf -O2 -c %s\n", path); + pr_err(" \twith proper -I and -D options.\n"); + goto errout; + } + + if (!p_obj_buf) + free(obj_buf); + else + *p_obj_buf = obj_buf; + + if (p_obj_buf_sz) + *p_obj_buf_sz = obj_buf_sz; + return 0; +errout: + free(obj_buf); + if (p_obj_buf) + *p_obj_buf = NULL; + if (p_obj_buf_sz) + *p_obj_buf_sz = 0; + return err; +} diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h index 504b799687fb..d23adbca120b 100644 --- a/tools/perf/util/llvm-utils.h +++ b/tools/perf/util/llvm-utils.h @@ -33,4 +33,7 @@ struct llvm_param { extern struct llvm_param llvm_param; extern int perf_llvm_config(const char *var, const char *value); + +extern int llvm__compile_bpf(const char *path, void **p_obj_buf, + size_t *p_obj_buf_sz); #endif -- cgit v1.2.3-70-g09d2 From d325d7887b960627dc686d70d07682e18383f9c4 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Tue, 14 Jul 2015 06:40:02 +0000 Subject: perf tools: Auto detecting kernel build directory This patch detects kernel build directory by checking the existence of include/generated/autoconf.h. clang working directory is changed to kbuild directory if it is found, to help user use relative include path. Following patch will detect kernel include directory, which contains relative include patch so this workdir changing is needed. Users are allowed to set 'kbuild-dir = ""' manually to disable this checking. Signed-off-by: Wang Nan Acked-by: Alexei Starovoitov Cc: Brendan Gregg Cc: Daniel Borkmann Cc: David Ahern Cc: He Kuang Cc: Jiri Olsa Cc: Kaixu Xia Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/n/tip-owyfwfbemrjn0tlj6tgk2nf5@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/llvm-utils.c | 80 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) (limited to 'tools/perf/util/llvm-utils.c') diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index 5ae11249baad..6bfcb2dd3161 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c @@ -4,6 +4,7 @@ */ #include +#include #include "util.h" #include "debug.h" #include "llvm-utils.h" @@ -204,6 +205,74 @@ version_notice(void) ); } +static int detect_kbuild_dir(char **kbuild_dir) +{ + const char *test_dir = llvm_param.kbuild_dir; + const char *prefix_dir = ""; + const char *suffix_dir = ""; + + char *autoconf_path; + struct utsname utsname; + + int err; + + if (!test_dir) { + err = uname(&utsname); + if (err) { + pr_warning("uname failed: %s\n", strerror(errno)); + return -EINVAL; + } + + test_dir = utsname.release; + prefix_dir = "/lib/modules/"; + suffix_dir = "/build"; + } + + err = asprintf(&autoconf_path, "%s%s%s/include/generated/autoconf.h", + prefix_dir, test_dir, suffix_dir); + if (err < 0) + return -ENOMEM; + + if (access(autoconf_path, R_OK) == 0) { + free(autoconf_path); + + err = asprintf(kbuild_dir, "%s%s%s", prefix_dir, test_dir, + suffix_dir); + if (err < 0) + return -ENOMEM; + return 0; + } + free(autoconf_path); + return -ENOENT; +} + +static inline void +get_kbuild_opts(char **kbuild_dir) +{ + int err; + + if (!kbuild_dir) + return; + + *kbuild_dir = NULL; + + if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) { + pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n"); + pr_debug("Skip kbuild options detection.\n"); + return; + } + + err = detect_kbuild_dir(kbuild_dir); + if (err) { + pr_warning( +"WARNING:\tunable to get correct kernel building directory.\n" +"Hint:\tSet correct kbuild directory using 'kbuild-dir' option in [llvm]\n" +" \tsection of ~/.perfconfig or set it to \"\" to suppress kbuild\n" +" \tdetection.\n\n"); + return; + } +} + int llvm__compile_bpf(const char *path, void **p_obj_buf, size_t *p_obj_buf_sz) { @@ -211,6 +280,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, char clang_path[PATH_MAX]; const char *clang_opt = llvm_param.clang_opt; const char *template = llvm_param.clang_bpf_cmd_template; + char *kbuild_dir = NULL; void *obj_buf = NULL; size_t obj_buf_sz; @@ -228,10 +298,16 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, return -ENOENT; } + /* + * This is an optional work. Even it fail we can continue our + * work. Needn't to check error return. + */ + get_kbuild_opts(&kbuild_dir); + force_set_env("CLANG_EXEC", clang_path); force_set_env("CLANG_OPTIONS", clang_opt); force_set_env("KERNEL_INC_OPTIONS", NULL); - force_set_env("WORKING_DIR", "."); + force_set_env("WORKING_DIR", kbuild_dir ? : "."); /* * Since we may reset clang's working dir, path of source file @@ -253,6 +329,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, goto errout; } + free(kbuild_dir); if (!p_obj_buf) free(obj_buf); else @@ -262,6 +339,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, *p_obj_buf_sz = obj_buf_sz; return 0; errout: + free(kbuild_dir); free(obj_buf); if (p_obj_buf) *p_obj_buf = NULL; -- cgit v1.2.3-70-g09d2 From 0c6d18bfd551622b438e216e4863155f47907b0d Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Thu, 11 Jun 2015 11:25:49 +0000 Subject: perf tools: Auto detecting kernel include options To help user find correct kernel include options, this patch extracts them from kbuild system by an embedded script kinc_fetch_script, which creates a temporary directory, generates Makefile and an empty dummy.o then use the Makefile to fetch $(NOSTDINC_FLAGS), $(LINUXINCLUDE) and $(EXTRA_CFLAGS) options. The result is passed to compiler script using 'KERNEL_INC_OPTIONS' environment variable. Because options from kbuild contains relative path like 'Iinclude/generated/uapi', the work directory must be changed. This is done by previous patch. Signed-off-by: Wang Nan Acked-by: Alexei Starovoitov Cc: Brendan Gregg Cc: Daniel Borkmann Cc: David Ahern Cc: He Kuang Cc: Jiri Olsa Cc: Kaixu Xia Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/r/1436445342-1402-16-git-send-email-wangnan0@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/llvm-utils.c | 60 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 5 deletions(-) (limited to 'tools/perf/util/llvm-utils.c') diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index 6bfcb2dd3161..5887bb8c1243 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c @@ -246,15 +246,42 @@ static int detect_kbuild_dir(char **kbuild_dir) return -ENOENT; } +static const char *kinc_fetch_script = +"#!/usr/bin/env sh\n" +"if ! test -d \"$KBUILD_DIR\"\n" +"then\n" +" exit -1\n" +"fi\n" +"if ! test -f \"$KBUILD_DIR/include/generated/autoconf.h\"\n" +"then\n" +" exit -1\n" +"fi\n" +"TMPDIR=`mktemp -d`\n" +"if test -z \"$TMPDIR\"\n" +"then\n" +" exit -1\n" +"fi\n" +"cat << EOF > $TMPDIR/Makefile\n" +"obj-y := dummy.o\n" +"\\$(obj)/%.o: \\$(src)/%.c\n" +"\t@echo -n \"\\$(NOSTDINC_FLAGS) \\$(LINUXINCLUDE) \\$(EXTRA_CFLAGS)\"\n" +"EOF\n" +"touch $TMPDIR/dummy.c\n" +"make -s -C $KBUILD_DIR M=$TMPDIR $KBUILD_OPTS dummy.o 2>/dev/null\n" +"RET=$?\n" +"rm -rf $TMPDIR\n" +"exit $RET\n"; + static inline void -get_kbuild_opts(char **kbuild_dir) +get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) { int err; - if (!kbuild_dir) + if (!kbuild_dir || !kbuild_include_opts) return; *kbuild_dir = NULL; + *kbuild_include_opts = NULL; if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) { pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n"); @@ -271,6 +298,27 @@ get_kbuild_opts(char **kbuild_dir) " \tdetection.\n\n"); return; } + + pr_debug("Kernel build dir is set to %s\n", *kbuild_dir); + force_set_env("KBUILD_DIR", *kbuild_dir); + force_set_env("KBUILD_OPTS", llvm_param.kbuild_opts); + err = read_from_pipe(kinc_fetch_script, + (void **)kbuild_include_opts, + NULL); + if (err) { + pr_warning( +"WARNING:\tunable to get kernel include directories from '%s'\n" +"Hint:\tTry set clang include options using 'clang-bpf-cmd-template'\n" +" \toption in [llvm] section of ~/.perfconfig and set 'kbuild-dir'\n" +" \toption in [llvm] to \"\" to suppress this detection.\n\n", + *kbuild_dir); + + free(*kbuild_dir); + *kbuild_dir = NULL; + return; + } + + pr_debug("include option is set to %s\n", *kbuild_include_opts); } int llvm__compile_bpf(const char *path, void **p_obj_buf, @@ -280,7 +328,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, char clang_path[PATH_MAX]; const char *clang_opt = llvm_param.clang_opt; const char *template = llvm_param.clang_bpf_cmd_template; - char *kbuild_dir = NULL; + char *kbuild_dir = NULL, *kbuild_include_opts = NULL; void *obj_buf = NULL; size_t obj_buf_sz; @@ -302,11 +350,11 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, * This is an optional work. Even it fail we can continue our * work. Needn't to check error return. */ - get_kbuild_opts(&kbuild_dir); + get_kbuild_opts(&kbuild_dir, &kbuild_include_opts); force_set_env("CLANG_EXEC", clang_path); force_set_env("CLANG_OPTIONS", clang_opt); - force_set_env("KERNEL_INC_OPTIONS", NULL); + force_set_env("KERNEL_INC_OPTIONS", kbuild_include_opts); force_set_env("WORKING_DIR", kbuild_dir ? : "."); /* @@ -330,6 +378,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, } free(kbuild_dir); + free(kbuild_include_opts); if (!p_obj_buf) free(obj_buf); else @@ -340,6 +389,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, return 0; errout: free(kbuild_dir); + free(kbuild_include_opts); free(obj_buf); if (p_obj_buf) *p_obj_buf = NULL; -- cgit v1.2.3-70-g09d2 From 9bc898c7019383b6aa2ae6cb2928c4ca926449f0 Mon Sep 17 00:00:00 2001 From: Wang Nan Date: Wed, 8 Jul 2015 10:04:02 +0000 Subject: perf tests: Add LLVM test for eBPF on-the-fly compiling Previous patches introduce llvm__compile_bpf() to compile source file to eBPF object. This patch adds testcase to test it. It also tests libbpf by opening generated object after applying next patch which introduces HAVE_LIBBPF_SUPPORT option. Since llvm__compile_bpf() prints long messages which users who don't explicitly test llvm doesn't care, this patch set verbose to -1 to suppress all debug, warning and error message, and hint user use 'perf test -v' to see the full output. For the same reason, if clang is not found in PATH and there's no [llvm] section in .perfconfig, skip this test. Signed-off-by: Wang Nan Acked-by: Alexei Starovoitov Cc: Brendan Gregg Cc: Daniel Borkmann Cc: David Ahern Cc: He Kuang Cc: Jiri Olsa Cc: Kaixu Xia Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Zefan Li Cc: pi3orama@163.com Link: http://lkml.kernel.org/n/1436445342-1402-17-git-send-email-wangnan0@huawei.com [ Add tools/lib/bpf/ to tools/perf/MANIFEST, so that the tarball targets build ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/MANIFEST | 1 + tools/perf/tests/Build | 1 + tools/perf/tests/builtin-test.c | 4 ++ tools/perf/tests/llvm.c | 98 +++++++++++++++++++++++++++++++++++++++++ tools/perf/tests/tests.h | 1 + tools/perf/util/llvm-utils.c | 9 ++++ tools/perf/util/llvm-utils.h | 10 +++++ 7 files changed, 124 insertions(+) create mode 100644 tools/perf/tests/llvm.c (limited to 'tools/perf/util/llvm-utils.c') diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index d01a0aad5a01..f31f15a5f873 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -18,6 +18,7 @@ tools/arch/x86/include/asm/atomic.h tools/arch/x86/include/asm/rmwcc.h tools/lib/traceevent tools/lib/api +tools/lib/bpf tools/lib/hweight.c tools/lib/rbtree.c tools/lib/symbol/kallsyms.c diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index d20d6e6ab65b..c1518bdd0f1b 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -32,6 +32,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 perf-$(CONFIG_X86) += perf-time-to-tsc.o diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index c1dde733c3a6..136cd934be66 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -174,6 +174,10 @@ static struct test { .desc = "Test thread map", .func = test__thread_map, }, + { + .desc = "Test LLVM searching and compiling", + .func = test__llvm, + }, { .func = NULL, }, diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c new file mode 100644 index 000000000000..a337356fd979 --- /dev/null +++ b/tools/perf/tests/llvm.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include "tests.h" +#include "debug.h" + +static int perf_config_cb(const char *var, const char *val, + void *arg __maybe_unused) +{ + return perf_default_config(var, val, arg); +} + +/* + * Randomly give it a "version" section since we don't really load it + * into kernel + */ +static const char test_bpf_prog[] = + "__attribute__((section(\"do_fork\"), used)) " + "int fork(void *ctx) {return 0;} " + "char _license[] __attribute__((section(\"license\"), used)) = \"GPL\";" + "int _version __attribute__((section(\"version\"), used)) = 0x40100;"; + +#ifdef HAVE_LIBBPF_SUPPORT +static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz) +{ + struct bpf_object *obj; + + obj = bpf_object__open_buffer(obj_buf, obj_buf_sz); + if (!obj) + return -1; + bpf_object__close(obj); + return 0; +} +#else +static int test__bpf_parsing(void *obj_buf __maybe_unused, + size_t obj_buf_sz __maybe_unused) +{ + fprintf(stderr, " (skip bpf parsing)"); + return 0; +} +#endif + +int test__llvm(void) +{ + char *tmpl_new, *clang_opt_new; + void *obj_buf; + size_t obj_buf_sz; + int err, old_verbose; + + perf_config(perf_config_cb, NULL); + + /* + * Skip this test if user's .perfconfig doesn't set [llvm] section + * and clang is not found in $PATH, and this is not perf test -v + */ + if (verbose == 0 && !llvm_param.user_set_param && llvm__search_clang()) { + fprintf(stderr, " (no clang, try 'perf test -v LLVM')"); + return TEST_SKIP; + } + + old_verbose = verbose; + /* + * llvm is verbosity when error. Suppress all error output if + * not 'perf test -v'. + */ + if (verbose == 0) + verbose = -1; + + if (!llvm_param.clang_bpf_cmd_template) + return -1; + + if (!llvm_param.clang_opt) + llvm_param.clang_opt = strdup(""); + + err = asprintf(&tmpl_new, "echo '%s' | %s", test_bpf_prog, + llvm_param.clang_bpf_cmd_template); + if (err < 0) + return -1; + err = asprintf(&clang_opt_new, "-xc %s", llvm_param.clang_opt); + if (err < 0) + return -1; + + llvm_param.clang_bpf_cmd_template = tmpl_new; + llvm_param.clang_opt = clang_opt_new; + err = llvm__compile_bpf("-", &obj_buf, &obj_buf_sz); + + verbose = old_verbose; + if (err) { + if (!verbose) + fprintf(stderr, " (use -v to see error message)"); + return -1; + } + + err = test__bpf_parsing(obj_buf, obj_buf_sz); + free(obj_buf); + return err; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index ebb47d96bc0b..bf113a247987 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -62,6 +62,7 @@ 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); #if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__) #ifdef HAVE_DWARF_UNWIND_SUPPORT diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index 5887bb8c1243..4f6a4780bd5f 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c @@ -22,6 +22,7 @@ struct llvm_param llvm_param = { .clang_opt = NULL, .kbuild_dir = NULL, .kbuild_opts = NULL, + .user_set_param = false, }; int perf_llvm_config(const char *var, const char *value) @@ -42,6 +43,7 @@ int perf_llvm_config(const char *var, const char *value) llvm_param.kbuild_opts = strdup(value); else return -1; + llvm_param.user_set_param = true; return 0; } @@ -397,3 +399,10 @@ errout: *p_obj_buf_sz = 0; return err; } + +int llvm__search_clang(void) +{ + char clang_path[PATH_MAX]; + + return search_program(llvm_param.clang_path, "clang", clang_path); +} diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h index d23adbca120b..5b3cf1c229e2 100644 --- a/tools/perf/util/llvm-utils.h +++ b/tools/perf/util/llvm-utils.h @@ -29,6 +29,13 @@ struct llvm_param { * compiling. Should not be used for dynamic compiling. */ const char *kbuild_opts; + /* + * Default is false. If one of the above fields is set by user + * explicitly then user_set_llvm is set to true. This is used + * for perf test. If user doesn't set anything in .perfconfig + * and clang is not found, don't trigger llvm test. + */ + bool user_set_param; }; extern struct llvm_param llvm_param; @@ -36,4 +43,7 @@ extern int perf_llvm_config(const char *var, const char *value); extern int llvm__compile_bpf(const char *path, void **p_obj_buf, size_t *p_obj_buf_sz); + +/* This function is for test__llvm() use only */ +extern int llvm__search_clang(void); #endif -- cgit v1.2.3-70-g09d2