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:
Ingo Molnar 2015-11-28 10:49:10 +01:00
commit ac675d0d58
10 changed files with 268 additions and 111 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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)) {

View File

@ -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)

View File

@ -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;
}

View File

@ -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 */

View File

@ -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)

View File

@ -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;

View File

@ -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;