[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <87o8u2dunl.fsf@toke.dk>
Date: Thu, 13 Feb 2020 22:50:06 +0100
From: Toke Høiland-Jørgensen <toke@...hat.com>
To: Song Liu <songliubraving@...com>, netdev@...r.kernel.org,
bpf@...r.kernel.org
Cc: kernel-team@...com, ast@...nel.org, daniel@...earbox.net,
Song Liu <songliubraving@...com>
Subject: Re: [RFC bpf-next 3/4] bpftool: introduce "prog profile" command
Song Liu <songliubraving@...com> writes:
> With fentry/fexit programs, it is possible to profile BPF program with
> hardware counters. Introduce bpftool "prog profile", which measures key
> metrics of a BPF program.
>
> bpftool prog profile command creates per-cpu perf events. Then it attaches
> fentry/fexit programs to the target BPF program. The fentry program saves
> perf event value to a map. The fexit program reads the perf event again,
> and calculates the difference, which is the instructions/cycles used by
> the target program.
>
> Example input and output:
>
> ./bpftool prog profile 20 id 810 cycles instructions
> cycles: duration 20 run_cnt 1368 miss_cnt 665
> counter 503377 enabled 668202 running 351857
> instructions: duration 20 run_cnt 1368 miss_cnt 707
> counter 398625 enabled 502330 running 272014
>
> This command measures cycles and instructions for BPF program with id
> 810 for 20 seconds. The program has triggered 1368 times. cycles was not
> measured in 665 out of these runs, because of perf event multiplexing
> (some perf commands are running in the background). In these runs, the BPF
> program consumed 503377 cycles. The perf_event enabled and running time
> are 668202 and 351857 respectively.
>
> Note that, this approach measures cycles and instructions in very small
> increments. So the fentry/fexit programs introduce noticable errors to
> the measurement results.
>
> The fentry/fexit programs are generated with BPF skeleton. Currently,
> generation of the skeleton requires some manual steps.
>
> Signed-off-by: Song Liu <songliubraving@...com>
> ---
> tools/bpf/bpftool/profiler.skel.h | 820 ++++++++++++++++++++++
> tools/bpf/bpftool/prog.c | 387 +++++++++-
> tools/bpf/bpftool/skeleton/README | 3 +
> tools/bpf/bpftool/skeleton/profiler.bpf.c | 185 +++++
> tools/bpf/bpftool/skeleton/profiler.h | 47 ++
> 5 files changed, 1441 insertions(+), 1 deletion(-)
> create mode 100644 tools/bpf/bpftool/profiler.skel.h
> create mode 100644 tools/bpf/bpftool/skeleton/README
> create mode 100644 tools/bpf/bpftool/skeleton/profiler.bpf.c
> create mode 100644 tools/bpf/bpftool/skeleton/profiler.h
>
> diff --git a/tools/bpf/bpftool/profiler.skel.h b/tools/bpf/bpftool/profiler.skel.h
> new file mode 100644
> index 000000000000..10e99989c03e
> --- /dev/null
> +++ b/tools/bpf/bpftool/profiler.skel.h
> @@ -0,0 +1,820 @@
> +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
> +
> +/* THIS FILE IS AUTOGENERATED! */
> +#ifndef __PROFILER_BPF_SKEL_H__
> +#define __PROFILER_BPF_SKEL_H__
> +
> +#include <stdlib.h>
> +#include <bpf/libbpf.h>
> +
> +struct profiler_bpf {
> + struct bpf_object_skeleton *skeleton;
> + struct bpf_object *obj;
> + struct {
> + struct bpf_map *events;
> + struct bpf_map *fentry_readings;
> + struct bpf_map *accum_readings;
> + struct bpf_map *counts;
> + struct bpf_map *miss_counts;
> + struct bpf_map *rodata;
> + } maps;
> + struct {
> + struct bpf_program *fentry_XXX;
> + struct bpf_program *fexit_XXX;
> + } progs;
> + struct {
> + struct bpf_link *fentry_XXX;
> + struct bpf_link *fexit_XXX;
> + } links;
> + struct profiler_bpf__rodata {
> + __u32 num_cpu;
> + __u32 num_metric;
> + } *rodata;
> +};
> +
> +static void
> +profiler_bpf__destroy(struct profiler_bpf *obj)
> +{
> + if (!obj)
> + return;
> + if (obj->skeleton)
> + bpf_object__destroy_skeleton(obj->skeleton);
> + free(obj);
> +}
> +
> +static inline int
> +profiler_bpf__create_skeleton(struct profiler_bpf *obj);
> +
> +static inline struct profiler_bpf *
> +profiler_bpf__open_opts(const struct bpf_object_open_opts *opts)
> +{
> + struct profiler_bpf *obj;
> +
> + obj = (typeof(obj))calloc(1, sizeof(*obj));
> + if (!obj)
> + return NULL;
> + if (profiler_bpf__create_skeleton(obj))
> + goto err;
> + if (bpf_object__open_skeleton(obj->skeleton, opts))
> + goto err;
> +
> + return obj;
> +err:
> + profiler_bpf__destroy(obj);
> + return NULL;
> +}
> +
> +static inline struct profiler_bpf *
> +profiler_bpf__open(void)
> +{
> + return profiler_bpf__open_opts(NULL);
> +}
> +
> +static inline int
> +profiler_bpf__load(struct profiler_bpf *obj)
> +{
> + return bpf_object__load_skeleton(obj->skeleton);
> +}
> +
> +static inline struct profiler_bpf *
> +profiler_bpf__open_and_load(void)
> +{
> + struct profiler_bpf *obj;
> +
> + obj = profiler_bpf__open();
> + if (!obj)
> + return NULL;
> + if (profiler_bpf__load(obj)) {
> + profiler_bpf__destroy(obj);
> + return NULL;
> + }
> + return obj;
> +}
> +
> +static inline int
> +profiler_bpf__attach(struct profiler_bpf *obj)
> +{
> + return bpf_object__attach_skeleton(obj->skeleton);
> +}
> +
> +static inline void
> +profiler_bpf__detach(struct profiler_bpf *obj)
> +{
> + return bpf_object__detach_skeleton(obj->skeleton);
> +}
> +
> +static inline int
> +profiler_bpf__create_skeleton(struct profiler_bpf *obj)
> +{
> + struct bpf_object_skeleton *s;
> +
> + s = (typeof(s))calloc(1, sizeof(*s));
> + if (!s)
> + return -1;
> + obj->skeleton = s;
> +
> + s->sz = sizeof(*s);
> + s->name = "profiler_bpf";
> + s->obj = &obj->obj;
> +
> + /* maps */
> + s->map_cnt = 6;
> + s->map_skel_sz = sizeof(*s->maps);
> + s->maps = (typeof(s->maps))calloc(s->map_cnt, s->map_skel_sz);
> + if (!s->maps)
> + goto err;
> +
> + s->maps[0].name = "events";
> + s->maps[0].map = &obj->maps.events;
> +
> + s->maps[1].name = "fentry_readings";
> + s->maps[1].map = &obj->maps.fentry_readings;
> +
> + s->maps[2].name = "accum_readings";
> + s->maps[2].map = &obj->maps.accum_readings;
> +
> + s->maps[3].name = "counts";
> + s->maps[3].map = &obj->maps.counts;
> +
> + s->maps[4].name = "miss_counts";
> + s->maps[4].map = &obj->maps.miss_counts;
> +
> + s->maps[5].name = "profiler.rodata";
> + s->maps[5].map = &obj->maps.rodata;
> + s->maps[5].mmaped = (void **)&obj->rodata;
> +
> + /* programs */
> + s->prog_cnt = 2;
> + s->prog_skel_sz = sizeof(*s->progs);
> + s->progs = (typeof(s->progs))calloc(s->prog_cnt, s->prog_skel_sz);
> + if (!s->progs)
> + goto err;
> +
> + s->progs[0].name = "fentry_XXX";
> + s->progs[0].prog = &obj->progs.fentry_XXX;
> + s->progs[0].link = &obj->links.fentry_XXX;
> +
> + s->progs[1].name = "fexit_XXX";
> + s->progs[1].prog = &obj->progs.fexit_XXX;
> + s->progs[1].link = &obj->links.fexit_XXX;
> +
> + s->data_sz = 18256;
> + s->data = (void *)"\
> +\x7f\x45\x4c\x46\x02\x01\x01\0\0\0\0\0\0\0\0\0\x01\0\xf7\0\x01\0\0\0\0\0\0\0\0\
Holy binary blob, Batman! :)
What is this blob, exactly? The bytecode output of a precompiled
program?
-Toke
Powered by blists - more mailing lists