OSDN Git Service

perf tools: Save bpf_prog_info and BTF of new BPF programs
authorSong Liu <songliubraving@fb.com>
Tue, 12 Mar 2019 05:30:51 +0000 (22:30 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 21 Mar 2019 14:27:04 +0000 (11:27 -0300)
To fully annotate BPF programs with source code mapping, 4 different
information are needed:

    1) PERF_RECORD_KSYMBOL
    2) PERF_RECORD_BPF_EVENT
    3) bpf_prog_info
    4) btf

This patch handles 3) and 4) for BPF programs loaded after 'perf
record|top'.

For timely process of these information, a dedicated event is added to
the side band evlist.

When PERF_RECORD_BPF_EVENT is received via the side band event, the
polling thread gathers 3) and 4) vis sys_bpf and store them in perf_env.

This information is saved to perf.data at the end of 'perf record'.

Committer testing:

The 'wakeup_watermark' member in 'struct perf_event_attr' is inside a
unnamed union, so can't be used in a struct designated initialization
with older gccs, get it out of that, isolating as 'attr.wakeup_watermark
= 1;' to work with all gcc versions.

We also need to add '--no-bpf-event' to the 'perf record'
perf_event_attr tests in 'perf test', as the way that that test goes is
to intercept the events being setup and looking if they match the fields
described in the control files, since now it finds first the side band
event used to catch the PERF_RECORD_BPF_EVENT, they all fail.

With these issues fixed:

Same scenario as for testing BPF programs loaded before 'perf record' or
'perf top' starts, only start the BPF programs after 'perf record|top',
so that its information get collected by the sideband threads, the rest
works as for the programs loaded before start monitoring.

Add missing 'inline' to the bpf_event__add_sb_event() when
HAVE_LIBBPF_SUPPORT is not defined, fixing the build in systems without
binutils devel files installed.

Signed-off-by: Song Liu <songliubraving@fb.com>
Reviewed-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stanislav Fomichev <sdf@google.com>
Link: http://lkml.kernel.org/r/20190312053051.2690567-16-songliubraving@fb.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
28 files changed:
tools/perf/builtin-record.c
tools/perf/builtin-top.c
tools/perf/tests/attr/test-record-C0
tools/perf/tests/attr/test-record-basic
tools/perf/tests/attr/test-record-branch-any
tools/perf/tests/attr/test-record-branch-filter-any
tools/perf/tests/attr/test-record-branch-filter-any_call
tools/perf/tests/attr/test-record-branch-filter-any_ret
tools/perf/tests/attr/test-record-branch-filter-hv
tools/perf/tests/attr/test-record-branch-filter-ind_call
tools/perf/tests/attr/test-record-branch-filter-k
tools/perf/tests/attr/test-record-branch-filter-u
tools/perf/tests/attr/test-record-count
tools/perf/tests/attr/test-record-data
tools/perf/tests/attr/test-record-freq
tools/perf/tests/attr/test-record-graph-default
tools/perf/tests/attr/test-record-graph-dwarf
tools/perf/tests/attr/test-record-graph-fp
tools/perf/tests/attr/test-record-group
tools/perf/tests/attr/test-record-group-sampling
tools/perf/tests/attr/test-record-group1
tools/perf/tests/attr/test-record-no-buffering
tools/perf/tests/attr/test-record-no-inherit
tools/perf/tests/attr/test-record-no-samples
tools/perf/tests/attr/test-record-period
tools/perf/tests/attr/test-record-raw
tools/perf/util/bpf-event.c
tools/perf/util/bpf-event.h

index 6f645fd..4e2d953 100644 (file)
@@ -1238,6 +1238,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
                goto out_child;
        }
 
+       if (!opts->no_bpf_event)
+               bpf_event__add_sb_event(&sb_evlist, &session->header.env);
+
        if (perf_evlist__start_sb_thread(sb_evlist, &rec->opts.target)) {
                pr_debug("Couldn't start the BPF side band thread:\nBPF programs starting from now on won't be annotatable\n");
                opts->no_bpf_event = true;
index 3ce8a8d..1999d65 100644 (file)
@@ -1637,6 +1637,9 @@ int cmd_top(int argc, const char **argv)
                goto out_delete_evlist;
        }
 
