* [PATCH v4 perf,bpf 01/15] perf, bpf: consider events with attr.bpf_event as side-band events
2019-02-26 0:20 [PATCH v4 perf,bpf 00/15] perf annotation of BPF programs Song Liu
@ 2019-02-26 0:20 ` Song Liu
2019-03-09 19:46 ` [tip:perf/urgent] perf, bpf: Consider " tip-bot for Song Liu
2019-02-26 0:20 ` [PATCH v4 perf,bpf 02/15] bpf: libbpf: introduce bpf_program__get_prog_info_linear() Song Liu
` (13 subsequent siblings)
14 siblings, 1 reply; 36+ messages in thread
From: Song Liu @ 2019-02-26 0:20 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: ast, daniel, kernel-team, peterz, acme, jolsa, namhyung, Song Liu
Events with bpf_event should be considered as side-band event, as they
carry information about BPF programs.
Fixes: 6ee52e2a3fe4 ("perf, bpf: Introduce PERF_RECORD_BPF_EVENT")
Signed-off-by: Song Liu <songliubraving@fb.com>
---
kernel/events/core.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 0a8dab322111..9403bdda5f8c 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -4238,7 +4238,8 @@ static bool is_sb_event(struct perf_event *event)
if (attr->mmap || attr->mmap_data || attr->mmap2 ||
attr->comm || attr->comm_exec ||
attr->task || attr->ksymbol ||
- attr->context_switch)
+ attr->context_switch ||
+ attr->bpf_event)
return true;
return false;
}
--
2.17.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [tip:perf/urgent] perf, bpf: Consider events with attr.bpf_event as side-band events
2019-02-26 0:20 ` [PATCH v4 perf,bpf 01/15] perf, bpf: consider events with attr.bpf_event as side-band events Song Liu
@ 2019-03-09 19:46 ` tip-bot for Song Liu
0 siblings, 0 replies; 36+ messages in thread
From: tip-bot for Song Liu @ 2019-03-09 19:46 UTC (permalink / raw)
To: linux-tip-commits
Cc: namhyung, ast, daniel, jolsa, linux-kernel, tglx, acme,
songliubraving, hpa, peterz, mingo
Commit-ID: 21038f2baa05a0550f56f010f609a5c871b6a274
Gitweb: https://git.kernel.org/tip/21038f2baa05a0550f56f010f609a5c871b6a274
Author: Song Liu <songliubraving@fb.com>
AuthorDate: Mon, 25 Feb 2019 16:20:05 -0800
Committer: Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Thu, 28 Feb 2019 14:20:35 -0300
perf, bpf: Consider events with attr.bpf_event as side-band events
Events with attr.bpf_event set should be considered as side-band events,
as they carry information about BPF programs.
Signed-off-by: Song Liu <songliubraving@fb.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: kernel-team@fb.com
Cc: netdev@vger.kernel.org
Fixes: 6ee52e2a3fe4 ("perf, bpf: Introduce PERF_RECORD_BPF_EVENT")
Link: http://lkml.kernel.org/r/20190226002019.3748539-2-songliubraving@fb.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
kernel/events/core.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 5f59d848171e..dd9698ad3d66 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -4238,7 +4238,8 @@ static bool is_sb_event(struct perf_event *event)
if (attr->mmap || attr->mmap_data || attr->mmap2 ||
attr->comm || attr->comm_exec ||
attr->task || attr->ksymbol ||
- attr->context_switch)
+ attr->context_switch ||
+ attr->bpf_event)
return true;
return false;
}
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v4 perf,bpf 02/15] bpf: libbpf: introduce bpf_program__get_prog_info_linear()
2019-02-26 0:20 [PATCH v4 perf,bpf 00/15] perf annotation of BPF programs Song Liu
2019-02-26 0:20 ` [PATCH v4 perf,bpf 01/15] perf, bpf: consider events with attr.bpf_event as side-band events Song Liu
@ 2019-02-26 0:20 ` Song Liu
2019-02-26 0:20 ` [PATCH v4 perf,bpf 03/15] bpf: bpftool: use bpf_program__get_prog_info_linear() in prog.c:do_dump() Song Liu
` (12 subsequent siblings)
14 siblings, 0 replies; 36+ messages in thread
From: Song Liu @ 2019-02-26 0:20 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: ast, daniel, kernel-team, peterz, acme, jolsa, namhyung, Song Liu
Currently, bpf_prog_info includes 9 arrays. The user has the option to
fetch any combination of these arrays. However, this requires a lot of
handling of these arrays. This work becomes more tricky when we need to
store bpf_prog_info to a file, because these arrays are allocated
independently.
This patch introduces struct bpf_prog_info_linear, which stores arrays
of bpf_prog_info in continues memory. Helper functions are introduced
to unify the work to get different information of bpf_prog_info.
Specifically, bpf_program__get_prog_info_linear() allows the user to
select which arrays to fetch, and handles details for the user.
Plesae see the comments before enum bpf_prog_info_array for more details
and examples.
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Song Liu <songliubraving@fb.com>
---
tools/lib/bpf/libbpf.c | 251 +++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 63 ++++++++++
tools/lib/bpf/libbpf.map | 3 +
3 files changed, 317 insertions(+)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index b38dcbe7460a..fa12729de283 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -112,6 +112,11 @@ void libbpf_print(enum libbpf_print_level level, const char *format, ...)
# define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
#endif
+static inline __u64 ptr_to_u64(const void *ptr)
+{
+ return (__u64) (unsigned long) ptr;
+}
+
struct bpf_capabilities {
/* v4.14: kernel support for program & map names. */
__u32 name:1;
@@ -2997,3 +3002,249 @@ bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
ring_buffer_write_tail(header, data_tail);
return ret;
}
+
+struct bpf_prog_info_array_desc {
+ int array_offset; /* e.g. offset of jited_prog_insns */
+ int count_offset; /* e.g. offset of jited_prog_len */
+ int size_offset; /* > 0: offset of rec size,
+ * < 0: fix size of -size_offset
+ */
+};
+
+static struct bpf_prog_info_array_desc bpf_prog_info_array_desc[] = {
+ [BPF_PROG_INFO_JITED_INSNS] = {
+ offsetof(struct bpf_prog_info, jited_prog_insns),
+ offsetof(struct bpf_prog_info, jited_prog_len),
+ -1,
+ },
+ [BPF_PROG_INFO_XLATED_INSNS] = {
+ offsetof(struct bpf_prog_info, xlated_prog_insns),
+ offsetof(struct bpf_prog_info, xlated_prog_len),
+ -1,
+ },
+ [BPF_PROG_INFO_MAP_IDS] = {
+ offsetof(struct bpf_prog_info, map_ids),
+ offsetof(struct bpf_prog_info, nr_map_ids),
+ -(int)sizeof(__u32),
+ },
+ [BPF_PROG_INFO_JITED_KSYMS] = {
+ offsetof(struct bpf_prog_info, jited_ksyms),
+ offsetof(struct bpf_prog_info, nr_jited_ksyms),
+ -(int)sizeof(__u64),
+ },
+ [BPF_PROG_INFO_JITED_FUNC_LENS] = {
+ offsetof(struct bpf_prog_info, jited_func_lens),
+ offsetof(struct bpf_prog_info, nr_jited_func_lens),
+ -(int)sizeof(__u32),
+ },
+ [BPF_PROG_INFO_FUNC_INFO] = {
+ offsetof(struct bpf_prog_info, func_info),
+ offsetof(struct bpf_prog_info, nr_func_info),
+ offsetof(struct bpf_prog_info, func_info_rec_size),
+ },
+ [BPF_PROG_INFO_LINE_INFO] = {
+ offsetof(struct bpf_prog_info, line_info),
+ offsetof(struct bpf_prog_info, nr_line_info),
+ offsetof(struct bpf_prog_info, line_info_rec_size),
+ },
+ [BPF_PROG_INFO_JITED_LINE_INFO] = {
+ offsetof(struct bpf_prog_info, jited_line_info),
+ offsetof(struct bpf_prog_info, nr_jited_line_info),
+ offsetof(struct bpf_prog_info, jited_line_info_rec_size),
+ },
+ [BPF_PROG_INFO_PROG_TAGS] = {
+ offsetof(struct bpf_prog_info, prog_tags),
+ offsetof(struct bpf_prog_info, nr_prog_tags),
+ -(int)sizeof(__u8) * BPF_TAG_SIZE,
+ },
+
+};
+
+static __u32 bpf_prog_info_read_offset_u32(struct bpf_prog_info *info, int offset)
+{
+ __u32 *array = (__u32 *)info;
+
+ if (offset >= 0)
+ return array[offset / sizeof(__u32)];
+ return -(int)offset;
+}
+
+static __u64 bpf_prog_info_read_offset_u64(struct bpf_prog_info *info, int offset)
+{
+ __u64 *array = (__u64 *)info;
+
+ if (offset >= 0)
+ return array[offset / sizeof(__u64)];
+ return -(int)offset;
+}
+
+static void bpf_prog_info_set_offset_u32(struct bpf_prog_info *info, int offset,
+ __u32 val)
+{
+ __u32 *array = (__u32 *)info;
+
+ if (offset >= 0)
+ array[offset / sizeof(__u32)] = val;
+}
+
+static void bpf_prog_info_set_offset_u64(struct bpf_prog_info *info, int offset,
+ __u64 val)
+{
+ __u64 *array = (__u64 *)info;
+
+ if (offset >= 0)
+ array[offset / sizeof(__u64)] = val;
+}
+
+struct bpf_prog_info_linear *
+bpf_program__get_prog_info_linear(int fd, __u64 arrays)
+{
+ struct bpf_prog_info_linear *info_linear;
+ struct bpf_prog_info info = {};
+ __u32 info_len = sizeof(info);
+ __u32 data_len = 0;
+ int i, err;
+ void *ptr;
+
+ if (arrays >> BPF_PROG_INFO_LAST_ARRAY)
+ return ERR_PTR(-EINVAL);
+
+ /* step 1: get array dimensions */
+ err = bpf_obj_get_info_by_fd(fd, &info, &info_len);
+ if (err) {
+ pr_debug("can't get prog info: %s", strerror(errno));
+ return ERR_PTR(-EFAULT);
+ }
+
+ /* step 2: calculate total size of all arrays */
+ for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
+ bool include_array = (arrays & (1UL << i)) > 0;
+ struct bpf_prog_info_array_desc *desc;
+ __u32 count, size;
+
+ desc = bpf_prog_info_array_desc + i;
+
+ /* kernel is too old to support this field */
+ if (info_len < desc->array_offset + sizeof(__u32) ||
+ info_len < desc->count_offset + sizeof(__u32) ||
+ (desc->size_offset > 0 && info_len < desc->size_offset))
+ include_array = false;
+
+ if (!include_array) {
+ arrays &= ~(1UL << i); /* clear the bit */
+ continue;
+ }
+
+ count = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
+ size = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
+
+ data_len += count * size;
+ }
+
+ /* step 3: allocate continuous memory */
+ data_len = roundup(data_len, sizeof(__u64));
+ info_linear = malloc(sizeof(struct bpf_prog_info_linear) + data_len);
+ if (!info_linear)
+ return ERR_PTR(-ENOMEM);
+
+ /* step 4: fill data to info_linear->info */
+ info_linear->arrays = arrays;
+ memset(&info_linear->info, 0, sizeof(info));
+ ptr = info_linear->data;
+
+ for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
+ struct bpf_prog_info_array_desc *desc;
+ __u32 count, size;
+
+ if ((arrays & (1UL << i)) == 0)
+ continue;
+
+ desc = bpf_prog_info_array_desc + i;
+ count = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
+ size = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
+ bpf_prog_info_set_offset_u32(&info_linear->info,
+ desc->count_offset, count);
+ bpf_prog_info_set_offset_u32(&info_linear->info,
+ desc->size_offset, size);
+ bpf_prog_info_set_offset_u64(&info_linear->info,
+ desc->array_offset,
+ ptr_to_u64(ptr));
+ ptr += count * size;
+ }
+
+ /* step 5: call syscall again to get required arrays */
+ err = bpf_obj_get_info_by_fd(fd, &info_linear->info, &info_len);
+ if (err) {
+ pr_debug("can't get prog info: %s", strerror(errno));
+ free(info_linear);
+ return ERR_PTR(-EFAULT);
+ }
+
+ /* step 6: verify the data */
+ for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
+ struct bpf_prog_info_array_desc *desc;
+ __u32 v1, v2;
+
+ if ((arrays & (1UL << i)) == 0)
+ continue;
+
+ desc = bpf_prog_info_array_desc + i;
+ v1 = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
+ v2 = bpf_prog_info_read_offset_u32(&info_linear->info,
+ desc->count_offset);
+ if (v1 != v2)
+ pr_warning("%s: mismatch in element count\n", __func__);
+
+ v1 = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
+ v2 = bpf_prog_info_read_offset_u32(&info_linear->info,
+ desc->size_offset);
+ if (v1 != v2)
+ pr_warning("%s: mismatch in rec size\n", __func__);
+ }
+
+ /* step 7: update info_len and data_len */
+ info_linear->info_len = sizeof(struct bpf_prog_info);
+ info_linear->data_len = data_len;
+
+ return info_linear;
+}
+
+void bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear)
+{
+ int i;
+
+ for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
+ struct bpf_prog_info_array_desc *desc;
+ __u64 addr, offs;
+
+ if ((info_linear->arrays & (1UL << i)) == 0)
+ continue;
+
+ desc = bpf_prog_info_array_desc + i;
+ addr = bpf_prog_info_read_offset_u64(&info_linear->info,
+ desc->array_offset);
+ offs = addr - ptr_to_u64(info_linear->data);
+ bpf_prog_info_set_offset_u64(&info_linear->info,
+ desc->array_offset, offs);
+ }
+}
+
+void bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear)
+{
+ int i;
+
+ for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
+ struct bpf_prog_info_array_desc *desc;
+ __u64 addr, offs;
+
+ if ((info_linear->arrays & (1UL << i)) == 0)
+ continue;
+
+ desc = bpf_prog_info_array_desc + i;
+ offs = bpf_prog_info_read_offset_u64(&info_linear->info,
+ desc->array_offset);
+ addr = offs + ptr_to_u64(info_linear->data);
+ bpf_prog_info_set_offset_u64(&info_linear->info,
+ desc->array_offset, addr);
+ }
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 6c0168f8bba5..809275c47bb5 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -376,6 +376,69 @@ LIBBPF_API bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex);
LIBBPF_API bool bpf_probe_helper(enum bpf_func_id id,
enum bpf_prog_type prog_type, __u32 ifindex);
+/*
+ * Get bpf_prog_info in continuous memory
+ *
+ * struct bpf_prog_info has multiple arrays. The user has option to choose
+ * arrays to fetch from kernel. The following APIs provide uniform way to
+ * fetch these data. All arrays in bpf_prog_info are stored in singile
+ * continuous memory region. This makes it easy to store the info in a
+ * file.
+ *
+ * Before writing bpf_prog_info_linear to files, it is necessary to
+ * translate pointers bpf_prog_info to offsets. Helper functions
+ * bpf_program__bpil_addr_to_offs() and bpf_program__bpil_offs_to_addr()
+ * are introduced to switch between pointers and offsets.
+ *
+ * Examples:
+ * # To fetch map_ids and prog_tags:
+ * __u64 arrays = (1UL << BPF_PROG_INFO_MAP_IDS) |
+ * (1UL << BPF_PROG_INFO_PROG_TAGS);
+ * struct bpf_prog_info_linear *info_linear =
+ * bpf_program__get_prog_info_linear(fd, arrays);
+ *
+ * # To save data in file
+ * bpf_program__bpil_addr_to_offs(info_linear);
+ * write(f, info_linear, sizeof(*info_linear) + info_linear->data_len);
+ *
+ * # To read data from file
+ * read(f, info_linear, <proper_size>);
+ * bpf_program__bpil_offs_to_addr(info_linear);
+ */
+enum bpf_prog_info_array {
+ BPF_PROG_INFO_FIRST_ARRAY = 0,
+ BPF_PROG_INFO_JITED_INSNS = 0,
+ BPF_PROG_INFO_XLATED_INSNS,
+ BPF_PROG_INFO_MAP_IDS,
+ BPF_PROG_INFO_JITED_KSYMS,
+ BPF_PROG_INFO_JITED_FUNC_LENS,
+ BPF_PROG_INFO_FUNC_INFO,
+ BPF_PROG_INFO_LINE_INFO,
+ BPF_PROG_INFO_JITED_LINE_INFO,
+ BPF_PROG_INFO_PROG_TAGS,
+ BPF_PROG_INFO_LAST_ARRAY,
+};
+
+struct bpf_prog_info_linear {
+ /* size of struct bpf_prog_info, when the tool is compiled */
+ __u32 info_len;
+ /* total bytes allocated for data, round up to 8 bytes */
+ __u32 data_len;
+ /* which arrays are included in data */
+ __u64 arrays;
+ struct bpf_prog_info info;
+ __u8 data[];
+};
+
+LIBBPF_API struct bpf_prog_info_linear *
+bpf_program__get_prog_info_linear(int fd, __u64 arrays);
+
+LIBBPF_API void
+bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear);
+
+LIBBPF_API void
+bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 99dfa710c818..24616162447e 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -147,4 +147,7 @@ LIBBPF_0.0.2 {
btf_ext__new;
btf_ext__reloc_func_info;
btf_ext__reloc_line_info;
+ bpf_program__get_prog_info_linear;
+ bpf_program__bpil_addr_to_offs;
+ bpf_program__bpil_offs_to_addr;
} LIBBPF_0.0.1;
--
2.17.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v4 perf,bpf 03/15] bpf: bpftool: use bpf_program__get_prog_info_linear() in prog.c:do_dump()
2019-02-26 0:20 [PATCH v4 perf,bpf 00/15] perf annotation of BPF programs Song Liu
2019-02-26 0:20 ` [PATCH v4 perf,bpf 01/15] perf, bpf: consider events with attr.bpf_event as side-band events Song Liu
2019-02-26 0:20 ` [PATCH v4 perf,bpf 02/15] bpf: libbpf: introduce bpf_program__get_prog_info_linear() Song Liu
@ 2019-02-26 0:20 ` Song Liu
2019-02-26 0:20 ` [PATCH v4 perf,bpf 04/15] perf, bpf: synthesize bpf events with bpf_program__get_prog_info_linear() Song Liu
` (11 subsequent siblings)
14 siblings, 0 replies; 36+ messages in thread
From: Song Liu @ 2019-02-26 0:20 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: ast, daniel, kernel-team, peterz, acme, jolsa, namhyung, Song Liu
This patches uses bpf_program__get_prog_info_linear() to simplify the
logic in prog.c do_dump().
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Song Liu <songliubraving@fb.com>
---
tools/bpf/bpftool/prog.c | 266 +++++++++------------------------------
1 file changed, 59 insertions(+), 207 deletions(-)
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index 0640e9bc0ada..206b820df7c2 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -393,41 +393,31 @@ static int do_show(int argc, char **argv)
static int do_dump(int argc, char **argv)
{
- unsigned int finfo_rec_size, linfo_rec_size, jited_linfo_rec_size;
- void *func_info = NULL, *linfo = NULL, *jited_linfo = NULL;
- unsigned int nr_finfo, nr_linfo = 0, nr_jited_linfo = 0;
+ struct bpf_prog_info_linear *info_linear;
struct bpf_prog_linfo *prog_linfo = NULL;
- unsigned long *func_ksyms = NULL;
- struct bpf_prog_info info = {};
- unsigned int *func_lens = NULL;
+ enum {DUMP_JITED, DUMP_XLATED} mode;
const char *disasm_opt = NULL;
- unsigned int nr_func_ksyms;
- unsigned int nr_func_lens;
+ struct bpf_prog_info *info;
struct dump_data dd = {};
- __u32 len = sizeof(info);
+ void *func_info = NULL;
struct btf *btf = NULL;
- unsigned int buf_size;
char *filepath = NULL;
bool opcodes = false;
bool visual = false;
char func_sig[1024];
unsigned char *buf;
bool linum = false;
- __u32 *member_len;
- __u64 *member_ptr;
+ __u32 member_len;
+ __u64 arrays;
ssize_t n;
- int err;
int fd;
if (is_prefix(*argv, "jited")) {
if (disasm_init())
return -1;
-
- member_len = &info.jited_prog_len;
- member_ptr = &info.jited_prog_insns;
+ mode = DUMP_JITED;
} else if (is_prefix(*argv, "xlated")) {
- member_len = &info.xlated_prog_len;
- member_ptr = &info.xlated_prog_insns;
+ mode = DUMP_XLATED;
} else {
p_err("expected 'xlated' or 'jited', got: %s", *argv);
return -1;
@@ -466,175 +456,50 @@ static int do_dump(int argc, char **argv)
return -1;
}
- err = bpf_obj_get_info_by_fd(fd, &info, &len);
- if (err) {
- p_err("can't get prog info: %s", strerror(errno));
- return -1;
- }
-
- if (!*member_len) {
- p_info("no instructions returned");
- close(fd);
- return 0;
- }
+ if (mode == DUMP_JITED)
+ arrays = 1UL << BPF_PROG_INFO_JITED_INSNS;
+ else
+ arrays = 1UL << BPF_PROG_INFO_XLATED_INSNS;
- buf_size = *member_len;
+ 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_LINE_INFO;
+ arrays |= 1UL << BPF_PROG_INFO_JITED_LINE_INFO;
- buf = malloc(buf_size);
- if (!buf) {
- p_err("mem alloc failed");
- close(fd);
+ info_linear = bpf_program__get_prog_info_linear(fd, arrays);
+ close(fd);
+ if (IS_ERR_OR_NULL(info_linear)) {
+ p_err("can't get prog info: %s", strerror(errno));
return -1;
}
- nr_func_ksyms = info.nr_jited_ksyms;
- if (nr_func_ksyms) {
- func_ksyms = malloc(nr_func_ksyms * sizeof(__u64));
- if (!func_ksyms) {
- p_err("mem alloc failed");
- close(fd);
- goto err_free;
- }
- }
-
- nr_func_lens = info.nr_jited_func_lens;
- if (nr_func_lens) {
- func_lens = malloc(nr_func_lens * sizeof(__u32));
- if (!func_lens) {
- p_err("mem alloc failed");
- close(fd);
+ info = &info_linear->info;
+ if (mode == DUMP_JITED) {
+ if (info->jited_prog_len == 0) {
+ p_info("no instructions returned");
goto err_free;
}
- }
-
- nr_finfo = info.nr_func_info;
- finfo_rec_size = info.func_info_rec_size;
- if (nr_finfo && finfo_rec_size) {
- func_info = malloc(nr_finfo * finfo_rec_size);
- if (!func_info) {
- p_err("mem alloc failed");
- close(fd);
+ buf = (unsigned char *)(info->jited_prog_insns);
+ member_len = info->jited_prog_len;
+ } else { /* DUMP_XLATED */
+ if (info->xlated_prog_len == 0) {
+ p_err("error retrieving insn dump: kernel.kptr_restrict set?");
goto err_free;
}
+ buf = (unsigned char *)info->xlated_prog_insns;
+ member_len = info->xlated_prog_len;
}
- linfo_rec_size = info.line_info_rec_size;
- if (info.nr_line_info && linfo_rec_size && info.btf_id) {
- nr_linfo = info.nr_line_info;
- linfo = malloc(nr_linfo * linfo_rec_size);
- if (!linfo) {
- p_err("mem alloc failed");
- close(fd);
- goto err_free;
- }
- }
-
- jited_linfo_rec_size = info.jited_line_info_rec_size;
- if (info.nr_jited_line_info &&
- jited_linfo_rec_size &&
- info.nr_jited_ksyms &&
- info.nr_jited_func_lens &&
- info.btf_id) {
- nr_jited_linfo = info.nr_jited_line_info;
- jited_linfo = malloc(nr_jited_linfo * jited_linfo_rec_size);
- if (!jited_linfo) {
- p_err("mem alloc failed");
- close(fd);
- goto err_free;
- }
- }
-
- memset(&info, 0, sizeof(info));
-
- *member_ptr = ptr_to_u64(buf);
- *member_len = buf_size;
- info.jited_ksyms = ptr_to_u64(func_ksyms);
- info.nr_jited_ksyms = nr_func_ksyms;
- info.jited_func_lens = ptr_to_u64(func_lens);
- info.nr_jited_func_lens = nr_func_lens;
- info.nr_func_info = nr_finfo;
- info.func_info_rec_size = finfo_rec_size;
- info.func_info = ptr_to_u64(func_info);
- info.nr_line_info = nr_linfo;
- info.line_info_rec_size = linfo_rec_size;
- info.line_info = ptr_to_u64(linfo);
- info.nr_jited_line_info = nr_jited_linfo;
- info.jited_line_info_rec_size = jited_linfo_rec_size;
- info.jited_line_info = ptr_to_u64(jited_linfo);
-
- err = bpf_obj_get_info_by_fd(fd, &info, &len);
- close(fd);
- if (err) {
- p_err("can't get prog info: %s", strerror(errno));
- goto err_free;
- }
-
- if (*member_len > buf_size) {
- p_err("too many instructions returned");
- goto err_free;
- }
-
- if (info.nr_jited_ksyms > nr_func_ksyms) {
- p_err("too many addresses returned");
- goto err_free;
- }
-
- if (info.nr_jited_func_lens > nr_func_lens) {
- p_err("too many values returned");
- goto err_free;
- }
-
- if (info.nr_func_info != nr_finfo) {
- p_err("incorrect nr_func_info %d vs. expected %d",
- info.nr_func_info, nr_finfo);
- goto err_free;
- }
-
- if (info.func_info_rec_size != finfo_rec_size) {
- p_err("incorrect func_info_rec_size %d vs. expected %d",
- info.func_info_rec_size, finfo_rec_size);
- goto err_free;
- }
-
- if (linfo && info.nr_line_info != nr_linfo) {
- p_err("incorrect nr_line_info %u vs. expected %u",
- info.nr_line_info, nr_linfo);
- goto err_free;
- }
-
- if (info.line_info_rec_size != linfo_rec_size) {
- p_err("incorrect line_info_rec_size %u vs. expected %u",
- info.line_info_rec_size, linfo_rec_size);
- goto err_free;
- }
-
- if (jited_linfo && info.nr_jited_line_info != nr_jited_linfo) {
- p_err("incorrect nr_jited_line_info %u vs. expected %u",
- info.nr_jited_line_info, nr_jited_linfo);
- goto err_free;
- }
-
- if (info.jited_line_info_rec_size != jited_linfo_rec_size) {
- p_err("incorrect jited_line_info_rec_size %u vs. expected %u",
- info.jited_line_info_rec_size, jited_linfo_rec_size);
- goto err_free;
- }
-
- if ((member_len == &info.jited_prog_len &&
- info.jited_prog_insns == 0) ||
- (member_len == &info.xlated_prog_len &&
- info.xlated_prog_insns == 0)) {
- p_err("error retrieving insn dump: kernel.kptr_restrict set?");
- goto err_free;
- }
-
- if (info.btf_id && btf__get_from_id(info.btf_id, &btf)) {
+ if (info->btf_id && btf__get_from_id(info->btf_id, &btf)) {
p_err("failed to get btf");
goto err_free;
}
- if (nr_linfo) {
- prog_linfo = bpf_prog_linfo__new(&info);
+ func_info = (void *)info->func_info;
+
+ if (info->nr_line_info) {
+ prog_linfo = bpf_prog_linfo__new(info);
if (!prog_linfo)
p_info("error in processing bpf_line_info. continue without it.");
}
@@ -647,9 +512,9 @@ static int do_dump(int argc, char **argv)
goto err_free;
}
- n = write(fd, buf, *member_len);
+ n = write(fd, buf, member_len);
close(fd);
- if (n != *member_len) {
+ if (n != member_len) {
p_err("error writing output file: %s",
n < 0 ? strerror(errno) : "short write");
goto err_free;
@@ -657,19 +522,19 @@ static int do_dump(int argc, char **argv)
if (json_output)
jsonw_null(json_wtr);
- } else if (member_len == &info.jited_prog_len) {
+ } else if (mode == DUMP_JITED) {
const char *name = NULL;
- if (info.ifindex) {
- name = ifindex_to_bfd_params(info.ifindex,
- info.netns_dev,
- info.netns_ino,
+ if (info->ifindex) {
+ name = ifindex_to_bfd_params(info->ifindex,
+ info->netns_dev,
+ info->netns_ino,
&disasm_opt);
if (!name)
goto err_free;
}
- if (info.nr_jited_func_lens && info.jited_func_lens) {
+ if (info->nr_jited_func_lens && info->jited_func_lens) {
struct kernel_sym *sym = NULL;
struct bpf_func_info *record;
char sym_name[SYM_MAX_NAME];
@@ -677,17 +542,16 @@ static int do_dump(int argc, char **argv)
__u64 *ksyms = NULL;
__u32 *lens;
__u32 i;
-
- if (info.nr_jited_ksyms) {
+ if (info->nr_jited_ksyms) {
kernel_syms_load(&dd);
- ksyms = (__u64 *) info.jited_ksyms;
+ ksyms = (__u64 *) info->jited_ksyms;
}
if (json_output)
jsonw_start_array(json_wtr);
- lens = (__u32 *) info.jited_func_lens;
- for (i = 0; i < info.nr_jited_func_lens; i++) {
+ lens = (__u32 *) info->jited_func_lens;
+ for (i = 0; i < info->nr_jited_func_lens; i++) {
if (ksyms) {
sym = kernel_syms_search(&dd, ksyms[i]);
if (sym)
@@ -699,7 +563,7 @@ static int do_dump(int argc, char **argv)
}
if (func_info) {
- record = func_info + i * finfo_rec_size;
+ record = func_info + i * info->func_info_rec_size;
btf_dumper_type_only(btf, record->type_id,
func_sig,
sizeof(func_sig));
@@ -736,49 +600,37 @@ static int do_dump(int argc, char **argv)
if (json_output)
jsonw_end_array(json_wtr);
} else {
- disasm_print_insn(buf, *member_len, opcodes, name,
+ disasm_print_insn(buf, member_len, opcodes, name,
disasm_opt, btf, NULL, 0, 0, false);
}
} else if (visual) {
if (json_output)
jsonw_null(json_wtr);
else
- dump_xlated_cfg(buf, *member_len);
+ dump_xlated_cfg(buf, member_len);
} else {
kernel_syms_load(&dd);
- dd.nr_jited_ksyms = info.nr_jited_ksyms;
- dd.jited_ksyms = (__u64 *) info.jited_ksyms;
+ dd.nr_jited_ksyms = info->nr_jited_ksyms;
+ dd.jited_ksyms = (__u64 *) info->jited_ksyms;
dd.btf = btf;
dd.func_info = func_info;
- dd.finfo_rec_size = finfo_rec_size;
+ dd.finfo_rec_size = info->func_info_rec_size;
dd.prog_linfo = prog_linfo;
if (json_output)
- dump_xlated_json(&dd, buf, *member_len, opcodes,
+ dump_xlated_json(&dd, buf, member_len, opcodes,
linum);
else
- dump_xlated_plain(&dd, buf, *member_len, opcodes,
+ dump_xlated_plain(&dd, buf, member_len, opcodes,
linum);
kernel_syms_destroy(&dd);
}
- free(buf);
- free(func_ksyms);
- free(func_lens);
- free(func_info);
- free(linfo);
- free(jited_linfo);
- bpf_prog_linfo__free(prog_linfo);
+ free(info_linear);
return 0;
err_free:
- free(buf);
- free(func_ksyms);
- free(func_lens);
- free(func_info);
- free(linfo);
- free(jited_linfo);
- bpf_prog_linfo__free(prog_linfo);
+ free(info_linear);
return -1;
}
--
2.17.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v4 perf,bpf 04/15] perf, bpf: synthesize bpf events with bpf_program__get_prog_info_linear()
2019-02-26 0:20 [PATCH v4 perf,bpf 00/15] perf annotation of BPF programs Song Liu
` (2 preceding siblings ...)
2019-02-26 0:20 ` [PATCH v4 perf,bpf 03/15] bpf: bpftool: use bpf_program__get_prog_info_linear() in prog.c:do_dump() Song Liu
@ 2019-02-26 0:20 ` Song Liu
2019-02-26 0:20 ` [PATCH v4 perf,bpf 05/15] perf: change prototype of perf_event__synthesize_bpf_events() Song Liu
` (10 subsequent siblings)
14 siblings, 0 replies; 36+ messages in thread
From: Song Liu @ 2019-02-26 0:20 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: ast, daniel, kernel-team, peterz, acme, jolsa, namhyung, Song Liu
With bpf_program__get_prog_info_linear, we can simplify the logic that
synthesizes bpf events.
This patch doesn't change the behavior of the code.
Signed-off-by: Song Liu <songliubraving@fb.com>
---
tools/perf/util/bpf-event.c | 118 ++++++++++++------------------------
1 file changed, 40 insertions(+), 78 deletions(-)
diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c
index 796ef793f4ce..e6dfb95029e5 100644
--- a/tools/perf/util/bpf-event.c
+++ b/tools/perf/util/bpf-event.c
@@ -3,7 +3,9 @@
#include <stdlib.h>
#include <bpf/bpf.h>
#include <bpf/btf.h>
+#include <bpf/libbpf.h>
#include <linux/btf.h>
+#include <linux/err.h>
#include "bpf-event.h"
#include "debug.h"
#include "symbol.h"
@@ -49,99 +51,62 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_tool *tool,
{
struct ksymbol_event *ksymbol_event = &event->ksymbol_event;
struct bpf_event *bpf_event = &event->bpf_event;
- u32 sub_prog_cnt, i, func_info_rec_size = 0;
- u8 (*prog_tags)[BPF_TAG_SIZE] = NULL;
- struct bpf_prog_info info = { .type = 0, };
- u32 info_len = sizeof(info);
- void *func_infos = NULL;
- u64 *prog_addrs = NULL;
+ struct bpf_prog_info_linear *info_linear;
+ struct bpf_prog_info *info;
struct btf *btf = NULL;
- u32 *prog_lens = NULL;
bool has_btf = false;
- char errbuf[512];
+ u32 sub_prog_cnt, i;
int err = 0;
+ u64 arrays;
- /* Call bpf_obj_get_info_by_fd() to get sizes of arrays */
- err = bpf_obj_get_info_by_fd(fd, &info, &info_len);
+ 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;
- if (err) {
- pr_debug("%s: failed to get BPF program info: %s, aborting\n",
- __func__, str_error_r(errno, errbuf, sizeof(errbuf)));
+ info_linear = bpf_program__get_prog_info_linear(fd, arrays);
+ if (IS_ERR_OR_NULL(info_linear)) {
+ info_linear = NULL;
+ pr_debug("%s: failed to get BPF program info. aborting\n", __func__);
return -1;
}
- if (info_len < offsetof(struct bpf_prog_info, prog_tags)) {
+
+ if (info_linear->info_len < offsetof(struct bpf_prog_info, prog_tags)) {
pr_debug("%s: the kernel is too old, aborting\n", __func__);
return -2;
}
+ info = &info_linear->info;
+
/* number of ksyms, func_lengths, and tags should match */
- sub_prog_cnt = info.nr_jited_ksyms;
- if (sub_prog_cnt != info.nr_prog_tags ||
- sub_prog_cnt != info.nr_jited_func_lens)
+ sub_prog_cnt = info->nr_jited_ksyms;
+ if (sub_prog_cnt != info->nr_prog_tags ||
+ sub_prog_cnt != info->nr_jited_func_lens)
return -1;
/* check BTF func info support */
- if (info.btf_id && info.nr_func_info && info.func_info_rec_size) {
+ if (info->btf_id && info->nr_func_info && info->func_info_rec_size) {
/* btf func info number should be same as sub_prog_cnt */
- if (sub_prog_cnt != info.nr_func_info) {
+ if (sub_prog_cnt != info->nr_func_info) {
pr_debug("%s: mismatch in BPF sub program count and BTF function info count, aborting\n", __func__);
- return -1;
- }
- if (btf__get_from_id(info.btf_id, &btf)) {
- pr_debug("%s: failed to get BTF of id %u, aborting\n", __func__, info.btf_id);
- return -1;
+ err = -1;
+ goto out;
}
- func_info_rec_size = info.func_info_rec_size;
- func_infos = calloc(sub_prog_cnt, func_info_rec_size);
- if (!func_infos) {
- pr_debug("%s: failed to allocate memory for func_infos, aborting\n", __func__);
- return -1;
+ if (btf__get_from_id(info->btf_id, &btf)) {
+ pr_debug("%s: failed to get BTF of id %u, aborting\n", __func__, info->btf_id);
+ err = -1;
+ btf = NULL;
+ goto out;
}
has_btf = true;
}
- /*
- * We need address, length, and tag for each sub program.
- * Allocate memory and call bpf_obj_get_info_by_fd() again
- */
- prog_addrs = calloc(sub_prog_cnt, sizeof(u64));
- if (!prog_addrs) {
- pr_debug("%s: failed to allocate memory for prog_addrs, aborting\n", __func__);
- goto out;
- }
- prog_lens = calloc(sub_prog_cnt, sizeof(u32));
- if (!prog_lens) {
- pr_debug("%s: failed to allocate memory for prog_lens, aborting\n", __func__);
- goto out;
- }
- prog_tags = calloc(sub_prog_cnt, BPF_TAG_SIZE);
- if (!prog_tags) {
- pr_debug("%s: failed to allocate memory for prog_tags, aborting\n", __func__);
- goto out;
- }
-
- memset(&info, 0, sizeof(info));
- info.nr_jited_ksyms = sub_prog_cnt;
- info.nr_jited_func_lens = sub_prog_cnt;
- info.nr_prog_tags = sub_prog_cnt;
- info.jited_ksyms = ptr_to_u64(prog_addrs);
- info.jited_func_lens = ptr_to_u64(prog_lens);
- info.prog_tags = ptr_to_u64(prog_tags);
- info_len = sizeof(info);
- if (has_btf) {
- info.nr_func_info = sub_prog_cnt;
- info.func_info_rec_size = func_info_rec_size;
- info.func_info = ptr_to_u64(func_infos);
- }
-
- err = bpf_obj_get_info_by_fd(fd, &info, &info_len);
- if (err) {
- pr_debug("%s: failed to get BPF program info, aborting\n", __func__);
- goto out;
- }
-
/* Synthesize PERF_RECORD_KSYMBOL */
for (i = 0; i < sub_prog_cnt; i++) {
+ u8 (*prog_tags)[BPF_TAG_SIZE] = (void *)(info->prog_tags);
+ __u32 *prog_lens = (__u32 *)(info->jited_func_lens);
+ __u64 *prog_addrs = (__u64 *)(info->jited_ksyms);
+ void *func_infos = (void *)(info->func_info);
const struct bpf_func_info *finfo;
const char *short_name = NULL;
const struct btf_type *t;
@@ -163,13 +128,13 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_tool *tool,
KSYM_NAME_LEN - name_len,
prog_tags[i], BPF_TAG_SIZE);
if (has_btf) {
- finfo = func_infos + i * info.func_info_rec_size;
+ finfo = func_infos + i * info->func_info_rec_size;
t = btf__type_by_id(btf, finfo->type_id);
short_name = btf__name_by_offset(btf, t->name_off);
} else if (i == 0 && sub_prog_cnt == 1) {
/* no subprog */
- if (info.name[0])
- short_name = info.name;
+ if (info->name[0])
+ short_name = info->name;
} else
short_name = "F";
if (short_name)
@@ -195,9 +160,9 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_tool *tool,
},
.type = PERF_BPF_EVENT_PROG_LOAD,
.flags = 0,
- .id = info.id,
+ .id = info->id,
};
- memcpy(bpf_event->tag, prog_tags[i], BPF_TAG_SIZE);
+ memcpy(bpf_event->tag, info->tag, BPF_TAG_SIZE);
memset((void *)event + event->header.size, 0, machine->id_hdr_size);
event->header.size += machine->id_hdr_size;
err = perf_tool__process_synth_event(tool, event,
@@ -205,10 +170,7 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_tool *tool,
}
out:
- free(prog_tags);
- free(prog_lens);
- free(prog_addrs);
- free(func_infos);
+ free(info_linear);
free(btf);
return err ? -1 : 0;
}
--
2.17.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v4 perf,bpf 05/15] perf: change prototype of perf_event__synthesize_bpf_events()
2019-02-26 0:20 [PATCH v4 perf,bpf 00/15] perf annotation of BPF programs Song Liu
` (3 preceding siblings ...)
2019-02-26 0:20 ` [PATCH v4 perf,bpf 04/15] perf, bpf: synthesize bpf events with bpf_program__get_prog_info_linear() Song Liu
@ 2019-02-26 0:20 ` Song Liu
2019-02-26 0:20 ` [PATCH v4 perf,bpf 06/15] perf, bpf: save bpf_prog_info in a rbtree in perf_env Song Liu
` (9 subsequent siblings)
14 siblings, 0 replies; 36+ messages in thread
From: Song Liu @ 2019-02-26 0:20 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: ast, daniel, kernel-team, peterz, acme, jolsa, namhyung, Song Liu
This patch changes the arguments of perf_event__synthesize_bpf_events()
to include perf_session* instead of perf_tool*. perf_session will be used
in the next patch.
Signed-off-by: Song Liu <songliubraving@fb.com>
---
tools/perf/builtin-record.c | 2 +-
tools/perf/builtin-top.c | 2 +-
tools/perf/util/bpf-event.c | 8 +++++---
tools/perf/util/bpf-event.h | 4 ++--
4 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 88ea11d57c6f..2355e0a9eda0 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1083,7 +1083,7 @@ static int record__synthesize(struct record *rec, bool tail)
return err;
}
- err = perf_event__synthesize_bpf_events(tool, process_synthesized_event,
+ err = perf_event__synthesize_bpf_events(session, process_synthesized_event,
machine, opts);
if (err < 0)
pr_warning("Couldn't synthesize bpf events.\n");
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 5a486d4de56e..27d8d42e0a4d 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -1216,7 +1216,7 @@ static int __cmd_top(struct perf_top *top)
init_process_thread(top);
- ret = perf_event__synthesize_bpf_events(&top->tool, perf_event__process,
+ ret = perf_event__synthesize_bpf_events(top->session, perf_event__process,
&top->session->machines.host,
&top->record_opts);
if (ret < 0)
diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c
index e6dfb95029e5..ff7ee149ec46 100644
--- a/tools/perf/util/bpf-event.c
+++ b/tools/perf/util/bpf-event.c
@@ -10,6 +10,7 @@
#include "debug.h"
#include "symbol.h"
#include "machine.h"
+#include "session.h"
#define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
@@ -42,7 +43,7 @@ int machine__process_bpf_event(struct machine *machine __maybe_unused,
* -1 for failures;
* -2 for lack of kernel support.
*/
-static int perf_event__synthesize_one_bpf_prog(struct perf_tool *tool,
+static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
perf_event__handler_t process,
struct machine *machine,
int fd,
@@ -52,6 +53,7 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_tool *tool,
struct ksymbol_event *ksymbol_event = &event->ksymbol_event;
struct bpf_event *bpf_event = &event->bpf_event;
struct bpf_prog_info_linear *info_linear;
+ struct perf_tool *tool = session->tool;
struct bpf_prog_info *info;
struct btf *btf = NULL;
bool has_btf = false;
@@ -175,7 +177,7 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_tool *tool,
return err ? -1 : 0;
}
-int perf_event__synthesize_bpf_events(struct perf_tool *tool,
+int perf_event__synthesize_bpf_events(struct perf_session *session,
perf_event__handler_t process,
struct machine *machine,
struct record_opts *opts)
@@ -209,7 +211,7 @@ int perf_event__synthesize_bpf_events(struct perf_tool *tool,
continue;
}
- err = perf_event__synthesize_one_bpf_prog(tool, process,
+ err = perf_event__synthesize_one_bpf_prog(session, process,
machine, fd,
event, opts);
close(fd);
diff --git a/tools/perf/util/bpf-event.h b/tools/perf/util/bpf-event.h
index 7890067e1a37..6698683612a7 100644
--- a/tools/perf/util/bpf-event.h
+++ b/tools/perf/util/bpf-event.h
@@ -15,7 +15,7 @@ struct record_opts;
int machine__process_bpf_event(struct machine *machine, union perf_event *event,
struct perf_sample *sample);
-int perf_event__synthesize_bpf_events(struct perf_tool *tool,
+int perf_event__synthesize_bpf_events(struct perf_session *session,
perf_event__handler_t process,
struct machine *machine,
struct record_opts *opts);
@@ -27,7 +27,7 @@ static inline int machine__process_bpf_event(struct machine *machine __maybe_unu
return 0;
}
-static inline int perf_event__synthesize_bpf_events(struct perf_tool *tool __maybe_unused,
+static inline int perf_event__synthesize_bpf_events(struct perf_session *session __maybe_unused,
perf_event__handler_t process __maybe_unused,
struct machine *machine __maybe_unused,
struct record_opts *opts __maybe_unused)
--
2.17.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v4 perf,bpf 06/15] perf, bpf: save bpf_prog_info in a rbtree in perf_env
2019-02-26 0:20 [PATCH v4 perf,bpf 00/15] perf annotation of BPF programs Song Liu
` (4 preceding siblings ...)
2019-02-26 0:20 ` [PATCH v4 perf,bpf 05/15] perf: change prototype of perf_event__synthesize_bpf_events() Song Liu
@ 2019-02-26 0:20 ` Song Liu
2019-02-27 13:21 ` Jiri Olsa
` (2 more replies)
2019-02-26 0:20 ` [PATCH v4 perf,bpf 07/15] perf, bpf: save bpf_prog_info information as headers to perf.data Song Liu
` (8 subsequent siblings)
14 siblings, 3 replies; 36+ messages in thread
From: Song Liu @ 2019-02-26 0:20 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: ast, daniel, kernel-team, peterz, acme, jolsa, namhyung, Song Liu
bpf_prog_info contains information necessary to annotate bpf programs.
This patch saves bpf_prog_info for bpf programs loaded in the system.
Signed-off-by: Song Liu <songliubraving@fb.com>
---
tools/perf/util/bpf-event.c | 32 +++++++++++++-
tools/perf/util/bpf-event.h | 7 ++-
tools/perf/util/env.c | 85 +++++++++++++++++++++++++++++++++++++
tools/perf/util/env.h | 17 ++++++++
4 files changed, 139 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c
index ff7ee149ec46..ce81b2c43a51 100644
--- a/tools/perf/util/bpf-event.c
+++ b/tools/perf/util/bpf-event.c
@@ -10,6 +10,7 @@
#include "debug.h"
#include "symbol.h"
#include "machine.h"
+#include "env.h"
#include "session.h"
#define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
@@ -54,17 +55,28 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
struct bpf_event *bpf_event = &event->bpf_event;
struct bpf_prog_info_linear *info_linear;
struct perf_tool *tool = session->tool;
+ struct bpf_prog_info_node *info_node;
struct bpf_prog_info *info;
struct btf *btf = NULL;
bool has_btf = false;
+ struct perf_env *env;
u32 sub_prog_cnt, i;
int err = 0;
u64 arrays;
+ /*
+ * for perf-record and perf-report use header.env;
+ * otherwise, use global perf_env.
+ */
+ env = session->data ? &session->header.env : &perf_env;
+
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)) {
@@ -153,8 +165,8 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
machine, process);
}
- /* Synthesize PERF_RECORD_BPF_EVENT */
if (opts->bpf_event) {
+ /* Synthesize PERF_RECORD_BPF_EVENT */
*bpf_event = (struct bpf_event){
.header = {
.type = PERF_RECORD_BPF_EVENT,
@@ -167,6 +179,24 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
memcpy(bpf_event->tag, info->tag, BPF_TAG_SIZE);
memset((void *)event + event->header.size, 0, machine->id_hdr_size);
event->header.size += machine->id_hdr_size;
+
+ /* save bpf_prog_info to env */
+ info_node = malloc(sizeof(struct bpf_prog_info_node));
+
+ /*
+ * Do not bail out for !info_node, as we still want to
+ * call perf_tool__process_synth_event()
+ */
+ if (info_node) {
+ info_node->info_linear = info_linear;
+ perf_env__insert_bpf_prog_info(env, info_node);
+ info_linear = NULL;
+ }
+
+ /*
+ * process after saving bpf_prog_info to env, so that
+ * required information is ready for look up
+ */
err = perf_tool__process_synth_event(tool, event,
machine, process);
}
diff --git a/tools/perf/util/bpf-event.h b/tools/perf/util/bpf-event.h
index 6698683612a7..fad932f7404f 100644
--- a/tools/perf/util/bpf-event.h
+++ b/tools/perf/util/bpf-event.h
@@ -3,14 +3,19 @@
#define __PERF_BPF_EVENT_H
#include <linux/compiler.h>
+#include <linux/rbtree.h>
#include "event.h"
struct machine;
union perf_event;
struct perf_sample;
-struct perf_tool;
struct record_opts;
+struct bpf_prog_info_node {
+ struct bpf_prog_info_linear *info_linear;
+ struct rb_node rb_node;
+};
+
#ifdef HAVE_LIBBPF_SUPPORT
int machine__process_bpf_event(struct machine *machine, union perf_event *event,
struct perf_sample *sample);
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 4c23779e271a..650c14ad7e9b 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -3,15 +3,93 @@
#include "env.h"
#include "sane_ctype.h"
#include "util.h"
+#include "bpf-event.h"
#include <errno.h>
#include <sys/utsname.h>
+#include <bpf/libbpf.h>
struct perf_env perf_env;
+void perf_env__insert_bpf_prog_info(struct perf_env *env,
+ struct bpf_prog_info_node *info_node)
+{
+ __u32 prog_id = info_node->info_linear->info.id;
+ struct bpf_prog_info_node *node;
+ struct rb_node *parent = NULL;
+ struct rb_node **p;
+
+ down_write(&env->bpf_progs.lock);
+ p = &env->bpf_progs.prog_infos.rb_node;
+
+ while (*p != NULL) {
+ parent = *p;
+ node = rb_entry(parent, struct bpf_prog_info_node, rb_node);
+ if (prog_id < node->info_linear->info.id) {
+ p = &(*p)->rb_left;
+ } else if (prog_id > node->info_linear->info.id) {
+ p = &(*p)->rb_right;
+ } else {
+ pr_debug("duplicated bpf prog info %u\n", prog_id);
+ goto out;
+ }
+ }
+
+ rb_link_node(&info_node->rb_node, parent, p);
+ rb_insert_color(&info_node->rb_node, &env->bpf_progs.prog_infos);
+out:
+ up_write(&env->bpf_progs.lock);
+}
+
+struct bpf_prog_info_node *perf_env__find_bpf_prog_info(struct perf_env *env,
+ __u32 prog_id)
+{
+ struct bpf_prog_info_node *node = NULL;
+ struct rb_node *n;
+
+ down_read(&env->bpf_progs.lock);
+ n = env->bpf_progs.prog_infos.rb_node;
+
+ while (n) {
+ node = rb_entry(n, struct bpf_prog_info_node, rb_node);
+ if (prog_id < node->info_linear->info.id)
+ n = n->rb_left;
+ else if (prog_id > node->info_linear->info.id)
+ n = n->rb_right;
+ else
+ break;
+ }
+
+ up_read(&env->bpf_progs.lock);
+ return node;
+}
+
+/* purge data in bpf_prog_infos tree */
+static void perf_env__purge_bpf(struct perf_env *env)
+{
+ struct rb_root *root;
+ struct rb_node *next;
+
+ down_write(&env->bpf_progs.lock);
+
+ root = &env->bpf_progs.prog_infos;
+ next = rb_first(root);
+
+ while (next) {
+ struct bpf_prog_info_node *node;
+
+ node = rb_entry(next, struct bpf_prog_info_node, rb_node);
+ next = rb_next(&node->rb_node);
+ rb_erase_init(&node->rb_node, root);
+ free(node);
+ }
+ up_write(&env->bpf_progs.lock);
+}
+
void perf_env__exit(struct perf_env *env)
{
int i;
+ perf_env__purge_bpf(env);
zfree(&env->hostname);
zfree(&env->os_release);
zfree(&env->version);
@@ -38,6 +116,12 @@ void perf_env__exit(struct perf_env *env)
zfree(&env->memory_nodes);
}
+static void init_bpf_rb_trees(struct perf_env *env)
+{
+ env->bpf_progs.prog_infos = RB_ROOT;
+ init_rwsem(&env->bpf_progs.lock);
+}
+
int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[])
{
int i;
@@ -59,6 +143,7 @@ int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[])
env->nr_cmdline = argc;
+ init_bpf_rb_trees(env);
return 0;
out_free:
zfree(&env->cmdline_argv);
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
index d01b8355f4ca..33ef4b2d2a29 100644
--- a/tools/perf/util/env.h
+++ b/tools/perf/util/env.h
@@ -3,7 +3,9 @@
#define __PERF_ENV_H
#include <linux/types.h>
+#include <linux/rbtree.h>
#include "cpumap.h"
+#include "rwsem.h"
struct cpu_topology_map {
int socket_id;
@@ -64,8 +66,19 @@ struct perf_env {
struct memory_node *memory_nodes;
unsigned long long memory_bsize;
u64 clockid_res_ns;
+
+ /*
+ * bpf_info_lock protects bpf rbtrees. This is needed because the
+ * trees are accessed by different threads in perf-top
+ */
+ struct {
+ struct rw_semaphore lock;
+ struct rb_root prog_infos;
+ } bpf_progs;
};
+struct bpf_prog_info_node;
+
extern struct perf_env perf_env;
void perf_env__exit(struct perf_env *env);
@@ -80,4 +93,8 @@ const char *perf_env__arch(struct perf_env *env);
const char *perf_env__raw_arch(struct perf_env *env);
int perf_env__nr_cpus_avail(struct perf_env *env);
+void perf_env__insert_bpf_prog_info(struct perf_env *env,
+ struct bpf_prog_info_node *info_node);
+struct bpf_prog_info_node *perf_env__find_bpf_prog_info(struct perf_env *env,
+ __u32 prog_id);
#endif /* __PERF_ENV_H */
--
2.17.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 06/15] perf, bpf: save bpf_prog_info in a rbtree in perf_env
2019-02-26 0:20 ` [PATCH v4 perf,bpf 06/15] perf, bpf: save bpf_prog_info in a rbtree in perf_env Song Liu
@ 2019-02-27 13:21 ` Jiri Olsa
2019-02-27 17:38 ` Song Liu
2019-02-27 13:21 ` Jiri Olsa
2019-02-27 13:21 ` Jiri Olsa
2 siblings, 1 reply; 36+ messages in thread
From: Jiri Olsa @ 2019-02-27 13:21 UTC (permalink / raw)
To: Song Liu
Cc: netdev, linux-kernel, ast, daniel, kernel-team, peterz, acme,
jolsa, namhyung
On Mon, Feb 25, 2019 at 04:20:10PM -0800, Song Liu wrote:
> bpf_prog_info contains information necessary to annotate bpf programs.
> This patch saves bpf_prog_info for bpf programs loaded in the system.
>
> Signed-off-by: Song Liu <songliubraving@fb.com>
> ---
> tools/perf/util/bpf-event.c | 32 +++++++++++++-
> tools/perf/util/bpf-event.h | 7 ++-
> tools/perf/util/env.c | 85 +++++++++++++++++++++++++++++++++++++
> tools/perf/util/env.h | 17 ++++++++
> 4 files changed, 139 insertions(+), 2 deletions(-)
>
> diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c
> index ff7ee149ec46..ce81b2c43a51 100644
> --- a/tools/perf/util/bpf-event.c
> +++ b/tools/perf/util/bpf-event.c
> @@ -10,6 +10,7 @@
> #include "debug.h"
> #include "symbol.h"
> #include "machine.h"
> +#include "env.h"
> #include "session.h"
>
> #define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
> @@ -54,17 +55,28 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
> struct bpf_event *bpf_event = &event->bpf_event;
> struct bpf_prog_info_linear *info_linear;
> struct perf_tool *tool = session->tool;
> + struct bpf_prog_info_node *info_node;
> struct bpf_prog_info *info;
> struct btf *btf = NULL;
> bool has_btf = false;
> + struct perf_env *env;
> u32 sub_prog_cnt, i;
> int err = 0;
> u64 arrays;
>
> + /*
> + * for perf-record and perf-report use header.env;
> + * otherwise, use global perf_env.
> + */
> + env = session->data ? &session->header.env : &perf_env;
> +
> 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)) {
> @@ -153,8 +165,8 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
> machine, process);
> }
>
> - /* Synthesize PERF_RECORD_BPF_EVENT */
> if (opts->bpf_event) {
> + /* Synthesize PERF_RECORD_BPF_EVENT */
> *bpf_event = (struct bpf_event){
> .header = {
> .type = PERF_RECORD_BPF_EVENT,
> @@ -167,6 +179,24 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
> memcpy(bpf_event->tag, info->tag, BPF_TAG_SIZE);
> memset((void *)event + event->header.size, 0, machine->id_hdr_size);
> event->header.size += machine->id_hdr_size;
> +
> + /* save bpf_prog_info to env */
why do we save this to perf_env in here? we just
synthesize the same data as event, so report and
top will read it and fill perf_env again, right?
could you please explain the whole flow of the
bpf events and its respective data in perf_env
and put it into the changelog
> + info_node = malloc(sizeof(struct bpf_prog_info_node));
> +
> + /*
> + * Do not bail out for !info_node, as we still want to
> + * call perf_tool__process_synth_event()
well, we are out of memory, so I dont think perf_tool__process_synth_event
will get too far.. also the perf_env data would be inconsistent with what
you store as event.. how can that work?
thanks,
jirka
> + */
> + if (info_node) {
> + info_node->info_linear = info_linear;
> + perf_env__insert_bpf_prog_info(env, info_node);
> + info_linear = NULL;
> + }
> +
SNIP
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 06/15] perf, bpf: save bpf_prog_info in a rbtree in perf_env
2019-02-27 13:21 ` Jiri Olsa
@ 2019-02-27 17:38 ` Song Liu
0 siblings, 0 replies; 36+ messages in thread
From: Song Liu @ 2019-02-27 17:38 UTC (permalink / raw)
To: Jiri Olsa
Cc: Netdev, linux-kernel, ast, daniel, Kernel Team, peterz, acme,
jolsa, namhyung
> On Feb 27, 2019, at 5:21 AM, Jiri Olsa <jolsa@redhat.com> wrote:
>
> On Mon, Feb 25, 2019 at 04:20:10PM -0800, Song Liu wrote:
>> bpf_prog_info contains information necessary to annotate bpf programs.
>> This patch saves bpf_prog_info for bpf programs loaded in the system.
>>
>> Signed-off-by: Song Liu <songliubraving@fb.com>
>> ---
>> tools/perf/util/bpf-event.c | 32 +++++++++++++-
>> tools/perf/util/bpf-event.h | 7 ++-
>> tools/perf/util/env.c | 85 +++++++++++++++++++++++++++++++++++++
>> tools/perf/util/env.h | 17 ++++++++
>> 4 files changed, 139 insertions(+), 2 deletions(-)
>>
>> diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c
>> index ff7ee149ec46..ce81b2c43a51 100644
>> --- a/tools/perf/util/bpf-event.c
>> +++ b/tools/perf/util/bpf-event.c
>> @@ -10,6 +10,7 @@
>> #include "debug.h"
>> #include "symbol.h"
>> #include "machine.h"
>> +#include "env.h"
>> #include "session.h"
>>
>> #define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
>> @@ -54,17 +55,28 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
>> struct bpf_event *bpf_event = &event->bpf_event;
>> struct bpf_prog_info_linear *info_linear;
>> struct perf_tool *tool = session->tool;
>> + struct bpf_prog_info_node *info_node;
>> struct bpf_prog_info *info;
>> struct btf *btf = NULL;
>> bool has_btf = false;
>> + struct perf_env *env;
>> u32 sub_prog_cnt, i;
>> int err = 0;
>> u64 arrays;
>>
>> + /*
>> + * for perf-record and perf-report use header.env;
>> + * otherwise, use global perf_env.
>> + */
>> + env = session->data ? &session->header.env : &perf_env;
>> +
>> 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)) {
>> @@ -153,8 +165,8 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
>> machine, process);
>> }
>>
>> - /* Synthesize PERF_RECORD_BPF_EVENT */
>> if (opts->bpf_event) {
>> + /* Synthesize PERF_RECORD_BPF_EVENT */
>> *bpf_event = (struct bpf_event){
>> .header = {
>> .type = PERF_RECORD_BPF_EVENT,
>> @@ -167,6 +179,24 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
>> memcpy(bpf_event->tag, info->tag, BPF_TAG_SIZE);
>> memset((void *)event + event->header.size, 0, machine->id_hdr_size);
>> event->header.size += machine->id_hdr_size;
>> +
>> + /* save bpf_prog_info to env */
>
> why do we save this to perf_env in here? we just
> synthesize the same data as event, so report and
> top will read it and fill perf_env again, right?
The synthesized events are same as PERF_RECORD_BPF_EVENT
PROG_LOAD. We need to process them and fill perf_env as
soon as we get PROG_LOAD. Otherwise, the program might
unload before we process them.
>
> could you please explain the whole flow of the
> bpf events and its respective data in perf_env
> and put it into the changelog
I will add more information to the change log.
>
>> + info_node = malloc(sizeof(struct bpf_prog_info_node));
>> +
>> + /*
>> + * Do not bail out for !info_node, as we still want to
>> + * call perf_tool__process_synth_event()
>
> well, we are out of memory, so I dont think perf_tool__process_synth_event
> will get too far.. also the perf_env data would be inconsistent with what
> you store as event.. how can that work?
It is OK we have PERF_RECORD_BPF_EVENT but not related
bpf_prog_info and btf. The perf.data file will show
a BPF program was loaded, but we won't be able to do
annotation. Does this make sense?
Thanks,
Song
>
> thanks,
> jirka
>
>> + */
>> + if (info_node) {
>> + info_node->info_linear = info_linear;
>> + perf_env__insert_bpf_prog_info(env, info_node);
>> + info_linear = NULL;
>> + }
>> +
>
> SNIP
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 06/15] perf, bpf: save bpf_prog_info in a rbtree in perf_env
2019-02-26 0:20 ` [PATCH v4 perf,bpf 06/15] perf, bpf: save bpf_prog_info in a rbtree in perf_env Song Liu
2019-02-27 13:21 ` Jiri Olsa
@ 2019-02-27 13:21 ` Jiri Olsa
2019-02-27 13:21 ` Jiri Olsa
2 siblings, 0 replies; 36+ messages in thread
From: Jiri Olsa @ 2019-02-27 13:21 UTC (permalink / raw)
To: Song Liu
Cc: netdev, linux-kernel, ast, daniel, kernel-team, peterz, acme,
jolsa, namhyung
On Mon, Feb 25, 2019 at 04:20:10PM -0800, Song Liu wrote:
SNIP
> @@ -38,6 +116,12 @@ void perf_env__exit(struct perf_env *env)
> zfree(&env->memory_nodes);
> }
>
> +static void init_bpf_rb_trees(struct perf_env *env)
> +{
> + env->bpf_progs.prog_infos = RB_ROOT;
> + init_rwsem(&env->bpf_progs.lock);
> +}
> +
> int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[])
> {
> int i;
> @@ -59,6 +143,7 @@ int perf_env__set_cmdline(struct perf_env *env, int argc, const char *argv[])
>
> env->nr_cmdline = argc;
>
> + init_bpf_rb_trees(env);
this souldn't be in perf_env__set_cmdline,
it's there to set the cmdline
struct bpf_progs is first in perf_env that
needs other than zero initialization, so
I think we need to add perf_env__init function,
that will do that for all the paths that uses
perf_env
jirka
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 06/15] perf, bpf: save bpf_prog_info in a rbtree in perf_env
2019-02-26 0:20 ` [PATCH v4 perf,bpf 06/15] perf, bpf: save bpf_prog_info in a rbtree in perf_env Song Liu
2019-02-27 13:21 ` Jiri Olsa
2019-02-27 13:21 ` Jiri Olsa
@ 2019-02-27 13:21 ` Jiri Olsa
2 siblings, 0 replies; 36+ messages in thread
From: Jiri Olsa @ 2019-02-27 13:21 UTC (permalink / raw)
To: Song Liu
Cc: netdev, linux-kernel, ast, daniel, kernel-team, peterz, acme,
jolsa, namhyung
On Mon, Feb 25, 2019 at 04:20:10PM -0800, Song Liu wrote:
SNIP
> diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
> index d01b8355f4ca..33ef4b2d2a29 100644
> --- a/tools/perf/util/env.h
> +++ b/tools/perf/util/env.h
> @@ -3,7 +3,9 @@
> #define __PERF_ENV_H
>
> #include <linux/types.h>
> +#include <linux/rbtree.h>
> #include "cpumap.h"
> +#include "rwsem.h"
>
> struct cpu_topology_map {
> int socket_id;
> @@ -64,8 +66,19 @@ struct perf_env {
> struct memory_node *memory_nodes;
> unsigned long long memory_bsize;
> u64 clockid_res_ns;
> +
> + /*
> + * bpf_info_lock protects bpf rbtrees. This is needed because the
> + * trees are accessed by different threads in perf-top
> + */
> + struct {
> + struct rw_semaphore lock;
> + struct rb_root prog_infos;
could be just 'infos' ^^^^ is already in struct name
> + } bpf_progs;
> };
>
jirka
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v4 perf,bpf 07/15] perf, bpf: save bpf_prog_info information as headers to perf.data
2019-02-26 0:20 [PATCH v4 perf,bpf 00/15] perf annotation of BPF programs Song Liu
` (5 preceding siblings ...)
2019-02-26 0:20 ` [PATCH v4 perf,bpf 06/15] perf, bpf: save bpf_prog_info in a rbtree in perf_env Song Liu
@ 2019-02-26 0:20 ` Song Liu
2019-02-27 13:21 ` Jiri Olsa
2019-02-26 0:20 ` [PATCH v4 perf,bpf 08/15] perf, bpf: save btf in a rbtree in perf_env Song Liu
` (7 subsequent siblings)
14 siblings, 1 reply; 36+ messages in thread
From: Song Liu @ 2019-02-26 0:20 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: ast, daniel, kernel-team, peterz, acme, jolsa, namhyung, Song Liu
This patch enables perf-record to save bpf_prog_info information as
headers to perf.data. A new header type HEADER_BPF_PROG_INFO is
introduced for this data.
Signed-off-by: Song Liu <songliubraving@fb.com>
---
| 143 ++++++++++++++++++++++++++++++++++++++-
| 1 +
2 files changed, 143 insertions(+), 1 deletion(-)
--git a/tools/perf/util/header.c b/tools/perf/util/header.c
index dec6d218c31c..c89dce7942d1 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -18,6 +18,7 @@
#include <sys/utsname.h>
#include <linux/time64.h>
#include <dirent.h>
+#include <bpf/libbpf.h>
#include "evlist.h"
#include "evsel.h"
@@ -39,6 +40,7 @@
#include "tool.h"
#include "time-utils.h"
#include "units.h"
+#include "bpf-event.h"
#include "sane_ctype.h"
@@ -1080,6 +1082,51 @@ static int write_clockid(struct feat_fd *ff,
sizeof(ff->ph->env.clockid_res_ns));
}
+static int write_bpf_prog_info(struct feat_fd *ff,
+ struct perf_evlist *evlist __maybe_unused)
+{
+ struct perf_env *env = &ff->ph->env;
+ struct rb_root *root;
+ struct rb_node *next;
+ u32 count = 0;
+ int ret;
+
+ down_read(&env->bpf_progs.lock);
+
+ root = &env->bpf_progs.prog_infos;
+ next = rb_first(root);
+ while (next) {
+ ++count;
+ next = rb_next(next);
+ }
+
+ ret = do_write(ff, &count, sizeof(count));
+
+ if (ret < 0)
+ goto out;
+
+ next = rb_first(root);
+ while (next) {
+ struct bpf_prog_info_node *node;
+ size_t len;
+
+ node = rb_entry(next, struct bpf_prog_info_node, rb_node);
+ next = rb_next(&node->rb_node);
+ len = sizeof(struct bpf_prog_info_linear) +
+ node->info_linear->data_len;
+
+ /* before writing to file, translate address to offset */
+ bpf_program__bpil_addr_to_offs(node->info_linear);
+ ret = do_write(ff, node->info_linear, len);
+ bpf_program__bpil_offs_to_addr(node->info_linear);
+ if (ret < 0)
+ goto out;
+ }
+out:
+ up_read(&env->bpf_progs.lock);
+ return ret;
+}
+
static int cpu_cache_level__sort(const void *a, const void *b)
{
struct cpu_cache_level *cache_a = (struct cpu_cache_level *)a;
@@ -1560,6 +1607,29 @@ static void print_clockid(struct feat_fd *ff, FILE *fp)
ff->ph->env.clockid_res_ns * 1000);
}
+static void print_bpf_prog_info(struct feat_fd *ff, FILE *fp)
+{
+ struct perf_env *env = &ff->ph->env;
+ struct rb_root *root;
+ struct rb_node *next;
+
+ down_read(&env->bpf_progs.lock);
+
+ root = &env->bpf_progs.prog_infos;
+ next = rb_first(root);
+
+ while (next) {
+ struct bpf_prog_info_node *node;
+
+ node = rb_entry(next, struct bpf_prog_info_node, rb_node);
+ next = rb_next(&node->rb_node);
+ fprintf(fp, "# bpf_prog_info of id %u\n",
+ node->info_linear->info.id);
+ }
+
+ up_read(&env->bpf_progs.lock);
+}
+
static void free_event_desc(struct perf_evsel *events)
{
struct perf_evsel *evsel;
@@ -2592,6 +2662,76 @@ static int process_clockid(struct feat_fd *ff,
return 0;
}
+static int process_bpf_prog_info(struct feat_fd *ff,
+ void *data __maybe_unused)
+{
+ struct bpf_prog_info_linear *info_linear;
+ struct bpf_prog_info_node *info_node;
+ struct perf_env *env = &ff->ph->env;
+ u32 count, i;
+ int err = -1;
+
+ if (do_read_u32(ff, &count))
+ return -1;
+
+ down_write(&env->bpf_progs.lock);
+
+ for (i = 0; i < count; ++i) {
+ u32 info_len, data_len;
+
+ info_linear = NULL;
+ info_node = NULL;
+ if (do_read_u32(ff, &info_len))
+ goto out;
+ if (do_read_u32(ff, &data_len))
+ goto out;
+
+ if (info_len > sizeof(struct bpf_prog_info)) {
+ pr_warning("detected invalid bpf_prog_info\n");
+ goto out;
+ }
+
+ info_linear = malloc(sizeof(struct bpf_prog_info_linear) +
+ data_len);
+ if (!info_linear)
+ goto out;
+ info_linear->info_len = sizeof(struct bpf_prog_info);
+ info_linear->data_len = data_len;
+ if (do_read_u64(ff, (u64 *)(&info_linear->arrays)))
+ goto out;
+ if (__do_read(ff, &info_linear->info, info_len))
+ goto out;
+ if (info_len < sizeof(struct bpf_prog_info))
+ memset(((void *)(&info_linear->info)) + info_len, 0,
+ sizeof(struct bpf_prog_info) - info_len);
+
+ if (__do_read(ff, info_linear->data, data_len))
+ goto out;
+
+ /* endian mismatch, drop the info, continue */
+ if (ff->ph->needs_swap) {
+ free(info_linear);
+ continue;
+ }
+
+ info_node = malloc(sizeof(struct bpf_prog_info_node));
+ if (!info_node)
+ goto out;
+
+ /* after reading from file, translate offset to address */
+ bpf_program__bpil_offs_to_addr(info_linear);
+ info_node->info_linear = info_linear;
+ perf_env__insert_bpf_prog_info(env, info_node);
+ }
+
+ return 0;
+out:
+ free(info_linear);
+ free(info_node);
+ up_write(&env->bpf_progs.lock);
+ return err;
+}
+
struct feature_ops {
int (*write)(struct feat_fd *ff, struct perf_evlist *evlist);
void (*print)(struct feat_fd *ff, FILE *fp);
@@ -2651,7 +2791,8 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPN(CACHE, cache, true),
FEAT_OPR(SAMPLE_TIME, sample_time, false),
FEAT_OPR(MEM_TOPOLOGY, mem_topology, true),
- FEAT_OPR(CLOCKID, clockid, false)
+ FEAT_OPR(CLOCKID, clockid, false),
+ FEAT_OPR(BPF_PROG_INFO, bpf_prog_info, false)
};
struct header_print_data {
--git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 0d553ddca0a3..0785c91b4c3a 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -39,6 +39,7 @@ enum {
HEADER_SAMPLE_TIME,
HEADER_MEM_TOPOLOGY,
HEADER_CLOCKID,
+ HEADER_BPF_PROG_INFO,
HEADER_LAST_FEATURE,
HEADER_FEAT_BITS = 256,
};
--
2.17.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 07/15] perf, bpf: save bpf_prog_info information as headers to perf.data
2019-02-26 0:20 ` [PATCH v4 perf,bpf 07/15] perf, bpf: save bpf_prog_info information as headers to perf.data Song Liu
@ 2019-02-27 13:21 ` Jiri Olsa
2019-02-27 17:28 ` Song Liu
0 siblings, 1 reply; 36+ messages in thread
From: Jiri Olsa @ 2019-02-27 13:21 UTC (permalink / raw)
To: Song Liu
Cc: netdev, linux-kernel, ast, daniel, kernel-team, peterz, acme,
jolsa, namhyung
On Mon, Feb 25, 2019 at 04:20:11PM -0800, Song Liu wrote:
SNIP
> + if (info_len > sizeof(struct bpf_prog_info)) {
> + pr_warning("detected invalid bpf_prog_info\n");
> + goto out;
> + }
> +
> + info_linear = malloc(sizeof(struct bpf_prog_info_linear) +
> + data_len);
> + if (!info_linear)
> + goto out;
> + info_linear->info_len = sizeof(struct bpf_prog_info);
> + info_linear->data_len = data_len;
> + if (do_read_u64(ff, (u64 *)(&info_linear->arrays)))
> + goto out;
> + if (__do_read(ff, &info_linear->info, info_len))
> + goto out;
> + if (info_len < sizeof(struct bpf_prog_info))
> + memset(((void *)(&info_linear->info)) + info_len, 0,
> + sizeof(struct bpf_prog_info) - info_len);
> +
> + if (__do_read(ff, info_linear->data, data_len))
> + goto out;
> +
> + /* endian mismatch, drop the info, continue */
so there's no way we can swap the data? why?
jirka
> + if (ff->ph->needs_swap) {
> + free(info_linear);
> + continue;
> + }
SNIP
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 07/15] perf, bpf: save bpf_prog_info information as headers to perf.data
2019-02-27 13:21 ` Jiri Olsa
@ 2019-02-27 17:28 ` Song Liu
0 siblings, 0 replies; 36+ messages in thread
From: Song Liu @ 2019-02-27 17:28 UTC (permalink / raw)
To: Jiri Olsa
Cc: Netdev, linux-kernel, Alexei Starovoitov, Daniel Borkmann,
Kernel Team, Peter Zijlstra, Arnaldo Carvalho de Melo, Jiri Olsa,
Namhyung Kim
> On Feb 27, 2019, at 5:21 AM, Jiri Olsa <jolsa@redhat.com> wrote:
>
> On Mon, Feb 25, 2019 at 04:20:11PM -0800, Song Liu wrote:
>
> SNIP
>
>> + if (info_len > sizeof(struct bpf_prog_info)) {
>> + pr_warning("detected invalid bpf_prog_info\n");
>> + goto out;
>> + }
>> +
>> + info_linear = malloc(sizeof(struct bpf_prog_info_linear) +
>> + data_len);
>> + if (!info_linear)
>> + goto out;
>> + info_linear->info_len = sizeof(struct bpf_prog_info);
>> + info_linear->data_len = data_len;
>> + if (do_read_u64(ff, (u64 *)(&info_linear->arrays)))
>> + goto out;
>> + if (__do_read(ff, &info_linear->info, info_len))
>> + goto out;
>> + if (info_len < sizeof(struct bpf_prog_info))
>> + memset(((void *)(&info_linear->info)) + info_len, 0,
>> + sizeof(struct bpf_prog_info) - info_len);
>> +
>> + if (__do_read(ff, info_linear->data, data_len))
>> + goto out;
>> +
>> + /* endian mismatch, drop the info, continue */
>
> so there's no way we can swap the data? why?
>
> jirka
Yes, we can swap the data. OTOH, the set is big enough. How about we
implement the swap later? Running perf-report on a system of with
different endianness is a relatively rare case.
Thanks,
Song
>> + if (ff->ph->needs_swap) {
>> + free(info_linear);
>> + continue;
>> + }
>
> SNIP
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v4 perf,bpf 08/15] perf, bpf: save btf in a rbtree in perf_env
2019-02-26 0:20 [PATCH v4 perf,bpf 00/15] perf annotation of BPF programs Song Liu
` (6 preceding siblings ...)
2019-02-26 0:20 ` [PATCH v4 perf,bpf 07/15] perf, bpf: save bpf_prog_info information as headers to perf.data Song Liu
@ 2019-02-26 0:20 ` Song Liu
2019-02-26 0:20 ` [PATCH v4 perf,bpf 09/15] perf, bpf: save btf information as headers to perf.data Song Liu
` (6 subsequent siblings)
14 siblings, 0 replies; 36+ messages in thread
From: Song Liu @ 2019-02-26 0:20 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: ast, daniel, kernel-team, peterz, acme, jolsa, namhyung, Song Liu
btf contains information necessary to annotate bpf programs. This patch
saves btf for bpf programs loaded in the system.
Signed-off-by: Song Liu <songliubraving@fb.com>
---
tools/perf/util/bpf-event.c | 24 ++++++++++++++
tools/perf/util/bpf-event.h | 7 ++++
tools/perf/util/env.c | 65 +++++++++++++++++++++++++++++++++++++
tools/perf/util/env.h | 4 +++
4 files changed, 100 insertions(+)
diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c
index ce81b2c43a51..370b830f2433 100644
--- a/tools/perf/util/bpf-event.c
+++ b/tools/perf/util/bpf-event.c
@@ -34,6 +34,29 @@ int machine__process_bpf_event(struct machine *machine __maybe_unused,
return 0;
}
+static int perf_env__fetch_btf(struct perf_env *env,
+ u32 btf_id,
+ struct btf *btf)
+{
+ struct btf_node *node;
+ u32 data_size;
+ const void *data;
+
+ data = btf__get_raw_data(btf, &data_size);
+
+ node = malloc(data_size + sizeof(struct btf_node));
+
+ if (!node)
+ return -1;
+
+ node->id = btf_id;
+ node->data_size = data_size;
+ memcpy(node->data, data, data_size);
+
+ perf_env__insert_btf(env, node);
+ return 0;
+}
+
/*
* Synthesize PERF_RECORD_KSYMBOL and PERF_RECORD_BPF_EVENT for one bpf
* program. One PERF_RECORD_BPF_EVENT is generated for the program. And
@@ -113,6 +136,7 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
goto out;
}
has_btf = true;
+ perf_env__fetch_btf(env, info->btf_id, btf);
}
/* Synthesize PERF_RECORD_KSYMBOL */
diff --git a/tools/perf/util/bpf-event.h b/tools/perf/util/bpf-event.h
index fad932f7404f..b9ec394dc7c7 100644
--- a/tools/perf/util/bpf-event.h
+++ b/tools/perf/util/bpf-event.h
@@ -16,6 +16,13 @@ struct bpf_prog_info_node {
struct rb_node rb_node;
};
+struct btf_node {
+ struct rb_node rb_node;
+ u32 id;
+ u32 data_size;
+ char data[];
+};
+
#ifdef HAVE_LIBBPF_SUPPORT
int machine__process_bpf_event(struct machine *machine, union perf_event *event,
struct perf_sample *sample);
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 650c14ad7e9b..291b21d8f858 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -63,6 +63,57 @@ struct bpf_prog_info_node *perf_env__find_bpf_prog_info(struct perf_env *env,
return node;
}
+void perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node)
+{
+ struct rb_node *parent = NULL;
+ __u32 btf_id = btf_node->id;
+ struct btf_node *node;
+ struct rb_node **p;
+
+ down_write(&env->bpf_progs.lock);
+ p = &env->bpf_progs.btfs.rb_node;
+
+ while (*p != NULL) {
+ parent = *p;
+ node = rb_entry(parent, struct btf_node, rb_node);
+ if (btf_id < node->id) {
+ p = &(*p)->rb_left;
+ } else if (btf_id > node->id) {
+ p = &(*p)->rb_right;
+ } else {
+ pr_debug("duplicated btf %u\n", btf_id);
+ goto out;
+ }
+ }
+
+ rb_link_node(&btf_node->rb_node, parent, p);
+ rb_insert_color(&btf_node->rb_node, &env->bpf_progs.btfs);
+out:
+ up_write(&env->bpf_progs.lock);
+}
+
+struct btf_node *perf_env__find_btf(struct perf_env *env, __u32 btf_id)
+{
+ struct btf_node *node = NULL;
+ struct rb_node *n;
+
+ down_read(&env->bpf_progs.lock);
+ n = env->bpf_progs.btfs.rb_node;
+
+ while (n) {
+ node = rb_entry(n, struct btf_node, rb_node);
+ if (btf_id < node->id)
+ n = n->rb_left;
+ else if (btf_id > node->id)
+ n = n->rb_right;
+ else
+ break;
+ }
+
+ up_read(&env->bpf_progs.lock);
+ return node;
+}
+
/* purge data in bpf_prog_infos tree */
static void perf_env__purge_bpf(struct perf_env *env)
{
@@ -82,6 +133,19 @@ static void perf_env__purge_bpf(struct perf_env *env)
rb_erase_init(&node->rb_node, root);
free(node);
}
+
+ root = &env->bpf_progs.btfs;
+ next = rb_first(root);
+
+ while (next) {
+ struct btf_node *node;
+
+ node = rb_entry(next, struct btf_node, rb_node);
+ next = rb_next(&node->rb_node);
+ rb_erase_init(&node->rb_node, root);
+ free(node);
+ }
+
up_write(&env->bpf_progs.lock);
}
@@ -119,6 +183,7 @@ void perf_env__exit(struct perf_env *env)
static void init_bpf_rb_trees(struct perf_env *env)
{
env->bpf_progs.prog_infos = RB_ROOT;
+ env->bpf_progs.btfs = RB_ROOT;
init_rwsem(&env->bpf_progs.lock);
}
diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h
index 33ef4b2d2a29..b7fc4c71ba6a 100644
--- a/tools/perf/util/env.h
+++ b/tools/perf/util/env.h
@@ -74,10 +74,12 @@ struct perf_env {
struct {
struct rw_semaphore lock;
struct rb_root prog_infos;
+ struct rb_root btfs;
} bpf_progs;
};
struct bpf_prog_info_node;
+struct btf_node;
extern struct perf_env perf_env;
@@ -97,4 +99,6 @@ void perf_env__insert_bpf_prog_info(struct perf_env *env,
struct bpf_prog_info_node *info_node);
struct bpf_prog_info_node *perf_env__find_bpf_prog_info(struct perf_env *env,
__u32 prog_id);
+void perf_env__insert_btf(struct perf_env *env, struct btf_node *btf_node);
+struct btf_node *perf_env__find_btf(struct perf_env *env, __u32 btf_id);
#endif /* __PERF_ENV_H */
--
2.17.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v4 perf,bpf 09/15] perf, bpf: save btf information as headers to perf.data
2019-02-26 0:20 [PATCH v4 perf,bpf 00/15] perf annotation of BPF programs Song Liu
` (7 preceding siblings ...)
2019-02-26 0:20 ` [PATCH v4 perf,bpf 08/15] perf, bpf: save btf in a rbtree in perf_env Song Liu
@ 2019-02-26 0:20 ` Song Liu
2019-02-26 0:20 ` [PATCH v4 perf,bpf 10/15] perf-top: add option --no-bpf-event Song Liu
` (5 subsequent siblings)
14 siblings, 0 replies; 36+ messages in thread
From: Song Liu @ 2019-02-26 0:20 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: ast, daniel, kernel-team, peterz, acme, jolsa, namhyung, Song Liu
This patch enables perf-record to save btf information as headers to
perf.data A new header type HEADER_BPF_BTF is introduced for this data.
Signed-off-by: Song Liu <songliubraving@fb.com>
---
| 108 ++++++++++++++++++++++++++++++++++++++-
| 1 +
2 files changed, 108 insertions(+), 1 deletion(-)
--git a/tools/perf/util/header.c b/tools/perf/util/header.c
index c89dce7942d1..ce5402101468 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1127,6 +1127,45 @@ static int write_bpf_prog_info(struct feat_fd *ff,
return ret;
}
+static int write_bpf_btf(struct feat_fd *ff,
+ struct perf_evlist *evlist __maybe_unused)
+{
+ struct perf_env *env = &ff->ph->env;
+ struct rb_root *root;
+ struct rb_node *next;
+ u32 count = 0;
+ int ret;
+
+ down_read(&env->bpf_progs.lock);
+
+ root = &env->bpf_progs.btfs;
+ next = rb_first(root);
+ while (next) {
+ ++count;
+ next = rb_next(next);
+ }
+
+ ret = do_write(ff, &count, sizeof(count));
+
+ if (ret < 0)
+ goto out;
+
+ next = rb_first(root);
+ while (next) {
+ struct btf_node *node;
+
+ node = rb_entry(next, struct btf_node, rb_node);
+ next = rb_next(&node->rb_node);
+ ret = do_write(ff, &node->id,
+ sizeof(u32) * 2 + node->data_size);
+ if (ret < 0)
+ goto out;
+ }
+out:
+ up_read(&env->bpf_progs.lock);
+ return ret;
+}
+
static int cpu_cache_level__sort(const void *a, const void *b)
{
struct cpu_cache_level *cache_a = (struct cpu_cache_level *)a;
@@ -1630,6 +1669,28 @@ static void print_bpf_prog_info(struct feat_fd *ff, FILE *fp)
up_read(&env->bpf_progs.lock);
}
+static void print_bpf_btf(struct feat_fd *ff, FILE *fp)
+{
+ struct perf_env *env = &ff->ph->env;
+ struct rb_root *root;
+ struct rb_node *next;
+
+ down_read(&env->bpf_progs.lock);
+
+ root = &env->bpf_progs.btfs;
+ next = rb_first(root);
+
+ while (next) {
+ struct btf_node *node;
+
+ node = rb_entry(next, struct btf_node, rb_node);
+ next = rb_next(&node->rb_node);
+ fprintf(fp, "# btf info of id %u\n", node->id);
+ }
+
+ up_read(&env->bpf_progs.lock);
+}
+
static void free_event_desc(struct perf_evsel *events)
{
struct perf_evsel *evsel;
@@ -2732,6 +2793,50 @@ static int process_bpf_prog_info(struct feat_fd *ff,
return err;
}
+static int process_bpf_btf(struct feat_fd *ff, void *data __maybe_unused)
+{
+ struct perf_env *env = &ff->ph->env;
+ u32 count, i;
+
+ if (do_read_u32(ff, &count))
+ return -1;
+
+ down_write(&env->bpf_progs.lock);
+
+ for (i = 0; i < count; ++i) {
+ struct btf_node *node;
+ u32 id, data_size;
+
+ if (do_read_u32(ff, &id))
+ return -1;
+ if (do_read_u32(ff, &data_size))
+ return -1;
+
+ node = malloc(sizeof(struct btf_node) + data_size);
+ if (!node)
+ return -1;
+
+ node->id = id;
+ node->data_size = data_size;
+
+ if (__do_read(ff, node->data, data_size)) {
+ free(node);
+ return -1;
+ }
+
+ /* endian mismatch, drop the btf, continue */
+ if (ff->ph->needs_swap) {
+ free(node);
+ continue;
+ }
+
+ perf_env__insert_btf(env, node);
+ }
+
+ up_write(&env->bpf_progs.lock);
+ return 0;
+}
+
struct feature_ops {
int (*write)(struct feat_fd *ff, struct perf_evlist *evlist);
void (*print)(struct feat_fd *ff, FILE *fp);
@@ -2792,7 +2897,8 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPR(SAMPLE_TIME, sample_time, false),
FEAT_OPR(MEM_TOPOLOGY, mem_topology, true),
FEAT_OPR(CLOCKID, clockid, false),
- FEAT_OPR(BPF_PROG_INFO, bpf_prog_info, false)
+ FEAT_OPR(BPF_PROG_INFO, bpf_prog_info, false),
+ FEAT_OPR(BPF_BTF, bpf_btf, false)
};
struct header_print_data {
--git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 0785c91b4c3a..9e7d931f7c0d 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -40,6 +40,7 @@ enum {
HEADER_MEM_TOPOLOGY,
HEADER_CLOCKID,
HEADER_BPF_PROG_INFO,
+ HEADER_BPF_BTF,
HEADER_LAST_FEATURE,
HEADER_FEAT_BITS = 256,
};
--
2.17.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v4 perf,bpf 10/15] perf-top: add option --no-bpf-event
2019-02-26 0:20 [PATCH v4 perf,bpf 00/15] perf annotation of BPF programs Song Liu
` (8 preceding siblings ...)
2019-02-26 0:20 ` [PATCH v4 perf,bpf 09/15] perf, bpf: save btf information as headers to perf.data Song Liu
@ 2019-02-26 0:20 ` Song Liu
2019-02-26 0:20 ` [PATCH v4 perf,bpf 11/15] perf: add -lopcodes to feature-libbfd Song Liu
` (4 subsequent siblings)
14 siblings, 0 replies; 36+ messages in thread
From: Song Liu @ 2019-02-26 0:20 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: ast, daniel, kernel-team, peterz, acme, jolsa, namhyung, Song Liu
bpf events should be tracked by default for perf-top. This patch makes it
on by default, and adds option to disable bpf events.
Signed-off-by: Song Liu <songliubraving@fb.com>
---
tools/perf/builtin-top.c | 3 +++
tools/perf/util/top.h | 1 +
2 files changed, 4 insertions(+)
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 27d8d42e0a4d..ccdf5689452f 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -1492,6 +1492,7 @@ int cmd_top(int argc, const char **argv)
"Display raw encoding of assembly instructions (default)"),
OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
"Enable kernel symbol demangling"),
+ OPT_BOOLEAN(0, "no-bpf-event", &top.no_bpf_event, "do not record bpf events"),
OPT_STRING(0, "objdump", &top.annotation_opts.objdump_path, "path",
"objdump binary to use for disassembly and annotations"),
OPT_STRING('M', "disassembler-style", &top.annotation_opts.disassembler_style, "disassembler style",
@@ -1651,6 +1652,8 @@ int cmd_top(int argc, const char **argv)
signal(SIGWINCH, winch_sig);
}
+ top.record_opts.bpf_event = !top.no_bpf_event;
+
status = __cmd_top(&top);
out_delete_evlist:
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h
index 19f95eaf75c8..862a37bd27ea 100644
--- a/tools/perf/util/top.h
+++ b/tools/perf/util/top.h
@@ -32,6 +32,7 @@ struct perf_top {
bool use_tui, use_stdio;
bool vmlinux_warned;
bool dump_symtab;
+ bool no_bpf_event;
struct hist_entry *sym_filter_entry;
struct perf_evsel *sym_evsel;
struct perf_session *session;
--
2.17.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v4 perf,bpf 11/15] perf: add -lopcodes to feature-libbfd
2019-02-26 0:20 [PATCH v4 perf,bpf 00/15] perf annotation of BPF programs Song Liu
` (9 preceding siblings ...)
2019-02-26 0:20 ` [PATCH v4 perf,bpf 10/15] perf-top: add option --no-bpf-event Song Liu
@ 2019-02-26 0:20 ` Song Liu
2019-02-26 0:20 ` [PATCH v4 perf,bpf 12/15] perf, bpf: enable annotation of bpf program Song Liu
` (3 subsequent siblings)
14 siblings, 0 replies; 36+ messages in thread
From: Song Liu @ 2019-02-26 0:20 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: ast, daniel, kernel-team, peterz, acme, jolsa, namhyung, Song Liu
Both libbfd and libopcodes are distributed with binutil-dev/devel. When
libbfd presents, it is OK to assume libopcodes also presents. This has
been a safe assumption for bpftool.
This patch adds -lopcodes to perf/Makefile.config. libopcodes will be
used in the next commit for bpf annotation.
Signed-off-by: Song Liu <songliubraving@fb.com>
---
tools/perf/Makefile.config | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index b441c88cafa1..e0bafbc273af 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -701,7 +701,7 @@ else
endif
ifeq ($(feature-libbfd), 1)
- EXTLIBS += -lbfd
+ EXTLIBS += -lbfd -lopcodes
else
# we are on a system that requires -liberty and (maybe) -lz
# to link against -lbfd; test each case individually here
--
2.17.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v4 perf,bpf 12/15] perf, bpf: enable annotation of bpf program
2019-02-26 0:20 [PATCH v4 perf,bpf 00/15] perf annotation of BPF programs Song Liu
` (10 preceding siblings ...)
2019-02-26 0:20 ` [PATCH v4 perf,bpf 11/15] perf: add -lopcodes to feature-libbfd Song Liu
@ 2019-02-26 0:20 ` Song Liu
2019-02-27 13:21 ` Jiri Olsa
2019-02-27 13:22 ` Jiri Olsa
2019-02-26 0:20 ` [PATCH v4 perf,bpf 13/15] perf, bpf: process PERF_BPF_EVENT_PROG_LOAD for annotation Song Liu
` (2 subsequent siblings)
14 siblings, 2 replies; 36+ messages in thread
From: Song Liu @ 2019-02-26 0:20 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: ast, daniel, kernel-team, peterz, acme, jolsa, namhyung, Song Liu
This patch enables the annotation of bpf program.
A new dso type DSO_BINARY_TYPE__BPF_PROG_INFO is introduced to for BPF
programs. In symbol__disassemble(), DSO_BINARY_TYPE__BPF_PROG_INFO dso
calls into a new function symbol__disassemble_bpf(), where annotation
line information is filled based bpf_prog_info and btf saved in given
perf_env.
symbol__disassemble_bpf() uses libbfd to disassemble bpf programs.
Signed-off-by: Song Liu <songliubraving@fb.com>
---
tools/build/Makefile.feature | 6 +-
tools/perf/Makefile.config | 4 +
tools/perf/util/annotate.c | 149 ++++++++++++++++++++++++++++++++++-
tools/perf/util/dso.c | 1 +
tools/perf/util/dso.h | 33 +++++---
| 7 +-
tools/perf/util/symbol.c | 1 +
tools/perf/util/util.c | 10 +++
tools/perf/util/util.h | 2 +
9 files changed, 193 insertions(+), 20 deletions(-)
diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 5467c6bf9ceb..4f35e9ff1e00 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -71,7 +71,8 @@ FEATURE_TESTS_BASIC := \
sdt \
setns \
libopencsd \
- libaio
+ libaio \
+ disassembler-four-args
# FEATURE_TESTS_BASIC + FEATURE_TESTS_EXTRA is the complete list
# of all feature tests
@@ -118,7 +119,8 @@ FEATURE_DISPLAY ?= \
lzma \
get_cpuid \
bpf \
- libaio
+ libaio \
+ disassembler-four-args
# Set FEATURE_CHECK_(C|LD)FLAGS-all for all FEATURE_TESTS features.
# If in the future we need per-feature checks/flags for features not
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index e0bafbc273af..ab223239f1fb 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -796,6 +796,10 @@ ifdef HAVE_KVM_STAT_SUPPORT
CFLAGS += -DHAVE_KVM_STAT_SUPPORT
endif
+ifeq ($(feature-disassembler-four-args), 1)
+ CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE
+endif
+
ifeq (${IS_64_BIT}, 1)
ifndef NO_PERF_READ_VDSO32
$(call feature_check,compile-32)
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 70de8f6b3aee..716fdb07f45e 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -9,6 +9,10 @@
#include <errno.h>
#include <inttypes.h>
+#include <bpf/bpf.h>
+#include <bpf/btf.h>
+#include <bpf/libbpf.h>
+#include <linux/btf.h>
#include "util.h"
#include "ui/ui.h"
#include "sort.h"
@@ -22,6 +26,7 @@
#include "annotate.h"
#include "evsel.h"
#include "evlist.h"
+#include "bpf-event.h"
#include "block-range.h"
#include "string2.h"
#include "arch/common.h"
@@ -29,6 +34,9 @@
#include <pthread.h>
#include <linux/bitops.h>
#include <linux/kernel.h>
+#include <bfd.h>
+#include <dis-asm.h>
+#include <bpf/libbpf.h>
/* FIXME: For the HE_COLORSET */
#include "ui/browser.h"
@@ -1672,6 +1680,143 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
return 0;
}
+static int symbol__disassemble_bpf(struct symbol *sym,
+ struct annotate_args *args)
+{
+ struct annotation *notes = symbol__annotation(sym);
+ struct annotation_options *opts = args->options;
+ struct bpf_prog_info_linear *info_linear;
+ struct bpf_prog_linfo *prog_linfo = NULL;
+ struct bpf_prog_info_node *info_node;
+ int len = sym->end - sym->start;
+ disassembler_ftype disassemble;
+ struct map *map = args->ms.map;
+ struct disassemble_info info;
+ struct dso *dso = map->dso;
+ int pc = 0, count, sub_id;
+ struct btf *btf = NULL;
+ char tpath[PATH_MAX];
+ size_t buf_size;
+ int nr_skip = 0;
+ int ret = -1;
+ char *buf;
+ bfd *bfdf;
+ FILE *s;
+
+ if (dso->binary_type != DSO_BINARY_TYPE__BPF_PROG_INFO)
+ return -1;
+
+ pr_debug("%s: handling sym %s addr %lx len %lx\n", __func__,
+ sym->name, sym->start, sym->end - sym->start);
+
+ memset(tpath, 0, sizeof(tpath));
+ get_exec_path(tpath, sizeof(tpath));
+
+ bfdf = bfd_openr(tpath, NULL);
+ assert(bfdf);
+ assert(bfd_check_format(bfdf, bfd_object));
+
+ s = open_memstream(&buf, &buf_size);
+ if (!s)
+ goto out;
+ init_disassemble_info(&info, s,
+ (fprintf_ftype) fprintf);
+
+ info.arch = bfd_get_arch(bfdf);
+ info.mach = bfd_get_mach(bfdf);
+
+ info_node = perf_env__find_bpf_prog_info(dso->bpf_prog.env,
+ dso->bpf_prog.id);
+ if (!info_node)
+ goto out;
+ info_linear = info_node->info_linear;
+ sub_id = dso->bpf_prog.sub_id;
+
+ info.buffer = (void *)(info_linear->info.jited_prog_insns);
+ info.buffer_length = info_linear->info.jited_prog_len;
+
+ if (info_linear->info.nr_line_info)
+ prog_linfo = bpf_prog_linfo__new(&info_linear->info);
+
+ if (info_linear->info.btf_id) {
+ struct btf_node *node;
+
+ node = perf_env__find_btf(dso->bpf_prog.env,
+ info_linear->info.btf_id);
+ if (node)
+ btf = btf__new((__u8 *)(node->data),
+ node->data_size);
+ }
+
+ disassemble_init_for_target(&info);
+
+#ifdef DISASM_FOUR_ARGS_SIGNATURE
+ disassemble = disassembler(info.arch,
+ bfd_big_endian(bfdf),
+ info.mach,
+ bfdf);
+#else
+ disassemble = disassembler(bfdf);
+#endif
+ assert(disassemble);
+
+ fflush(s);
+ do {
+ const struct bpf_line_info *linfo = NULL;
+ struct disasm_line *dl;
+ size_t prev_buf_size;
+ const char *srcline;
+ u64 addr;
+
+ addr = pc + ((u64 *)(info_linear->info.jited_ksyms))[sub_id];
+ count = disassemble(pc, &info);
+
+ if (prog_linfo)
+ linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo,
+ addr, sub_id,
+ nr_skip);
+
+ if (linfo && btf) {
+ srcline = btf__name_by_offset(btf, linfo->line_off);
+ nr_skip++;
+ } else
+ srcline = NULL;
+
+ fprintf(s, "\n");
+ prev_buf_size = buf_size;
+ fflush(s);
+
+ if (!opts->hide_src_code && srcline) {
+ args->offset = -1;
+ args->line = strdup(srcline);
+ args->line_nr = 0;
+ args->ms.sym = sym;
+ dl = disasm_line__new(args);
+ if (dl)
+ annotation_line__add(&dl->al,
+ ¬es->src->source);
+ }
+
+ args->offset = pc;
+ args->line = buf + prev_buf_size;
+ args->line_nr = 0;
+ args->ms.sym = sym;
+ dl = disasm_line__new(args);
+ if (dl)
+ annotation_line__add(&dl->al, ¬es->src->source);
+
+ pc += count;
+ } while (count > 0 && pc < len);
+
+ ret = 0;
+out:
+ free(prog_linfo);
+ free(btf);
+ fclose(s);
+ bfd_close(bfdf);
+ return ret;
+}
+
static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
{
struct annotation_options *opts = args->options;
@@ -1699,7 +1844,9 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args)
pr_debug("annotating [%p] %30s : [%p] %30s\n",
dso, dso->long_name, sym, sym->name);
- if (dso__is_kcore(dso)) {
+ if (dso->binary_type == DSO_BINARY_TYPE__BPF_PROG_INFO) {
+ return symbol__disassemble_bpf(sym, args);
+ } else if (dso__is_kcore(dso)) {
kce.kcore_filename = symfs_filename;
kce.addr = map__rip_2objdump(map, sym->start);
kce.offs = sym->start;
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 62c8cf622607..1798192bf0f9 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -181,6 +181,7 @@ int dso__read_binary_type_filename(const struct dso *dso,
case DSO_BINARY_TYPE__KALLSYMS:
case DSO_BINARY_TYPE__GUEST_KALLSYMS:
case DSO_BINARY_TYPE__JAVA_JIT:
+ case DSO_BINARY_TYPE__BPF_PROG_INFO:
case DSO_BINARY_TYPE__NOT_FOUND:
ret = -1;
break;
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 8c8a7abe809d..f20d319463f1 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -14,6 +14,8 @@
#include "namespaces.h"
#include "build-id.h"
+struct perf_env;
+
enum dso_binary_type {
DSO_BINARY_TYPE__KALLSYMS = 0,
DSO_BINARY_TYPE__GUEST_KALLSYMS,
@@ -34,6 +36,7 @@ enum dso_binary_type {
DSO_BINARY_TYPE__KCORE,
DSO_BINARY_TYPE__GUEST_KCORE,
DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
+ DSO_BINARY_TYPE__BPF_PROG_INFO,
DSO_BINARY_TYPE__NOT_FOUND,
};
@@ -177,17 +180,25 @@ struct dso {
struct auxtrace_cache *auxtrace_cache;
int comp;
- /* dso data file */
- struct {
- struct rb_root cache;
- int fd;
- int status;
- u32 status_seen;
- size_t file_size;
- struct list_head open_entry;
- u64 debug_frame_offset;
- u64 eh_frame_hdr_offset;
- } data;
+ union {
+ /* dso data file */
+ struct {
+ struct rb_root cache;
+ int fd;
+ int status;
+ u32 status_seen;
+ size_t file_size;
+ struct list_head open_entry;
+ u64 debug_frame_offset;
+ u64 eh_frame_hdr_offset;
+ } data;
+ /* bpf prog information */
+ struct {
+ u32 id;
+ u32 sub_id;
+ struct perf_env *env;
+ } bpf_prog;
+ };
union { /* Tool specific area */
void *priv;
--git a/tools/perf/util/header.c b/tools/perf/util/header.c
index ce5402101468..c39e107ac5b8 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -533,12 +533,7 @@ static int write_cmdline(struct feat_fd *ff,
int i, ret;
/* actual path to perf binary */
- ret = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
- if (ret <= 0)
- return -1;
-
- /* readlink() does not add null termination */
- buf[ret] = '\0';
+ get_exec_path(buf, sizeof(buf));
/* account for binary path */
n = perf_env.nr_cmdline + 1;
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 48efad6d0f90..33ae59e89da2 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1441,6 +1441,7 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,
case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
return true;
+ case DSO_BINARY_TYPE__BPF_PROG_INFO:
case DSO_BINARY_TYPE__NOT_FOUND:
default:
return false;
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index 093352e93d50..0bc9ec69f38a 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -506,3 +506,13 @@ const char *perf_tip(const char *dirpath)
return tip;
}
+
+void get_exec_path(char *tpath, size_t size)
+{
+ const char *path = "/proc/self/exe";
+ ssize_t len;
+
+ len = readlink(path, tpath, size - 1);
+ assert(len > 0);
+ tpath[len] = 0;
+}
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index ece040b799f6..8d0a88eb8685 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -76,6 +76,8 @@ extern bool perf_singlethreaded;
void perf_set_singlethreaded(void);
void perf_set_multithreaded(void);
+void get_exec_path(char *tpath, size_t size);
+
#ifndef O_CLOEXEC
#ifdef __sparc__
#define O_CLOEXEC 0x400000
--
2.17.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 12/15] perf, bpf: enable annotation of bpf program
2019-02-26 0:20 ` [PATCH v4 perf,bpf 12/15] perf, bpf: enable annotation of bpf program Song Liu
@ 2019-02-27 13:21 ` Jiri Olsa
2019-02-27 13:22 ` Jiri Olsa
1 sibling, 0 replies; 36+ messages in thread
From: Jiri Olsa @ 2019-02-27 13:21 UTC (permalink / raw)
To: Song Liu
Cc: netdev, linux-kernel, ast, daniel, kernel-team, peterz, acme,
jolsa, namhyung
On Mon, Feb 25, 2019 at 04:20:16PM -0800, Song Liu wrote:
SNIP
> diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
> index 093352e93d50..0bc9ec69f38a 100644
> --- a/tools/perf/util/util.c
> +++ b/tools/perf/util/util.c
> @@ -506,3 +506,13 @@ const char *perf_tip(const char *dirpath)
>
> return tip;
> }
> +
> +void get_exec_path(char *tpath, size_t size)
> +{
> + const char *path = "/proc/self/exe";
> + ssize_t len;
> +
> + len = readlink(path, tpath, size - 1);
> + assert(len > 0);
> + tpath[len] = 0;
> +}
we have now helper for that, please rebase this on top of
arnaldo's perf/core:
94816add0005 perf tools: Add perf_exe() helper to find perf binary
jirka
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 12/15] perf, bpf: enable annotation of bpf program
2019-02-26 0:20 ` [PATCH v4 perf,bpf 12/15] perf, bpf: enable annotation of bpf program Song Liu
2019-02-27 13:21 ` Jiri Olsa
@ 2019-02-27 13:22 ` Jiri Olsa
1 sibling, 0 replies; 36+ messages in thread
From: Jiri Olsa @ 2019-02-27 13:22 UTC (permalink / raw)
To: Song Liu
Cc: netdev, linux-kernel, ast, daniel, kernel-team, peterz, acme,
jolsa, namhyung
On Mon, Feb 25, 2019 at 04:20:16PM -0800, Song Liu wrote:
SNIP
> + if (!opts->hide_src_code && srcline) {
> + args->offset = -1;
> + args->line = strdup(srcline);
> + args->line_nr = 0;
> + args->ms.sym = sym;
> + dl = disasm_line__new(args);
> + if (dl)
> + annotation_line__add(&dl->al,
> + ¬es->src->source);
please use { } around multiline if leg
jirka
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v4 perf,bpf 13/15] perf, bpf: process PERF_BPF_EVENT_PROG_LOAD for annotation
2019-02-26 0:20 [PATCH v4 perf,bpf 00/15] perf annotation of BPF programs Song Liu
` (11 preceding siblings ...)
2019-02-26 0:20 ` [PATCH v4 perf,bpf 12/15] perf, bpf: enable annotation of bpf program Song Liu
@ 2019-02-26 0:20 ` Song Liu
2019-02-26 0:20 ` [PATCH v4 perf,bpf 14/15] perf: introduce side band thread Song Liu
2019-02-26 0:20 ` [PATCH v4 perf,bpf 15/15] perf, bpf: save information about short living bpf programs Song Liu
14 siblings, 0 replies; 36+ messages in thread
From: Song Liu @ 2019-02-26 0:20 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: ast, daniel, kernel-team, peterz, acme, jolsa, namhyung, Song Liu
This patch adds processing of PERF_BPF_EVENT_PROG_LOAD, which sets proper
DSO type/id/etc of memory regions mapped to BPF programs to
DSO_BINARY_TYPE__BPF_PROG_INFO
Signed-off-by: Song Liu <songliubraving@fb.com>
---
tools/perf/util/bpf-event.c | 53 +++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c
index 370b830f2433..048ef00371ad 100644
--- a/tools/perf/util/bpf-event.c
+++ b/tools/perf/util/bpf-event.c
@@ -25,12 +25,65 @@ static int snprintf_hex(char *buf, size_t size, unsigned char *data, size_t len)
return ret;
}
+static int machine__process_bpf_event_load(struct machine *machine __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample __maybe_unused)
+{
+ struct bpf_prog_info_linear *info_linear;
+ struct bpf_prog_info_node *info_node;
+ struct perf_env *env = machine->env;
+ int id = event->bpf_event.id;
+ unsigned int i;
+
+ /* perf-record, no need to handle bpf-event */
+ if (env == NULL)
+ return 0;
+
+ info_node = perf_env__find_bpf_prog_info(env, id);
+ if (!info_node)
+ return 0;
+ info_linear = info_node->info_linear;
+
+ for (i = 0; i < info_linear->info.nr_jited_ksyms; i++) {
+ u64 *addrs = (u64 *)(info_linear->info.jited_ksyms);
+ u64 addr = addrs[i];
+ struct map *map;
+
+ map = map_groups__find(&machine->kmaps, addr);
+
+ if (map) {
+ map->dso->binary_type = DSO_BINARY_TYPE__BPF_PROG_INFO;
+ map->dso->bpf_prog.id = id;
+ map->dso->bpf_prog.sub_id = i;
+ map->dso->bpf_prog.env = env;
+ }
+ }
+ return 0;
+}
+
int machine__process_bpf_event(struct machine *machine __maybe_unused,
union perf_event *event,
struct perf_sample *sample __maybe_unused)
{
if (dump_trace)
perf_event__fprintf_bpf_event(event, stdout);
+
+ switch (event->bpf_event.type) {
+ case PERF_BPF_EVENT_PROG_LOAD:
+ return machine__process_bpf_event_load(machine, event, sample);
+
+ 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;
}
--
2.17.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [PATCH v4 perf,bpf 14/15] perf: introduce side band thread
2019-02-26 0:20 [PATCH v4 perf,bpf 00/15] perf annotation of BPF programs Song Liu
` (12 preceding siblings ...)
2019-02-26 0:20 ` [PATCH v4 perf,bpf 13/15] perf, bpf: process PERF_BPF_EVENT_PROG_LOAD for annotation Song Liu
@ 2019-02-26 0:20 ` Song Liu
2019-02-27 13:21 ` Jiri Olsa
2019-02-26 0:20 ` [PATCH v4 perf,bpf 15/15] perf, bpf: save information about short living bpf programs Song Liu
14 siblings, 1 reply; 36+ messages in thread
From: Song Liu @ 2019-02-26 0:20 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: ast, daniel, kernel-team, peterz, acme, jolsa, namhyung, Song Liu
This patch introduces side band thread that captures extended information
for events like PERF_RECORD_BPF_EVENT.
This new thread uses its own evlist that uses ring buffer with very low
watermark for lower latency. In the next patch, we uses this thread to
handle PERF_RECORD_BPF_EVENT.
Signed-off-by: Song Liu <songliubraving@fb.com>
---
tools/perf/builtin-record.c | 7 +++
tools/perf/builtin-top.c | 7 +++
tools/perf/util/evlist.c | 100 ++++++++++++++++++++++++++++++++++++
tools/perf/util/evlist.h | 13 +++++
4 files changed, 127 insertions(+)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 2355e0a9eda0..d10c1d5a9e89 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1106,6 +1106,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
struct perf_data *data = &rec->data;
struct perf_session *session;
bool disabled = false, draining = false;
+ struct perf_evlist_sb_poll_args poll_args;
int fd;
atexit(record__sig_exit);
@@ -1206,6 +1207,10 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
goto out_child;
}
+ poll_args.env = &session->header.env;
+ poll_args.done = &done;
+ perf_evlist__start_polling_thread(&rec->opts.target, &poll_args);
+
err = record__synthesize(rec, false);
if (err < 0)
goto out_child;
@@ -1456,6 +1461,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
out_delete_session:
perf_session__delete(session);
+
+ perf_evlist__stop_polling_thread();
return status;
}
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index ccdf5689452f..f41545445917 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -1524,6 +1524,7 @@ int cmd_top(int argc, const char **argv)
"number of thread to run event synthesize"),
OPT_END()
};
+ struct perf_evlist_sb_poll_args poll_args;
const char * const top_usage[] = {
"perf top [<options>]",
NULL
@@ -1654,8 +1655,14 @@ int cmd_top(int argc, const char **argv)
top.record_opts.bpf_event = !top.no_bpf_event;
+ poll_args.env = &perf_env;
+ poll_args.done = &done;
+ perf_evlist__start_polling_thread(target, &poll_args);
+
status = __cmd_top(&top);
+ perf_evlist__stop_polling_thread();
+
out_delete_evlist:
perf_evlist__delete(top.evlist);
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 8c902276d4b4..61b87c8111e6 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -19,6 +19,7 @@
#include "debug.h"
#include "units.h"
#include "asm/bug.h"
+#include "bpf-event.h"
#include <signal.h>
#include <unistd.h>
@@ -1841,3 +1842,102 @@ struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list,
}
return leader;
}
+
+static struct perf_evlist *sb_evlist;
+pthread_t poll_thread;
+
+int perf_evlist__new_side_band_event(struct perf_event_attr *attr)
+{
+ struct perf_evsel *evsel;
+
+ if (!sb_evlist)
+ sb_evlist = perf_evlist__new();
+
+ if (!sb_evlist)
+ return -1;
+
+ evsel = perf_evsel__new_idx(attr, sb_evlist->nr_entries);
+ if (!evsel)
+ goto out_err;
+
+ perf_evlist__add(sb_evlist, evsel);
+ return 0;
+
+out_err:
+ perf_evlist__delete(sb_evlist);
+ return -1;
+}
+
+static void *perf_evlist__poll_thread(void *arg)
+{
+ struct perf_evlist_sb_poll_args *args = arg;
+ int i;
+
+ while (!*(args->done)) {
+ perf_evlist__poll(sb_evlist, 1000);
+
+ for (i = 0; i < sb_evlist->nr_mmaps; i++) {
+ struct perf_mmap *map = &sb_evlist->mmap[i];
+ union perf_event *event;
+
+ if (perf_mmap__read_init(map))
+ continue;
+ while ((event = perf_mmap__read_event(map)) != NULL) {
+ pr_debug("processing vip event of type %d\n",
+ event->header.type);
+ switch (event->header.type) {
+ default:
+ break;
+ }
+ perf_mmap__consume(map);
+ }
+ perf_mmap__read_done(map);
+ }
+ }
+ return NULL;
+}
+
+int perf_evlist__start_polling_thread(struct target *target,
+ struct perf_evlist_sb_poll_args *args)
+{
+ struct perf_evsel *counter;
+
+ if (sb_evlist == NULL)
+ return 0;
+
+ if (perf_evlist__create_maps(sb_evlist, target))
+ goto out_delete_evlist;
+
+ evlist__for_each_entry(sb_evlist, counter) {
+ if (perf_evsel__open(counter, sb_evlist->cpus,
+ sb_evlist->threads) < 0)
+ goto out_delete_evlist;
+ }
+
+ if (perf_evlist__mmap(sb_evlist, UINT_MAX))
+ goto out_delete_evlist;
+
+ evlist__for_each_entry(sb_evlist, counter) {
+ if (perf_evsel__enable(counter))
+ goto out_delete_evlist;
+ }
+
+ if (pthread_create(&poll_thread, NULL, perf_evlist__poll_thread, args))
+ goto out_delete_evlist;
+
+ return 0;
+
+out_delete_evlist:
+ perf_evlist__delete(sb_evlist);
+ sb_evlist = NULL;
+ return -1;
+}
+
+void perf_evlist__stop_polling_thread(void)
+{
+ if (!sb_evlist)
+ return;
+ pthread_join(poll_thread, NULL);
+ perf_evlist__exit(sb_evlist);
+ sb_evlist = NULL;
+}
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 868294491194..4182e50659e0 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -58,6 +58,12 @@ struct perf_evsel_str_handler {
void *handler;
};
+struct perf_evlist_sb_poll_args {
+ struct perf_env *env;
+
+ volatile int *done;
+};
+
struct perf_evlist *perf_evlist__new(void);
struct perf_evlist *perf_evlist__new_default(void);
struct perf_evlist *perf_evlist__new_dummy(void);
@@ -84,6 +90,13 @@ int __perf_evlist__add_default_attrs(struct perf_evlist *evlist,
int perf_evlist__add_dummy(struct perf_evlist *evlist);
+int perf_evlist__new_side_band_event(struct perf_event_attr *attr);
+
+int perf_evlist__start_polling_thread(struct target *target,
+ struct perf_evlist_sb_poll_args *args);
+
+void perf_evlist__stop_polling_thread(void);
+
int perf_evlist__add_newtp(struct perf_evlist *evlist,
const char *sys, const char *name, void *handler);
--
2.17.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 14/15] perf: introduce side band thread
2019-02-26 0:20 ` [PATCH v4 perf,bpf 14/15] perf: introduce side band thread Song Liu
@ 2019-02-27 13:21 ` Jiri Olsa
2019-02-27 17:52 ` Song Liu
2019-03-04 21:40 ` Song Liu
0 siblings, 2 replies; 36+ messages in thread
From: Jiri Olsa @ 2019-02-27 13:21 UTC (permalink / raw)
To: Song Liu
Cc: netdev, linux-kernel, ast, daniel, kernel-team, peterz, acme,
jolsa, namhyung
On Mon, Feb 25, 2019 at 04:20:18PM -0800, Song Liu wrote:
SNIP
> diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
> index 8c902276d4b4..61b87c8111e6 100644
> --- a/tools/perf/util/evlist.c
> +++ b/tools/perf/util/evlist.c
> @@ -19,6 +19,7 @@
> #include "debug.h"
> #include "units.h"
> #include "asm/bug.h"
> +#include "bpf-event.h"
> #include <signal.h>
> #include <unistd.h>
>
> @@ -1841,3 +1842,102 @@ struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list,
> }
> return leader;
> }
> +
> +static struct perf_evlist *sb_evlist;
> +pthread_t poll_thread;
so some of the things are static and some like poll_args
you alloced on the stack.. I dont like this interface,
could we come up with something generic? perhaps
encapsulated in perf_evlist, like:
struct perf_evlist {
...
struct {
pthread_t th;
int state;
} thread;
};
typedef int (perf_evlist__thread_cb_t)(perf_evlist, union perf_event *event,....)
perf_evlist__start_thread(perf_evlist, perf_evlist__thread_cb_t cb);
perf_evlist__stop_thread(perf_evlist);
jirka
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 14/15] perf: introduce side band thread
2019-02-27 13:21 ` Jiri Olsa
@ 2019-02-27 17:52 ` Song Liu
2019-03-04 13:52 ` Jiri Olsa
2019-03-04 21:40 ` Song Liu
1 sibling, 1 reply; 36+ messages in thread
From: Song Liu @ 2019-02-27 17:52 UTC (permalink / raw)
To: Jiri Olsa
Cc: Netdev, linux-kernel, ast, daniel, Kernel Team, peterz, acme,
jolsa, namhyung
> On Feb 27, 2019, at 5:21 AM, Jiri Olsa <jolsa@redhat.com> wrote:
>
> On Mon, Feb 25, 2019 at 04:20:18PM -0800, Song Liu wrote:
>
> SNIP
>
>> diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
>> index 8c902276d4b4..61b87c8111e6 100644
>> --- a/tools/perf/util/evlist.c
>> +++ b/tools/perf/util/evlist.c
>> @@ -19,6 +19,7 @@
>> #include "debug.h"
>> #include "units.h"
>> #include "asm/bug.h"
>> +#include "bpf-event.h"
>> #include <signal.h>
>> #include <unistd.h>
>>
>> @@ -1841,3 +1842,102 @@ struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list,
>> }
>> return leader;
>> }
>> +
>> +static struct perf_evlist *sb_evlist;
>> +pthread_t poll_thread;
>
> so some of the things are static and some like poll_args
> you alloced on the stack.. I dont like this interface,
> could we come up with something generic? perhaps
> encapsulated in perf_evlist, like:
I picked global sb_evlist and poll_thread because there
should be only one sb_evlist and one thread polling it.
There might be multiple evsel on the sb_evlist.
I am not sure I understand your suggestion..
> struct perf_evlist {
> ...
> struct {
> pthread_t th;
> int state;
> } thread;
This will not be used by the main perf_evlist, right?
> };
>
> typedef int (perf_evlist__thread_cb_t)(perf_evlist, union perf_event *event,....)
How do we use this callback? We need a way to specify which
events to poll (mmap, bpf_event, ksymbol, etc). How do we do
that with the callback function?
Thanks,
Song
> perf_evlist__start_thread(perf_evlist, perf_evlist__thread_cb_t cb);
> perf_evlist__stop_thread(perf_evlist);
>
>
> jirka
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 14/15] perf: introduce side band thread
2019-02-27 17:52 ` Song Liu
@ 2019-03-04 13:52 ` Jiri Olsa
2019-03-04 19:49 ` Song Liu
0 siblings, 1 reply; 36+ messages in thread
From: Jiri Olsa @ 2019-03-04 13:52 UTC (permalink / raw)
To: Song Liu
Cc: Netdev, linux-kernel, ast, daniel, Kernel Team, peterz, acme,
jolsa, namhyung
On Wed, Feb 27, 2019 at 05:52:52PM +0000, Song Liu wrote:
>
>
> > On Feb 27, 2019, at 5:21 AM, Jiri Olsa <jolsa@redhat.com> wrote:
> >
> > On Mon, Feb 25, 2019 at 04:20:18PM -0800, Song Liu wrote:
> >
> > SNIP
> >
> >> diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
> >> index 8c902276d4b4..61b87c8111e6 100644
> >> --- a/tools/perf/util/evlist.c
> >> +++ b/tools/perf/util/evlist.c
> >> @@ -19,6 +19,7 @@
> >> #include "debug.h"
> >> #include "units.h"
> >> #include "asm/bug.h"
> >> +#include "bpf-event.h"
> >> #include <signal.h>
> >> #include <unistd.h>
> >>
> >> @@ -1841,3 +1842,102 @@ struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list,
> >> }
> >> return leader;
> >> }
> >> +
> >> +static struct perf_evlist *sb_evlist;
> >> +pthread_t poll_thread;
> >
> > so some of the things are static and some like poll_args
> > you alloced on the stack.. I dont like this interface,
> > could we come up with something generic? perhaps
> > encapsulated in perf_evlist, like:
>
> I picked global sb_evlist and poll_thread because there
> should be only one sb_evlist and one thread polling it.
> There might be multiple evsel on the sb_evlist.
>
> I am not sure I understand your suggestion..
>
> > struct perf_evlist {
> > ...
> > struct {
> > pthread_t th;
> > int state;
> > } thread;
>
> This will not be used by the main perf_evlist, right?
no, just by the thread interface
>
> > };
> >
> > typedef int (perf_evlist__thread_cb_t)(perf_evlist, union perf_event *event,....)
>
> How do we use this callback? We need a way to specify which
> events to poll (mmap, bpf_event, ksymbol, etc). How do we do
> that with the callback function?
you provide your own callback
jirka
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 14/15] perf: introduce side band thread
2019-03-04 13:52 ` Jiri Olsa
@ 2019-03-04 19:49 ` Song Liu
2019-03-04 20:41 ` Jiri Olsa
0 siblings, 1 reply; 36+ messages in thread
From: Song Liu @ 2019-03-04 19:49 UTC (permalink / raw)
To: Jiri Olsa
Cc: Netdev, linux-kernel, ast, daniel, Kernel Team, peterz, acme,
jolsa, namhyung
> On Mar 4, 2019, at 5:52 AM, Jiri Olsa <jolsa@redhat.com> wrote:
>
> On Wed, Feb 27, 2019 at 05:52:52PM +0000, Song Liu wrote:
>>
>>
>>> On Feb 27, 2019, at 5:21 AM, Jiri Olsa <jolsa@redhat.com> wrote:
>>>
>>> On Mon, Feb 25, 2019 at 04:20:18PM -0800, Song Liu wrote:
>>>
>>> SNIP
>>>
>>>> diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
>>>> index 8c902276d4b4..61b87c8111e6 100644
>>>> --- a/tools/perf/util/evlist.c
>>>> +++ b/tools/perf/util/evlist.c
>>>> @@ -19,6 +19,7 @@
>>>> #include "debug.h"
>>>> #include "units.h"
>>>> #include "asm/bug.h"
>>>> +#include "bpf-event.h"
>>>> #include <signal.h>
>>>> #include <unistd.h>
>>>>
>>>> @@ -1841,3 +1842,102 @@ struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list,
>>>> }
>>>> return leader;
>>>> }
>>>> +
>>>> +static struct perf_evlist *sb_evlist;
>>>> +pthread_t poll_thread;
>>>
>>> so some of the things are static and some like poll_args
>>> you alloced on the stack.. I dont like this interface,
>>> could we come up with something generic? perhaps
>>> encapsulated in perf_evlist, like:
>>
>> I picked global sb_evlist and poll_thread because there
>> should be only one sb_evlist and one thread polling it.
>> There might be multiple evsel on the sb_evlist.
>>
>> I am not sure I understand your suggestion..
>>
>>> struct perf_evlist {
>>> ...
>>> struct {
>>> pthread_t th;
>>> int state;
>>> } thread;
>>
>> This will not be used by the main perf_evlist, right?
>
> no, just by the thread interface
What is "state" here?
>
>>
>>> };
>>>
>>> typedef int (perf_evlist__thread_cb_t)(perf_evlist, union perf_event *event,....)
>>
>> How do we use this callback? We need a way to specify which
>> events to poll (mmap, bpf_event, ksymbol, etc). How do we do
>> that with the callback function?
>
> you provide your own callback
I guess I get your point now. I can try that.
Thanks,
Song
> jirka
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 14/15] perf: introduce side band thread
2019-03-04 19:49 ` Song Liu
@ 2019-03-04 20:41 ` Jiri Olsa
2019-03-04 20:44 ` Song Liu
0 siblings, 1 reply; 36+ messages in thread
From: Jiri Olsa @ 2019-03-04 20:41 UTC (permalink / raw)
To: Song Liu
Cc: Netdev, linux-kernel, ast, daniel, Kernel Team, peterz, acme,
jolsa, namhyung
On Mon, Mar 04, 2019 at 07:49:06PM +0000, Song Liu wrote:
>
>
> > On Mar 4, 2019, at 5:52 AM, Jiri Olsa <jolsa@redhat.com> wrote:
> >
> > On Wed, Feb 27, 2019 at 05:52:52PM +0000, Song Liu wrote:
> >>
> >>
> >>> On Feb 27, 2019, at 5:21 AM, Jiri Olsa <jolsa@redhat.com> wrote:
> >>>
> >>> On Mon, Feb 25, 2019 at 04:20:18PM -0800, Song Liu wrote:
> >>>
> >>> SNIP
> >>>
> >>>> diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
> >>>> index 8c902276d4b4..61b87c8111e6 100644
> >>>> --- a/tools/perf/util/evlist.c
> >>>> +++ b/tools/perf/util/evlist.c
> >>>> @@ -19,6 +19,7 @@
> >>>> #include "debug.h"
> >>>> #include "units.h"
> >>>> #include "asm/bug.h"
> >>>> +#include "bpf-event.h"
> >>>> #include <signal.h>
> >>>> #include <unistd.h>
> >>>>
> >>>> @@ -1841,3 +1842,102 @@ struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list,
> >>>> }
> >>>> return leader;
> >>>> }
> >>>> +
> >>>> +static struct perf_evlist *sb_evlist;
> >>>> +pthread_t poll_thread;
> >>>
> >>> so some of the things are static and some like poll_args
> >>> you alloced on the stack.. I dont like this interface,
> >>> could we come up with something generic? perhaps
> >>> encapsulated in perf_evlist, like:
> >>
> >> I picked global sb_evlist and poll_thread because there
> >> should be only one sb_evlist and one thread polling it.
> >> There might be multiple evsel on the sb_evlist.
> >>
> >> I am not sure I understand your suggestion..
> >>
> >>> struct perf_evlist {
> >>> ...
> >>> struct {
> >>> pthread_t th;
> >>> int state;
> >>> } thread;
> >>
> >> This will not be used by the main perf_evlist, right?
> >
> > no, just by the thread interface
>
> What is "state" here?
it's replacement for the 'done' pointer u had,
which I did not like.. leting each thread having
its own state is better IMO
jirka
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 14/15] perf: introduce side band thread
2019-03-04 20:41 ` Jiri Olsa
@ 2019-03-04 20:44 ` Song Liu
0 siblings, 0 replies; 36+ messages in thread
From: Song Liu @ 2019-03-04 20:44 UTC (permalink / raw)
To: Jiri Olsa
Cc: Netdev, linux-kernel, ast, daniel, Kernel Team, peterz, acme,
jolsa, namhyung
> On Mar 4, 2019, at 12:41 PM, Jiri Olsa <jolsa@redhat.com> wrote:
>
> On Mon, Mar 04, 2019 at 07:49:06PM +0000, Song Liu wrote:
>>
>>
>>> On Mar 4, 2019, at 5:52 AM, Jiri Olsa <jolsa@redhat.com> wrote:
>>>
>>> On Wed, Feb 27, 2019 at 05:52:52PM +0000, Song Liu wrote:
>>>>
>>>>
>>>>> On Feb 27, 2019, at 5:21 AM, Jiri Olsa <jolsa@redhat.com> wrote:
>>>>>
>>>>> On Mon, Feb 25, 2019 at 04:20:18PM -0800, Song Liu wrote:
>>>>>
>>>>> SNIP
>>>>>
>>>>>> diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
>>>>>> index 8c902276d4b4..61b87c8111e6 100644
>>>>>> --- a/tools/perf/util/evlist.c
>>>>>> +++ b/tools/perf/util/evlist.c
>>>>>> @@ -19,6 +19,7 @@
>>>>>> #include "debug.h"
>>>>>> #include "units.h"
>>>>>> #include "asm/bug.h"
>>>>>> +#include "bpf-event.h"
>>>>>> #include <signal.h>
>>>>>> #include <unistd.h>
>>>>>>
>>>>>> @@ -1841,3 +1842,102 @@ struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list,
>>>>>> }
>>>>>> return leader;
>>>>>> }
>>>>>> +
>>>>>> +static struct perf_evlist *sb_evlist;
>>>>>> +pthread_t poll_thread;
>>>>>
>>>>> so some of the things are static and some like poll_args
>>>>> you alloced on the stack.. I dont like this interface,
>>>>> could we come up with something generic? perhaps
>>>>> encapsulated in perf_evlist, like:
>>>>
>>>> I picked global sb_evlist and poll_thread because there
>>>> should be only one sb_evlist and one thread polling it.
>>>> There might be multiple evsel on the sb_evlist.
>>>>
>>>> I am not sure I understand your suggestion..
>>>>
>>>>> struct perf_evlist {
>>>>> ...
>>>>> struct {
>>>>> pthread_t th;
>>>>> int state;
>>>>> } thread;
>>>>
>>>> This will not be used by the main perf_evlist, right?
>>>
>>> no, just by the thread interface
>>
>> What is "state" here?
>
> it's replacement for the 'done' pointer u had,
> which I did not like.. leting each thread having
> its own state is better IMO
>
> jirka
I see. Let me try that.
Song
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 14/15] perf: introduce side band thread
2019-02-27 13:21 ` Jiri Olsa
2019-02-27 17:52 ` Song Liu
@ 2019-03-04 21:40 ` Song Liu
2019-03-05 11:03 ` Jiri Olsa
1 sibling, 1 reply; 36+ messages in thread
From: Song Liu @ 2019-03-04 21:40 UTC (permalink / raw)
To: Jiri Olsa
Cc: Networking, linux-kernel, Alexei Starovoitov, Daniel Borkmann,
Kernel Team, Peter Zijlstra, Arnaldo Carvalho de Melo, jolsa,
namhyung
> On Feb 27, 2019, at 5:21 AM, Jiri Olsa <jolsa@redhat.com> wrote:
>
> On Mon, Feb 25, 2019 at 04:20:18PM -0800, Song Liu wrote:
>
> SNIP
>
>> diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
>> index 8c902276d4b4..61b87c8111e6 100644
>> --- a/tools/perf/util/evlist.c
>> +++ b/tools/perf/util/evlist.c
>> @@ -19,6 +19,7 @@
>> #include "debug.h"
>> #include "units.h"
>> #include "asm/bug.h"
>> +#include "bpf-event.h"
>> #include <signal.h>
>> #include <unistd.h>
>>
>> @@ -1841,3 +1842,102 @@ struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list,
>> }
>> return leader;
>> }
>> +
>> +static struct perf_evlist *sb_evlist;
>> +pthread_t poll_thread;
>
> so some of the things are static and some like poll_args
> you alloced on the stack.. I dont like this interface,
> could we come up with something generic? perhaps
> encapsulated in perf_evlist, like:
>
> struct perf_evlist {
> ...
> struct {
> pthread_t th;
> int state;
> } thread;
> };
>
> typedef int (perf_evlist__thread_cb_t)(perf_evlist, union perf_event *event,....)
>
> perf_evlist__start_thread(perf_evlist, perf_evlist__thread_cb_t cb);
> perf_evlist__stop_thread(perf_evlist);
>
>
> jirka
More questions on this proposal:
IIUC, this approach creates one perf_evlist and one thread for each side band
event (only bpf for now, more afterwards). Each of these perf_evlists will
create its own ring buffer.
On the other hand, current patch allows different events to share the thread,
the perf_evlist, and the ring buffer.
If my understanding is correct, current patch would be more efficient down the
road? Did I miss some downsides of current patch?
Thanks,
Song
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 14/15] perf: introduce side band thread
2019-03-04 21:40 ` Song Liu
@ 2019-03-05 11:03 ` Jiri Olsa
2019-03-05 20:37 ` Song Liu
0 siblings, 1 reply; 36+ messages in thread
From: Jiri Olsa @ 2019-03-05 11:03 UTC (permalink / raw)
To: Song Liu
Cc: Networking, linux-kernel, Alexei Starovoitov, Daniel Borkmann,
Kernel Team, Peter Zijlstra, Arnaldo Carvalho de Melo, jolsa,
namhyung
On Mon, Mar 04, 2019 at 09:40:07PM +0000, Song Liu wrote:
>
>
> > On Feb 27, 2019, at 5:21 AM, Jiri Olsa <jolsa@redhat.com> wrote:
> >
> > On Mon, Feb 25, 2019 at 04:20:18PM -0800, Song Liu wrote:
> >
> > SNIP
> >
> >> diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
> >> index 8c902276d4b4..61b87c8111e6 100644
> >> --- a/tools/perf/util/evlist.c
> >> +++ b/tools/perf/util/evlist.c
> >> @@ -19,6 +19,7 @@
> >> #include "debug.h"
> >> #include "units.h"
> >> #include "asm/bug.h"
> >> +#include "bpf-event.h"
> >> #include <signal.h>
> >> #include <unistd.h>
> >>
> >> @@ -1841,3 +1842,102 @@ struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list,
> >> }
> >> return leader;
> >> }
> >> +
> >> +static struct perf_evlist *sb_evlist;
> >> +pthread_t poll_thread;
> >
> > so some of the things are static and some like poll_args
> > you alloced on the stack.. I dont like this interface,
> > could we come up with something generic? perhaps
> > encapsulated in perf_evlist, like:
> >
> > struct perf_evlist {
> > ...
> > struct {
> > pthread_t th;
> > int state;
> > } thread;
> > };
> >
> > typedef int (perf_evlist__thread_cb_t)(perf_evlist, union perf_event *event,....)
> >
> > perf_evlist__start_thread(perf_evlist, perf_evlist__thread_cb_t cb);
> > perf_evlist__stop_thread(perf_evlist);
> >
> >
> > jirka
>
> More questions on this proposal:
>
> IIUC, this approach creates one perf_evlist and one thread for each side band
> event (only bpf for now, more afterwards). Each of these perf_evlists will
> create its own ring buffer.
>
> On the other hand, current patch allows different events to share the thread,
> the perf_evlist, and the ring buffer.
you can have those events in single evlist no?
>
> If my understanding is correct, current patch would be more efficient down the
> road? Did I miss some downsides of current patch?
I'd just like something configurable and with single handle
not scattered around the code, so it's easy to add new callback
jirka
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 14/15] perf: introduce side band thread
2019-03-05 11:03 ` Jiri Olsa
@ 2019-03-05 20:37 ` Song Liu
0 siblings, 0 replies; 36+ messages in thread
From: Song Liu @ 2019-03-05 20:37 UTC (permalink / raw)
To: Jiri Olsa
Cc: Networking, linux-kernel, Alexei Starovoitov, Daniel Borkmann,
Kernel Team, Peter Zijlstra, Arnaldo Carvalho de Melo, jolsa,
namhyung
> On Mar 5, 2019, at 3:03 AM, Jiri Olsa <jolsa@redhat.com> wrote:
>
> On Mon, Mar 04, 2019 at 09:40:07PM +0000, Song Liu wrote:
>>
>>
>>> On Feb 27, 2019, at 5:21 AM, Jiri Olsa <jolsa@redhat.com> wrote:
>>>
>>> On Mon, Feb 25, 2019 at 04:20:18PM -0800, Song Liu wrote:
>>>
>>> SNIP
>>>
>>>> diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
>>>> index 8c902276d4b4..61b87c8111e6 100644
>>>> --- a/tools/perf/util/evlist.c
>>>> +++ b/tools/perf/util/evlist.c
>>>> @@ -19,6 +19,7 @@
>>>> #include "debug.h"
>>>> #include "units.h"
>>>> #include "asm/bug.h"
>>>> +#include "bpf-event.h"
>>>> #include <signal.h>
>>>> #include <unistd.h>
>>>>
>>>> @@ -1841,3 +1842,102 @@ struct perf_evsel *perf_evlist__reset_weak_group(struct perf_evlist *evsel_list,
>>>> }
>>>> return leader;
>>>> }
>>>> +
>>>> +static struct perf_evlist *sb_evlist;
>>>> +pthread_t poll_thread;
>>>
>>> so some of the things are static and some like poll_args
>>> you alloced on the stack.. I dont like this interface,
>>> could we come up with something generic? perhaps
>>> encapsulated in perf_evlist, like:
>>>
>>> struct perf_evlist {
>>> ...
>>> struct {
>>> pthread_t th;
>>> int state;
>>> } thread;
>>> };
>>>
>>> typedef int (perf_evlist__thread_cb_t)(perf_evlist, union perf_event *event,....)
>>>
>>> perf_evlist__start_thread(perf_evlist, perf_evlist__thread_cb_t cb);
>>> perf_evlist__stop_thread(perf_evlist);
>>>
>>>
>>> jirka
>>
>> More questions on this proposal:
>>
>> IIUC, this approach creates one perf_evlist and one thread for each side band
>> event (only bpf for now, more afterwards). Each of these perf_evlists will
>> create its own ring buffer.
>>
>> On the other hand, current patch allows different events to share the thread,
>> the perf_evlist, and the ring buffer.
>
> you can have those events in single evlist no?
>
>>
>> If my understanding is correct, current patch would be more efficient down the
>> road? Did I miss some downsides of current patch?
>
> I'd just like something configurable and with single handle
> not scattered around the code, so it's easy to add new callback
>
> jirka
To make adding callbacks easy, we need to register callback per perf_evsel, so
multiple side band events could share the perf_evlist. It will be something like:
typedef int (perf_evsel__sb_cb_t)(union perf_event *event, void *data);
struct perf_evsel {
...
struct {
perf_evsel__sb_cb_t *cb;
void *data;
} side_band;
};
perf_evlist__add_sb_event(struct perf_evlist *evlist,
struct perf_event_attr *attr,
perf_evsel__sb_cb_t cb,
void *data);
perf_evlist__start_sb_evlist(struct perf_evlist *evlist);
perf_evlist__stop_sb_evlist(struct perf_evlist *evlist);
Does this look like a good approach?
Thanks,
Song
^ permalink raw reply [flat|nested] 36+ messages in thread
* [PATCH v4 perf,bpf 15/15] perf, bpf: save information about short living bpf programs
2019-02-26 0:20 [PATCH v4 perf,bpf 00/15] perf annotation of BPF programs Song Liu
` (13 preceding siblings ...)
2019-02-26 0:20 ` [PATCH v4 perf,bpf 14/15] perf: introduce side band thread Song Liu
@ 2019-02-26 0:20 ` Song Liu
2019-02-27 13:21 ` Jiri Olsa
14 siblings, 1 reply; 36+ messages in thread
From: Song Liu @ 2019-02-26 0:20 UTC (permalink / raw)
To: netdev, linux-kernel
Cc: ast, daniel, kernel-team, peterz, acme, jolsa, namhyung, Song Liu
This patch adds and side band event to capture bpf_prog_info and btf of
short living bpf programs.
Signed-off-by: Song Liu <songliubraving@fb.com>
---
tools/perf/builtin-record.c | 3 ++
tools/perf/builtin-top.c | 3 ++
tools/perf/util/bpf-event.c | 66 +++++++++++++++++++++++++++++++++++++
tools/perf/util/bpf-event.h | 18 ++++++++++
tools/perf/util/evlist.c | 5 +++
5 files changed, 95 insertions(+)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index d10c1d5a9e89..ce26d37c2871 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1207,6 +1207,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
goto out_child;
}
+ if (opts->bpf_event)
+ bpf_event__add_polling_event();
+
poll_args.env = &session->header.env;
poll_args.done = &done;
perf_evlist__start_polling_thread(&rec->opts.target, &poll_args);
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index f41545445917..851c4dcce66f 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -1655,6 +1655,9 @@ int cmd_top(int argc, const char **argv)
top.record_opts.bpf_event = !top.no_bpf_event;
+ if (top.record_opts.bpf_event)
+ bpf_event__add_polling_event();
+
poll_args.env = &perf_env;
poll_args.done = &done;
perf_evlist__start_polling_thread(target, &poll_args);
diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c
index 048ef00371ad..664906237ffb 100644
--- a/tools/perf/util/bpf-event.c
+++ b/tools/perf/util/bpf-event.c
@@ -12,6 +12,7 @@
#include "machine.h"
#include "env.h"
#include "session.h"
+#include "evlist.h"
#define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
@@ -332,3 +333,68 @@ int perf_event__synthesize_bpf_events(struct perf_session *session,
free(event);
return err;
}
+
+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);
+}
+
+int bpf_event__add_polling_event(void)
+{
+ struct perf_event_attr attr = {
+ .type = PERF_TYPE_SOFTWARE,
+ .config = PERF_COUNT_SW_DUMMY,
+ .watermark = 1,
+ .bpf_event = 1,
+ .wakeup_watermark = 1,
+ .size = sizeof(attr), /* to capture ABI version */
+ };
+
+ return perf_evlist__new_side_band_event(&attr);
+}
diff --git a/tools/perf/util/bpf-event.h b/tools/perf/util/bpf-event.h
index b9ec394dc7c7..03a6f018e219 100644
--- a/tools/perf/util/bpf-event.h
+++ b/tools/perf/util/bpf-event.h
@@ -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_polling_event(void);
+void perf_env__add_bpf_info(struct perf_env *env, u32 id);
+
#else
static inline int machine__process_bpf_event(struct machine *machine __maybe_unused,
union perf_event *event __maybe_unused,
@@ -46,5 +54,15 @@ static inline int perf_event__synthesize_bpf_events(struct perf_session *session
{
return 0;
}
+
+static inline int bpf_event__add_polling_event(void)
+{
+ return 0;
+}
+
+void perf_env__add_bpf_info(struct perf_env *env __maybe_unused,
+ u32 id __maybe_unused)
+{
+}
#endif // HAVE_LIBBPF_SUPPORT
#endif
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 61b87c8111e6..58ac42878c0a 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -1886,6 +1886,11 @@ static void *perf_evlist__poll_thread(void *arg)
pr_debug("processing vip event of type %d\n",
event->header.type);
switch (event->header.type) {
+ case PERF_RECORD_BPF_EVENT:
+ if (event->bpf_event.type != PERF_BPF_EVENT_PROG_LOAD)
+ break;
+ perf_env__add_bpf_info(args->env, event->bpf_event.id);
+ break;
default:
break;
}
--
2.17.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 15/15] perf, bpf: save information about short living bpf programs
2019-02-26 0:20 ` [PATCH v4 perf,bpf 15/15] perf, bpf: save information about short living bpf programs Song Liu
@ 2019-02-27 13:21 ` Jiri Olsa
2019-02-27 17:42 ` Song Liu
0 siblings, 1 reply; 36+ messages in thread
From: Jiri Olsa @ 2019-02-27 13:21 UTC (permalink / raw)
To: Song Liu
Cc: netdev, linux-kernel, ast, daniel, kernel-team, peterz, acme,
jolsa, namhyung
On Mon, Feb 25, 2019 at 04:20:19PM -0800, Song Liu wrote:
SNIP
> + 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);
so is this the main reason we are doing this? getting the btf
data for bpf prog ids and store them?
please describe the whole bpf events/features data flow in
changelog as I asked in previous email
thanks,
jirka
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [PATCH v4 perf,bpf 15/15] perf, bpf: save information about short living bpf programs
2019-02-27 13:21 ` Jiri Olsa
@ 2019-02-27 17:42 ` Song Liu
0 siblings, 0 replies; 36+ messages in thread
From: Song Liu @ 2019-02-27 17:42 UTC (permalink / raw)
To: Jiri Olsa
Cc: Netdev, linux-kernel, ast, daniel, Kernel Team, peterz, acme,
jolsa, namhyung
> On Feb 27, 2019, at 5:21 AM, Jiri Olsa <jolsa@redhat.com> wrote:
>
> On Mon, Feb 25, 2019 at 04:20:19PM -0800, Song Liu wrote:
>
> SNIP
>
>> + 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);
Getting bpf_prog_info here.
>> + } 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);
>
> so is this the main reason we are doing this? getting the btf
> data for bpf prog ids and store them?
We are getting both bpf_prog_info (see above) and btf.
>
> please describe the whole bpf events/features data flow in
> changelog as I asked in previous email
I will add more details to the change log.
Thanks,
Song
>
> thanks,
> jirka
^ permalink raw reply [flat|nested] 36+ messages in thread