Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933041AbbFIF4f (ORCPT ); Tue, 9 Jun 2015 01:56:35 -0400 Received: from szxga02-in.huawei.com ([119.145.14.65]:49381 "EHLO szxga02-in.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751228AbbFIFv0 (ORCPT ); Tue, 9 Jun 2015 01:51:26 -0400 From: Wang Nan To: , , , , , , , , , , CC: , , , , Subject: [RFC PATCH v6 24/32] perf record: Compile scriptlets if pass '.c' to --event Date: Tue, 9 Jun 2015 05:50:28 +0000 Message-ID: <1433829036-23687-25-git-send-email-wangnan0@huawei.com> X-Mailer: git-send-email 1.8.3.4 In-Reply-To: <1433829036-23687-1-git-send-email-wangnan0@huawei.com> References: <1433829036-23687-1-git-send-email-wangnan0@huawei.com> MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [10.107.197.200] X-CFilter-Loop: Reflected Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 9943 Lines: 353 This patch enables passing source files to --event directly using: # perf record --event bpf-file.c command This patch does following works: 1) 'struct bpf_param' is introduced to store compiler related options; 2) bpf__compile() is introduced for compiling source code by calling clang and llc. 3) Allow passing '.c' file to '--event'. parse_events_load_bpf() is expanded to allow caller tell it whether the passed file is source file or object. Signed-off-by: Wang Nan --- tools/perf/util/bpf-loader.c | 177 ++++++++++++++++++++++++++++++++++++++++- tools/perf/util/bpf-loader.h | 10 ++- tools/perf/util/parse-events.c | 4 +- tools/perf/util/parse-events.h | 2 +- tools/perf/util/parse-events.l | 3 + tools/perf/util/parse-events.y | 15 +++- 6 files changed, 203 insertions(+), 8 deletions(-) diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index e950baa..e5c03e9 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -28,7 +28,168 @@ DEFINE_PRINT_FN(debug, 1) static bool libbpf_initialized = false; -int bpf__prepare_load(const char *filename) +/* + * Group all bpf related programs together. Futher we can config them + * using perf_default_config(). + */ +struct bpf_param bpf_param = { + .clang_path = "clang", + .llc_path = "llc", + .clang_opt = "", + .llc_opt = "", +}; + +static int search_program(const char *def, const char *name, + char *output) +{ + char *env, *path, *tmp; + char buf[PATH_MAX]; + int ret; + + if (def && def[0] == '/') + if (access(def, F_OK) == 0) { + strncpy(output, def, PATH_MAX); + return 0; + } + + env = getenv("PATH"); + if (!env) + return -1; + + env = strdup(env); + if (!env) + return -1; + + ret = -1; + path = strtok_r(env, ":", &tmp); + while (path) { + scnprintf(buf, sizeof(buf), "%s/%s", path, name); + if (access(buf, F_OK) == 0) { + strncpy(output, buf, PATH_MAX); + ret = 0; + goto out; + } + path = strtok_r(NULL, ":", &tmp); + } + +out: + free(env); + return ret; +} + +#define READ_SIZE 4096 +/* + * A tricky way to ensure pclose() always get failure exit status. + * If simple passing 'clang | llc' to popen(), and clang fails, its + * exit status is !0 but stdout is empty, which is valid for llc, so + * pclose() gets 0 as final exit status. + * + * In CMD_FMT, use 'if !clang ; then echo ERROR ; fi | llc' to ensure + * llc get bad input and return !0 exit status. + */ +#define CMD_FMT "if ! %s %s -Wno-unused-value -Wno-pointer-sign " \ + "-emit-llvm -c \"%s\" -O2 -o - ; then echo ERROR ; fi " \ + " | %s %s -march=bpf -filetype=obj -o %s" + +static int bpf__compile(const char *filename, void **p_obj_buf, + size_t *p_obj_buf_sz) +{ + char clang_path[PATH_MAX], llc_path[PATH_MAX], *cmd = NULL; + int err, cmd_sz; + FILE *file; + void *buf = NULL; + size_t buf_sz = 0, read_sz = 0; + + *p_obj_buf = NULL; + *p_obj_buf_sz = 0; + + err = search_program(bpf_param.clang_path, + "clang", clang_path); + if (!err) + err = search_program(bpf_param.llc_path, + "llc", llc_path); + if (err) { + err = -ENOENT; + pr_err("Error:\tunable to find clang or llc in $PATH\n"); + pr_err("Hint:\tTry install LLVM with BPF backend.\n"); + goto out; + } + + cmd_sz = snprintf(NULL, 0, CMD_FMT, + clang_path, bpf_param.clang_opt, filename, + llc_path, bpf_param.llc_opt, "-"); + + if (cmd_sz <= 0) { + err = -EINVAL; + pr_err("Internal error: sprintf returns %d\n", cmd_sz); + goto out; + } + + cmd = malloc(cmd_sz + 1); + if (!cmd) { + err = -ENOMEM; + pr_err("Unable to alloc compiling command\n"); + goto out; + } + + err = snprintf(cmd, cmd_sz + 1, CMD_FMT, + clang_path, bpf_param.clang_opt, filename, + llc_path, bpf_param.llc_opt, "-"); + if (err != cmd_sz) { + err = -EINVAL; + pr_err("Internal error: snprintf returns %d\n", err); + goto out; + } + cmd[cmd_sz] = '\0'; + + file = popen(cmd, "r"); + if (!file) { + err = -EINVAL; + pr_err("Error:\tfailed to run command:\n%s\nReason:\t%s\n", + cmd, strerror(errno)); + goto out; + } + + while (!feof(file) && !ferror(file)) { + void *new_buf; + + if (buf_sz - read_sz < READ_SIZE) { + buf_sz += READ_SIZE; + new_buf = realloc(buf, buf_sz); + if (!new_buf) { + err = -ENOMEM; + pr_err("readlloc() failed\n"); + goto out; + } + buf = new_buf; + } + + read_sz += fread(buf + read_sz, 1, READ_SIZE, file); + } + + if (ferror(file)) + read_sz = -EINVAL; + + err = WEXITSTATUS(pclose(file)); + if (err || read_sz <= 0) { + err = -EINVAL; + pr_err("Error:\tsomething went wrong when compiling %s\n", + filename); + pr_err("Hint:\tTry manually run following command and check:\n"); + pr_err(" # " CMD_FMT "\n\n", + clang_path, bpf_param.clang_opt, filename, + llc_path, bpf_param.llc_opt, "/dev/null"); + goto out; + } + + *p_obj_buf_sz = read_sz; + *p_obj_buf = buf; +out: + free(cmd); + return err; +} + +int bpf__prepare_load(const char *filename, bool source) { struct bpf_object *obj; @@ -37,7 +198,19 @@ int bpf__prepare_load(const char *filename) libbpf_info, libbpf_debug); - obj = bpf_object__open(filename); + if (source) { + void *obj_buf; + size_t obj_buf_sz; + int err; + + err = bpf__compile(filename, &obj_buf, &obj_buf_sz); + if (err) + return err; + obj = bpf_object__open_buffer(obj_buf, obj_buf_sz); + free(obj_buf); + } else + obj = bpf_object__open(filename); + if (!obj) { pr_err("bpf: failed to load %s\n", filename); return -EINVAL; diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h index 39d8d1a..5fd015c 100644 --- a/tools/perf/util/bpf-loader.h +++ b/tools/perf/util/bpf-loader.h @@ -8,8 +8,16 @@ #include #include "debug.h" +struct bpf_param { + const char *clang_path; + const char *llc_path; + const char *clang_opt; + const char *llc_opt; +}; +extern struct bpf_param bpf_param; + #ifdef HAVE_LIBBPF_SUPPORT -int bpf__prepare_load(const char *filename); +int bpf__prepare_load(const char *filename, bool source); void bpf__clear(void); #else diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 6f7a7b1..aaee24c 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -475,7 +475,7 @@ int parse_events_add_tracepoint(struct list_head *list, int *idx, int parse_events_load_bpf(struct list_head *list __maybe_unused, int *idx __maybe_unused, - char *bpf_file_name) + char *bpf_file_name, bool source) { /* * Currently don't link any event to list. BPF object files @@ -485,7 +485,7 @@ int parse_events_load_bpf(struct list_head *list __maybe_unused, * problem. After that probe events file by file is possible. * However, probing cost is still need to be considered. */ - return bpf__prepare_load(bpf_file_name); + return bpf__prepare_load(bpf_file_name, source); } static int diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 41b962a..5841d4f 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -115,7 +115,7 @@ int parse_events_name(struct list_head *list, char *name); int parse_events_add_tracepoint(struct list_head *list, int *idx, char *sys, char *event); int parse_events_load_bpf(struct list_head *list, int *idx, - char *bpf_file_name); + char *bpf_file_name, bool source); int parse_events_add_numeric(struct parse_events_evlist *data, struct list_head *list, u32 type, u64 config, diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 4acea38..eb821d4 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -116,6 +116,7 @@ group [^,{}/]*[{][^}]*[}][^,{}/]* event_pmu [^,{}/]+[/][^/]*[/][^,{}/]* event [^,{}/]+ bpf_object .*\.(o|bpf) +bpf_source .*\.c num_dec [0-9]+ num_hex 0x[a-fA-F0-9]+ @@ -161,6 +162,7 @@ modifier_bp [rwx]{1,3} {event_pmu} | {bpf_object} | +{bpf_source} | {event} { BEGIN(INITIAL); REWIND(1); @@ -265,6 +267,7 @@ r{num_raw_hex} { return raw(yyscanner); } {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); } {name} { return pmu_str_check(yyscanner); } {bpf_object} { return str(yyscanner, PE_BPF_OBJECT); } +{bpf_source} { return str(yyscanner, PE_BPF_SOURCE); } "/" { BEGIN(config); return '/'; } - { return '-'; } , { BEGIN(event); return ','; } diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index 481f3cd..eeb9768 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -42,7 +42,7 @@ static inc_group_count(struct list_head *list, %token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM %token PE_EVENT_NAME %token PE_NAME -%token PE_BPF_OBJECT +%token PE_BPF_OBJECT PE_BPF_SOURCE %token PE_MODIFIER_EVENT PE_MODIFIER_BP %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT %token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP @@ -55,6 +55,7 @@ static inc_group_count(struct list_head *list, %type PE_TERM %type PE_NAME %type PE_BPF_OBJECT +%type PE_BPF_SOURCE %type PE_NAME_CACHE_TYPE %type PE_NAME_CACHE_OP_RESULT %type PE_MODIFIER_EVENT @@ -431,7 +432,17 @@ PE_BPF_OBJECT struct list_head *list; ALLOC_LIST(list); - ABORT_ON(parse_events_load_bpf(list, &data->idx, $1)); + ABORT_ON(parse_events_load_bpf(list, &data->idx, $1, false)); + $$ = list; +} +| +PE_BPF_SOURCE +{ + struct parse_events_evlist *data = _data; + struct list_head *list; + + ALLOC_LIST(list); + ABORT_ON(parse_events_load_bpf(list, &data->idx, $1, true)); $$ = list; } -- 1.8.3.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/