+       if (!top.record_opts.no_bpf_event)
+               bpf_event__add_sb_event(&sb_evlist, &perf_env);
+
        if (perf_evlist__start_sb_thread(sb_evlist, target)) {
                pr_debug("Couldn't start the BPF side band thread:\nBPF programs starting from now on won't be annotatable\n");
                opts->no_bpf_event = true;
index cb0a313..9381805 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -C 0 kill >/dev/null 2>&1
+args    = --no-bpf-event -C 0 kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index 85a23cf..b0ca42a 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = kill >/dev/null 2>&1
+args    = --no-bpf-event kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index 81f839e..1a99b3c 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -b kill >/dev/null 2>&1
+args    = --no-bpf-event -b kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index 357421f..709768b 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -j any kill >/dev/null 2>&1
+args    = --no-bpf-event -j any kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index dbc55f2..f943221 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -j any_call kill >/dev/null 2>&1
+args    = --no-bpf-event -j any_call kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index a0824ff..fd4f5b4 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -j any_ret kill >/dev/null 2>&1
+args    = --no-bpf-event -j any_ret kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index f34d6f1..4e52d68 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -j hv kill >/dev/null 2>&1
+args    = --no-bpf-event -j hv kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index b86a352..e08c6ab 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -j ind_call kill >/dev/null 2>&1
+args    = --no-bpf-event -j ind_call kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index d3fbc5e..b4b98f8 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -j k kill >/dev/null 2>&1
+args    = --no-bpf-event -j k kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index a318f0d..fb9610e 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -j u kill >/dev/null 2>&1
+args    = --no-bpf-event -j u kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index 34f6cc5..5e9b901 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -c 123 kill >/dev/null 2>&1
+args    = --no-bpf-event -c 123 kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index a9cf223..a99bb13 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -d kill >/dev/null 2>&1
+args    = --no-bpf-event -d kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index bf4cb45..89e29f6 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -F 100 kill >/dev/null 2>&1
+args    = --no-bpf-event -F 100 kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index 0b216e6..5d8234d 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -g kill >/dev/null 2>&1
+args    = --no-bpf-event -g kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index da2fa73..ae92061 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = --call-graph dwarf -- kill >/dev/null 2>&1
+args    = --no-bpf-event --call-graph dwarf -- kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index 625d190..5630521 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = --call-graph fp kill >/dev/null 2>&1
+args    = --no-bpf-event --call-graph fp kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index 618ba1c..14ee60f 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = --group -e cycles,instructions kill >/dev/null 2>&1
+args    = --no-bpf-event --group -e cycles,instructions kill >/dev/null 2>&1
 ret     = 1
 
 [event-1:base-record]
index f0729c4..300b9f7 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1
+args    = --no-bpf-event -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1
 ret     = 1
 
 [event-1:base-record]
index 48e8bd1..3ffe246 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -e '{cycles,instructions}' kill >/dev/null 2>&1
+args    = --no-bpf-event -e '{cycles,instructions}' kill >/dev/null 2>&1
 ret     = 1
 
 [event-1:base-record]
index aa3956d..583dcbb 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = --no-buffering kill >/dev/null 2>&1
+args    = --no-bpf-event --no-buffering kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index 560943d..15d1dc1 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -i kill >/dev/null 2>&1
+args    = --no-bpf-event -i kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index 8eb73ab..596fbd6 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -n kill >/dev/null 2>&1
+args    = --no-bpf-event -n kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index 69bc748..1191011 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -c 100 -P kill >/dev/null 2>&1
+args    = --no-bpf-event -c 100 -P kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index a188a61..13a5f78 100644 (file)
@@ -1,6 +1,6 @@
 [config]
 command = record
-args    = -R kill >/dev/null 2>&1
+args    = --no-bpf-event -R kill >/dev/null 2>&1
 ret     = 1
 
 [event:base-record]
index 7ffe7db..2a8c245 100644 (file)
@@ -13,6 +13,7 @@
 #include "env.h"
 #include "session.h"
 #include "map.h"
+#include "evlist.h"
 
 #define ptr_to_u64(ptr)    ((__u64)(unsigned long)(ptr))
 
@@ -330,3 +331,102 @@ int perf_event__synthesize_bpf_events(struct perf_session *session,
        free(event);
        return err;
 }
+
+static void perf_env__add_bpf_info(struct perf_env *env, u32 id)
+{
+       struct bpf_prog_info_linear *info_linear;
+       struct bpf_prog_info_node *info_node;
+       struct btf *btf = NULL;
+       u64 arrays;
+       u32 btf_id;
+       int fd;
+
+       fd = bpf_prog_get_fd_by_id(id);
+       if (fd < 0)
+               return;
+
+       arrays = 1UL << BPF_PROG_INFO_JITED_KSYMS;
+       arrays |= 1UL << BPF_PROG_INFO_JITED_FUNC_LENS;
+       arrays |= 1UL << BPF_PROG_INFO_FUNC_INFO;
+       arrays |= 1UL << BPF_PROG_INFO_PROG_TAGS;
+       arrays |= 1UL << BPF_PROG_INFO_JITED_INSNS;
+       arrays |= 1UL << BPF_PROG_INFO_LINE_INFO;
+       arrays |= 1UL << BPF_PROG_INFO_JITED_LINE_INFO;
+
+       info_linear = bpf_program__get_prog_info_linear(fd, arrays);
+       if (IS_ERR_OR_NULL(info_linear)) {
+               pr_debug("%s: failed to get BPF program info. aborting\n", __func__);
+               goto out;
+       }
+
+       btf_id = info_linear->info.btf_id;
+
+       info_node = malloc(sizeof(struct bpf_prog_info_node));
+       if (info_node) {
+               info_node->info_linear = info_linear;
+               perf_env__insert_bpf_prog_info(env, info_node);
+       } else
+               free(info_linear);
+
+       if (btf_id == 0)
+               goto out;
+
+       if (btf__get_from_id(btf_id, &btf)) {
+               pr_debug("%s: failed to get BTF of id %u, aborting\n",
+                        __func__, btf_id);
+               goto out;
+       }
+       perf_env__fetch_btf(env, btf_id, btf);
+
+out:
+       free(btf);
+       close(fd);
+}
+
+static int bpf_event__sb_cb(union perf_event *event, void *data)
+{
+       struct perf_env *env = data;
+
+       if (event->header.type != PERF_RECORD_BPF_EVENT)
+               return -1;
+
+       switch (event->bpf_event.type) {
+       case PERF_BPF_EVENT_PROG_LOAD:
+               perf_env__add_bpf_info(env, event->bpf_event.id);
+
+       case PERF_BPF_EVENT_PROG_UNLOAD:
+               /*
+                * Do not free bpf_prog_info and btf of the program here,
+                * as annotation still need them. They will be freed at
+                * the end of the session.
+                */
+               break;
+       default:
+               pr_debug("unexpected bpf_event type of %d\n",
+                        event->bpf_event.type);
+               break;
+       }
+
+       return 0;
+}
+
+int bpf_event__add_sb_event(struct perf_evlist **evlist,
+                           struct perf_env *env)
+{
+       struct perf_event_attr attr = {
+               .type             = PERF_TYPE_SOFTWARE,
+               .config           = PERF_COUNT_SW_DUMMY,
+               .sample_id_all    = 1,
+               .watermark        = 1,
+               .bpf_event        = 1,
+               .size      = sizeof(attr), /* to capture ABI version */
+       };
+
+       /*
+        * Older gcc versions don't support designated initializers, like above,
+        * for unnamed union members, such as the following:
+        */
+       attr.wakeup_watermark = 1;
+
+       return perf_evlist__add_sb_event(evlist, &attr, bpf_event__sb_cb, env);
+}
index b9ec394..8cb1189 100644 (file)
@@ -4,12 +4,17 @@
 
 #include <linux/compiler.h>
 #include <linux/rbtree.h>
+#include <pthread.h>
+#include <api/fd/array.h>
 #include "event.h"
 
 struct machine;
 union perf_event;
+struct perf_env;
 struct perf_sample;
 struct record_opts;
+struct evlist;
+struct target;
 
 struct bpf_prog_info_node {
        struct bpf_prog_info_linear     *info_linear;
@@ -31,6 +36,9 @@ int perf_event__synthesize_bpf_events(struct perf_session *session,
                                      perf_event__handler_t process,
                                      struct machine *machine,
                                      struct record_opts *opts);
+int bpf_event__add_sb_event(struct perf_evlist **evlist,
+                                struct perf_env *env);
+
 #else
 static inline int machine__process_bpf_event(struct machine *machine __maybe_unused,
                                             union perf_event *event __maybe_unused,
@@ -46,5 +54,12 @@ static inline int perf_event__synthesize_bpf_events(struct perf_session *session
 {
        return 0;
 }
+
+static inline int bpf_event__add_sb_event(struct perf_evlist **evlist __maybe_unused,
+                                         struct perf_env *env __maybe_unused)
+{
+       return 0;
+}
+
 #endif // HAVE_LIBBPF_SUPPORT
 #endif