lkml.org 
[lkml]   [2015]   [Sep]   [16]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
From
SubjectRe: [PATCH 05/27] perf record, bpf: Parse and create probe points for BPF programs


On 2015/9/17 5:43, Arnaldo Carvalho de Melo wrote:
> Em Sun, Sep 06, 2015 at 07:13:21AM +0000, Wang Nan escreveu:
>> This patch introduces bpf__{un,}probe() functions to enable callers to
>> create kprobe points based on section names of BPF programs. It parses
> Ok, so now I see that when we do:
>
> perf record --event bpf_prog.o usleep 1
>
> We are potentially inserting multiple events, one for each eBPF section
> found in bpf_prog.o, right?

Yes.

> I.e. multiple evsels, so, when parsing, we should create the kprobes and
> from there then create evsels and insert in the normal list that would
> eventually get spliced to the evlist being processed, I think.
>
> So, and reading that comment on 4/27, we need to allow that to happen at
> parse_events() time, i.e. avoid adding a dummy evsel that would then,
> later, be "expanded" into potentially multiple non-dummy evsels.
>
> I put the first 3 patches, with some adjustments, in my perf/ebpf
> branch, will try to do the above, i.e. do away with that dummy, allow
> parsing the bpf_prog.o sections at parse_events() time.

Then I suggest a small interface modification in libbpf to return
'struct bpf_object *',
then after loading we can iterator on each program in that object, creat
kprobe points
then made evsels. We need to call convert_perf_probe_events() multiple
times for each
object. Since Namhyung updates the probing interface, I think it becomes
possible.

I'm glad to see you will working on this problem. If you have other
ugrent business
to do, I think I can also start this work from Sept. 21 and will have 3
days before
my vacation.

Thank you.

> - Arnaldo
>
>> the section names of each eBPF program and creates corresponding 'struct
>> perf_probe_event' structures. The parse_perf_probe_command() function is
>> used to do the main parsing work.
>>
>> Parsing result is stored into an array to satisify
>> {convert,apply}_perf_probe_events(). It accepts an array of
>> 'struct perf_probe_event' and do all the work in one call.
>>
>> Define PERF_BPF_PROBE_GROUP as "perf_bpf_probe", which will be used as
>> the group name of all eBPF probing points.
>>
>> probe_conf.max_probes is set to MAX_PROBES to support glob matching.
>>
>> Before ending of bpf__probe(), data in each 'struct perf_probe_event' is
>> cleaned. Things will be changed by following patches because they need
>> 'struct probe_trace_event' in them,
>>
>> Signed-off-by: Wang Nan <wangnan0@huawei.com>
>> Cc: Alexei Starovoitov <ast@plumgrid.com>
>> Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
>> Cc: Daniel Borkmann <daniel@iogearbox.net>
>> Cc: David Ahern <dsahern@gmail.com>
>> Cc: He Kuang <hekuang@huawei.com>
>> Cc: Jiri Olsa <jolsa@kernel.org>
>> Cc: Kaixu Xia <xiakaixu@huawei.com>
>> Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
>> Cc: Namhyung Kim <namhyung@kernel.org>
>> Cc: Paul Mackerras <paulus@samba.org>
>> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
>> Cc: Zefan Li <lizefan@huawei.com>
>> Cc: pi3orama@163.com
>> Link: http://lkml.kernel.org/n/1436445342-1402-21-git-send-email-wangnan0@huawei.com
>> Link: http://lkml.kernel.org/n/1436445342-1402-23-git-send-email-wangnan0@huawei.com
>> [Merged by two patches
>> wangnan: Utilize new perf probe API {convert,apply,cleanup}_perf_probe_events()
>> ]
>> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
>> ---
>> tools/perf/builtin-record.c | 19 +++++-
>> tools/perf/util/bpf-loader.c | 149 +++++++++++++++++++++++++++++++++++++++++++
>> tools/perf/util/bpf-loader.h | 13 ++++
>> 3 files changed, 180 insertions(+), 1 deletion(-)
>>
>> diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
>> index f886706..b56109f 100644
>> --- a/tools/perf/builtin-record.c
>> +++ b/tools/perf/builtin-record.c
>> @@ -1141,7 +1141,23 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
>> if (err)
>> goto out_bpf_clear;
>>
>> - err = -ENOMEM;
>> + /*
>> + * bpf__probe must be called before symbol__init() because we
>> + * need init_symbol_maps. If called after symbol__init,
>> + * symbol_conf.sort_by_name won't take effect.
>> + *
>> + * bpf__unprobe() is safe even if bpf__probe() failed, and it
>> + * also calls symbol__init. Therefore, goto out_symbol_exit
>> + * is safe when probe failed.
>> + */
>> + err = bpf__probe();
>> + if (err) {
>> + bpf__strerror_probe(err, errbuf, sizeof(errbuf));
>> +
>> + pr_err("Probing at events in BPF object failed.\n");
>> + pr_err("\t%s\n", errbuf);
>> + goto out_symbol_exit;
>> + }
>>
>> symbol__init(NULL);
>>
>> @@ -1202,6 +1218,7 @@ out_symbol_exit:
>> perf_evlist__delete(rec->evlist);
>> symbol__exit();
>> auxtrace_record__free(rec->itr);
>> + bpf__unprobe();
>> out_bpf_clear:
>> bpf__clear();
>> return err;
>> diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
>> index 88531ea..10505cb 100644
>> --- a/tools/perf/util/bpf-loader.c
>> +++ b/tools/perf/util/bpf-loader.c
>> @@ -9,6 +9,8 @@
>> #include "perf.h"
>> #include "debug.h"
>> #include "bpf-loader.h"
>> +#include "probe-event.h"
>> +#include "probe-finder.h"
>>
>> #define DEFINE_PRINT_FN(name, level) \
>> static int libbpf_##name(const char *fmt, ...) \
>> @@ -28,6 +30,58 @@ DEFINE_PRINT_FN(debug, 1)
>>
>> static bool libbpf_initialized;
>>
>> +static int
>> +config_bpf_program(struct bpf_program *prog, struct perf_probe_event *pev)
>> +{
>> + const char *config_str;
>> + int err;
>> +
>> + config_str = bpf_program__title(prog, false);
>> + if (!config_str) {
>> + pr_debug("bpf: unable to get title for program\n");
>> + return -EINVAL;
>> + }
>> +
>> + pr_debug("bpf: config program '%s'\n", config_str);
>> + err = parse_perf_probe_command(config_str, pev);
>> + if (err < 0) {
>> + pr_debug("bpf: '%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_debug("bpf: '%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_debug("bpf: strdup failed\n");
>> + err = -ENOMEM;
>> + goto errout;
>> + }
>> +
>> + if (!pev->event) {
>> + pr_debug("bpf: '%s': event name is missing\n",
>> + config_str);
>> + err = -EINVAL;
>> + goto errout;
>> + }
>> +
>> + pr_debug("bpf: config '%s' is ok\n", config_str);
>> +
>> + return 0;
>> +
>> +errout:
>> + if (pev)
>> + clear_perf_probe_event(pev);
>> + return err;
>> +}
>> +
>> int bpf__prepare_load(const char *filename)
>> {
>> struct bpf_object *obj;
>> @@ -59,6 +113,90 @@ void bpf__clear(void)
>> bpf_object__close(obj);
>> }
>>
>> +static bool is_probed;
>> +
>> +int bpf__unprobe(void)
>> +{
>> + struct strfilter *delfilter;
>> + int ret;
>> +
>> + if (!is_probed)
>> + return 0;
>> +
>> + delfilter = strfilter__new(PERF_BPF_PROBE_GROUP ":*", NULL);
>> + if (!delfilter) {
>> + pr_debug("Failed to create delfilter when unprobing\n");
>> + return -ENOMEM;
>> + }
>> +
>> + ret = del_perf_probe_events(delfilter);
>> + strfilter__delete(delfilter);
>> + if (ret < 0 && is_probed)
>> + pr_debug("Error: failed to delete events: %s\n",
>> + strerror(-ret));
>> + else
>> + is_probed = false;
>> + return ret < 0 ? ret : 0;
>> +}
>> +
>> +int bpf__probe(void)
>> +{
>> + int err, nr_events = 0;
>> + struct bpf_object *obj, *tmp;
>> + struct bpf_program *prog;
>> + struct perf_probe_event *pevs;
>> +
>> + pevs = calloc(MAX_PROBES, sizeof(pevs[0]));
>> + if (!pevs)
>> + return -ENOMEM;
>> +
>> + bpf_object__for_each_safe(obj, tmp) {
>> + bpf_object__for_each_program(prog, obj) {
>> + err = config_bpf_program(prog, &pevs[nr_events++]);
>> + if (err < 0)
>> + goto out;
>> +
>> + if (nr_events >= MAX_PROBES) {
>> + pr_debug("Too many (more than %d) events\n",
>> + MAX_PROBES);
>> + err = -ERANGE;
>> + goto out;
>> + };
>> + }
>> + }
>> +
>> + if (!nr_events) {
>> + /*
>> + * Don't call following code to prevent perf report failure
>> + * init_symbol_maps can fail when perf is started by non-root
>> + * user, which prevent non-root user track normal events even
>> + * without using BPF, because bpf__probe() is called by
>> + * 'perf record' unconditionally.
>> + */
>> + err = 0;
>> + goto out;
>> + }
>> +
>> + probe_conf.max_probes = MAX_PROBES;
>> + /* Let convert_perf_probe_events generates probe_trace_event (tevs) */
>> + err = convert_perf_probe_events(pevs, nr_events);
>> + if (err < 0) {
>> + pr_debug("bpf_probe: failed to convert perf probe events");
>> + goto out;
>> + }
>> +
>> + err = apply_perf_probe_events(pevs, nr_events);
>> + if (err < 0)
>> + pr_debug("bpf probe: failed to probe events\n");
>> + else
>> + is_probed = true;
>> +out_cleanup:
>> + cleanup_perf_probe_events(pevs, nr_events);
>> +out:
>> + free(pevs);
>> + return err < 0 ? err : 0;
>> +}
>> +
>> #define bpf__strerror_head(err, buf, size) \
>> char sbuf[STRERR_BUFSIZE], *emsg;\
>> if (!size)\
>> @@ -90,3 +228,14 @@ int bpf__strerror_prepare_load(const char *filename, int err,
>> bpf__strerror_end(buf, size);
>> return 0;
>> }
>> +
>> +int bpf__strerror_probe(int err, char *buf, size_t size)
>> +{
>> + bpf__strerror_head(err, buf, size);
>> + bpf__strerror_entry(ERANGE, "Too many (more than %d) events",
>> + MAX_PROBES);
>> + bpf__strerror_entry(ENOENT, "Selected kprobe point doesn't exist.");
>> + bpf__strerror_entry(EEXIST, "Selected kprobe point already exist, try perf probe -d '*'.");
>> + bpf__strerror_end(buf, size);
>> + return 0;
>> +}
>> diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
>> index 12be630..6b09a85 100644
>> --- a/tools/perf/util/bpf-loader.h
>> +++ b/tools/perf/util/bpf-loader.h
>> @@ -9,10 +9,15 @@
>> #include <string.h>
>> #include "debug.h"
>>
>> +#define PERF_BPF_PROBE_GROUP "perf_bpf_probe"
>> +
>> #ifdef HAVE_LIBBPF_SUPPORT
>> int bpf__prepare_load(const char *filename);
>> int bpf__strerror_prepare_load(const char *filename, int err,
>> char *buf, size_t size);
>> +int bpf__probe(void);
>> +int bpf__unprobe(void);
>> +int bpf__strerror_probe(int err, char *buf, size_t size);
>>
>> void bpf__clear(void);
>> #else
>> @@ -22,6 +27,8 @@ static inline int bpf__prepare_load(const char *filename __maybe_unused)
>> return -1;
>> }
>>
>> +static inline int bpf__probe(void) { return 0; }
>> +static inline int bpf__unprobe(void) { return 0; }
>> static inline void bpf__clear(void) { }
>>
>> static inline int
>> @@ -43,5 +50,11 @@ bpf__strerror_prepare_load(const char *filename __maybe_unused,
>> {
>> return __bpf_strerror(buf, size);
>> }
>> +
>> +static inline int bpf__strerror_probe(int err __maybe_unused,
>> + char *buf, size_t size)
>> +{
>> + return __bpf_strerror(buf, size);
>> +}
>> #endif
>> #endif
>> --
>> 2.1.0
>>
>> --
>> 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/




\
 
 \ /
  Last update: 2015-09-17 04:21    [W:0.129 / U:2.668 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site