From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752393AbbD3LAV (ORCPT ); Thu, 30 Apr 2015 07:00:21 -0400 Received: from szxga03-in.huawei.com ([119.145.14.66]:14532 "EHLO szxga03-in.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751532AbbD3Kxf (ORCPT ); Thu, 30 Apr 2015 06:53:35 -0400 From: Wang Nan To: , , , , , , CC: , , , Subject: [RFC PATCH 14/22] perf bpf: config eBPF programs based on their names. Date: Thu, 30 Apr 2015 10:52:37 +0000 Message-ID: <1430391165-30267-15-git-send-email-wangnan0@huawei.com> X-Mailer: git-send-email 1.8.3.4 In-Reply-To: <1430391165-30267-1-git-send-email-wangnan0@huawei.com> References: <1430391165-30267-1-git-send-email-wangnan0@huawei.com> MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [10.107.197.210] X-CFilter-Loop: Reflected X-Mirapoint-Virus-RAPID-Raw: score=unknown(0), refid=str=0001.0A020206.554209AD.013D,ss=1,re=0.000,recu=0.000,reip=0.000,cl=1,cld=1,fgs=0, ip=0.0.0.0, so=2013-05-26 15:14:31, dmn=2013-03-21 17:37:32 X-Mirapoint-Loop-Id: a1fbe5401387497d17a6105ffffc47c1 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This patch partially implements bpf_obj_config(), which is used for define k(ret)probe positions which will be attached eBPF programs. parse_perf_probe_command() is used to do the main parsing works. Parsing result is stored into a global array. This is because add_perf_probe_events() is non-reentrantable. In following patch, add_perf_probe_events will be introduced to insert kprobes. It accepts an array of 'struct perf_probe_event' and do works together. This patch deals programs with 'kprobe/myprobe' like name only by generating perf probe command string then calling parse_perf_probe_command(). Signed-off-by: Wang Nan --- tools/perf/util/bpf-loader.c | 201 +++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/bpf-loader.h | 2 + 2 files changed, 203 insertions(+) diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 66fbca2..b2871fc 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c @@ -22,6 +22,38 @@ static LIST_HEAD(bpf_obj_list); +#define MAX_CMDLEN 256 +#define PERF_BPF_PROBE_GROUP "perf_bpf_probe" +/* + * perf probe is no non-reentrantable, so we must group all events + * together and call add_perf_probe_events only once. + */ + +struct params { + struct perf_probe_event event_array[MAX_PROBES]; + size_t nr_events; +}; +static struct params params = { + .nr_events = 0, +}; + +static struct perf_probe_event * +alloc_perf_probe_event(void) +{ + struct perf_probe_event *pev; + int n = params.nr_events; + + if (n >= MAX_PROBES) { + pr_err("bpf: too many events, increase MAX_PROBES\n"); + return NULL; + } + + params.nr_events = n + 1; + pev = ¶ms.event_array[n]; + bzero(pev, sizeof(*pev)); + return pev; +} + static struct bpf_obj *__bpf_obj_alloc(const char *path) { struct bpf_obj *obj; @@ -279,6 +311,10 @@ bpf_perf_prog_free(struct bpf_perf_prog *prog) free(prog->name); if (prog->insns) free(prog->insns); + if (prog->pev) { + clear_perf_probe_event(prog->pev); + bzero(prog->pev, sizeof(*prog->pev)); + } free(prog); } @@ -448,6 +484,169 @@ static int bpf_obj_validate(struct bpf_obj *obj) return 0; } +static struct bpf_perf_prog * +bpf_find_prog_by_name(struct bpf_obj *obj, const char *name) +{ + struct bpf_perf_prog *prog; + + list_for_each_entry(prog, &obj->progs_list, list) + if (strcmp(name, prog->name) == 0) + return prog; + return NULL; +} + +/* + * Use config_str to config program. If prog is NULL, find a + * prog based on config_str. config_str should not be NULL. + */ +static int __bpf_perf_prog_config(struct bpf_obj *obj, + struct bpf_perf_prog *prog, + char *config_str) +{ + struct perf_probe_event *pev = alloc_perf_probe_event(); + int err = 0; + + if (!pev) + return -ENOMEM; + + if ((err = parse_perf_probe_command(config_str, pev)) < 0) { + pr_err("bpf config: %s is not a valid config string\n", + config_str); + /* parse failed, don't need clear pev. */ + return -EINVAL; + } + + if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) { + pr_err("bpf config: '%s': group for event is set " + "and not '%s'.\n", config_str, + PERF_BPF_PROBE_GROUP); + err = -EINVAL; + goto errout; + } else if (!pev->group) + pev->group = strdup(PERF_BPF_PROBE_GROUP); + + if (!pev->group) { + pr_err("bpf config: strdup failed\n"); + err = -ENOMEM; + goto errout; + } + + if (!pev->event) { + pr_err("bpf config: '%s': event name is missing\n", + config_str); + err = -EINVAL; + goto errout; + } + + if (!prog) + prog = bpf_find_prog_by_name(obj, pev->event); + if (!prog) { + pr_err("bpf config: section %s not found for" + " config '%s'\n", pev->event, config_str); + err = -ENOENT; + goto errout; + } + + if (prog->pev) { + pr_err("bpf config: duplicate config for section %s\n", + prog->name); + err = -EEXIST; + goto errout; + } + + prog->pev = pev; + pr_debug("bpf config: config %s ok\n", prog->name); + return 0; +errout: + if (pev) + clear_perf_probe_event(pev); + return err; +} + +/* + * Config specific prog using config_str. Both prog and config_str + * can be set to NULL, but not both. If prog is NULL, search prog + * based on config_str. If config_str is NULL, try to generate a + * config_str using prog->name. + */ +static int bpf_perf_prog_config(struct bpf_obj *obj, + struct bpf_perf_prog *prog, + char *config_str) +{ + char __config_str[MAX_CMDLEN]; + char *func_str; + char *name = NULL; + + if (!prog && !config_str) { + pr_err("bpf config: internal error\n"); + return -EINVAL; + } + + if (prog) + name = prog->name; + + if (name && (func_str = strchr(name, '/'))) { + const char *ret_str; + int err = 0; + + /* try to config prog based on its name */ + if (config_str) { + pr_err("bpf config: bad config %s for %s\n", + config_str, name); + return -EINVAL; + } + config_str = __config_str; + + if (memcmp(name, "kprobe/", 7) == 0) + ret_str = ""; + else if (memcmp(name, "kretprobe/", 10) == 0) + ret_str = "%return"; + else { + pr_err("bpf: bad section name: '%s'\n", name); + return -EINVAL; + } + + /* skip '/' */ + func_str += 1; + err = snprintf(config_str, MAX_CMDLEN, "%s=%s%s", + func_str, func_str, ret_str); + if (err >= MAX_CMDLEN) { + pr_err("bpf: function name %s too long\n", func_str); + return -EINVAL; + } + + err = __bpf_perf_prog_config(obj, prog, config_str); + if (err) + return err; + return 0; + } + + if (config_str) + /* prog should be NULL in this case. */ + return __bpf_perf_prog_config(obj, prog, config_str); + + /* + * prog->name is a symbol and config_str is NULL. + * Return normally. It will be config again with config_str. + */ + return 0; +} + +static int bpf_obj_config(struct bpf_obj *obj) +{ + struct bpf_perf_prog *prog; + int err; + + /* try to config progs based on their names */ + list_for_each_entry(prog, &obj->progs_list, list) { + err = bpf_perf_prog_config(obj, prog, NULL); + if (err) + return err; + } + + return 0; +} + int bpf__load(const char *path) { struct bpf_obj *obj; @@ -474,6 +673,8 @@ int bpf__load(const char *path) goto out; if ((err = bpf_obj_validate(obj))) goto out; + if ((err = bpf_obj_config(obj))) + goto out; list_add(&obj->list, &bpf_obj_list); return 0; diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h index 1417c0d..09f77a5 100644 --- a/tools/perf/util/bpf-loader.h +++ b/tools/perf/util/bpf-loader.h @@ -27,6 +27,8 @@ struct bpf_perf_prog { char *name; struct bpf_insn *insns; size_t insns_cnt; + + struct perf_probe_event *pev; }; struct bpf_obj { -- 1.8.3.4