Commit 8388092b authored by Andrii Nakryiko's avatar Andrii Nakryiko

Merge branch 'libbpf: deprecate bpf_program__get_prog_info_linear'

Dave Marchevsky says:

====================

bpf_program__get_prog_info_linear is a helper which wraps the
bpf_obj_get_info_by_fd BPF syscall with some niceties that put
all dynamic-length bpf_prog_info in one buffer contiguous with struct
bpf_prog_info, and simplify the selection of which dynamic data to grab.

The resultant combined struct, bpf_prog_info_linear, is persisted to
file by 'perf' to enable later annotation of BPF prog data. libbpf
includes some vaddr <-> offset conversion helpers for
struct bpf_prog_info_linear to simplify this.

This functionality is heavily tailored to perf's usecase, so its use as
a general prog info API should be deemphasized in favor of just calling
bpf_obj_get_info_by_fd, which can be more easily fit to purpose. Some
examples from caller migrations in this series:

  * Some callers weren't requesting or using dynamic-sized prog info and
    are well served by a simple get_info_by_fd call (e.g.
    dump_prog_id_as_func_ptr in bpftool)
  * Some callers were requesting all of a specific dynamic info type but
    only using the first record, so can avoid unnecessary malloc by
    only requesting 1 (e.g. profile_target_name in bpftool)
  * bpftool's do_dump saves some malloc/free by growing and reusing its
    dynamic prog_info buf as it loops over progs to grab info and dump.

Perf does need the full functionality of
bpf_program__get_prog_info_linear and its accompanying structs +
helpers, so copy the code to its codebase, migrate all other uses in the
tree, and deprecate the helper in libbpf.

Since the deprecated symbols continue to be included in perf some
renaming was necessary in perf's copy, otherwise functionality is
unchanged.

This work was previously discussed in libbpf's issue tracker [0].

[0]: https://github.com/libbpf/libbpf/issues/313

v2->v3:
  * Remove v2's patch 1 ("libbpf: Migrate internal use of
    bpf_program__get_prog_info_linear"), which was applied [Andrii]
  * Add new patch 1 migrating error checking of libbpf calls to
    new scheme [Andrii, Quentin]
  * In patch 2, fix != -1 error check of libbpf call, improper realloc
    handling, and get rid of confusing macros [Andrii]
  * In patch 4, deprecate starting from 0.6 instead of 0.7 [Andrii]

v1->v2: fix bpftool do_dump changes to clear bpf_prog_info after use and
correctly pass realloc'd ptr back (patch 2)
====================
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
parents cc0356d6 f5aafbc2
...@@ -32,14 +32,16 @@ static int dump_prog_id_as_func_ptr(const struct btf_dumper *d, ...@@ -32,14 +32,16 @@ static int dump_prog_id_as_func_ptr(const struct btf_dumper *d,
const struct btf_type *func_proto, const struct btf_type *func_proto,
__u32 prog_id) __u32 prog_id)
{ {
struct bpf_prog_info_linear *prog_info = NULL;
const struct btf_type *func_type; const struct btf_type *func_type;
int prog_fd = -1, func_sig_len;
struct bpf_prog_info info = {};
__u32 info_len = sizeof(info);
const char *prog_name = NULL; const char *prog_name = NULL;
struct bpf_func_info *finfo;
struct btf *prog_btf = NULL; struct btf *prog_btf = NULL;
struct bpf_prog_info *info; struct bpf_func_info finfo;
int prog_fd, func_sig_len; __u32 finfo_rec_size;
char prog_str[1024]; char prog_str[1024];
int err;
/* Get the ptr's func_proto */ /* Get the ptr's func_proto */
func_sig_len = btf_dump_func(d->btf, prog_str, func_proto, NULL, 0, func_sig_len = btf_dump_func(d->btf, prog_str, func_proto, NULL, 0,
...@@ -52,25 +54,30 @@ static int dump_prog_id_as_func_ptr(const struct btf_dumper *d, ...@@ -52,25 +54,30 @@ static int dump_prog_id_as_func_ptr(const struct btf_dumper *d,
/* Get the bpf_prog's name. Obtain from func_info. */ /* Get the bpf_prog's name. Obtain from func_info. */
prog_fd = bpf_prog_get_fd_by_id(prog_id); prog_fd = bpf_prog_get_fd_by_id(prog_id);
if (prog_fd == -1) if (prog_fd < 0)
goto print; goto print;
prog_info = bpf_program__get_prog_info_linear(prog_fd, err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
1UL << BPF_PROG_INFO_FUNC_INFO); if (err)
close(prog_fd);
if (IS_ERR(prog_info)) {
prog_info = NULL;
goto print; goto print;
}
info = &prog_info->info;
if (!info->btf_id || !info->nr_func_info) if (!info.btf_id || !info.nr_func_info)
goto print;
finfo_rec_size = info.func_info_rec_size;
memset(&info, 0, sizeof(info));
info.nr_func_info = 1;
info.func_info_rec_size = finfo_rec_size;
info.func_info = ptr_to_u64(&finfo);
err = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
if (err)
goto print; goto print;
prog_btf = btf__load_from_kernel_by_id(info->btf_id);
prog_btf = btf__load_from_kernel_by_id(info.btf_id);
if (libbpf_get_error(prog_btf)) if (libbpf_get_error(prog_btf))
goto print; goto print;
finfo = u64_to_ptr(info->func_info); func_type = btf__type_by_id(prog_btf, finfo.type_id);
func_type = btf__type_by_id(prog_btf, finfo->type_id);
if (!func_type || !btf_is_func(func_type)) if (!func_type || !btf_is_func(func_type))
goto print; goto print;
...@@ -92,7 +99,8 @@ static int dump_prog_id_as_func_ptr(const struct btf_dumper *d, ...@@ -92,7 +99,8 @@ static int dump_prog_id_as_func_ptr(const struct btf_dumper *d,
prog_str[sizeof(prog_str) - 1] = '\0'; prog_str[sizeof(prog_str) - 1] = '\0';
jsonw_string(d->jw, prog_str); jsonw_string(d->jw, prog_str);
btf__free(prog_btf); btf__free(prog_btf);
free(prog_info); if (prog_fd >= 0)
close(prog_fd);
return 0; return 0;
} }
......
...@@ -100,6 +100,76 @@ static enum bpf_attach_type parse_attach_type(const char *str) ...@@ -100,6 +100,76 @@ static enum bpf_attach_type parse_attach_type(const char *str)
return __MAX_BPF_ATTACH_TYPE; return __MAX_BPF_ATTACH_TYPE;
} }
static int prep_prog_info(struct bpf_prog_info *const info, enum dump_mode mode,
void **info_data, size_t *const info_data_sz)
{
struct bpf_prog_info holder = {};
size_t needed = 0;
void *ptr;
if (mode == DUMP_JITED) {
holder.jited_prog_len = info->jited_prog_len;
needed += info->jited_prog_len;
} else {
holder.xlated_prog_len = info->xlated_prog_len;
needed += info->xlated_prog_len;
}
holder.nr_jited_ksyms = info->nr_jited_ksyms;
needed += info->nr_jited_ksyms * sizeof(__u64);
holder.nr_jited_func_lens = info->nr_jited_func_lens;
needed += info->nr_jited_func_lens * sizeof(__u32);
holder.nr_func_info = info->nr_func_info;
holder.func_info_rec_size = info->func_info_rec_size;
needed += info->nr_func_info * info->func_info_rec_size;
holder.nr_line_info = info->nr_line_info;
holder.line_info_rec_size = info->line_info_rec_size;
needed += info->nr_line_info * info->line_info_rec_size;
holder.nr_jited_line_info = info->nr_jited_line_info;
holder.jited_line_info_rec_size = info->jited_line_info_rec_size;
needed += info->nr_jited_line_info * info->jited_line_info_rec_size;
if (needed > *info_data_sz) {
ptr = realloc(*info_data, needed);
if (!ptr)
return -1;
*info_data = ptr;
*info_data_sz = needed;
}
ptr = *info_data;
if (mode == DUMP_JITED) {
holder.jited_prog_insns = ptr_to_u64(ptr);
ptr += holder.jited_prog_len;
} else {
holder.xlated_prog_insns = ptr_to_u64(ptr);
ptr += holder.xlated_prog_len;
}
holder.jited_ksyms = ptr_to_u64(ptr);
ptr += holder.nr_jited_ksyms * sizeof(__u64);
holder.jited_func_lens = ptr_to_u64(ptr);
ptr += holder.nr_jited_func_lens * sizeof(__u32);
holder.func_info = ptr_to_u64(ptr);
ptr += holder.nr_func_info * holder.func_info_rec_size;
holder.line_info = ptr_to_u64(ptr);
ptr += holder.nr_line_info * holder.line_info_rec_size;
holder.jited_line_info = ptr_to_u64(ptr);
ptr += holder.nr_jited_line_info * holder.jited_line_info_rec_size;
*info = holder;
return 0;
}
static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
{ {
struct timespec real_time_ts, boot_time_ts; struct timespec real_time_ts, boot_time_ts;
...@@ -803,16 +873,18 @@ prog_dump(struct bpf_prog_info *info, enum dump_mode mode, ...@@ -803,16 +873,18 @@ prog_dump(struct bpf_prog_info *info, enum dump_mode mode,
static int do_dump(int argc, char **argv) static int do_dump(int argc, char **argv)
{ {
struct bpf_prog_info_linear *info_linear; struct bpf_prog_info info;
__u32 info_len = sizeof(info);
size_t info_data_sz = 0;
void *info_data = NULL;
char *filepath = NULL; char *filepath = NULL;
bool opcodes = false; bool opcodes = false;
bool visual = false; bool visual = false;
enum dump_mode mode; enum dump_mode mode;
bool linum = false; bool linum = false;
int *fds = NULL;
int nb_fds, i = 0; int nb_fds, i = 0;
int *fds = NULL;
int err = -1; int err = -1;
__u64 arrays;
if (is_prefix(*argv, "jited")) { if (is_prefix(*argv, "jited")) {
if (disasm_init()) if (disasm_init())
...@@ -872,43 +944,44 @@ static int do_dump(int argc, char **argv) ...@@ -872,43 +944,44 @@ static int do_dump(int argc, char **argv)
goto exit_close; goto exit_close;
} }
if (mode == DUMP_JITED)
arrays = 1UL << BPF_PROG_INFO_JITED_INSNS;
else
arrays = 1UL << BPF_PROG_INFO_XLATED_INSNS;
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;
if (json_output && nb_fds > 1) if (json_output && nb_fds > 1)
jsonw_start_array(json_wtr); /* root array */ jsonw_start_array(json_wtr); /* root array */
for (i = 0; i < nb_fds; i++) { for (i = 0; i < nb_fds; i++) {
info_linear = bpf_program__get_prog_info_linear(fds[i], arrays); memset(&info, 0, sizeof(info));
if (IS_ERR_OR_NULL(info_linear)) {
err = bpf_obj_get_info_by_fd(fds[i], &info, &info_len);
if (err) {
p_err("can't get prog info: %s", strerror(errno));
break;
}
err = prep_prog_info(&info, mode, &info_data, &info_data_sz);
if (err) {
p_err("can't grow prog info_data");
break;
}
err = bpf_obj_get_info_by_fd(fds[i], &info, &info_len);
if (err) {
p_err("can't get prog info: %s", strerror(errno)); p_err("can't get prog info: %s", strerror(errno));
break; break;
} }
if (json_output && nb_fds > 1) { if (json_output && nb_fds > 1) {
jsonw_start_object(json_wtr); /* prog object */ jsonw_start_object(json_wtr); /* prog object */
print_prog_header_json(&info_linear->info); print_prog_header_json(&info);
jsonw_name(json_wtr, "insns"); jsonw_name(json_wtr, "insns");
} else if (nb_fds > 1) { } else if (nb_fds > 1) {
print_prog_header_plain(&info_linear->info); print_prog_header_plain(&info);
} }
err = prog_dump(&info_linear->info, mode, filepath, opcodes, err = prog_dump(&info, mode, filepath, opcodes, visual, linum);
visual, linum);
if (json_output && nb_fds > 1) if (json_output && nb_fds > 1)
jsonw_end_object(json_wtr); /* prog object */ jsonw_end_object(json_wtr); /* prog object */
else if (i != nb_fds - 1 && nb_fds > 1) else if (i != nb_fds - 1 && nb_fds > 1)
printf("\n"); printf("\n");
free(info_linear);
if (err) if (err)
break; break;
close(fds[i]); close(fds[i]);
...@@ -920,6 +993,7 @@ static int do_dump(int argc, char **argv) ...@@ -920,6 +993,7 @@ static int do_dump(int argc, char **argv)
for (; i < nb_fds; i++) for (; i < nb_fds; i++)
close(fds[i]); close(fds[i]);
exit_free: exit_free:
free(info_data);
free(fds); free(fds);
return err; return err;
} }
...@@ -2016,41 +2090,58 @@ static void profile_print_readings(void) ...@@ -2016,41 +2090,58 @@ static void profile_print_readings(void)
static char *profile_target_name(int tgt_fd) static char *profile_target_name(int tgt_fd)
{ {
struct bpf_prog_info_linear *info_linear; struct bpf_func_info func_info;
struct bpf_func_info *func_info; struct bpf_prog_info info = {};
__u32 info_len = sizeof(info);
const struct btf_type *t; const struct btf_type *t;
__u32 func_info_rec_size;
struct btf *btf = NULL; struct btf *btf = NULL;
char *name = NULL; char *name = NULL;
int err;
info_linear = bpf_program__get_prog_info_linear( err = bpf_obj_get_info_by_fd(tgt_fd, &info, &info_len);
tgt_fd, 1UL << BPF_PROG_INFO_FUNC_INFO); if (err) {
if (IS_ERR_OR_NULL(info_linear)) { p_err("failed to bpf_obj_get_info_by_fd for prog FD %d", tgt_fd);
p_err("failed to get info_linear for prog FD %d", tgt_fd); goto out;
return NULL;
} }
if (info_linear->info.btf_id == 0) { if (info.btf_id == 0) {
p_err("prog FD %d doesn't have valid btf", tgt_fd); p_err("prog FD %d doesn't have valid btf", tgt_fd);
goto out; goto out;
} }
btf = btf__load_from_kernel_by_id(info_linear->info.btf_id); func_info_rec_size = info.func_info_rec_size;
if (info.nr_func_info == 0) {
p_err("bpf_obj_get_info_by_fd for prog FD %d found 0 func_info", tgt_fd);
goto out;
}
memset(&info, 0, sizeof(info));
info.nr_func_info = 1;
info.func_info_rec_size = func_info_rec_size;
info.func_info = ptr_to_u64(&func_info);
err = bpf_obj_get_info_by_fd(tgt_fd, &info, &info_len);
if (err) {
p_err("failed to get func_info for prog FD %d", tgt_fd);
goto out;
}
btf = btf__load_from_kernel_by_id(info.btf_id);
if (libbpf_get_error(btf)) { if (libbpf_get_error(btf)) {
p_err("failed to load btf for prog FD %d", tgt_fd); p_err("failed to load btf for prog FD %d", tgt_fd);
goto out; goto out;
} }
func_info = u64_to_ptr(info_linear->info.func_info); t = btf__type_by_id(btf, func_info.type_id);
t = btf__type_by_id(btf, func_info[0].type_id);
if (!t) { if (!t) {
p_err("btf %d doesn't have type %d", p_err("btf %d doesn't have type %d",
info_linear->info.btf_id, func_info[0].type_id); info.btf_id, func_info.type_id);
goto out; goto out;
} }
name = strdup(btf__name_by_offset(btf, t->name_off)); name = strdup(btf__name_by_offset(btf, t->name_off));
out: out:
btf__free(btf); btf__free(btf);
free(info_linear);
return name; return name;
} }
......
...@@ -252,7 +252,7 @@ static struct res do_one_id(const char *id_str, work_func func, void *data, ...@@ -252,7 +252,7 @@ static struct res do_one_id(const char *id_str, work_func func, void *data,
} }
fd = bpf_map_get_fd_by_id(id); fd = bpf_map_get_fd_by_id(id);
if (fd == -1) { if (fd < 0) {
p_err("can't get map by id (%lu): %s", id, strerror(errno)); p_err("can't get map by id (%lu): %s", id, strerror(errno));
res.nr_errs++; res.nr_errs++;
return res; return res;
......
...@@ -918,12 +918,15 @@ struct bpf_prog_info_linear { ...@@ -918,12 +918,15 @@ struct bpf_prog_info_linear {
__u8 data[]; __u8 data[];
}; };
LIBBPF_DEPRECATED_SINCE(0, 6, "use a custom linear prog_info wrapper")
LIBBPF_API struct bpf_prog_info_linear * LIBBPF_API struct bpf_prog_info_linear *
bpf_program__get_prog_info_linear(int fd, __u64 arrays); bpf_program__get_prog_info_linear(int fd, __u64 arrays);
LIBBPF_DEPRECATED_SINCE(0, 6, "use a custom linear prog_info wrapper")
LIBBPF_API void LIBBPF_API void
bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear); bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear);
LIBBPF_DEPRECATED_SINCE(0, 6, "use a custom linear prog_info wrapper")
LIBBPF_API void LIBBPF_API void
bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear); bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear);
......
...@@ -346,7 +346,7 @@ to special needs. ...@@ -346,7 +346,7 @@ to special needs.
HEADER_BPF_PROG_INFO = 25, HEADER_BPF_PROG_INFO = 25,
struct bpf_prog_info_linear, which contains detailed information about struct perf_bpil, which contains detailed information about
a BPF program, including type, id, tag, jited/xlated instructions, etc. a BPF program, including type, id, tag, jited/xlated instructions, etc.
HEADER_BPF_BTF = 26, HEADER_BPF_BTF = 26,
......
...@@ -201,6 +201,7 @@ endif ...@@ -201,6 +201,7 @@ endif
perf-y += perf-hooks.o perf-y += perf-hooks.o
perf-$(CONFIG_LIBBPF) += bpf-event.o perf-$(CONFIG_LIBBPF) += bpf-event.o
perf-$(CONFIG_LIBBPF) += bpf-utils.o
perf-$(CONFIG_CXX) += c++/ perf-$(CONFIG_CXX) += c++/
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "evsel.h" #include "evsel.h"
#include "evlist.h" #include "evlist.h"
#include "bpf-event.h" #include "bpf-event.h"
#include "bpf-utils.h"
#include "block-range.h" #include "block-range.h"
#include "string2.h" #include "string2.h"
#include "util/event.h" #include "util/event.h"
...@@ -1700,12 +1701,12 @@ static int symbol__disassemble_bpf(struct symbol *sym, ...@@ -1700,12 +1701,12 @@ static int symbol__disassemble_bpf(struct symbol *sym,
{ {
struct annotation *notes = symbol__annotation(sym); struct annotation *notes = symbol__annotation(sym);
struct annotation_options *opts = args->options; struct annotation_options *opts = args->options;
struct bpf_prog_info_linear *info_linear;
struct bpf_prog_linfo *prog_linfo = NULL; struct bpf_prog_linfo *prog_linfo = NULL;
struct bpf_prog_info_node *info_node; struct bpf_prog_info_node *info_node;
int len = sym->end - sym->start; int len = sym->end - sym->start;
disassembler_ftype disassemble; disassembler_ftype disassemble;
struct map *map = args->ms.map; struct map *map = args->ms.map;
struct perf_bpil *info_linear;
struct disassemble_info info; struct disassemble_info info;
struct dso *dso = map->dso; struct dso *dso = map->dso;
int pc = 0, count, sub_id; int pc = 0, count, sub_id;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <internal/lib.h> #include <internal/lib.h>
#include <symbol/kallsyms.h> #include <symbol/kallsyms.h>
#include "bpf-event.h" #include "bpf-event.h"
#include "bpf-utils.h"
#include "debug.h" #include "debug.h"
#include "dso.h" #include "dso.h"
#include "symbol.h" #include "symbol.h"
...@@ -32,8 +33,6 @@ struct btf * __weak btf__load_from_kernel_by_id(__u32 id) ...@@ -32,8 +33,6 @@ struct btf * __weak btf__load_from_kernel_by_id(__u32 id)
return err ? ERR_PTR(err) : btf; return err ? ERR_PTR(err) : btf;
} }
#define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
static int snprintf_hex(char *buf, size_t size, unsigned char *data, size_t len) static int snprintf_hex(char *buf, size_t size, unsigned char *data, size_t len)
{ {
int ret = 0; int ret = 0;
...@@ -48,9 +47,9 @@ static int machine__process_bpf_event_load(struct machine *machine, ...@@ -48,9 +47,9 @@ static int machine__process_bpf_event_load(struct machine *machine,
union perf_event *event, union perf_event *event,
struct perf_sample *sample __maybe_unused) struct perf_sample *sample __maybe_unused)
{ {
struct bpf_prog_info_linear *info_linear;
struct bpf_prog_info_node *info_node; struct bpf_prog_info_node *info_node;
struct perf_env *env = machine->env; struct perf_env *env = machine->env;
struct perf_bpil *info_linear;
int id = event->bpf.id; int id = event->bpf.id;
unsigned int i; unsigned int i;
...@@ -175,9 +174,9 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session, ...@@ -175,9 +174,9 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
{ {
struct perf_record_ksymbol *ksymbol_event = &event->ksymbol; struct perf_record_ksymbol *ksymbol_event = &event->ksymbol;
struct perf_record_bpf_event *bpf_event = &event->bpf; struct perf_record_bpf_event *bpf_event = &event->bpf;
struct bpf_prog_info_linear *info_linear;
struct perf_tool *tool = session->tool; struct perf_tool *tool = session->tool;
struct bpf_prog_info_node *info_node; struct bpf_prog_info_node *info_node;
struct perf_bpil *info_linear;
struct bpf_prog_info *info; struct bpf_prog_info *info;
struct btf *btf = NULL; struct btf *btf = NULL;
struct perf_env *env; struct perf_env *env;
...@@ -191,15 +190,15 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session, ...@@ -191,15 +190,15 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
*/ */
env = session->data ? &session->header.env : &perf_env; env = session->data ? &session->header.env : &perf_env;
arrays = 1UL << BPF_PROG_INFO_JITED_KSYMS; arrays = 1UL << PERF_BPIL_JITED_KSYMS;
arrays |= 1UL << BPF_PROG_INFO_JITED_FUNC_LENS; arrays |= 1UL << PERF_BPIL_JITED_FUNC_LENS;
arrays |= 1UL << BPF_PROG_INFO_FUNC_INFO; arrays |= 1UL << PERF_BPIL_FUNC_INFO;
arrays |= 1UL << BPF_PROG_INFO_PROG_TAGS; arrays |= 1UL << PERF_BPIL_PROG_TAGS;
arrays |= 1UL << BPF_PROG_INFO_JITED_INSNS; arrays |= 1UL << PERF_BPIL_JITED_INSNS;
arrays |= 1UL << BPF_PROG_INFO_LINE_INFO; arrays |= 1UL << PERF_BPIL_LINE_INFO;
arrays |= 1UL << BPF_PROG_INFO_JITED_LINE_INFO; arrays |= 1UL << PERF_BPIL_JITED_LINE_INFO;
info_linear = bpf_program__get_prog_info_linear(fd, arrays); info_linear = get_bpf_prog_info_linear(fd, arrays);
if (IS_ERR_OR_NULL(info_linear)) { if (IS_ERR_OR_NULL(info_linear)) {
info_linear = NULL; info_linear = NULL;
pr_debug("%s: failed to get BPF program info. aborting\n", __func__); pr_debug("%s: failed to get BPF program info. aborting\n", __func__);
...@@ -452,8 +451,8 @@ int perf_event__synthesize_bpf_events(struct perf_session *session, ...@@ -452,8 +451,8 @@ int perf_event__synthesize_bpf_events(struct perf_session *session,
static void perf_env__add_bpf_info(struct perf_env *env, u32 id) static void perf_env__add_bpf_info(struct perf_env *env, u32 id)
{ {
struct bpf_prog_info_linear *info_linear;
struct bpf_prog_info_node *info_node; struct bpf_prog_info_node *info_node;
struct perf_bpil *info_linear;
struct btf *btf = NULL; struct btf *btf = NULL;
u64 arrays; u64 arrays;
u32 btf_id; u32 btf_id;
...@@ -463,15 +462,15 @@ static void perf_env__add_bpf_info(struct perf_env *env, u32 id) ...@@ -463,15 +462,15 @@ static void perf_env__add_bpf_info(struct perf_env *env, u32 id)
if (fd < 0) if (fd < 0)
return; return;
arrays = 1UL << BPF_PROG_INFO_JITED_KSYMS; arrays = 1UL << PERF_BPIL_JITED_KSYMS;
arrays |= 1UL << BPF_PROG_INFO_JITED_FUNC_LENS; arrays |= 1UL << PERF_BPIL_JITED_FUNC_LENS;
arrays |= 1UL << BPF_PROG_INFO_FUNC_INFO; arrays |= 1UL << PERF_BPIL_FUNC_INFO;
arrays |= 1UL << BPF_PROG_INFO_PROG_TAGS; arrays |= 1UL << PERF_BPIL_PROG_TAGS;
arrays |= 1UL << BPF_PROG_INFO_JITED_INSNS; arrays |= 1UL << PERF_BPIL_JITED_INSNS;
arrays |= 1UL << BPF_PROG_INFO_LINE_INFO; arrays |= 1UL << PERF_BPIL_LINE_INFO;
arrays |= 1UL << BPF_PROG_INFO_JITED_LINE_INFO; arrays |= 1UL << PERF_BPIL_JITED_LINE_INFO;
info_linear = bpf_program__get_prog_info_linear(fd, arrays); info_linear = get_bpf_prog_info_linear(fd, arrays);
if (IS_ERR_OR_NULL(info_linear)) { if (IS_ERR_OR_NULL(info_linear)) {
pr_debug("%s: failed to get BPF program info. aborting\n", __func__); pr_debug("%s: failed to get BPF program info. aborting\n", __func__);
goto out; goto out;
......
...@@ -19,7 +19,7 @@ struct evlist; ...@@ -19,7 +19,7 @@ struct evlist;
struct target; struct target;
struct bpf_prog_info_node { struct bpf_prog_info_node {
struct bpf_prog_info_linear *info_linear; struct perf_bpil *info_linear;
struct rb_node rb_node; struct rb_node rb_node;
}; };
......
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <errno.h>
#include <stdlib.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <bpf/bpf.h>
#include "bpf-utils.h"
#include "debug.h"
struct bpil_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 bpil_array_desc bpil_array_desc[] = {
[PERF_BPIL_JITED_INSNS] = {
offsetof(struct bpf_prog_info, jited_prog_insns),
offsetof(struct bpf_prog_info, jited_prog_len),
-1,
},
[PERF_BPIL_XLATED_INSNS] = {
offsetof(struct bpf_prog_info, xlated_prog_insns),
offsetof(struct bpf_prog_info, xlated_prog_len),
-1,
},
[PERF_BPIL_MAP_IDS] = {
offsetof(struct bpf_prog_info, map_ids),
offsetof(struct bpf_prog_info, nr_map_ids),
-(int)sizeof(__u32),
},
[PERF_BPIL_JITED_KSYMS] = {
offsetof(struct bpf_prog_info, jited_ksyms),
offsetof(struct bpf_prog_info, nr_jited_ksyms),
-(int)sizeof(__u64),
},
[PERF_BPIL_JITED_FUNC_LENS] = {
offsetof(struct bpf_prog_info, jited_func_lens),
offsetof(struct bpf_prog_info, nr_jited_func_lens),
-(int)sizeof(__u32),
},
[PERF_BPIL_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),
},
[PERF_BPIL_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),
},
[PERF_BPIL_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),
},
[PERF_BPIL_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 perf_bpil *
get_bpf_prog_info_linear(int fd, __u64 arrays)
{
struct bpf_prog_info info = {};
struct perf_bpil *info_linear;
__u32 info_len = sizeof(info);
__u32 data_len = 0;
int i, err;
void *ptr;
if (arrays >> PERF_BPIL_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 = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) {
bool include_array = (arrays & (1UL << i)) > 0;
struct bpil_array_desc *desc;
__u32 count, size;
desc = bpil_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 < (__u32)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 perf_bpil) + 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 = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) {
struct bpil_array_desc *desc;
__u32 count, size;
if ((arrays & (1UL << i)) == 0)
continue;
desc = bpil_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 = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) {
struct bpil_array_desc *desc;
__u32 v1, v2;
if ((arrays & (1UL << i)) == 0)
continue;
desc = bpil_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 bpil_addr_to_offs(struct perf_bpil *info_linear)
{
int i;
for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) {
struct bpil_array_desc *desc;
__u64 addr, offs;
if ((info_linear->arrays & (1UL << i)) == 0)
continue;
desc = bpil_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 bpil_offs_to_addr(struct perf_bpil *info_linear)
{
int i;
for (i = PERF_BPIL_FIRST_ARRAY; i < PERF_BPIL_LAST_ARRAY; ++i) {
struct bpil_array_desc *desc;
__u64 addr, offs;
if ((info_linear->arrays & (1UL << i)) == 0)
continue;
desc = bpil_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);
}
}
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __PERF_BPF_UTILS_H
#define __PERF_BPF_UTILS_H
#define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
#ifdef HAVE_LIBBPF_SUPPORT
#include <bpf/libbpf.h>
/*
* 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 an uniform way to
* fetch these data. All arrays in bpf_prog_info are stored in a single
* continuous memory region. This makes it easy to store the info in a
* file.
*
* Before writing perf_bpil to files, it is necessary to
* translate pointers in bpf_prog_info to offsets. Helper functions
* bpil_addr_to_offs() and bpil_offs_to_addr()
* are introduced to switch between pointers and offsets.
*
* Examples:
* # To fetch map_ids and prog_tags:
* __u64 arrays = (1UL << PERF_BPIL_MAP_IDS) |
* (1UL << PERF_BPIL_PROG_TAGS);
* struct perf_bpil *info_linear =
* get_bpf_prog_info_linear(fd, arrays);
*
* # To save data in file
* 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>);
* bpil_offs_to_addr(info_linear);
*/
enum perf_bpil_array_types {
PERF_BPIL_FIRST_ARRAY = 0,
PERF_BPIL_JITED_INSNS = 0,
PERF_BPIL_XLATED_INSNS,
PERF_BPIL_MAP_IDS,
PERF_BPIL_JITED_KSYMS,
PERF_BPIL_JITED_FUNC_LENS,
PERF_BPIL_FUNC_INFO,
PERF_BPIL_LINE_INFO,
PERF_BPIL_JITED_LINE_INFO,
PERF_BPIL_PROG_TAGS,
PERF_BPIL_LAST_ARRAY,
};
struct perf_bpil {
/* 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[];
};
struct perf_bpil *
get_bpf_prog_info_linear(int fd, __u64 arrays);
void
bpil_addr_to_offs(struct perf_bpil *info_linear);
void
bpil_offs_to_addr(struct perf_bpil *info_linear);
#endif /* HAVE_LIBBPF_SUPPORT */
#endif /* __PERF_BPF_UTILS_H */
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <perf/bpf_perf.h> #include <perf/bpf_perf.h>
#include "bpf_counter.h" #include "bpf_counter.h"
#include "bpf-utils.h"
#include "counts.h" #include "counts.h"
#include "debug.h" #include "debug.h"
#include "evsel.h" #include "evsel.h"
...@@ -61,14 +62,13 @@ static int bpf_program_profiler__destroy(struct evsel *evsel) ...@@ -61,14 +62,13 @@ static int bpf_program_profiler__destroy(struct evsel *evsel)
static char *bpf_target_prog_name(int tgt_fd) static char *bpf_target_prog_name(int tgt_fd)
{ {
struct bpf_prog_info_linear *info_linear;
struct bpf_func_info *func_info; struct bpf_func_info *func_info;
struct perf_bpil *info_linear;
const struct btf_type *t; const struct btf_type *t;
struct btf *btf = NULL; struct btf *btf = NULL;
char *name = NULL; char *name = NULL;
info_linear = bpf_program__get_prog_info_linear( info_linear = get_bpf_prog_info_linear(tgt_fd, 1UL << PERF_BPIL_FUNC_INFO);
tgt_fd, 1UL << BPF_PROG_INFO_FUNC_INFO);
if (IS_ERR_OR_NULL(info_linear)) { if (IS_ERR_OR_NULL(info_linear)) {
pr_debug("failed to get info_linear for prog FD %d\n", tgt_fd); pr_debug("failed to get info_linear for prog FD %d\n", tgt_fd);
return NULL; return NULL;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#ifdef HAVE_LIBBPF_SUPPORT #ifdef HAVE_LIBBPF_SUPPORT
#include <bpf/libbpf.h> #include <bpf/libbpf.h>
#include "bpf-event.h" #include "bpf-event.h"
#include "bpf-utils.h"
#endif #endif
#include "compress.h" #include "compress.h"
#include "env.h" #include "env.h"
......
...@@ -16,6 +16,7 @@ struct perf_env perf_env; ...@@ -16,6 +16,7 @@ struct perf_env perf_env;
#ifdef HAVE_LIBBPF_SUPPORT #ifdef HAVE_LIBBPF_SUPPORT
#include "bpf-event.h" #include "bpf-event.h"
#include "bpf-utils.h"
#include <bpf/libbpf.h> #include <bpf/libbpf.h>
void perf_env__insert_bpf_prog_info(struct perf_env *env, void perf_env__insert_bpf_prog_info(struct perf_env *env,
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#include "util/util.h" // perf_exe() #include "util/util.h" // perf_exe()
#include "cputopo.h" #include "cputopo.h"
#include "bpf-event.h" #include "bpf-event.h"
#include "bpf-utils.h"
#include "clockid.h" #include "clockid.h"
#include "pmu-hybrid.h" #include "pmu-hybrid.h"
...@@ -1006,17 +1007,17 @@ static int write_bpf_prog_info(struct feat_fd *ff, ...@@ -1006,17 +1007,17 @@ static int write_bpf_prog_info(struct feat_fd *ff,
node = rb_entry(next, struct bpf_prog_info_node, rb_node); node = rb_entry(next, struct bpf_prog_info_node, rb_node);
next = rb_next(&node->rb_node); next = rb_next(&node->rb_node);
len = sizeof(struct bpf_prog_info_linear) + len = sizeof(struct perf_bpil) +
node->info_linear->data_len; node->info_linear->data_len;
/* before writing to file, translate address to offset */ /* before writing to file, translate address to offset */
bpf_program__bpil_addr_to_offs(node->info_linear); bpil_addr_to_offs(node->info_linear);
ret = do_write(ff, node->info_linear, len); ret = do_write(ff, node->info_linear, len);
/* /*
* translate back to address even when do_write() fails, * translate back to address even when do_write() fails,
* so that this function never changes the data. * so that this function never changes the data.
*/ */
bpf_program__bpil_offs_to_addr(node->info_linear); bpil_offs_to_addr(node->info_linear);
if (ret < 0) if (ret < 0)
goto out; goto out;
} }
...@@ -3018,9 +3019,9 @@ static int process_dir_format(struct feat_fd *ff, ...@@ -3018,9 +3019,9 @@ static int process_dir_format(struct feat_fd *ff,
#ifdef HAVE_LIBBPF_SUPPORT #ifdef HAVE_LIBBPF_SUPPORT
static int process_bpf_prog_info(struct feat_fd *ff, void *data __maybe_unused) 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 bpf_prog_info_node *info_node;
struct perf_env *env = &ff->ph->env; struct perf_env *env = &ff->ph->env;
struct perf_bpil *info_linear;
u32 count, i; u32 count, i;
int err = -1; int err = -1;
...@@ -3049,7 +3050,7 @@ static int process_bpf_prog_info(struct feat_fd *ff, void *data __maybe_unused) ...@@ -3049,7 +3050,7 @@ static int process_bpf_prog_info(struct feat_fd *ff, void *data __maybe_unused)
goto out; goto out;
} }
info_linear = malloc(sizeof(struct bpf_prog_info_linear) + info_linear = malloc(sizeof(struct perf_bpil) +
data_len); data_len);
if (!info_linear) if (!info_linear)
goto out; goto out;
...@@ -3071,7 +3072,7 @@ static int process_bpf_prog_info(struct feat_fd *ff, void *data __maybe_unused) ...@@ -3071,7 +3072,7 @@ static int process_bpf_prog_info(struct feat_fd *ff, void *data __maybe_unused)
goto out; goto out;
/* after reading from file, translate offset to address */ /* after reading from file, translate offset to address */
bpf_program__bpil_offs_to_addr(info_linear); bpil_offs_to_addr(info_linear);
info_node->info_linear = info_linear; info_node->info_linear = info_linear;
perf_env__insert_bpf_prog_info(env, info_node); perf_env__insert_bpf_prog_info(env, info_node);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment