Commit d56354dc authored by Song Liu's avatar Song Liu Committed by Arnaldo Carvalho de Melo

perf tools: Save bpf_prog_info and BTF of new BPF programs

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: default avatarSong Liu <songliubraving@fb.com>
Reviewed-by: default avatarJiri Olsa <jolsa@kernel.org>
Tested-by: default avatarArnaldo 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.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 657ee553
......@@ -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;
......
......@@ -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;
......
[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]
......
[config]
command = record
args = kill >/dev/null 2>&1
args = --no-bpf-event kill >/dev/null 2>&1
ret = 1
[event:base-record]
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
[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]
......
......@@ -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);
}
......@@ -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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment