perf/core improvements and fixes:
User visible: - Fix 'perf list' segfault due to lack of support for PERF_CONF_SW_BPF_OUTPUT in an array used just for printing available events, robustify the code involved (Arnaldo Carvalho de Melo) - 'perf test unwind' should create kernel maps, now that entry works and the test passes (Jiri Olsa) - Fix showing the running kernel build id in 'perf buildid-list' (Michael Petlan) - Fix command line symbol filtering in 'perf report' (Namhyung Kim) Infrastructure: - Extract and collect map info from BPF object files in libbpf (Wang Nan) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWWQE0AAoJENZQFvNTUqpAwnUP/3/4ObYvypD+QWw3FAFwHYHf 0Wp1qP9kpzT1Up+8kDEUs4idhoAQ2O0uKT6k7ThyZfyvHISg2P6C2fcGDt1txrDu Lm/eO5IyzohSS0xn0ubQW3IjAaiOJXnMiB3dg0LYulO7rDF4aTogbaZhVtyTiw/t dlkYq3exh/0uzdm14os72BfeTxVPiSQmNsMUiM5nKJHYK/VxopKexFJcVUTGyavd rkzKK0StMsQOPtEBcT12TUHpLh0Fka0eD62Qp4o2LKTK19ck9GzpeyHwAY2D8t23 8zNimZix3ZieF0TufHPDQZMomgpDizpqP5TpP9k4eTV9hUHvrFBRybOyrPB7d//l CXfyWxDPfnWN7KftsCVIDSkKoWzrtTtsw8fJDqYKHDetxD4Nuh5CprgC90WxzuKy aoCqopjjq+YELLTotsh25E5ZcduHCiLZU/FHVEqd9LW1JSAWIcG0XtOIDm2BrXoI C1pfkh7z9/+QgGE0M7eU8lPTm7AWO4fIhqHF/f0Yr6QB6gqeYkD0ZkwCVyMqk8Dp wn/nwxDUELCzP6+QHbhItCk5qZVH/+lAPF68A/K4paQheRTNjO4Cs9MFrVEqHROK DjBM61vEkJ1OBap+vx91KCggTBVIwWZcHzPkPPYJQWGnSp+MgBuz/BChc1+GKB1S 68DXMb7qaGKK5sVcWrTS =FhYQ -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: User visible changes: - Fix 'perf list' segfault due to lack of support for PERF_CONF_SW_BPF_OUTPUT in an array used just for printing available events, robustify the code involved (Arnaldo Carvalho de Melo) - 'perf test unwind' should create kernel maps, now that entry works and the test passes (Jiri Olsa) - Fix showing the running kernel build id in 'perf buildid-list' (Michael Petlan) - Fix command line symbol filtering in 'perf report' (Namhyung Kim) Infrastructure changes: - Extract and collect map info from BPF object files in libbpf (Wang Nan) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
ac675d0d58
|
@ -163,22 +163,25 @@ struct bpf_program {
|
|||
bpf_program_clear_priv_t clear_priv;
|
||||
};
|
||||
|
||||
struct bpf_map {
|
||||
int fd;
|
||||
char *name;
|
||||
struct bpf_map_def def;
|
||||
void *priv;
|
||||
bpf_map_clear_priv_t clear_priv;
|
||||
};
|
||||
|
||||
static LIST_HEAD(bpf_objects_list);
|
||||
|
||||
struct bpf_object {
|
||||
char license[64];
|
||||
u32 kern_version;
|
||||
void *maps_buf;
|
||||
size_t maps_buf_sz;
|
||||
|
||||
struct bpf_program *programs;
|
||||
size_t nr_programs;
|
||||
int *map_fds;
|
||||
/*
|
||||
* This field is required because maps_buf will be freed and
|
||||
* maps_buf_sz will be set to 0 after loaded.
|
||||
*/
|
||||
size_t nr_map_fds;
|
||||
struct bpf_map *maps;
|
||||
size_t nr_maps;
|
||||
|
||||
bool loaded;
|
||||
|
||||
/*
|
||||
|
@ -489,30 +492,81 @@ static int
|
|||
bpf_object__init_maps(struct bpf_object *obj, void *data,
|
||||
size_t size)
|
||||
{
|
||||
if (size == 0) {
|
||||
size_t nr_maps;
|
||||
int i;
|
||||
|
||||
nr_maps = size / sizeof(struct bpf_map_def);
|
||||
if (!data || !nr_maps) {
|
||||
pr_debug("%s doesn't need map definition\n",
|
||||
obj->path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
obj->maps_buf = malloc(size);
|
||||
if (!obj->maps_buf) {
|
||||
pr_warning("malloc maps failed: %s\n", obj->path);
|
||||
pr_debug("maps in %s: %zd bytes\n", obj->path, size);
|
||||
|
||||
obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));
|
||||
if (!obj->maps) {
|
||||
pr_warning("alloc maps for object failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
obj->nr_maps = nr_maps;
|
||||
|
||||
obj->maps_buf_sz = size;
|
||||
memcpy(obj->maps_buf, data, size);
|
||||
pr_debug("maps in %s: %ld bytes\n", obj->path, (long)size);
|
||||
for (i = 0; i < nr_maps; i++) {
|
||||
struct bpf_map_def *def = &obj->maps[i].def;
|
||||
|
||||
/*
|
||||
* fill all fd with -1 so won't close incorrect
|
||||
* fd (fd=0 is stdin) when failure (zclose won't close
|
||||
* negative fd)).
|
||||
*/
|
||||
obj->maps[i].fd = -1;
|
||||
|
||||
/* Save map definition into obj->maps */
|
||||
*def = ((struct bpf_map_def *)data)[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
bpf_object__init_maps_name(struct bpf_object *obj, int maps_shndx)
|
||||
{
|
||||
int i;
|
||||
Elf_Data *symbols = obj->efile.symbols;
|
||||
|
||||
if (!symbols || maps_shndx < 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) {
|
||||
GElf_Sym sym;
|
||||
size_t map_idx;
|
||||
const char *map_name;
|
||||
|
||||
if (!gelf_getsym(symbols, i, &sym))
|
||||
continue;
|
||||
if (sym.st_shndx != maps_shndx)
|
||||
continue;
|
||||
|
||||
map_name = elf_strptr(obj->efile.elf,
|
||||
obj->efile.ehdr.e_shstrndx,
|
||||
sym.st_name);
|
||||
map_idx = sym.st_value / sizeof(struct bpf_map_def);
|
||||
if (map_idx >= obj->nr_maps) {
|
||||
pr_warning("index of map \"%s\" is buggy: %zu > %zu\n",
|
||||
map_name, map_idx, obj->nr_maps);
|
||||
continue;
|
||||
}
|
||||
obj->maps[map_idx].name = strdup(map_name);
|
||||
pr_debug("map %zu is \"%s\"\n", map_idx,
|
||||
obj->maps[map_idx].name);
|
||||
}
|
||||
}
|
||||
|
||||
static int bpf_object__elf_collect(struct bpf_object *obj)
|
||||
{
|
||||
Elf *elf = obj->efile.elf;
|
||||
GElf_Ehdr *ep = &obj->efile.ehdr;
|
||||
Elf_Scn *scn = NULL;
|
||||
int idx = 0, err = 0;
|
||||
int idx = 0, err = 0, maps_shndx = -1;
|
||||
|
||||
/* Elf is corrupted/truncated, avoid calling elf_strptr. */
|
||||
if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) {
|
||||
|
@ -562,10 +616,11 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
|
|||
err = bpf_object__init_kversion(obj,
|
||||
data->d_buf,
|
||||
data->d_size);
|
||||
else if (strcmp(name, "maps") == 0)
|
||||
else if (strcmp(name, "maps") == 0) {
|
||||
err = bpf_object__init_maps(obj, data->d_buf,
|
||||
data->d_size);
|
||||
else if (sh.sh_type == SHT_SYMTAB) {
|
||||
maps_shndx = idx;
|
||||
} else if (sh.sh_type == SHT_SYMTAB) {
|
||||
if (obj->efile.symbols) {
|
||||
pr_warning("bpf: multiple SYMTAB in %s\n",
|
||||
obj->path);
|
||||
|
@ -606,6 +661,9 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
|
|||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (maps_shndx >= 0)
|
||||
bpf_object__init_maps_name(obj, maps_shndx);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
@ -688,37 +746,15 @@ static int
|
|||
bpf_object__create_maps(struct bpf_object *obj)
|
||||
{
|
||||
unsigned int i;
|
||||
size_t nr_maps;
|
||||
int *pfd;
|
||||
|
||||
nr_maps = obj->maps_buf_sz / sizeof(struct bpf_map_def);
|
||||
if (!obj->maps_buf || !nr_maps) {
|
||||
pr_debug("don't need create maps for %s\n",
|
||||
obj->path);
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < obj->nr_maps; i++) {
|
||||
struct bpf_map_def *def = &obj->maps[i].def;
|
||||
int *pfd = &obj->maps[i].fd;
|
||||
|
||||
obj->map_fds = malloc(sizeof(int) * nr_maps);
|
||||
if (!obj->map_fds) {
|
||||
pr_warning("realloc perf_bpf_map_fds failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
obj->nr_map_fds = nr_maps;
|
||||
|
||||
/* fill all fd with -1 */
|
||||
memset(obj->map_fds, -1, sizeof(int) * nr_maps);
|
||||
|
||||
pfd = obj->map_fds;
|
||||
for (i = 0; i < nr_maps; i++) {
|
||||
struct bpf_map_def def;
|
||||
|
||||
def = *(struct bpf_map_def *)(obj->maps_buf +
|
||||
i * sizeof(struct bpf_map_def));
|
||||
|
||||
*pfd = bpf_create_map(def.type,
|
||||
def.key_size,
|
||||
def.value_size,
|
||||
def.max_entries);
|
||||
*pfd = bpf_create_map(def->type,
|
||||
def->key_size,
|
||||
def->value_size,
|
||||
def->max_entries);
|
||||
if (*pfd < 0) {
|
||||
size_t j;
|
||||
int err = *pfd;
|
||||
|
@ -726,22 +762,17 @@ bpf_object__create_maps(struct bpf_object *obj)
|
|||
pr_warning("failed to create map: %s\n",
|
||||
strerror(errno));
|
||||
for (j = 0; j < i; j++)
|
||||
zclose(obj->map_fds[j]);
|
||||
obj->nr_map_fds = 0;
|
||||
zfree(&obj->map_fds);
|
||||
zclose(obj->maps[j].fd);
|
||||
return err;
|
||||
}
|
||||
pr_debug("create map: fd=%d\n", *pfd);
|
||||
pfd++;
|
||||
}
|
||||
|
||||
zfree(&obj->maps_buf);
|
||||
obj->maps_buf_sz = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
bpf_program__relocate(struct bpf_program *prog, int *map_fds)
|
||||
bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -761,7 +792,7 @@ bpf_program__relocate(struct bpf_program *prog, int *map_fds)
|
|||
return -LIBBPF_ERRNO__RELOC;
|
||||
}
|
||||
insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
|
||||
insns[insn_idx].imm = map_fds[map_idx];
|
||||
insns[insn_idx].imm = obj->maps[map_idx].fd;
|
||||
}
|
||||
|
||||
zfree(&prog->reloc_desc);
|
||||
|
@ -780,7 +811,7 @@ bpf_object__relocate(struct bpf_object *obj)
|
|||
for (i = 0; i < obj->nr_programs; i++) {
|
||||
prog = &obj->programs[i];
|
||||
|
||||
err = bpf_program__relocate(prog, obj->map_fds);
|
||||
err = bpf_program__relocate(prog, obj);
|
||||
if (err) {
|
||||
pr_warning("failed to relocate '%s'\n",
|
||||
prog->section_name);
|
||||
|
@ -804,8 +835,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
|
|||
Elf_Data *data = obj->efile.reloc[i].data;
|
||||
int idx = shdr->sh_info;
|
||||
struct bpf_program *prog;
|
||||
size_t nr_maps = obj->maps_buf_sz /
|
||||
sizeof(struct bpf_map_def);
|
||||
size_t nr_maps = obj->nr_maps;
|
||||
|
||||
if (shdr->sh_type != SHT_REL) {
|
||||
pr_warning("internal error at %d\n", __LINE__);
|
||||
|
@ -1050,10 +1080,8 @@ int bpf_object__unload(struct bpf_object *obj)
|
|||
if (!obj)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < obj->nr_map_fds; i++)
|
||||
zclose(obj->map_fds[i]);
|
||||
zfree(&obj->map_fds);
|
||||
obj->nr_map_fds = 0;
|
||||
for (i = 0; i < obj->nr_maps; i++)
|
||||
zclose(obj->maps[i].fd);
|
||||
|
||||
for (i = 0; i < obj->nr_programs; i++)
|
||||
bpf_program__unload(&obj->programs[i]);
|
||||
|
@ -1096,7 +1124,16 @@ void bpf_object__close(struct bpf_object *obj)
|
|||
bpf_object__elf_finish(obj);
|
||||
bpf_object__unload(obj);
|
||||
|
||||
zfree(&obj->maps_buf);
|
||||
for (i = 0; i < obj->nr_maps; i++) {
|
||||
zfree(&obj->maps[i].name);
|
||||
if (obj->maps[i].clear_priv)
|
||||
obj->maps[i].clear_priv(&obj->maps[i],
|
||||
obj->maps[i].priv);
|
||||
obj->maps[i].priv = NULL;
|
||||
obj->maps[i].clear_priv = NULL;
|
||||
}
|
||||
zfree(&obj->maps);
|
||||
obj->nr_maps = 0;
|
||||
|
||||
if (obj->programs && obj->nr_programs) {
|
||||
for (i = 0; i < obj->nr_programs; i++)
|
||||
|
@ -1251,3 +1288,92 @@ int bpf_program__nth_fd(struct bpf_program *prog, int n)
|
|||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int bpf_map__get_fd(struct bpf_map *map)
|
||||
{
|
||||
if (!map)
|
||||
return -EINVAL;
|
||||
|
||||
return map->fd;
|
||||
}
|
||||
|
||||
int bpf_map__get_def(struct bpf_map *map, struct bpf_map_def *pdef)
|
||||
{
|
||||
if (!map || !pdef)
|
||||
return -EINVAL;
|
||||
|
||||
*pdef = map->def;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *bpf_map__get_name(struct bpf_map *map)
|
||||
{
|
||||
if (!map)
|
||||
return NULL;
|
||||
return map->name;
|
||||
}
|
||||
|
||||
int bpf_map__set_private(struct bpf_map *map, void *priv,
|
||||
bpf_map_clear_priv_t clear_priv)
|
||||
{
|
||||
if (!map)
|
||||
return -EINVAL;
|
||||
|
||||
if (map->priv) {
|
||||
if (map->clear_priv)
|
||||
map->clear_priv(map, map->priv);
|
||||
}
|
||||
|
||||
map->priv = priv;
|
||||
map->clear_priv = clear_priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bpf_map__get_private(struct bpf_map *map, void **ppriv)
|
||||
{
|
||||
if (!map)
|
||||
return -EINVAL;
|
||||
|
||||
if (ppriv)
|
||||
*ppriv = map->priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bpf_map *
|
||||
bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
|
||||
{
|
||||
size_t idx;
|
||||
struct bpf_map *s, *e;
|
||||
|
||||
if (!obj || !obj->maps)
|
||||
return NULL;
|
||||
|
||||
s = obj->maps;
|
||||
e = obj->maps + obj->nr_maps;
|
||||
|
||||
if (prev == NULL)
|
||||
return s;
|
||||
|
||||
if ((prev < s) || (prev >= e)) {
|
||||
pr_warning("error in %s: map handler doesn't belong to object\n",
|
||||
__func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
idx = (prev - obj->maps) + 1;
|
||||
if (idx >= obj->nr_maps)
|
||||
return NULL;
|
||||
return &obj->maps[idx];
|
||||
}
|
||||
|
||||
struct bpf_map *
|
||||
bpf_object__get_map_by_name(struct bpf_object *obj, const char *name)
|
||||
{
|
||||
struct bpf_map *pos;
|
||||
|
||||
bpf_map__for_each(pos, obj) {
|
||||
if (strcmp(pos->name, name) == 0)
|
||||
return pos;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -165,4 +165,28 @@ struct bpf_map_def {
|
|||
unsigned int max_entries;
|
||||
};
|
||||
|
||||
/*
|
||||
* There is another 'struct bpf_map' in include/linux/map.h. However,
|
||||
* it is not a uapi header so no need to consider name clash.
|
||||
*/
|
||||
struct bpf_map;
|
||||
struct bpf_map *
|
||||
bpf_object__get_map_by_name(struct bpf_object *obj, const char *name);
|
||||
|
||||
struct bpf_map *
|
||||
bpf_map__next(struct bpf_map *map, struct bpf_object *obj);
|
||||
#define bpf_map__for_each(pos, obj) \
|
||||
for ((pos) = bpf_map__next(NULL, (obj)); \
|
||||
(pos) != NULL; \
|
||||
(pos) = bpf_map__next((pos), (obj)))
|
||||
|
||||
int bpf_map__get_fd(struct bpf_map *map);
|
||||
int bpf_map__get_def(struct bpf_map *map, struct bpf_map_def *pdef);
|
||||
const char *bpf_map__get_name(struct bpf_map *map);
|
||||
|
||||
typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *);
|
||||
int bpf_map__set_private(struct bpf_map *map, void *priv,
|
||||
bpf_map_clear_priv_t clear_priv);
|
||||
int bpf_map__get_private(struct bpf_map *map, void **ppriv);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -513,20 +513,26 @@ static int __cmd_report(struct report *rep)
|
|||
if (rep->cpu_list) {
|
||||
ret = perf_session__cpu_bitmap(session, rep->cpu_list,
|
||||
rep->cpu_bitmap);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
ui__error("failed to set cpu bitmap\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (rep->show_threads)
|
||||
perf_read_values_init(&rep->show_threads_values);
|
||||
|
||||
ret = report__setup_sample_type(rep);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
/* report__setup_sample_type() already showed error message */
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = perf_session__process_events(session);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
ui__error("failed to process sample\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
report__warn_kptr_restrict(rep);
|
||||
|
||||
|
|
|
@ -173,6 +173,11 @@ int test__dwarf_unwind(int subtest __maybe_unused)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (machine__create_kernel_maps(machine)) {
|
||||
pr_err("Failed to create kernel maps\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
callchain_param.record_mode = CALLCHAIN_DWARF;
|
||||
|
||||
if (init_live_machine(machine)) {
|
||||
|
|
|
@ -2055,10 +2055,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|||
SLang_reset_tty();
|
||||
SLang_init_tty(0, 0, 0);
|
||||
|
||||
if (min_pcnt) {
|
||||
if (min_pcnt)
|
||||
browser->min_pcnt = min_pcnt;
|
||||
hist_browser__update_nr_entries(browser);
|
||||
}
|
||||
hist_browser__update_nr_entries(browser);
|
||||
|
||||
browser->pstack = pstack__new(3);
|
||||
if (browser->pstack == NULL)
|
||||
|
|
|
@ -120,7 +120,7 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
|
|||
}
|
||||
|
||||
static int
|
||||
config__exec(const char *value, struct perf_probe_event *pev)
|
||||
prog_config__exec(const char *value, struct perf_probe_event *pev)
|
||||
{
|
||||
pev->uprobes = true;
|
||||
pev->target = strdup(value);
|
||||
|
@ -130,7 +130,7 @@ config__exec(const char *value, struct perf_probe_event *pev)
|
|||
}
|
||||
|
||||
static int
|
||||
config__module(const char *value, struct perf_probe_event *pev)
|
||||
prog_config__module(const char *value, struct perf_probe_event *pev)
|
||||
{
|
||||
pev->uprobes = false;
|
||||
pev->target = strdup(value);
|
||||
|
@ -140,8 +140,7 @@ config__module(const char *value, struct perf_probe_event *pev)
|
|||
}
|
||||
|
||||
static int
|
||||
config__bool(const char *value,
|
||||
bool *pbool, bool invert)
|
||||
prog_config__bool(const char *value, bool *pbool, bool invert)
|
||||
{
|
||||
int err;
|
||||
bool bool_value;
|
||||
|
@ -158,17 +157,17 @@ config__bool(const char *value,
|
|||
}
|
||||
|
||||
static int
|
||||
config__inlines(const char *value,
|
||||
struct perf_probe_event *pev __maybe_unused)
|
||||
prog_config__inlines(const char *value,
|
||||
struct perf_probe_event *pev __maybe_unused)
|
||||
{
|
||||
return config__bool(value, &probe_conf.no_inlines, true);
|
||||
return prog_config__bool(value, &probe_conf.no_inlines, true);
|
||||
}
|
||||
|
||||
static int
|
||||
config__force(const char *value,
|
||||
struct perf_probe_event *pev __maybe_unused)
|
||||
prog_config__force(const char *value,
|
||||
struct perf_probe_event *pev __maybe_unused)
|
||||
{
|
||||
return config__bool(value, &probe_conf.force_add, false);
|
||||
return prog_config__bool(value, &probe_conf.force_add, false);
|
||||
}
|
||||
|
||||
static struct {
|
||||
|
@ -176,58 +175,58 @@ static struct {
|
|||
const char *usage;
|
||||
const char *desc;
|
||||
int (*func)(const char *, struct perf_probe_event *);
|
||||
} bpf_config_terms[] = {
|
||||
} bpf_prog_config_terms[] = {
|
||||
{
|
||||
.key = "exec",
|
||||
.usage = "exec=<full path of file>",
|
||||
.desc = "Set uprobe target",
|
||||
.func = config__exec,
|
||||
.func = prog_config__exec,
|
||||
},
|
||||
{
|
||||
.key = "module",
|
||||
.usage = "module=<module name> ",
|
||||
.desc = "Set kprobe module",
|
||||
.func = config__module,
|
||||
.func = prog_config__module,
|
||||
},
|
||||
{
|
||||
.key = "inlines",
|
||||
.usage = "inlines=[yes|no] ",
|
||||
.desc = "Probe at inline symbol",
|
||||
.func = config__inlines,
|
||||
.func = prog_config__inlines,
|
||||
},
|
||||
{
|
||||
.key = "force",
|
||||
.usage = "force=[yes|no] ",
|
||||
.desc = "Forcibly add events with existing name",
|
||||
.func = config__force,
|
||||
.func = prog_config__force,
|
||||
},
|
||||
};
|
||||
|
||||
static int
|
||||
do_config(const char *key, const char *value,
|
||||
struct perf_probe_event *pev)
|
||||
do_prog_config(const char *key, const char *value,
|
||||
struct perf_probe_event *pev)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
pr_debug("config bpf program: %s=%s\n", key, value);
|
||||
for (i = 0; i < ARRAY_SIZE(bpf_config_terms); i++)
|
||||
if (strcmp(key, bpf_config_terms[i].key) == 0)
|
||||
return bpf_config_terms[i].func(value, pev);
|
||||
for (i = 0; i < ARRAY_SIZE(bpf_prog_config_terms); i++)
|
||||
if (strcmp(key, bpf_prog_config_terms[i].key) == 0)
|
||||
return bpf_prog_config_terms[i].func(value, pev);
|
||||
|
||||
pr_debug("BPF: ERROR: invalid config option in object: %s=%s\n",
|
||||
pr_debug("BPF: ERROR: invalid program config option: %s=%s\n",
|
||||
key, value);
|
||||
|
||||
pr_debug("\nHint: Currently valid options are:\n");
|
||||
for (i = 0; i < ARRAY_SIZE(bpf_config_terms); i++)
|
||||
pr_debug("\t%s:\t%s\n", bpf_config_terms[i].usage,
|
||||
bpf_config_terms[i].desc);
|
||||
pr_debug("\nHint: Valid options are:\n");
|
||||
for (i = 0; i < ARRAY_SIZE(bpf_prog_config_terms); i++)
|
||||
pr_debug("\t%s:\t%s\n", bpf_prog_config_terms[i].usage,
|
||||
bpf_prog_config_terms[i].desc);
|
||||
pr_debug("\n");
|
||||
|
||||
return -BPF_LOADER_ERRNO__CONFIG_TERM;
|
||||
return -BPF_LOADER_ERRNO__PROGCONF_TERM;
|
||||
}
|
||||
|
||||
static const char *
|
||||
parse_config_kvpair(const char *config_str, struct perf_probe_event *pev)
|
||||
parse_prog_config_kvpair(const char *config_str, struct perf_probe_event *pev)
|
||||
{
|
||||
char *text = strdup(config_str);
|
||||
char *sep, *line;
|
||||
|
@ -253,7 +252,7 @@ parse_config_kvpair(const char *config_str, struct perf_probe_event *pev)
|
|||
}
|
||||
*equ = '\0';
|
||||
|
||||
err = do_config(line, equ + 1, pev);
|
||||
err = do_prog_config(line, equ + 1, pev);
|
||||
if (err)
|
||||
break;
|
||||
nextline:
|
||||
|
@ -268,10 +267,10 @@ nextline:
|
|||
}
|
||||
|
||||
static int
|
||||
parse_config(const char *config_str, struct perf_probe_event *pev)
|
||||
parse_prog_config(const char *config_str, struct perf_probe_event *pev)
|
||||
{
|
||||
int err;
|
||||
const char *main_str = parse_config_kvpair(config_str, pev);
|
||||
const char *main_str = parse_prog_config_kvpair(config_str, pev);
|
||||
|
||||
if (IS_ERR(main_str))
|
||||
return PTR_ERR(main_str);
|
||||
|
@ -312,7 +311,7 @@ config_bpf_program(struct bpf_program *prog)
|
|||
pev = &priv->pev;
|
||||
|
||||
pr_debug("bpf: config program '%s'\n", config_str);
|
||||
err = parse_config(config_str, pev);
|
||||
err = parse_prog_config(config_str, pev);
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
|
@ -750,7 +749,7 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = {
|
|||
[ERRCODE_OFFSET(EVENTNAME)] = "No event name found in config string",
|
||||
[ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error",
|
||||
[ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet",
|
||||
[ERRCODE_OFFSET(CONFIG_TERM)] = "Invalid config term in config string",
|
||||
[ERRCODE_OFFSET(PROGCONF_TERM)] = "Invalid program config term in config string",
|
||||
[ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue",
|
||||
[ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program",
|
||||
[ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue",
|
||||
|
@ -834,7 +833,7 @@ int bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
|
|||
int err, char *buf, size_t size)
|
||||
{
|
||||
bpf__strerror_head(err, buf, size);
|
||||
case BPF_LOADER_ERRNO__CONFIG_TERM: {
|
||||
case BPF_LOADER_ERRNO__PROGCONF_TERM: {
|
||||
scnprintf(buf, size, "%s (add -v to see detail)", emsg);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ enum bpf_loader_errno {
|
|||
BPF_LOADER_ERRNO__EVENTNAME, /* Event name is missing */
|
||||
BPF_LOADER_ERRNO__INTERNAL, /* BPF loader internal error */
|
||||
BPF_LOADER_ERRNO__COMPILE, /* Error when compiling BPF scriptlet */
|
||||
BPF_LOADER_ERRNO__CONFIG_TERM, /* Invalid config term in config term */
|
||||
BPF_LOADER_ERRNO__PROGCONF_TERM,/* Invalid program config term in config string */
|
||||
BPF_LOADER_ERRNO__PROLOGUE, /* Failed to generate prologue */
|
||||
BPF_LOADER_ERRNO__PROLOGUE2BIG, /* Prologue too big for program */
|
||||
BPF_LOADER_ERRNO__PROLOGUEOOB, /* Offset out of bound for prologue */
|
||||
|
|
|
@ -91,7 +91,7 @@ int build_id__sprintf(const u8 *build_id, int len, char *bf)
|
|||
bid += 2;
|
||||
}
|
||||
|
||||
return raw - build_id;
|
||||
return (bid - bf) + 1;
|
||||
}
|
||||
|
||||
int sysfs__sprintf_build_id(const char *root_dir, char *sbuild_id)
|
||||
|
|
|
@ -924,9 +924,6 @@ hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
|
|||
int64_t cmp = 0;
|
||||
|
||||
perf_hpp__for_each_sort_list(fmt) {
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
cmp = fmt->cmp(fmt, left, right);
|
||||
if (cmp)
|
||||
break;
|
||||
|
@ -942,9 +939,6 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
|
|||
int64_t cmp = 0;
|
||||
|
||||
perf_hpp__for_each_sort_list(fmt) {
|
||||
if (perf_hpp__should_skip(fmt))
|
||||
continue;
|
||||
|
||||
cmp = fmt->collapse(fmt, left, right);
|
||||
if (cmp)
|
||||
break;
|
||||
|
|
|
@ -124,6 +124,10 @@ struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
|
|||
.symbol = "dummy",
|
||||
.alias = "",
|
||||
},
|
||||
[PERF_COUNT_SW_BPF_OUTPUT] = {
|
||||
.symbol = "bpf-output",
|
||||
.alias = "",
|
||||
},
|
||||
};
|
||||
|
||||
#define __PERF_EVENT_FIELD(config, name) \
|
||||
|
@ -1879,7 +1883,7 @@ restart:
|
|||
|
||||
for (i = 0; i < max; i++, syms++) {
|
||||
|
||||
if (event_glob != NULL &&
|
||||
if (event_glob != NULL && syms->symbol != NULL &&
|
||||
!(strglobmatch(syms->symbol, event_glob) ||
|
||||
(syms->alias && strglobmatch(syms->alias, event_glob))))
|
||||
continue;
|
||||
|
|
Loading…
Reference in New Issue