2015-11-27 08:51:58

by Wang Nan

[permalink] [raw]
Subject: [PATCH v2 00/13] perf tools: BPF related update

This patch set is based on perf/core.

Compare with v1:
- Patch 1/13 and 9/13: make code compat based on Arnaldo's suggestion;
- Patch 4/13: fix a bug which incorrectly use list_is_singular() test
whether a list entry is deleted from a list;
- Patch 13/13: change another test case.

This patch set improves perf's BPF support:

- Support filling BPF array with values

Users are allowed to pass something to BPF program through command
line without changing the program itself.

- Support filling BPF event array with events

BPF program can read PMU counters through BPF's perf_event_read()
helper.

- Support bpf_output_event() helper

BPF program can issue perf event to perf.data.

In most of the patches I list commands for testing them, both normal
case and error case.

He Kuang (1):
perf tools: Support perf event alias name

Wang Nan (12):
bpf tools: Collect map definition in bpf_object
bpf tools: Extract and collect map names from BPF object file
perf tools: Rename bpf config to program config
perf tools: Add API to config maps in bpf object
perf tools: Enable BPF object configure syntax
perf record: Apply config to BPF objects before recording
perf tools: Enable passing event to BPF object
perf tools: Support setting different slots in a BPF map separately
perf tools: Enable indices setting syntax for BPF maps
perf tools: Introduce bpf-output event
perf data: Add u32_hex data type
perf data: Support converting data from bpf_perf_event_output()

tools/lib/bpf/libbpf.c | 252 +++++++++----
tools/lib/bpf/libbpf.h | 24 ++
tools/perf/builtin-record.c | 11 +
tools/perf/util/bpf-loader.c | 765 ++++++++++++++++++++++++++++++++++++--
tools/perf/util/bpf-loader.h | 61 ++-
tools/perf/util/data-convert-bt.c | 117 +++++-
tools/perf/util/evlist.c | 16 +
tools/perf/util/evlist.h | 4 +
tools/perf/util/evsel.c | 7 +
tools/perf/util/evsel.h | 1 +
tools/perf/util/parse-events.c | 129 ++++++-
tools/perf/util/parse-events.h | 20 +-
tools/perf/util/parse-events.l | 16 +-
tools/perf/util/parse-events.y | 123 +++++-
14 files changed, 1433 insertions(+), 113 deletions(-)

--
1.8.3.4


2015-11-27 08:52:06

by Wang Nan

[permalink] [raw]
Subject: [PATCH v2 01/13] bpf tools: Collect map definition in bpf_object

This patch collects more information from maps sections in BPF object
files into 'struct bpf_object', enables later patches access those
information (such as the type and size of the map).

In this patch, a new handler 'struct bpf_map' is extracted in parallel
with bpf_object and bpf_program. Its iterator and accessor is also
created.

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/lib/bpf/libbpf.c | 187 +++++++++++++++++++++++++++++++++----------------
tools/lib/bpf/libbpf.h | 21 ++++++
2 files changed, 148 insertions(+), 60 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index e3f4c33..f509825 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -163,22 +163,24 @@ struct bpf_program {
bpf_program_clear_priv_t clear_priv;
};

+struct bpf_map {
+ int fd;
+ 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,21 +491,38 @@ 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;
+
+ for (i = 0; i < nr_maps; i++) {
+ struct bpf_map_def *def = &obj->maps[i].def;

- obj->maps_buf_sz = size;
- memcpy(obj->maps_buf, data, size);
- pr_debug("maps in %s: %ld bytes\n", obj->path, (long)size);
+ /*
+ * 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;
}

@@ -688,37 +707,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;
- }

- 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);
+ for (i = 0; i < obj->nr_maps; i++) {
+ struct bpf_map_def *def = &obj->maps[i].def;
+ int *pfd = &obj->maps[i].fd;

- 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 +723,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 +753,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 +772,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 +796,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 +1041,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 +1085,15 @@ 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++) {
+ 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 +1248,73 @@ 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;
+}
+
+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];
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 949df4b..ef63125 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -165,4 +165,25 @@ 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_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);
+
+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
--
1.8.3.4

2015-11-27 08:48:41

by Wang Nan

[permalink] [raw]
Subject: [PATCH v2 02/13] bpf tools: Extract and collect map names from BPF object file

This patch collects name of maps in BPF object files and saves them into
'maps' field in 'struct bpf_object'. 'bpf_object__get_map_by_name' is
introduced to retrive fd and definitions of a map through its name.

Signed-off-by: Wang Nan <[email protected]>
Signed-off-by: He Kuang <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/lib/bpf/libbpf.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++---
tools/lib/bpf/libbpf.h | 3 +++
2 files changed, 65 insertions(+), 3 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index f509825..a298614 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -165,6 +165,7 @@ struct bpf_program {

struct bpf_map {
int fd;
+ char *name;
struct bpf_map_def def;
void *priv;
bpf_map_clear_priv_t clear_priv;
@@ -526,12 +527,46 @@ bpf_object__init_maps(struct bpf_object *obj, void *data,
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)) {
@@ -581,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);
@@ -625,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;
}
@@ -1086,6 +1125,7 @@ void bpf_object__close(struct bpf_object *obj)
bpf_object__unload(obj);

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);
@@ -1266,6 +1306,13 @@ int bpf_map__get_def(struct bpf_map *map, struct bpf_map_def *pdef)
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)
{
@@ -1318,3 +1365,15 @@ bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
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;
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index ef63125..a51594c 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -170,6 +170,8 @@ struct bpf_map_def {
* 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);
@@ -180,6 +182,7 @@ bpf_map__next(struct bpf_map *map, struct bpf_object *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,
--
1.8.3.4

2015-11-27 08:48:52

by Wang Nan

[permalink] [raw]
Subject: [PATCH v2 03/13] perf tools: Rename bpf config to program config

Following patches are going to introduce BPF object level
configuration to enable setting values into BPF maps. To avoid
confusion, this patch renames existing 'config' in bpf-loader.c to
'program config'. Following patches would introduce 'object config'.

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/bpf-loader.c | 65 ++++++++++++++++++++++----------------------
tools/perf/util/bpf-loader.h | 2 +-
2 files changed, 33 insertions(+), 34 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 36544e5..540a7ef 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -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;
}
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index a58740b..6fdc045 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -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 */
--
1.8.3.4

2015-11-27 08:50:45

by Wang Nan

[permalink] [raw]
Subject: [PATCH v2 04/13] perf tools: Add API to config maps in bpf object

bpf__config_obj() is introduced as a core API to config BPF object
after loading. One configuration option of maps is introduced. After
this patch BPF object can accept configuration like:

maps:my_map:value=1234

(maps.my_map.value looks pretty. However, there's a small but hard
to fixed problem related to flex's greedy matching. Please see [1].
Choose ':' to avoid it in a simpler way.)

This patch is more complex than the work it really does because the
consideration of extension. In designing of BPF map configuration,
following things should be considered:

1. Array indics selection: perf should allow user setting different
value to different slots in an array, with syntax like:
maps:my_map:value[0,3...6]=1234;

2. A map can be config by different config terms, each for a part
of it. For example, set each slot to pid of a thread;

3. Type of value: integer is not the only valid value type. Perf
event can also be put into a map after commit 35578d7984003097af2b1e3
(bpf: Implement function bpf_perf_event_read() that get the selected
hardware PMU conuter);

4. For hash table, it is possible to use string or other as key;

5. It is possible that map configuration is unable to be setup
during parsing. Perf event is an example.

Therefore, this patch does following:

1. Instead of updating map element during parsing, this patch stores
map config options in 'struct bpf_map_priv'. Following patches
would apply those configs at proper time;

2. Link map operations to a list so a map can have multiple config
terms attached, so different parts can be configured separately;

3. Make 'struct bpf_map_priv' extensible so following patches can
add new types of keys and operations;

4. Use bpf_config_map_funcs array to support more maps config options.

Since the patch changing event parser to parse BPF object config is
relative large, I put in another commit. Code in this patch
could be tested after applying next patch.

[1] http://lkml.kernel.org/g/[email protected]

Signed-off-by: Wang Nan <[email protected]>
Signed-off-by: He Kuang <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/bpf-loader.c | 266 +++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/bpf-loader.h | 38 +++++++
2 files changed, 304 insertions(+)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 540a7ef..989c80f 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -739,6 +739,251 @@ int bpf__foreach_tev(struct bpf_object *obj,
return 0;
}

+enum bpf_map_op_type {
+ BPF_MAP_OP_SET_VALUE,
+};
+
+enum bpf_map_key_type {
+ BPF_MAP_KEY_ALL,
+};
+
+struct bpf_map_op {
+ struct list_head list;
+ enum bpf_map_op_type op_type;
+ enum bpf_map_key_type key_type;
+ union {
+ u64 value;
+ } v;
+};
+
+struct bpf_map_priv {
+ struct list_head ops_list;
+};
+
+static void
+bpf_map_op__free(struct bpf_map_op *op)
+{
+ struct list_head *list = &op->list;
+ /*
+ * bpf_map_op__free() needs to consider following cases:
+ * 1. When the op is created but not linked to any list:
+ * impossible. This only happen in bpf_map_op__alloc()
+ * and it would be freed directly;
+ * 2. Normal case, when the op is linked to a list;
+ * 3. After the op has already be removed.
+ * Thanks to list.h, if it has removed by list_del() then
+ * list->{next,prev} should have been set to LIST_POISON{1,2}.
+ */
+ if ((list->next != LIST_POISON1) && (list->prev != LIST_POISON2))
+ list_del(list);
+ free(op);
+}
+
+static void
+bpf_map_priv__clear(struct bpf_map *map __maybe_unused,
+ void *_priv)
+{
+ struct bpf_map_priv *priv = _priv;
+ struct bpf_map_op *pos, *n;
+
+ list_for_each_entry_safe(pos, n, &priv->ops_list, list)
+ bpf_map_op__free(pos);
+ free(priv);
+}
+
+static struct bpf_map_op *
+bpf_map_op__alloc(struct bpf_map *map)
+{
+ struct bpf_map_op *op;
+ struct bpf_map_priv *priv;
+ const char *map_name;
+ int err;
+
+ map_name = bpf_map__get_name(map);
+ err = bpf_map__get_private(map, (void **)&priv);
+ if (err) {
+ pr_debug("Failed to get private from map %s\n", map_name);
+ return ERR_PTR(err);
+ }
+
+ if (!priv) {
+ priv = zalloc(sizeof(*priv));
+ if (!priv) {
+ pr_debug("No enough memory to alloc map private\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ INIT_LIST_HEAD(&priv->ops_list);
+
+ if (bpf_map__set_private(map, priv, bpf_map_priv__clear)) {
+ free(priv);
+ return ERR_PTR(-BPF_LOADER_ERRNO__INTERNAL);
+ }
+ }
+
+ op = zalloc(sizeof(*op));
+ if (!op) {
+ pr_debug("Failed to alloc bpf_map_op\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ op->key_type = BPF_MAP_KEY_ALL;
+ list_add_tail(&op->list, &priv->ops_list);
+ return op;
+}
+
+static int
+bpf__obj_config_map_array_value(struct bpf_map *map,
+ struct parse_events_term *term)
+{
+ struct bpf_map_def def;
+ struct bpf_map_op *op;
+ const char *map_name;
+ int err;
+
+ map_name = bpf_map__get_name(map);
+
+ err = bpf_map__get_def(map, &def);
+ if (err) {
+ pr_debug("Unable to get map definition from '%s'\n",
+ map_name);
+ return -BPF_LOADER_ERRNO__INTERNAL;
+ }
+
+ if (def.type != BPF_MAP_TYPE_ARRAY) {
+ pr_debug("Map %s type is not BPF_MAP_TYPE_ARRAY\n",
+ map_name);
+ return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE;
+ }
+ if (def.key_size < sizeof(unsigned int)) {
+ pr_debug("Map %s has incorrect key size\n", map_name);
+ return -BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE;
+ }
+ switch (def.value_size) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+ default:
+ pr_debug("Map %s has incorrect value size\n", map_name);
+ return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE;
+ }
+
+ op = bpf_map_op__alloc(map);
+ if (IS_ERR(op))
+ return PTR_ERR(op);
+ op->op_type = BPF_MAP_OP_SET_VALUE;
+ op->v.value = term->val.num;
+ return 0;
+}
+
+static int
+bpf__obj_config_map_value(struct bpf_map *map,
+ struct parse_events_term *term,
+ struct perf_evlist *evlist __maybe_unused)
+{
+ if (!term->err_val) {
+ pr_debug("Config value not set\n");
+ return -BPF_LOADER_ERRNO__OBJCONF_CONF;
+ }
+
+ if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM)
+ return bpf__obj_config_map_array_value(map, term);
+
+ pr_debug("ERROR: wrong value type\n");
+ return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE;
+}
+
+struct bpf_obj_config_map_func {
+ const char *config_opt;
+ int (*config_func)(struct bpf_map *, struct parse_events_term *,
+ struct perf_evlist *);
+};
+
+struct bpf_obj_config_map_func bpf_obj_config_map_funcs[] = {
+ {"value", bpf__obj_config_map_value},
+};
+
+static int
+bpf__obj_config_map(struct bpf_object *obj,
+ struct parse_events_term *term,
+ struct perf_evlist *evlist,
+ int *key_scan_pos)
+{
+ /* key is "maps:<mapname>:<config opt>" */
+ char *map_name = strdup(term->config + sizeof("maps:") - 1);
+ struct bpf_map *map;
+ int err = -BPF_LOADER_ERRNO__OBJCONF_OPT;
+ char *map_opt;
+ size_t i;
+
+ if (!map_name)
+ return -ENOMEM;
+
+ map_opt = strchr(map_name, ':');
+ if (!map_opt) {
+ pr_debug("ERROR: Invalid map config: %s\n", map_name);
+ goto out;
+ }
+
+ *map_opt++ = '\0';
+ if (*map_opt == '\0') {
+ pr_debug("ERROR: Invalid map option: %s\n", term->config);
+ goto out;
+ }
+
+ map = bpf_object__get_map_by_name(obj, map_name);
+ if (!map) {
+ pr_debug("ERROR: Map %s is not exist\n", map_name);
+ err = -BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST;
+ goto out;
+ }
+
+ *key_scan_pos += map_opt - map_name;
+ for (i = 0; i < ARRAY_SIZE(bpf_obj_config_map_funcs); i++) {
+ struct bpf_obj_config_map_func *func =
+ &bpf_obj_config_map_funcs[i];
+
+ if (strcmp(map_opt, func->config_opt) == 0) {
+ err = func->config_func(map, term, evlist);
+ goto out;
+ }
+ }
+
+ pr_debug("ERROR: invalid config option '%s' for maps\n",
+ map_opt);
+ err = -BPF_LOADER_ERRNO__OBJCONF_MAP_OPT;
+out:
+ free(map_name);
+ if (!err)
+ key_scan_pos += strlen(map_opt);
+ return err;
+}
+
+int bpf__config_obj(struct bpf_object *obj,
+ struct parse_events_term *term,
+ struct perf_evlist *evlist,
+ int *error_pos)
+{
+ int key_scan_pos = 0;
+ int err;
+
+ if (!obj || !term || !term->config)
+ return -EINVAL;
+
+ if (!prefixcmp(term->config, "maps:")) {
+ key_scan_pos = sizeof("maps:") - 1;
+ err = bpf__obj_config_map(obj, term, evlist, &key_scan_pos);
+ goto out;
+ }
+ err = -BPF_LOADER_ERRNO__OBJCONF_OPT;
+out:
+ if (error_pos)
+ *error_pos = key_scan_pos;
+ return err;
+
+}
+
#define ERRNO_OFFSET(e) ((e) - __BPF_LOADER_ERRNO__START)
#define ERRCODE_OFFSET(c) ERRNO_OFFSET(BPF_LOADER_ERRNO__##c)
#define NR_ERRNO (__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START)
@@ -753,6 +998,14 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = {
[ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue",
[ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program",
[ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue",
+ [ERRCODE_OFFSET(OBJCONF_OPT)] = "Invalid object config option",
+ [ERRCODE_OFFSET(OBJCONF_CONF)] = "Config value not set (lost '=')",
+ [ERRCODE_OFFSET(OBJCONF_MAP_OPT)] = "Invalid object maps config option",
+ [ERRCODE_OFFSET(OBJCONF_MAP_NOTEXIST)] = "Target map not exist",
+ [ERRCODE_OFFSET(OBJCONF_MAP_VALUE)] = "Incorrect value type for map",
+ [ERRCODE_OFFSET(OBJCONF_MAP_TYPE)] = "Incorrect map type",
+ [ERRCODE_OFFSET(OBJCONF_MAP_KEYSIZE)] = "Incorrect map key size",
+ [ERRCODE_OFFSET(OBJCONF_MAP_VALUESIZE)] = "Incorrect map value size",
};

static int
@@ -872,3 +1125,16 @@ int bpf__strerror_load(struct bpf_object *obj,
bpf__strerror_end(buf, size);
return 0;
}
+
+int bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused,
+ struct parse_events_term *term __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused,
+ int *error_pos __maybe_unused, int err,
+ char *buf, size_t size)
+{
+ bpf__strerror_head(err, buf, size);
+ bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE,
+ "Can't use this config term to this type of map");
+ bpf__strerror_end(buf, size);
+ return 0;
+}
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 6fdc045..2464db9 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -10,6 +10,7 @@
#include <string.h>
#include <bpf/libbpf.h>
#include "probe-event.h"
+#include "evlist.h"
#include "debug.h"

enum bpf_loader_errno {
@@ -24,10 +25,19 @@ enum bpf_loader_errno {
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 */
+ BPF_LOADER_ERRNO__OBJCONF_OPT, /* Invalid object config option */
+ BPF_LOADER_ERRNO__OBJCONF_CONF, /* Config value not set (lost '=')) */
+ BPF_LOADER_ERRNO__OBJCONF_MAP_OPT, /* Invalid object maps config option */
+ BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST, /* Target map not exist */
+ BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE, /* Incorrect value type for map */
+ BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE, /* Incorrect map type */
+ BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE, /* Incorrect map key size */
+ BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE,/* Incorrect map value size */
__BPF_LOADER_ERRNO__END,
};

struct bpf_object;
+struct parse_events_term;
#define PERF_BPF_PROBE_GROUP "perf_bpf_probe"

typedef int (*bpf_prog_iter_callback_t)(struct probe_trace_event *tev,
@@ -53,6 +63,14 @@ int bpf__strerror_load(struct bpf_object *obj, int err,
char *buf, size_t size);
int bpf__foreach_tev(struct bpf_object *obj,
bpf_prog_iter_callback_t func, void *arg);
+
+int bpf__config_obj(struct bpf_object *obj, struct parse_events_term *term,
+ struct perf_evlist *evlist, int *error_pos);
+int bpf__strerror_config_obj(struct bpf_object *obj,
+ struct parse_events_term *term,
+ struct perf_evlist *evlist,
+ int *error_pos, int err, char *buf,
+ size_t size);
#else
static inline struct bpf_object *
bpf__prepare_load(const char *filename __maybe_unused,
@@ -84,6 +102,15 @@ bpf__foreach_tev(struct bpf_object *obj __maybe_unused,
}

static inline int
+bpf__config_obj(struct bpf_object *obj __maybe_unused,
+ struct parse_events_term *term __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused,
+ int *error_pos __maybe_unused)
+{
+ return 0;
+}
+
+static inline int
__bpf_strerror(char *buf, size_t size)
{
if (!size)
@@ -118,5 +145,16 @@ static inline int bpf__strerror_load(struct bpf_object *obj __maybe_unused,
{
return __bpf_strerror(buf, size);
}
+
+static inline int
+bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused,
+ struct parse_events_term *term __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused,
+ int *error_pos __maybe_unused,
+ int err __maybe_unused,
+ char *buf, size_t size)
+{
+ return __bpf_strerror(buf, size);
+}
#endif
#endif
--
1.8.3.4

2015-11-27 08:51:35

by Wang Nan

[permalink] [raw]
Subject: [PATCH v2 05/13] perf tools: Enable BPF object configure syntax

This patch adds the final step for BPF map configuration. A new syntax
is appended into parser so user can config BPF objects through '/' '/'
enclosed config terms.

After this patch, following syntax is available:

# perf record -e bpf_file.c/maps:mymap:value=123/ ...

It would takes effect after appling following commits.

Test result:

# cat ./test_bpf_map_1.c
/************************ BEGIN **************************/
#define SEC(NAME) __attribute__((section(NAME), used))
enum bpf_map_type {
BPF_MAP_TYPE_ARRAY = 2,
};
struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
};
static void *(*map_lookup_elem)(struct bpf_map_def *, void *) =
(void *)1;
static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
(void *)6;
struct bpf_map_def SEC("maps") channel = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = 1,
};
SEC("func=sys_nanosleep")
int func(void *ctx)
{
int key = 0;
char fmt[] = "%d\n";
int *pval = map_lookup_elem(&channel, &key);
if (!pval)
return 0;
bpf_trace_printk(fmt, sizeof(fmt), *pval);
return 0;
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
/************************* END ***************************/

- Normal case:
# ./perf record -e './test_bpf_map_1.c/maps:channel:value=10/' usleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.012 MB perf.data ]

- Error case:

# ./perf record -e './test_bpf_map_1.c/maps:channel:value/' usleep 10
event syntax error: '..ps:channel:value/'
\___ Config value not set (lost '=')
Hint: Valid config term:
maps:[<arraymap>]:value=[value]
(add -v to see detail)
Run 'perf list' for a list of valid events

Usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]

-e, --event <event> event selector. use 'perf list' to list available events

# ./perf record -e './test_bpf_map_1.c/xmaps:channel:value=10/' usleep 10
event syntax error: '..pf_map_1.c/xmaps:channel:value=10/'
\___ Invalid object config option
[SNIP]

# ./perf record -e './test_bpf_map_1.c/maps:xchannel:value=10/' usleep 10
event syntax error: '..p_1.c/maps:xchannel:value=10/'
\___ Target map not exist
[SNIP]

# ./perf record -e './test_bpf_map_1.c/maps:channel:xvalue=10/' usleep 10
event syntax error: '..ps:channel:xvalue=10/'
\___ Invalid object maps config option
[SNIP]

# ./perf record -e './test_bpf_map_1.c/maps:channel:value=x10/' usleep 10
event syntax error: '..ps:channel:xvalue=10/'
\___ Invalid object maps config option
[SNIP]

Change BPF_MAP_TYPE_ARRAY = 2 tp BPF_MAP_TYPE_ARRAY = 1:

# ./perf record -e './test_bpf_map_1.c/maps:channel:value=10/' usleep 10
event syntax error: '..ps:channel:xvalue=10/'
\___ Invalid object maps config option

Hint: Valid config term:
maps:[<arraymap>]:value=[value]
(add -v to see detail)

Signed-off-by: Wang Nan <[email protected]>
Signed-off-by: He Kuang <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/parse-events.c | 56 +++++++++++++++++++++++++++++++++++++++---
tools/perf/util/parse-events.h | 3 ++-
tools/perf/util/parse-events.l | 2 +-
tools/perf/util/parse-events.y | 23 ++++++++++++++---
4 files changed, 75 insertions(+), 9 deletions(-)

diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index e48d9da..6a626cc 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -624,17 +624,64 @@ errout:
return err;
}

+static int
+parse_events_config_bpf(struct parse_events_evlist *data,
+ struct bpf_object *obj,
+ struct list_head *head_config)
+{
+ struct parse_events_term *term;
+ int error_pos;
+
+ if (!head_config || list_empty(head_config))
+ return 0;
+
+ list_for_each_entry(term, head_config, list) {
+ char errbuf[BUFSIZ];
+ int err;
+
+ if (term->type_term != PARSE_EVENTS__TERM_TYPE_USER) {
+ snprintf(errbuf, sizeof(errbuf),
+ "Invalid config term for BPF object");
+ errbuf[BUFSIZ - 1] = '\0';
+
+ data->error->idx = term->err_term;
+ data->error->str = strdup(errbuf);
+ return -EINVAL;
+ }
+
+ err = bpf__config_obj(obj, term, NULL, &error_pos);
+ if (err) {
+ bpf__strerror_config_obj(obj, term, NULL,
+ &error_pos, err, errbuf,
+ sizeof(errbuf));
+ data->error->help = strdup(
+"Hint:\tValid config term:\n"
+" \tmaps:[<arraymap>]:value=[value]\n"
+" \t(add -v to see detail)");
+ data->error->str = strdup(errbuf);
+ if (err == -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE)
+ data->error->idx = term->err_val;
+ else
+ data->error->idx = term->err_term + error_pos;
+ return err;
+ }
+ }
+ return 0;
+
+}
+
int parse_events_load_bpf(struct parse_events_evlist *data,
struct list_head *list,
char *bpf_file_name,
- bool source)
+ bool source,
+ struct list_head *head_config)
{
struct bpf_object *obj;
+ int err;

obj = bpf__prepare_load(bpf_file_name, source);
if (IS_ERR(obj)) {
char errbuf[BUFSIZ];
- int err;

err = PTR_ERR(obj);

@@ -652,7 +699,10 @@ int parse_events_load_bpf(struct parse_events_evlist *data,
return err;
}

- return parse_events_load_bpf_obj(data, list, obj);
+ err = parse_events_load_bpf_obj(data, list, obj);
+ if (err)
+ return err;
+ return parse_events_config_bpf(data, obj, head_config);
}

static int
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index f1a6db1..84694f3 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -126,7 +126,8 @@ int parse_events_add_tracepoint(struct list_head *list, int *idx,
int parse_events_load_bpf(struct parse_events_evlist *data,
struct list_head *list,
char *bpf_file_name,
- bool source);
+ bool source,
+ struct list_head *head_config);
/* Provide this function for perf test */
struct bpf_object;
int parse_events_load_bpf_obj(struct parse_events_evlist *data,
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 58c5831..4387728 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -122,7 +122,7 @@ num_dec [0-9]+
num_hex 0x[a-fA-F0-9]+
num_raw_hex [a-fA-F0-9]+
name [a-zA-Z_*?][a-zA-Z0-9_*?.]*
-name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.]*
+name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?.:]*
/* If you add a modifier you need to update check_modifier() */
modifier_event [ukhpPGHSDI]+
modifier_bp [rwx]{1,3}
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index ad37996..8992d16 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -64,6 +64,7 @@ static inc_group_count(struct list_head *list,
%type <str> PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT
%type <num> value_sym
%type <head> event_config
+%type <head> event_bpf_config
%type <term> event_term
%type <head> event_pmu
%type <head> event_legacy_symbol
@@ -455,27 +456,41 @@ PE_RAW
}

event_bpf_file:
-PE_BPF_OBJECT
+PE_BPF_OBJECT event_bpf_config
{
struct parse_events_evlist *data = _data;
struct parse_events_error *error = data->error;
struct list_head *list;

ALLOC_LIST(list);
- ABORT_ON(parse_events_load_bpf(data, list, $1, false));
+ ABORT_ON(parse_events_load_bpf(data, list, $1, false, $2));
+ if ($2)
+ parse_events__free_terms($2);
$$ = list;
}
|
-PE_BPF_SOURCE
+PE_BPF_SOURCE event_bpf_config
{
struct parse_events_evlist *data = _data;
struct list_head *list;

ALLOC_LIST(list);
- ABORT_ON(parse_events_load_bpf(data, list, $1, true));
+ ABORT_ON(parse_events_load_bpf(data, list, $1, true, $2));
+ if ($2)
+ parse_events__free_terms($2);
$$ = list;
}

+event_bpf_config:
+'/' event_config '/'
+{
+ $$ = $2;
+}
+|
+{
+ $$ = NULL;
+}
+
start_terms: event_config
{
struct parse_events_terms *data = _data;
--
1.8.3.4

2015-11-27 08:48:48

by Wang Nan

[permalink] [raw]
Subject: [PATCH v2 06/13] perf record: Apply config to BPF objects before recording

bpf__apply_obj_config() is introduced as the core API to apply object
config options to all BPF objects. This patch also does the real work
for setting values for BPF_MAP_TYPE_PERF_ARRAY maps by inserting value
stored in map's private field into the BPF map.

This patch is required because we are not always able to set all
BPF config during parsing. Further patch will set events created
by perf to BPF_MAP_TYPE_PERF_EVENT_ARRAY maps, which is not exist
until perf_evsel__open().

bpf_map_foreach_key() is introduced to iterate over each key
needs to be configured. This function would be extended to support
more map types and different key settings.

In perf record, before start recording, call bpf__apply_config() to
turn on all BPF config options.

Test result:

# cat ./test_bpf_map_1.c
/************************ BEGIN **************************/
#define SEC(NAME) __attribute__((section(NAME), used))
enum bpf_map_type {
BPF_MAP_TYPE_ARRAY = 2,
};
struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
};
static void *(*map_lookup_elem)(struct bpf_map_def *, void *) =
(void *)1;
static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
(void *)6;
struct bpf_map_def SEC("maps") channel = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = 1,
};
SEC("func=sys_nanosleep")
int func(void *ctx)
{
int key = 0;
char fmt[] = "%d\n";
int *pval = map_lookup_elem(&channel, &key);
if (!pval)
return 0;
bpf_trace_printk(fmt, sizeof(fmt), *pval);
return 0;
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
/************************* END ***************************/


# echo "" > /sys/kernel/debug/tracing/trace
# ./perf record -e './test_bpf_map_1.c/maps:channel:value=11/' usleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.012 MB perf.data ]
# cat /sys/kernel/debug/tracing/trace
# tracer: nop
#
# entries-in-buffer/entries-written: 1/1 #P:8
[SNIP]
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
usleep-18593 [007] d... 2394714.395539: : 11
# ./perf record -e './test_bpf_map.c/maps:channel:value=101/' usleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.012 MB perf.data ]
# cat /sys/kernel/debug/tracing/trace
# tracer: nop
#
# entries-in-buffer/entries-written: 1/1 #P:8
[SNIP]
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
usleep-18593 [007] d... 2394714.395539: : 11
usleep-19000 [006] d... 2394831.057840: : 101

Signed-off-by: Wang Nan <[email protected]>
Signed-off-by: He Kuang <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/builtin-record.c | 11 +++
tools/perf/util/bpf-loader.c | 180 +++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/bpf-loader.h | 15 ++++
3 files changed, 206 insertions(+)

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 199fc31..8479821 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -32,6 +32,7 @@
#include "util/parse-branch-options.h"
#include "util/parse-regs-options.h"
#include "util/llvm-utils.h"
+#include "util/bpf-loader.h"

#include <unistd.h>
#include <sched.h>
@@ -524,6 +525,16 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
goto out_child;
}

+ err = bpf__apply_obj_config();
+ if (err) {
+ char errbuf[BUFSIZ];
+
+ bpf__strerror_apply_obj_config(err, errbuf, sizeof(errbuf));
+ pr_err("ERROR: Apply config to BPF failed: %s\n",
+ errbuf);
+ goto out_child;
+ }
+
/*
* Normally perf_session__new would do this, but it doesn't have the
* evlist.
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 989c80f..c9ec9c6 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -7,6 +7,7 @@

#include <linux/bpf.h>
#include <bpf/libbpf.h>
+#include <bpf/bpf.h>
#include <linux/err.h>
#include <linux/string.h>
#include "perf.h"
@@ -984,6 +985,178 @@ out:

}

+typedef int (*map_config_func_t)(const char *name, int map_fd,
+ struct bpf_map_def *pdef,
+ struct bpf_map_op *op,
+ void *pkey, void *arg);
+
+static int
+foreach_key_array_all(map_config_func_t func,
+ void *arg, const char *name,
+ int map_fd, struct bpf_map_def *pdef,
+ struct bpf_map_op *op)
+{
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < pdef->max_entries; i++) {
+ err = func(name, map_fd, pdef, op, &i, arg);
+ if (err) {
+ pr_debug("ERROR: failed to insert value to %s[%u]\n",
+ name, i);
+ return err;
+ }
+ }
+ return 0;
+}
+
+static int
+bpf_map_config_foreach_key(struct bpf_map *map,
+ map_config_func_t func,
+ void *arg)
+{
+ int err, map_fd;
+ const char *name;
+ struct bpf_map_op *op;
+ struct bpf_map_def def;
+ struct bpf_map_priv *priv;
+
+ name = bpf_map__get_name(map);
+
+ err = bpf_map__get_private(map, (void **)&priv);
+ if (err) {
+ pr_debug("ERROR: failed to get private from map %s\n", name);
+ return -BPF_LOADER_ERRNO__INTERNAL;
+ }
+ if (!priv || list_empty(&priv->ops_list)) {
+ pr_debug("INFO: nothing to config for map %s\n", name);
+ return 0;
+ }
+
+ err = bpf_map__get_def(map, &def);
+ if (err) {
+ pr_debug("ERROR: failed to get definition from map %s\n", name);
+ return -BPF_LOADER_ERRNO__INTERNAL;
+ }
+ map_fd = bpf_map__get_fd(map);
+ if (map_fd < 0) {
+ pr_debug("ERROR: failed to get fd from map %s\n", name);
+ return map_fd;
+ }
+
+ list_for_each_entry(op, &priv->ops_list, list) {
+ switch (def.type) {
+ case BPF_MAP_TYPE_ARRAY:
+ switch (op->key_type) {
+ case BPF_MAP_KEY_ALL:
+ return foreach_key_array_all(func, arg, name,
+ map_fd, &def, op);
+ default:
+ pr_debug("ERROR: keytype for map '%s' invalid\n",
+ name);
+ return -BPF_LOADER_ERRNO__INTERNAL;
+ }
+ default:
+ pr_debug("ERROR: type of '%s' incorrect\n", name);
+ return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE;
+ }
+ }
+
+ return 0;
+}
+
+static int
+apply_config_value_for_key(int map_fd, void *pkey,
+ size_t val_size, u64 val)
+{
+ int err = 0;
+
+ switch (val_size) {
+ case 1: {
+ u8 _val = (u8)(val);
+ err = bpf_map_update_elem(map_fd, pkey, &_val, BPF_ANY);
+ break;
+ }
+ case 2: {
+ u16 _val = (u16)(val);
+ err = bpf_map_update_elem(map_fd, pkey, &_val, BPF_ANY);
+ break;
+ }
+ case 4: {
+ u32 _val = (u32)(val);
+ err = bpf_map_update_elem(map_fd, pkey, &_val, BPF_ANY);
+ break;
+ }
+ case 8: {
+ err = bpf_map_update_elem(map_fd, pkey, &val, BPF_ANY);
+ break;
+ }
+ default:
+ pr_debug("ERROR: invalid value size\n");
+ return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE;
+ }
+ if (err && errno)
+ err = -errno;
+ return err;
+}
+
+static int
+apply_obj_config_map_for_key(const char *name, int map_fd,
+ struct bpf_map_def *pdef __maybe_unused,
+ struct bpf_map_op *op,
+ void *pkey, void *arg __maybe_unused)
+{
+ int err;
+
+ switch (op->op_type) {
+ case BPF_MAP_OP_SET_VALUE:
+ err = apply_config_value_for_key(map_fd, pkey,
+ pdef->value_size,
+ op->v.value);
+ break;
+ default:
+ pr_debug("ERROR: unknown value type for '%s'\n", name);
+ err = -BPF_LOADER_ERRNO__INTERNAL;
+ }
+ return err;
+}
+
+static int
+apply_obj_config_map(struct bpf_map *map)
+{
+ return bpf_map_config_foreach_key(map,
+ apply_obj_config_map_for_key,
+ NULL);
+}
+
+static int
+apply_obj_config_object(struct bpf_object *obj)
+{
+ struct bpf_map *map;
+ int err;
+
+ bpf_map__for_each(map, obj) {
+ err = apply_obj_config_map(map);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+int bpf__apply_obj_config(void)
+{
+ struct bpf_object *obj, *tmp;
+ int err;
+
+ bpf_object__for_each_safe(obj, tmp) {
+ err = apply_obj_config_object(obj);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
#define ERRNO_OFFSET(e) ((e) - __BPF_LOADER_ERRNO__START)
#define ERRCODE_OFFSET(c) ERRNO_OFFSET(BPF_LOADER_ERRNO__##c)
#define NR_ERRNO (__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START)
@@ -1138,3 +1311,10 @@ int bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused,
bpf__strerror_end(buf, size);
return 0;
}
+
+int bpf__strerror_apply_obj_config(int err, char *buf, size_t size)
+{
+ bpf__strerror_head(err, buf, size);
+ bpf__strerror_end(buf, size);
+ return 0;
+}
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 2464db9..db3c34c 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -71,6 +71,8 @@ int bpf__strerror_config_obj(struct bpf_object *obj,
struct perf_evlist *evlist,
int *error_pos, int err, char *buf,
size_t size);
+int bpf__apply_obj_config(void);
+int bpf__strerror_apply_obj_config(int err, char *buf, size_t size);
#else
static inline struct bpf_object *
bpf__prepare_load(const char *filename __maybe_unused,
@@ -111,6 +113,12 @@ bpf__config_obj(struct bpf_object *obj __maybe_unused,
}

static inline int
+bpf__apply_obj_config(void)
+{
+ return 0;
+}
+
+static inline int
__bpf_strerror(char *buf, size_t size)
{
if (!size)
@@ -156,5 +164,12 @@ bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused,
{
return __bpf_strerror(buf, size);
}
+
+static inline int
+bpf__strerror_apply_obj_config(int err __maybe_unused,
+ char *buf, size_t size)
+{
+ return __bpf_strerror(buf, size);
+}
#endif
#endif
--
1.8.3.4

2015-11-27 08:50:43

by Wang Nan

[permalink] [raw]
Subject: [PATCH v2 07/13] perf tools: Support perf event alias name

From: He Kuang <[email protected]>

This patch adds new bison rules for specifying an alias name to a perf
event, which allows cmdline refer to previous defined perf event through
its name. With this patch user can give alias name to a perf event using
following cmdline:

# perf record -e mypmu=cycles ...

If alias is not provided (normal case):

# perf record -e cycles ...

It will be set to event's name automatically ('cycles' in the above
example).

To allow parser refer to existing event selector, pass event list to
'struct parse_events_evlist'. perf_evlist__find_evsel_by_alias() is
introduced to get evsel through its alias.

Test result:

Before this patch:

# ./perf record -e evt=cycles usleep 10
event syntax error: 'evt=cycles'
\___ parser error
Run 'perf list' for a list of valid events
[SNIP]

After this patch:

# ./perf record -e evt=cycles usleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.012 MB perf.data (2 samples) ]

Signed-off-by: He Kuang <[email protected]>
Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/evlist.c | 16 ++++++++++++++++
tools/perf/util/evlist.h | 4 ++++
tools/perf/util/evsel.c | 1 +
tools/perf/util/evsel.h | 1 +
tools/perf/util/parse-events.c | 37 ++++++++++++++++++++++++++++++++-----
tools/perf/util/parse-events.h | 5 +++++
tools/perf/util/parse-events.y | 15 ++++++++++++++-
7 files changed, 73 insertions(+), 6 deletions(-)

diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index d139219..8dd59aa 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -1753,3 +1753,19 @@ void perf_evlist__set_tracking_event(struct perf_evlist *evlist,

tracking_evsel->tracking = true;
}
+
+struct perf_evsel *
+perf_evlist__find_evsel_by_alias(struct perf_evlist *evlist,
+ const char *alias)
+{
+ struct perf_evsel *evsel;
+
+ evlist__for_each(evlist, evsel) {
+ if (!evsel->alias)
+ continue;
+ if (strcmp(alias, evsel->alias) == 0)
+ return evsel;
+ }
+
+ return NULL;
+}
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index a459fe7..4e25342 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -292,4 +292,8 @@ void perf_evlist__set_tracking_event(struct perf_evlist *evlist,
struct perf_evsel *tracking_evsel);

void perf_event_attr__set_max_precise_ip(struct perf_event_attr *attr);
+
+struct perf_evsel *
+perf_evlist__find_evsel_by_alias(struct perf_evlist *evlist, const char *alias);
+
#endif /* __PERF_EVLIST_H */
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 0a1f4d9..32131fc 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1060,6 +1060,7 @@ void perf_evsel__exit(struct perf_evsel *evsel)
thread_map__put(evsel->threads);
zfree(&evsel->group_name);
zfree(&evsel->name);
+ zfree(&evsel->alias);
perf_evsel__object.fini(evsel);
}

diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 0e49bd7..51bab0f 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -89,6 +89,7 @@ struct perf_evsel {
int idx;
u32 ids;
char *name;
+ char *alias;
double scale;
const char *unit;
struct event_format *tp_format;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 6a626cc..4e51ab3 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -649,9 +649,9 @@ parse_events_config_bpf(struct parse_events_evlist *data,
return -EINVAL;
}

- err = bpf__config_obj(obj, term, NULL, &error_pos);
+ err = bpf__config_obj(obj, term, data->evlist, &error_pos);
if (err) {
- bpf__strerror_config_obj(obj, term, NULL,
+ bpf__strerror_config_obj(obj, term, data->evlist,
&error_pos, err, errbuf,
sizeof(errbuf));
data->error->help = strdup(
@@ -1085,6 +1085,30 @@ int parse_events__modifier_group(struct list_head *list,
return parse_events__modifier_event(list, event_mod, true);
}

+int parse_events__set_event_alias(struct parse_events_evlist *data,
+ struct list_head *list,
+ const char *str,
+ void *loc_alias_)
+{
+ struct perf_evsel *evsel;
+ YYLTYPE *loc_alias = loc_alias_;
+
+ if (!str)
+ return 0;
+
+ if (!list_is_singular(list)) {
+ struct parse_events_error *err = data->error;
+
+ err->idx = loc_alias->first_column;
+ err->str = strdup("One alias can be applied to one event only");
+ return -EINVAL;
+ }
+
+ evsel = list_first_entry(list, struct perf_evsel, node);
+ evsel->alias = strdup(str);
+ return evsel->alias ? 0 : -ENOMEM;
+}
+
void parse_events__set_leader(char *name, struct list_head *list)
{
struct perf_evsel *leader;
@@ -1277,6 +1301,8 @@ int parse_events_name(struct list_head *list, char *name)
__evlist__for_each(list, evsel) {
if (!evsel->name)
evsel->name = strdup(name);
+ if (!evsel->alias)
+ evsel->alias = strdup(name);
}

return 0;
@@ -1438,9 +1464,10 @@ int parse_events(struct perf_evlist *evlist, const char *str,
struct parse_events_error *err)
{
struct parse_events_evlist data = {
- .list = LIST_HEAD_INIT(data.list),
- .idx = evlist->nr_entries,
- .error = err,
+ .list = LIST_HEAD_INIT(data.list),
+ .idx = evlist->nr_entries,
+ .error = err,
+ .evlist = evlist,
};
int ret;

diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 84694f3..20ad3c2 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -98,6 +98,7 @@ struct parse_events_evlist {
int idx;
int nr_groups;
struct parse_events_error *error;
+ struct perf_evlist *evlist;
};

struct parse_events_terms {
@@ -171,4 +172,8 @@ extern int is_valid_tracepoint(const char *event_string);
int valid_event_mount(const char *eventfs);
char *parse_events_formats_error_string(char *additional_terms);

+int parse_events__set_event_alias(struct parse_events_evlist *data,
+ struct list_head *list,
+ const char *str,
+ void *loc_alias_);
#endif /* __PERF_PARSE_EVENTS_H */
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 8992d16..c3cbd7a 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -77,6 +77,7 @@ static inc_group_count(struct list_head *list,
%type <head> event_bpf_file
%type <head> event_def
%type <head> event_mod
+%type <head> event_alias
%type <head> event_name
%type <head> event
%type <head> events
@@ -193,13 +194,25 @@ event_name PE_MODIFIER_EVENT
event_name

event_name:
-PE_EVENT_NAME event_def
+PE_EVENT_NAME event_alias
{
ABORT_ON(parse_events_name($2, $1));
free($1);
$$ = $2;
}
|
+event_alias
+
+event_alias:
+PE_NAME '=' event_def
+{
+ struct list_head *list = $3;
+ struct parse_events_evlist *data = _data;
+
+ ABORT_ON(parse_events__set_event_alias(data, list, $1, &@1));
+ $$ = list;
+}
+|
event_def

event_def: event_pmu |
--
1.8.3.4

2015-11-27 08:49:01

by Wang Nan

[permalink] [raw]
Subject: [PATCH v2 08/13] perf tools: Enable passing event to BPF object

A new syntax is appended into parser so user can pass predefined perf
events into BPF objects.

After this patch, BPF programs for perf are finally able to utilize
bpf_perf_event_read() introduced in commit 35578d7984003097af2b1e3
(bpf: Implement function bpf_perf_event_read() that get the selected
hardware PMU conuter).

Test result:

# cat ./test_bpf_map_2.c
/************************ BEGIN **************************/
#define SEC(NAME) __attribute__((section(NAME), used))
enum bpf_map_type {
BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4,
};
struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
};
static void *(*map_lookup_elem)(struct bpf_map_def *, void *) =
(void *)1;
static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
(void *)6;
static int (*bpf_get_smp_processor_id)(void) =
(void *)8;
static int (*bpf_perf_event_read)(struct bpf_map_def *, int) =
(void *)22;

struct bpf_map_def SEC("maps") pmu_map = {
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = __NR_CPUS__,
};
SEC("func_write=sys_write")
int func_write(void *ctx)
{
unsigned long long val;
char fmt[] = "sys_write: pmu=%llu\n";
val = bpf_perf_event_read(&pmu_map, bpf_get_smp_processor_id());
bpf_trace_printk(fmt, sizeof(fmt), val);
return 0;
}

SEC("func_write_return=sys_write%return")
int func_write_return(void *ctx)
{
unsigned long long val = 0;
char fmt[] = "sys_write_return: pmu=%llu\n";
val = bpf_perf_event_read(&pmu_map, bpf_get_smp_processor_id());
bpf_trace_printk(fmt, sizeof(fmt), val);
return 0;
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
/************************* END ***************************/

Normal case 1:
# echo "" > /sys/kernel/debug/tracing/trace
# ./perf record -e evt=cycles/no-inherit/ -e './test_bpf_map_2.c/maps:pmu_map:event=evt/' ls /
[SNIP]
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.013 MB perf.data (7 samples) ]
# cat /sys/kernel/debug/tracing/trace | grep ls
ls-13865 [006] d... 2722740.933204: : sys_write: pmu=1121685
ls-13865 [006] dN.. 2722740.933242: : sys_write_return: pmu=1178149
ls-13865 [006] d... 2722740.933248: : sys_write: pmu=1194986
ls-13865 [006] dN.. 2722740.933270: : sys_write_return: pmu=1220862

Normal case 2:
# echo "" > /sys/kernel/debug/tracing/trace
# ./perf record -e evt=cycles/period=0x7fffffffffffffff,no-inherit/ \
-e './test_bpf_map_2.c/maps:pmu_map:event=evt/' ls /
[SNIP]
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.013 MB perf.data ]
# ./perf report --stdio
Error:
The perf.data file has no samples!

(This is expected because we set period of cycles to a very large
value to period of cycles event because we want to use this event
as a counter only, don't need sampling)

# cat /sys/kernel/debug/tracing/trace | grep ls
ls-14446 [006] d... 2722976.486458: : sys_write: pmu=1116233
ls-14446 [006] dN.. 2722976.486486: : sys_write_return: pmu=1162108
ls-14446 [006] d... 2722976.486491: : sys_write: pmu=1177122
ls-14446 [006] dN.. 2722976.486511: : sys_write_return: pmu=1202417

Normal case 3:
# echo "" > /sys/kernel/debug/tracing/trace
# ./perf record -i -e cycles -e './test_bpf_map_2.c/maps:pmu_map:event=cycles/' ls /

(When doesn't explicitly set alias, event name can be used to search events)

[SNIP]
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.013 MB perf.data (7 samples) ]
# cat /sys/kernel/debug/tracing/trace | grep ls
ls-16480 [005] d... 2724143.955040: : sys_write: pmu=1150794
ls-16480 [005] dN.. 2724143.955077: : sys_write_return: pmu=1207161
ls-16480 [005] d... 2724143.955083: : sys_write: pmu=1219145
ls-16480 [005] dN.. 2724143.955104: : sys_write_return: pmu=1245433

Normal case 4 (one thread case):
# ls /proc/11808/task/
11808
# echo "" > /sys/kernel/debug/tracing/trace
# ./perf record -e evt=cycles/no-inherit/ -e './test_bpf_map_2.c/maps:pmu_map:event=evt/' -p 11808
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.019 MB perf.data (2 samples) ]

# cat /sys/kernel/debug/tracing/trace | grep 11808
sshd-11808 [000] d... 2740454.781150: : sys_write: pmu=18446744073709551594
sshd-11808 [000] d... 2740454.781168: : sys_write_return: pmu=18446744073709551594
sshd-11808 [003] d... 2740467.411799: : sys_write: pmu=131031
sshd-11808 [003] dN.. 2740467.411806: : sys_write_return: pmu=161549
sshd-11808 [003] d... 2740467.411834: : sys_write: pmu=210269

Normal case 5 (system wide):
# echo "" > /sys/kernel/debug/tracing/trace
# ./perf record -e evt=cycles/no-inherit/ -e './test_bpf_map_2.c/maps:pmu_map:event=evt/' -a
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.811 MB perf.data (120 samples) ]

# cat /sys/kernel/debug/tracing/trace | grep -v '18446744073709551594' | grep -v perf | head -n 20
[SNIP]
# TASK-PID CPU# |||| TIMESTAMP FUNCTION
# | | | |||| | |
gmain-30828 [002] d... 2740551.068992: : sys_write: pmu=84373
gmain-30828 [002] d... 2740551.068992: : sys_write_return: pmu=87696
gmain-30828 [002] d... 2740551.068996: : sys_write: pmu=100658
gmain-30828 [002] d... 2740551.068997: : sys_write_return: pmu=102572

Error case 1:

# ./perf record -e './test_bpf_map_2.c' ls /
[SNIP]
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.014 MB perf.data ]
# cat /sys/kernel/debug/tracing/trace | grep ls
ls-17115 [007] d... 2724279.665625: : sys_write: pmu=18446744073709551614
ls-17115 [007] dN.. 2724279.665651: : sys_write_return: pmu=18446744073709551614
ls-17115 [007] d... 2724279.665658: : sys_write: pmu=18446744073709551614
ls-17115 [007] dN.. 2724279.665677: : sys_write_return: pmu=18446744073709551614

(18446744073709551614 is 0xfffffffffffffffe (-2))

Error case 2:
# ./perf record -e cycles -e './test_bpf_map_2.c/maps:pmu_map:event=evt/' -a
event syntax error: '..ps:pmu_map:event=evt/'
\___ Event not found for map setting

Hint: Valid config terms:
maps:[<arraymap>]:value=[value]
maps:[<eventmap>]:event=[event]
[SNIP]

Error case 3:
# ls /proc/2342/task/
2342 2373 2374 2375 2376
# ./perf record -e evt=cycles/no-inherit/ -e './test_bpf_map_2.c/maps:pmu_map:event=evt/' -p 2342
ERROR: Apply config to BPF failed: Cannot set event to BPF maps in multi-thread tracing

Error case 4:
# ./perf record -e cycles -e './test_bpf_map_2.c/maps:pmu_map:event=cycles/' ls /
ERROR: Apply config to BPF failed: Doesn't support inherit event (Hint: use /no-inherit/ config term or use -i)

Error case 5:
# ./perf record -e evt=raw_syscalls:sys_enter/no-inherit/ -e './test_bpf_map_2.c/maps:pmu_map:event=evt/' ls
ERROR: Apply config to BPF failed: Can only put raw, hardware and BPF output event into a BPF map

Signed-off-by: Wang Nan <[email protected]>
Signed-off-by: He Kuang <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/bpf-loader.c | 138 ++++++++++++++++++++++++++++++++++++++++-
tools/perf/util/bpf-loader.h | 5 ++
tools/perf/util/parse-events.c | 4 +-
3 files changed, 145 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index c9ec9c6..c33ecf5 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -742,6 +742,7 @@ int bpf__foreach_tev(struct bpf_object *obj,

enum bpf_map_op_type {
BPF_MAP_OP_SET_VALUE,
+ BPF_MAP_OP_SET_EVSEL,
};

enum bpf_map_key_type {
@@ -754,6 +755,7 @@ struct bpf_map_op {
enum bpf_map_key_type key_type;
union {
u64 value;
+ struct perf_evsel *evsel;
} v;
};

@@ -891,10 +893,73 @@ bpf__obj_config_map_value(struct bpf_map *map,
if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM)
return bpf__obj_config_map_array_value(map, term);

- pr_debug("ERROR: wrong value type\n");
+ pr_debug("ERROR: wrong value type for 'value'\n");
return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE;
}

+static int
+bpf__obj_config_map_array_event(struct bpf_map *map,
+ struct parse_events_term *term,
+ struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel;
+ struct bpf_map_def def;
+ struct bpf_map_op *op;
+ const char *map_name;
+ int err;
+
+ map_name = bpf_map__get_name(map);
+ evsel = perf_evlist__find_evsel_by_alias(evlist, term->val.str);
+ if (!evsel) {
+ pr_debug("Event (for '%s') '%s' doesn't exist\n",
+ map_name, term->val.str);
+ return -BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT;
+ }
+
+ err = bpf_map__get_def(map, &def);
+ if (err) {
+ pr_debug("Unable to get map definition from '%s'\n",
+ map_name);
+ return err;
+ }
+
+ /*
+ * No need to check key_size and value_size:
+ * kernel has already checked them.
+ */
+ if (def.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) {
+ pr_debug("Map %s type is not BPF_MAP_TYPE_PERF_EVENT_ARRAY\n",
+ map_name);
+ return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE;
+ }
+
+ op = bpf_map_op__alloc(map);
+ if (IS_ERR(op))
+ return PTR_ERR(op);
+
+ op->v.evsel = evsel;
+ op->op_type = BPF_MAP_OP_SET_EVSEL;
+ return 0;
+}
+
+static int
+bpf__obj_config_map_event(struct bpf_map *map,
+ struct parse_events_term *term,
+ struct perf_evlist *evlist)
+{
+ if (!term->err_val) {
+ pr_debug("Config value not set\n");
+ return -BPF_LOADER_ERRNO__OBJCONF_CONF;
+ }
+
+ if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR)
+ return bpf__obj_config_map_array_event(map, term, evlist);
+
+ pr_debug("ERROR: wrong value type for 'event'\n");
+ return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE;
+}
+
+
struct bpf_obj_config_map_func {
const char *config_opt;
int (*config_func)(struct bpf_map *, struct parse_events_term *,
@@ -903,6 +968,7 @@ struct bpf_obj_config_map_func {

struct bpf_obj_config_map_func bpf_obj_config_map_funcs[] = {
{"value", bpf__obj_config_map_value},
+ {"event", bpf__obj_config_map_event},
};

static int
@@ -1047,6 +1113,7 @@ bpf_map_config_foreach_key(struct bpf_map *map,
list_for_each_entry(op, &priv->ops_list, list) {
switch (def.type) {
case BPF_MAP_TYPE_ARRAY:
+ case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
switch (op->key_type) {
case BPF_MAP_KEY_ALL:
return foreach_key_array_all(func, arg, name,
@@ -1101,6 +1168,60 @@ apply_config_value_for_key(int map_fd, void *pkey,
}

static int
+apply_config_evsel_for_key(const char *name, int map_fd, void *pkey,
+ struct perf_evsel *evsel)
+{
+ struct xyarray *xy = evsel->fd;
+ struct perf_event_attr *attr;
+ unsigned int key, events;
+ bool check_pass = false;
+ int *evt_fd;
+ int err;
+
+ if (!xy) {
+ pr_debug("ERROR: evsel not ready for map %s\n", name);
+ return -BPF_LOADER_ERRNO__INTERNAL;
+ }
+
+ if (xy->row_size / xy->entry_size != 1) {
+ pr_debug("ERROR: Dimension of target event is incorrect for map %s\n",
+ name);
+ return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM;
+ }
+
+ attr = &evsel->attr;
+ if (attr->inherit) {
+ pr_debug("ERROR: Can't put inherit event into map %s\n", name);
+ return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH;
+ }
+
+ if (attr->type == PERF_TYPE_RAW)
+ check_pass = true;
+ if (attr->type == PERF_TYPE_HARDWARE)
+ check_pass = true;
+ if (attr->type == PERF_TYPE_SOFTWARE &&
+ attr->config == PERF_COUNT_SW_BPF_OUTPUT)
+ check_pass = true;
+ if (!check_pass) {
+ pr_debug("ERROR: Event type is wrong for map %s\n", name);
+ return -BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE;
+ }
+
+ events = xy->entries / (xy->row_size / xy->entry_size);
+ key = *((unsigned int *)pkey);
+ if (key >= events) {
+ pr_debug("ERROR: there is no event %d for map %s\n",
+ key, name);
+ return -BPF_LOADER_ERRNO__OBJCONF_MAP_MAPSIZE;
+ }
+ evt_fd = xyarray__entry(xy, key, 0);
+ err = bpf_map_update_elem(map_fd, pkey, evt_fd, BPF_ANY);
+ if (err && errno)
+ err = -errno;
+ return err;
+}
+
+static int
apply_obj_config_map_for_key(const char *name, int map_fd,
struct bpf_map_def *pdef __maybe_unused,
struct bpf_map_op *op,
@@ -1114,6 +1235,10 @@ apply_obj_config_map_for_key(const char *name, int map_fd,
pdef->value_size,
op->v.value);
break;
+ case BPF_MAP_OP_SET_EVSEL:
+ err = apply_config_evsel_for_key(name, map_fd, pkey,
+ op->v.evsel);
+ break;
default:
pr_debug("ERROR: unknown value type for '%s'\n", name);
err = -BPF_LOADER_ERRNO__INTERNAL;
@@ -1179,6 +1304,11 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = {
[ERRCODE_OFFSET(OBJCONF_MAP_TYPE)] = "Incorrect map type",
[ERRCODE_OFFSET(OBJCONF_MAP_KEYSIZE)] = "Incorrect map key size",
[ERRCODE_OFFSET(OBJCONF_MAP_VALUESIZE)] = "Incorrect map value size",
+ [ERRCODE_OFFSET(OBJCONF_MAP_NOEVT)] = "Event not found for map setting",
+ [ERRCODE_OFFSET(OBJCONF_MAP_MAPSIZE)] = "Invalid map size for event setting",
+ [ERRCODE_OFFSET(OBJCONF_MAP_EVTDIM)] = "Event dimension too large",
+ [ERRCODE_OFFSET(OBJCONF_MAP_EVTINH)] = "Doesn't support inherit event",
+ [ERRCODE_OFFSET(OBJCONF_MAP_EVTTYPE)] = "Wrong event type for map",
};

static int
@@ -1315,6 +1445,12 @@ int bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused,
int bpf__strerror_apply_obj_config(int err, char *buf, size_t size)
{
bpf__strerror_head(err, buf, size);
+ bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM,
+ "Cannot set event to BPF maps in multi-thread tracing");
+ bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH,
+ "%s (Hint: use /no-inherit/ config term or use -i)", emsg);
+ bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE,
+ "Can only put raw, hardware and BPF output event into a BPF map");
bpf__strerror_end(buf, size);
return 0;
}
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index db3c34c..c9ce792 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -33,6 +33,11 @@ enum bpf_loader_errno {
BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE, /* Incorrect map type */
BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE, /* Incorrect map key size */
BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE,/* Incorrect map value size */
+ BPF_LOADER_ERRNO__OBJCONF_MAP_NOEVT, /* Event not found for map setting */
+ BPF_LOADER_ERRNO__OBJCONF_MAP_MAPSIZE, /* Invalid map size for event setting */
+ BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM, /* Event dimension too large */
+ BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH, /* Doesn't support inherit event */
+ BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE, /* Wrong event type for map */
__BPF_LOADER_ERRNO__END,
};

diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 4e51ab3..799bfd2 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -655,8 +655,10 @@ parse_events_config_bpf(struct parse_events_evlist *data,
&error_pos, err, errbuf,
sizeof(errbuf));
data->error->help = strdup(
-"Hint:\tValid config term:\n"
+"Hint:\tValid config terms:\n"
" \tmaps:[<arraymap>]:value=[value]\n"
+" \tmaps:[<eventmap>]:event=[event]\n"
+"\n"
" \t(add -v to see detail)");
data->error->str = strdup(errbuf);
if (err == -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE)
--
1.8.3.4

2015-11-27 08:51:32

by Wang Nan

[permalink] [raw]
Subject: [PATCH v2 09/13] perf tools: Support setting different slots in a BPF map separately

This patch introduces basic facilities to support config different
slots in a BPF map one by one.

array.nr_ranges and array.ranges are introduced into 'struct
parse_events_term', where ranges is an array of indices range (start,
length) which will be configured by this config term. nr_ranges
is the size of the array. The array is passed to 'struct bpf_map_priv'.
To indicate the new type of configuration, BPF_MAP_KEY_RANGES is
added as a new key type. bpf_map_config_foreach_key() is extended to
iterate over those indices instead of all possible keys.

Code in this commit will be enabled by following commit which enables
the indices syntax for array configuration.

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/bpf-loader.c | 132 ++++++++++++++++++++++++++++++++++++++---
tools/perf/util/bpf-loader.h | 1 +
tools/perf/util/parse-events.c | 33 ++++++++++-
tools/perf/util/parse-events.h | 12 ++++
4 files changed, 170 insertions(+), 8 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index c33ecf5..18ade25 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -17,6 +17,7 @@
#include "llvm-utils.h"
#include "probe-event.h"
#include "probe-finder.h" // for MAX_PROBES
+#include "parse-events.h"
#include "llvm-utils.h"

#define DEFINE_PRINT_FN(name, level) \
@@ -747,6 +748,7 @@ enum bpf_map_op_type {

enum bpf_map_key_type {
BPF_MAP_KEY_ALL,
+ BPF_MAP_KEY_RANGES,
};

struct bpf_map_op {
@@ -754,6 +756,9 @@ struct bpf_map_op {
enum bpf_map_op_type op_type;
enum bpf_map_key_type key_type;
union {
+ struct parse_events_array array;
+ } k;
+ union {
u64 value;
struct perf_evsel *evsel;
} v;
@@ -779,6 +784,8 @@ bpf_map_op__free(struct bpf_map_op *op)
*/
if ((list->next != LIST_POISON1) && (list->prev != LIST_POISON2))
list_del(list);
+ if (op->key_type == BPF_MAP_KEY_RANGES)
+ parse_events__clear_array(&op->k.array);
free(op);
}

@@ -794,8 +801,30 @@ bpf_map_priv__clear(struct bpf_map *map __maybe_unused,
free(priv);
}

+static int
+bpf_map_op_setkey(struct bpf_map_op *op, struct parse_events_term *term,
+ const char *map_name)
+{
+ op->key_type = BPF_MAP_KEY_ALL;
+
+ if (term->array.nr_ranges) {
+ size_t memsz = term->array.nr_ranges *
+ sizeof(op->k.array.ranges[0]);
+
+ op->k.array.ranges = memdup(term->array.ranges, memsz);
+ if (!op->k.array.ranges) {
+ pr_debug("No enough memory to alloc indices for %s\n",
+ map_name);
+ return -ENOMEM;
+ }
+ op->key_type = BPF_MAP_KEY_RANGES;
+ op->k.array.nr_ranges = term->array.nr_ranges;
+ }
+ return 0;
+}
+
static struct bpf_map_op *
-bpf_map_op__alloc(struct bpf_map *map)
+bpf_map_op__alloc(struct bpf_map *map, struct parse_events_term *term)
{
struct bpf_map_op *op;
struct bpf_map_priv *priv;
@@ -829,7 +858,12 @@ bpf_map_op__alloc(struct bpf_map *map)
return ERR_PTR(-ENOMEM);
}

- op->key_type = BPF_MAP_KEY_ALL;
+ err = bpf_map_op_setkey(op, term, map_name);
+ if (err) {
+ free(op);
+ return ERR_PTR(err);
+ }
+
list_add_tail(&op->list, &priv->ops_list);
return op;
}
@@ -872,7 +906,7 @@ bpf__obj_config_map_array_value(struct bpf_map *map,
return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE;
}

- op = bpf_map_op__alloc(map);
+ op = bpf_map_op__alloc(map, term);
if (IS_ERR(op))
return PTR_ERR(op);
op->op_type = BPF_MAP_OP_SET_VALUE;
@@ -933,7 +967,7 @@ bpf__obj_config_map_array_event(struct bpf_map *map,
return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE;
}

- op = bpf_map_op__alloc(map);
+ op = bpf_map_op__alloc(map, term);
if (IS_ERR(op))
return PTR_ERR(op);

@@ -972,6 +1006,44 @@ struct bpf_obj_config_map_func bpf_obj_config_map_funcs[] = {
};

static int
+config_map_indices_range_check(struct parse_events_term *term,
+ struct bpf_map *map,
+ const char *map_name)
+{
+ struct parse_events_array *array = &term->array;
+ struct bpf_map_def def;
+ unsigned int i;
+ int err;
+
+ if (!array->nr_ranges)
+ return 0;
+ if (!array->ranges) {
+ pr_debug("ERROR: map %s: array->nr_ranges is %d but range array is NULL\n",
+ map_name, (int)array->nr_ranges);
+ return -BPF_LOADER_ERRNO__INTERNAL;
+ }
+
+ err = bpf_map__get_def(map, &def);
+ if (err) {
+ pr_debug("ERROR: Unable to get map definition from '%s'\n",
+ map_name);
+ return -BPF_LOADER_ERRNO__INTERNAL;
+ }
+
+ for (i = 0; i < array->nr_ranges; i++) {
+ unsigned int start = array->ranges[i].start;
+ size_t length = array->ranges[i].length;
+ unsigned int idx = start + length - 1;
+
+ if (idx >= def.max_entries) {
+ pr_debug("ERROR: index %d too large\n", idx);
+ return -BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG;
+ }
+ }
+ return 0;
+}
+
+static int
bpf__obj_config_map(struct bpf_object *obj,
struct parse_events_term *term,
struct perf_evlist *evlist,
@@ -1007,6 +1079,13 @@ bpf__obj_config_map(struct bpf_object *obj,
}

*key_scan_pos += map_opt - map_name;
+
+ *key_scan_pos += strlen(map_opt);
+ err = config_map_indices_range_check(term, map, map_name);
+ if (err)
+ goto out;
+ *key_scan_pos -= strlen(map_opt);
+
for (i = 0; i < ARRAY_SIZE(bpf_obj_config_map_funcs); i++) {
struct bpf_obj_config_map_func *func =
&bpf_obj_config_map_funcs[i];
@@ -1077,6 +1156,33 @@ foreach_key_array_all(map_config_func_t func,
}

static int
+foreach_key_array_ranges(map_config_func_t func, void *arg,
+ const char *name, int map_fd,
+ struct bpf_map_def *pdef,
+ struct bpf_map_op *op)
+{
+ unsigned int i, j;
+ int err;
+
+ for (i = 0; i < op->k.array.nr_ranges; i++) {
+ unsigned int start = op->k.array.ranges[i].start;
+ size_t length = op->k.array.ranges[i].length;
+
+ for (j = 0; j < length; j++) {
+ unsigned int idx = start + j;
+
+ err = func(name, map_fd, pdef, op, &idx, arg);
+ if (err) {
+ pr_debug("ERROR: failed to insert value to %s[%u]\n",
+ name, idx);
+ return err;
+ }
+ }
+ }
+ return 0;
+}
+
+static int
bpf_map_config_foreach_key(struct bpf_map *map,
map_config_func_t func,
void *arg)
@@ -1116,13 +1222,24 @@ bpf_map_config_foreach_key(struct bpf_map *map,
case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
switch (op->key_type) {
case BPF_MAP_KEY_ALL:
- return foreach_key_array_all(func, arg, name,
- map_fd, &def, op);
+ err = foreach_key_array_all(func, arg, name,
+ map_fd, &def, op);
+ if (err)
+ return err;
+ break;
+ case BPF_MAP_KEY_RANGES:
+ err = foreach_key_array_ranges(func, arg, name,
+ map_fd, &def,
+ op);
+ if (err)
+ return err;
+ break;
default:
pr_debug("ERROR: keytype for map '%s' invalid\n",
name);
return -BPF_LOADER_ERRNO__INTERNAL;
- }
+ }
+ break;
default:
pr_debug("ERROR: type of '%s' incorrect\n", name);
return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE;
@@ -1309,6 +1426,7 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = {
[ERRCODE_OFFSET(OBJCONF_MAP_EVTDIM)] = "Event dimension too large",
[ERRCODE_OFFSET(OBJCONF_MAP_EVTINH)] = "Doesn't support inherit event",
[ERRCODE_OFFSET(OBJCONF_MAP_EVTTYPE)] = "Wrong event type for map",
+ [ERRCODE_OFFSET(OBJCONF_MAP_IDX2BIG)] = "Index too large",
};

static int
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index c9ce792..30ee519 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -38,6 +38,7 @@ enum bpf_loader_errno {
BPF_LOADER_ERRNO__OBJCONF_MAP_EVTDIM, /* Event dimension too large */
BPF_LOADER_ERRNO__OBJCONF_MAP_EVTINH, /* Doesn't support inherit event */
BPF_LOADER_ERRNO__OBJCONF_MAP_EVTTYPE, /* Wrong event type for map */
+ BPF_LOADER_ERRNO__OBJCONF_MAP_IDX2BIG, /* Index too large */
__BPF_LOADER_ERRNO__END,
};

diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 799bfd2..ef794c5 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -2144,8 +2144,39 @@ void parse_events__free_terms(struct list_head *terms)
{
struct parse_events_term *term, *h;

- list_for_each_entry_safe(term, h, terms, list)
+ list_for_each_entry_safe(term, h, terms, list) {
+ if (term->array.nr_ranges)
+ free(term->array.ranges);
free(term);
+ }
+}
+
+int parse_events__merge_arrays(struct parse_events_array *dest,
+ struct parse_events_array *another)
+{
+ struct parse_events_array new;
+
+ if (!dest || !another)
+ return -EINVAL;
+
+ new.nr_ranges = dest->nr_ranges + another->nr_ranges;
+ new.ranges = malloc(sizeof(new.ranges[0]) * new.nr_ranges);
+ if (!new.ranges)
+ return -ENOMEM;
+
+ memcpy(&new.ranges[0], dest->ranges,
+ sizeof(new.ranges[0]) * dest->nr_ranges);
+ memcpy(&new.ranges[dest->nr_ranges], another->ranges,
+ sizeof(new.ranges[0]) * another->nr_ranges);
+ free(dest->ranges);
+ free(another->ranges);
+ *dest = new;
+ return 0;
+}
+
+void parse_events__clear_array(struct parse_events_array *a)
+{
+ free(a->ranges);
}

void parse_events_evlist_error(struct parse_events_evlist *data,
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 20ad3c2..c34615f 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -71,8 +71,17 @@ enum {
PARSE_EVENTS__TERM_TYPE_INHERIT
};

+struct parse_events_array {
+ size_t nr_ranges;
+ struct {
+ unsigned int start;
+ size_t length;
+ } *ranges;
+};
+
struct parse_events_term {
char *config;
+ struct parse_events_array array;
union {
char *str;
u64 num;
@@ -117,6 +126,9 @@ int parse_events_term__sym_hw(struct parse_events_term **term,
int parse_events_term__clone(struct parse_events_term **new,
struct parse_events_term *term);
void parse_events__free_terms(struct list_head *terms);
+int parse_events__merge_arrays(struct parse_events_array *dest,
+ struct parse_events_array *another);
+void parse_events__clear_array(struct parse_events_array *a);
int parse_events__modifier_event(struct list_head *list, char *str, bool add);
int parse_events__modifier_group(struct list_head *list, char *event_mod);
int parse_events_name(struct list_head *list, char *name);
--
1.8.3.4

2015-11-27 08:48:50

by Wang Nan

[permalink] [raw]
Subject: [PATCH v2 10/13] perf tools: Enable indices setting syntax for BPF maps

This patch introduce a new syntax to perf event parser:

# perf record -e bpf_file.c/maps.mymap.value[0,3...5,7]=1234/ ...

By utilizing the basic facilities in bpf-loader.c which allow setting
different slots in a BPF map separately, the newly introduced syntax
allows perf to control specific elements in a BPF map.

Test result:

# cat ./test_bpf_map_3.c
/************************ BEGIN **************************/
#define SEC(NAME) __attribute__((section(NAME), used))
enum bpf_map_type {
BPF_MAP_TYPE_ARRAY = 2,
};
struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
};
static void *(*map_lookup_elem)(struct bpf_map_def *, void *) =
(void *)1;
static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
(void *)6;
struct bpf_map_def SEC("maps") channel = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(unsigned char),
.max_entries = 100,
};
SEC("func=hrtimer_nanosleep rqtp->tv_nsec")
int func(void *ctx, int err, long nsec)
{
char fmt[] = "%ld\n";
long usec = nsec * 0x10624dd3 >> 38; // nsec / 1000
int key = (int)usec;
unsigned char *pval = map_lookup_elem(&channel, &key);

if (!pval)
return 0;
bpf_trace_printk(fmt, sizeof(fmt), (unsigned char)*pval);
return 0;
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
/************************* END ***************************/

Normal case:
# echo "" > /sys/kernel/debug/tracing/trace
# ./perf record -e './test_bpf_map_3.c/maps:channel:value[0,1,2,3...5]=101/' usleep 2
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.012 MB perf.data ]
# cat /sys/kernel/debug/tracing/trace | grep usleep
usleep-405 [004] d... 2745423.547822: : 101
# ./perf record -e './test_bpf_map_3.c/maps:channel:value[0...9,20...29]=102,maps:channel:value[10...19]=103/' usleep 3
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.012 MB perf.data ]
# ./perf record -e './test_bpf_map_3.c/maps:channel:value[0...9,20...29]=102,maps:channel:value[10...19]=103/' usleep 15
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.012 MB perf.data ]
# cat /sys/kernel/debug/tracing/trace | grep usleep
usleep-405 [004] d... 2745423.547822: : 101
usleep-655 [006] d... 2745434.122814: : 102
usleep-904 [006] d... 2745439.916264: : 103
# ./perf record -e './test_bpf_map_3.c/maps:channel:value[all]=104/' usleep 99
# cat /sys/kernel/debug/tracing/trace | grep usleep
usleep-405 [004] d... 2745423.547822: : 101
usleep-655 [006] d... 2745434.122814: : 102
usleep-904 [006] d... 2745439.916264: : 103
usleep-1537 [003] d... 2745538.053737: : 104

Error case:
# ./perf record -e './test_bpf_map_3.c/maps:channel:value[10...1000]=104/' usleep 99
event syntax error: '..annel:value[10...1000]=104/'
\___ Index too large
Hint: Valid config terms:
maps:[<arraymap>]:value<indics>=[value]
maps:[<eventmap>]:event<indics>=[event]

where <indics> is something like [0,3...5] or [all]
(add -v to see detail)
Run 'perf list' for a list of valid events

Usage: perf record [<options>] [<command>]
or: perf record [<options>] -- <command> [<options>]

-e, --event <event> event selector. use 'perf list' to list available events

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/parse-events.c | 5 ++-
tools/perf/util/parse-events.l | 13 ++++++-
tools/perf/util/parse-events.y | 85 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 100 insertions(+), 3 deletions(-)

diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index ef794c5..a419571 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -656,9 +656,10 @@ parse_events_config_bpf(struct parse_events_evlist *data,
sizeof(errbuf));
data->error->help = strdup(
"Hint:\tValid config terms:\n"
-" \tmaps:[<arraymap>]:value=[value]\n"
-" \tmaps:[<eventmap>]:event=[event]\n"
+" \tmaps:[<arraymap>]:value<indics>=[value]\n"
+" \tmaps:[<eventmap>]:event<indics>=[event]\n"
"\n"
+" \twhere <indics> is something like [0,3...5] or [all]\n"
" \t(add -v to see detail)");
data->error->str = strdup(errbuf);
if (err == -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE)
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 4387728..8bb3437 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -9,8 +9,8 @@
%{
#include <errno.h>
#include "../perf.h"
-#include "parse-events-bison.h"
#include "parse-events.h"
+#include "parse-events-bison.h"

char *parse_events_get_text(yyscan_t yyscanner);
YYSTYPE *parse_events_get_lval(yyscan_t yyscanner);
@@ -111,6 +111,7 @@ do { \
%x mem
%s config
%x event
+%x array

group [^,{}/]*[{][^}]*[}][^,{}/]*
event_pmu [^,{}/]+[/][^/]*[/][^,{}/]*
@@ -176,6 +177,14 @@ modifier_bp [rwx]{1,3}

}

+<array>{
+"]" { BEGIN(config); return ']'; }
+{num_dec} { return value(yyscanner, 10); }
+{num_hex} { return value(yyscanner, 16); }
+, { return ','; }
+"\.\.\." { return PE_ARRAY_RANGE; }
+}
+
<config>{
/*
* Please update parse_events_formats_error_string any time
@@ -196,6 +205,8 @@ no-inherit { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NOINHERIT); }
, { return ','; }
"/" { BEGIN(INITIAL); return '/'; }
{name_minus} { return str(yyscanner, PE_NAME); }
+\[all\] { return PE_ARRAY_ALL; }
+"[" { BEGIN(array); return '['; }
}

<mem>{
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index c3cbd7a..7e93b9f 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -48,6 +48,7 @@ static inc_group_count(struct list_head *list,
%token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP
%token PE_ERROR
%token PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT
+%token PE_ARRAY_ALL PE_ARRAY_RANGE
%type <num> PE_VALUE
%type <num> PE_VALUE_SYM_HW
%type <num> PE_VALUE_SYM_SW
@@ -84,6 +85,9 @@ static inc_group_count(struct list_head *list,
%type <head> group_def
%type <head> group
%type <head> groups
+%type <array> array
+%type <array> array_term
+%type <array> array_terms

%union
{
@@ -95,6 +99,7 @@ static inc_group_count(struct list_head *list,
char *sys;
char *event;
} tracepoint_name;
+ struct parse_events_array array;
}
%%

@@ -601,6 +606,86 @@ PE_TERM
ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1, &@1, NULL));
$$ = term;
}
+|
+PE_NAME array '=' PE_NAME
+{
+ struct parse_events_term *term;
+ int i;
+
+ ABORT_ON(parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER,
+ $1, $4, &@1, &@4));
+
+ term->array = $2;
+ $$ = term;
+}
+|
+PE_NAME array '=' PE_VALUE
+{
+ struct parse_events_term *term;
+
+ ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
+ $1, $4, &@1, &@4));
+ term->array = $2;
+ $$ = term;
+}
+
+array:
+'[' array_terms ']'
+{
+ $$ = $2;
+}
+|
+PE_ARRAY_ALL
+{
+ $$.nr_ranges = 0;
+ $$.ranges = NULL;
+}
+
+array_terms:
+array_terms ',' array_term
+{
+ struct parse_events_array new_array;
+
+ new_array.nr_ranges = $1.nr_ranges + $3.nr_ranges;
+ new_array.ranges = malloc(sizeof(new_array.ranges[0]) *
+ new_array.nr_ranges);
+ ABORT_ON(!new_array.ranges);
+ memcpy(&new_array.ranges[0], $1.ranges,
+ $1.nr_ranges * sizeof(new_array.ranges[0]));
+ memcpy(&new_array.ranges[$1.nr_ranges], $3.ranges,
+ $3.nr_ranges * sizeof(new_array.ranges[0]));
+ free($1.ranges);
+ free($3.ranges);
+ $$ = new_array;
+}
+|
+array_term
+
+array_term:
+PE_VALUE
+{
+ struct parse_events_array array;
+
+ array.nr_ranges = 1;
+ array.ranges = malloc(sizeof(array.ranges[0]));
+ ABORT_ON(!array.ranges);
+ array.ranges[0].start = $1;
+ array.ranges[0].length = 1;
+ $$ = array;
+}
+|
+PE_VALUE PE_ARRAY_RANGE PE_VALUE
+{
+ struct parse_events_array array;
+
+ ABORT_ON($3 < $1);
+ array.nr_ranges = 1;
+ array.ranges = malloc(sizeof(array.ranges[0]));
+ ABORT_ON(!array.ranges);
+ array.ranges[0].start = $1;
+ array.ranges[0].length = $3 - $1 + 1;
+ $$ = array;
+}

sep_dc: ':' |

--
1.8.3.4

2015-11-27 08:48:56

by Wang Nan

[permalink] [raw]
Subject: [PATCH v2 11/13] perf tools: Introduce bpf-output event

Commit a43eec304259a6c637f4014a6d4767159b6a3aa3 (bpf: introduce
bpf_perf_event_output() helper) add a helper to enable BPF program
output data to perf ring buffer through a new type of perf event
PERF_COUNT_SW_BPF_OUTPUT. This patch enable perf to create perf
event of that type. Now perf user can use following cmdline to
receive output data from BPF programs:

# ./perf record -a -e evt=bpf-output/no-inherit/ \
-e ./test_bpf_output.c/maps:channel:event=evt/ ls /
# ./perf script
perf 12927 [004] 355971.129276: 0 evt=bpf-output/no-inherit/: ffffffff811ed5f1 sys_write
perf 12927 [004] 355971.129279: 0 evt=bpf-output/no-inherit/: ffffffff811ed5f1 sys_write
...

Test result:
# cat ./test_bpf_output.c
/************************ BEGIN **************************/
typedef int u32;
typedef unsigned long long u64;

enum bpf_map_type {
BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4,
};

struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
};

#define SEC(NAME) __attribute__((section(NAME), used))
static u64 (*bpf_ktime_get_ns)(void) =
(void *)5;
static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
(void *)6;
static int (*bpf_get_smp_processor_id)(void) =
(void *)8;
static int (*bpf_perf_event_output)(void *, struct bpf_map_def *, int, void *, unsigned long) =
(void *)23;

struct bpf_map_def SEC("maps") channel = {
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(u32),
.max_entries = __NR_CPUS__,
};

SEC("func_write=sys_write")
int func_write(void *ctx)
{
struct {
u64 ktime;
int cpuid;
} __attribute__((packed)) output_data;
char error_data[] = "Error: failed to output\n";

output_data.cpuid = bpf_get_smp_processor_id();
output_data.ktime = bpf_ktime_get_ns();
int err = bpf_perf_event_output(ctx, &channel, bpf_get_smp_processor_id(),
&output_data, sizeof(output_data));
if (err)
bpf_trace_printk(error_data, sizeof(error_data));
return 0;
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
/************************ END ***************************/

# ./perf record -a -e evt=bpf-output/no-inherit/ \
-e ./test_bpf_output.c/maps:channel:event=evt/ ls /
# ./perf script | grep ls
ls 4085 [000] 2746114.230215: evt=bpf-output/no-inherit/: ffffffff811ed5f1 sys_write (/lib/modules/4.3.0-rc4+/build/vmlinux)
ls 4085 [000] 2746114.230244: evt=bpf-output/no-inherit/: ffffffff811ed5f1 sys_write (/lib/modules/4.3.0-rc4+/build/vmlinux)

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/evsel.c | 6 ++++++
tools/perf/util/parse-events.c | 4 ++++
tools/perf/util/parse-events.l | 1 +
3 files changed, 11 insertions(+)

diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 32131fc..4dee8e3 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -224,6 +224,12 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
if (evsel != NULL)
perf_evsel__init(evsel, attr, idx);

+ if ((evsel->attr.type == PERF_TYPE_SOFTWARE) &&
+ (evsel->attr.config == PERF_COUNT_SW_BPF_OUTPUT)) {
+ evsel->attr.sample_type |= PERF_SAMPLE_RAW;
+ evsel->attr.sample_period = 1;
+ }
+
return evsel;
}

diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index a419571..1fddc69 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -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) \
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 8bb3437..27d567f 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -249,6 +249,7 @@ cpu-migrations|migrations { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COU
alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); }
emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }
dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); }
+bpf-output { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_BPF_OUTPUT); }

/*
* We have to handle the kernel PMU event cycles-ct/cycles-t/mem-loads/mem-stores separately.
--
1.8.3.4

2015-11-27 08:49:32

by Wang Nan

[permalink] [raw]
Subject: [PATCH v2 12/13] perf data: Add u32_hex data type

Add hexdamical u32 to base data type, which is useful for raw output
because raw data are u32 aligned.

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/data-convert-bt.c | 2 ++
1 file changed, 2 insertions(+)

diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c
index 5bfc119..34cd1e4 100644
--- a/tools/perf/util/data-convert-bt.c
+++ b/tools/perf/util/data-convert-bt.c
@@ -63,6 +63,7 @@ struct ctf_writer {
struct bt_ctf_field_type *s32;
struct bt_ctf_field_type *u32;
struct bt_ctf_field_type *string;
+ struct bt_ctf_field_type *u32_hex;
struct bt_ctf_field_type *u64_hex;
};
struct bt_ctf_field_type *array[6];
@@ -982,6 +983,7 @@ do { \
CREATE_INT_TYPE(cw->data.u64, 64, false, false);
CREATE_INT_TYPE(cw->data.s32, 32, true, false);
CREATE_INT_TYPE(cw->data.u32, 32, false, false);
+ CREATE_INT_TYPE(cw->data.u32_hex, 32, false, true);
CREATE_INT_TYPE(cw->data.u64_hex, 64, false, true);

cw->data.string = bt_ctf_field_type_string_create();
--
1.8.3.4

2015-11-27 08:50:39

by Wang Nan

[permalink] [raw]
Subject: [PATCH v2 13/13] perf data: Support converting data from bpf_perf_event_output()

bpf_perf_event_output() outputs data through sample->raw_data. This
patch adds support to convert those data into CTF. A python script
then can be used to process output data from BPF programs.

Test result:

# cat ./test_bpf_output.c
/************************ BEGIN **************************/
typedef int u32;
typedef unsigned long long u64;

enum bpf_map_type {
BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4,
};

struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
};
#define SEC(NAME) __attribute__((section(NAME), used))
static u64 (*bpf_ktime_get_ns)(void) =
(void *)5;
static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
(void *)6;
static int (*bpf_get_smp_processor_id)(void) =
(void *)8;
static int (*bpf_perf_event_output)(void *, struct bpf_map_def *, int, void *, unsigned long) =
(void *)23;
struct bpf_map_def SEC("maps") channel = {
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(u32),
.max_entries = __NR_CPUS__,
};
static inline int __attribute__((always_inline))
func(void *ctx, int type)
{
struct {
u64 ktime;
int type;
} __attribute__((packed)) output_data;
char error_data[] = "Error: failed to output\n";
int err;
output_data.type = type;
output_data.ktime = bpf_ktime_get_ns();
err = bpf_perf_event_output(ctx, &channel, bpf_get_smp_processor_id(),
&output_data, sizeof(output_data));
if (err)
bpf_trace_printk(error_data, sizeof(error_data));
return 0;
}
SEC("func_begin=sys_nanosleep")
int func_begin(void *ctx) {return func(ctx, 1);}
SEC("func_end=sys_nanosleep%return")
int func_end(void *ctx) { return func(ctx, 2);}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
/************************ END ***************************/

# ./perf record -e evt=bpf-output/no-inherit/ \
-e ./test_bpf_output_2.c/maps:channel:event=evt/ \
usleep 100000
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.012 MB perf.data (2 samples) ]

# ./perf script
usleep 14942 92503.198504: evt=bpf-output/no-inherit/: ffffffff810e0ba1 sys_nanosleep (/lib/modules/4.3.0....
usleep 14942 92503.298562: evt=bpf-output/no-inherit/: ffffffff810585e9 kretprobe_trampoline_holder (/lib....

# ./perf data convert --to-ctf ./out.ctf
[ perf data convert: Converted 'perf.data' into CTF data './out.ctf' ]
[ perf data convert: Converted and wrote 0.000 MB (2 samples) ]

# babeltrace ./out.ctf
[01:41:43.198504134] (+?.?????????) evt=bpf-output/no-inherit/: { cpu_id = 0 }, { perf_ip = 0xFFFFFFFF810E0BA1, perf_tid = 14942, perf_pid = 14942, perf_id = 1044, raw_len = 3, raw_data = [ [0] = 0x32C0C07B, [1] = 0x5421, [2] = 0x1 ] }
[01:41:43.298562257] (+0.100058123) evt=bpf-output/no-inherit/: { cpu_id = 0 }, { perf_ip = 0xFFFFFFFF810585E9, perf_tid = 14942, perf_pid = 14942, perf_id = 1044, raw_len = 3, raw_data = [ [0] = 0x38B77FAA, [1] = 0x5421, [2] = 0x2 ] }

# cat ./test_bpf_output_2.py
from babeltrace import TraceCollection
tc = TraceCollection(
tc.add_trace('./out.ctf', 'ctf')
d = {1:[], 2:[]}
for event in tc.events:
if not event.name.startswith('evt=bpf-output/no-inherit/'):
continue
raw_data = event['raw_data']
(time, type) = ((raw_data[0] + (raw_data[1] << 32)), raw_data[2])
d[type].append(time)
print(list(map(lambda i: d[2][i] - d[1][i], range(len(d[1]))))));

# python3 ./test_bpf_output_2.py
[100056879]

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/data-convert-bt.c | 115 +++++++++++++++++++++++++++++++++++++-
1 file changed, 114 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c
index 34cd1e4..1fb472b 100644
--- a/tools/perf/util/data-convert-bt.c
+++ b/tools/perf/util/data-convert-bt.c
@@ -352,6 +352,84 @@ static int add_tracepoint_values(struct ctf_writer *cw,
return ret;
}

+static int
+add_bpf_output_values(struct bt_ctf_event_class *event_class,
+ struct bt_ctf_event *event,
+ struct perf_sample *sample)
+{
+ struct bt_ctf_field_type *len_type, *seq_type;
+ struct bt_ctf_field *len_field, *seq_field;
+ unsigned int raw_size = sample->raw_size;
+ unsigned int nr_elements = raw_size / sizeof(u32);
+ unsigned int i;
+ int ret;
+
+ if (nr_elements * sizeof(u32) != raw_size)
+ pr_warning("Incorrect raw_size (%u) in bpf output event, skip %lu bytes\n",
+ raw_size, nr_elements * sizeof(u32) - raw_size);
+
+ len_type = bt_ctf_event_class_get_field_by_name(event_class, "raw_len");
+ len_field = bt_ctf_field_create(len_type);
+ if (!len_field) {
+ pr_err("failed to create 'raw_len' for bpf output event\n");
+ ret = -1;
+ goto put_len_type;
+ }
+
+ ret = bt_ctf_field_unsigned_integer_set_value(len_field, nr_elements);
+ if (ret) {
+ pr_err("failed to set field value for raw_len\n");
+ goto put_len_field;
+ }
+ ret = bt_ctf_event_set_payload(event, "raw_len", len_field);
+ if (ret) {
+ pr_err("failed to set payload to raw_len\n");
+ goto put_len_field;
+ }
+
+ seq_type = bt_ctf_event_class_get_field_by_name(event_class, "raw_data");
+ seq_field = bt_ctf_field_create(seq_type);
+ if (!seq_field) {
+ pr_err("failed to create 'raw_data' for bpf output event\n");
+ ret = -1;
+ goto put_seq_type;
+ }
+
+ ret = bt_ctf_field_sequence_set_length(seq_field, len_field);
+ if (ret) {
+ pr_err("failed to set length of 'raw_data'\n");
+ goto put_seq_field;
+ }
+
+ for (i = 0; i < nr_elements; i++) {
+ struct bt_ctf_field *elem_field =
+ bt_ctf_field_sequence_get_field(seq_field, i);
+
+ ret = bt_ctf_field_unsigned_integer_set_value(elem_field,
+ ((u32 *)(sample->raw_data))[i]);
+
+ bt_ctf_field_put(elem_field);
+ if (ret) {
+ pr_err("failed to set raw_data[%d]\n", i);
+ goto put_seq_field;
+ }
+ }
+
+ ret = bt_ctf_event_set_payload(event, "raw_data", seq_field);
+ if (ret)
+ pr_err("failed to set payload for raw_data\n");
+
+put_seq_field:
+ bt_ctf_field_put(seq_field);
+put_seq_type:
+ bt_ctf_field_type_put(seq_type);
+put_len_field:
+ bt_ctf_field_put(len_field);
+put_len_type:
+ bt_ctf_field_type_put(len_type);
+ return ret;
+}
+
static int add_generic_values(struct ctf_writer *cw,
struct bt_ctf_event *event,
struct perf_evsel *evsel,
@@ -597,6 +675,13 @@ static int process_sample_event(struct perf_tool *tool,
return -1;
}

+ if ((evsel->attr.type == PERF_TYPE_SOFTWARE) &&
+ (evsel->attr.config == PERF_COUNT_SW_BPF_OUTPUT)) {
+ ret = add_bpf_output_values(event_class, event, sample);
+ if (ret)
+ return -1;
+ }
+
cs = ctf_stream(cw, get_sample_cpu(cw, sample, evsel));
if (cs) {
if (is_flush_needed(cs))
@@ -744,6 +829,25 @@ static int add_tracepoint_types(struct ctf_writer *cw,
return ret;
}

+static int add_bpf_output_types(struct ctf_writer *cw,
+ struct bt_ctf_event_class *class)
+{
+ struct bt_ctf_field_type *len_type = cw->data.u32;
+ struct bt_ctf_field_type *seq_base_type = cw->data.u32_hex;
+ struct bt_ctf_field_type *seq_type;
+ int ret;
+
+ ret = bt_ctf_event_class_add_field(class, len_type, "raw_len");
+ if (ret)
+ return ret;
+
+ seq_type = bt_ctf_field_type_sequence_create(seq_base_type, "raw_len");
+ if (!seq_type)
+ return -1;
+
+ return bt_ctf_event_class_add_field(class, seq_type, "raw_data");
+}
+
static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel,
struct bt_ctf_event_class *event_class)
{
@@ -755,7 +859,8 @@ static int add_generic_types(struct ctf_writer *cw, struct perf_evsel *evsel,
* ctf event header
* PERF_SAMPLE_READ - TODO
* PERF_SAMPLE_CALLCHAIN - TODO
- * PERF_SAMPLE_RAW - tracepoint fields are handled separately
+ * PERF_SAMPLE_RAW - tracepoint fields and BPF output
+ * are handled separately
* PERF_SAMPLE_BRANCH_STACK - TODO
* PERF_SAMPLE_REGS_USER - TODO
* PERF_SAMPLE_STACK_USER - TODO
@@ -824,6 +929,14 @@ static int add_event(struct ctf_writer *cw, struct perf_evsel *evsel)
goto err;
}

+ if ((evsel->attr.type == PERF_TYPE_SOFTWARE) &&
+ (evsel->attr.config == PERF_COUNT_SW_BPF_OUTPUT)) {
+ ret = add_bpf_output_types(cw, event_class);
+ if (ret)
+ goto err;
+
+ }
+
ret = bt_ctf_stream_class_add_event_class(cw->stream_class, event_class);
if (ret) {
pr("Failed to add event class into stream.\n");
--
1.8.3.4

2015-11-28 01:11:04

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: RFC Re: [PATCH v2 04/13] perf tools: Add API to config maps in bpf object

Em Fri, Nov 27, 2015 at 08:47:38AM +0000, Wang Nan escreveu:
> bpf__config_obj() is introduced as a core API to config BPF object
> after loading. One configuration option of maps is introduced. After
> this patch BPF object can accept configuration like:
>
> maps:my_map:value=1234
>
> (maps.my_map.value looks pretty. However, there's a small but hard
> to fixed problem related to flex's greedy matching. Please see [1].
> Choose ':' to avoid it in a simpler way.)

Understood the issues, but I would like to hear from Ingo, Jiri,
Namhyung, Brian and others here, since we're setting up syntax, and yes,
using:

maps.my_map.value[0,3...6]=1234;

or even:

maps->my_map->value[0,3...6]=1234;

Looks more natural than:

maps:my_map:value[0,3...6]=1234;

I'll think harder about this, maybe we can find a way to use the more
familiar dot notation somehow.

And I have to anyway test this more extensively, so I'll push the
patches I have and try this as soon as I can.

- Arnaldo
>
> This patch is more complex than the work it really does because the
> consideration of extension. In designing of BPF map configuration,
> following things should be considered:
>
> 1. Array indics selection: perf should allow user setting different

indices :-)

> value to different slots in an array, with syntax like:
> maps:my_map:value[0,3...6]=1234;
>
> 2. A map can be config by different config terms, each for a part
> of it. For example, set each slot to pid of a thread;
>
> 3. Type of value: integer is not the only valid value type. Perf
> event can also be put into a map after commit 35578d7984003097af2b1e3
> (bpf: Implement function bpf_perf_event_read() that get the selected
> hardware PMU conuter);
>
> 4. For hash table, it is possible to use string or other as key;
>
> 5. It is possible that map configuration is unable to be setup
> during parsing. Perf event is an example.
>
> Therefore, this patch does following:
>
> 1. Instead of updating map element during parsing, this patch stores
> map config options in 'struct bpf_map_priv'. Following patches
> would apply those configs at proper time;
>
> 2. Link map operations to a list so a map can have multiple config
> terms attached, so different parts can be configured separately;
>
> 3. Make 'struct bpf_map_priv' extensible so following patches can
> add new types of keys and operations;
>
> 4. Use bpf_config_map_funcs array to support more maps config options.
>
> Since the patch changing event parser to parse BPF object config is
> relative large, I put in another commit. Code in this patch
> could be tested after applying next patch.
>
> [1] http://lkml.kernel.org/g/[email protected]

> Signed-off-by: Wang Nan <[email protected]>
> Signed-off-by: He Kuang <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Masami Hiramatsu <[email protected]>
> Cc: Namhyung Kim <[email protected]>
> Cc: Zefan Li <[email protected]>
> Cc: [email protected]
> ---
> tools/perf/util/bpf-loader.c | 266 +++++++++++++++++++++++++++++++++++++++++++
> tools/perf/util/bpf-loader.h | 38 +++++++
> 2 files changed, 304 insertions(+)
>
> diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
> index 540a7ef..989c80f 100644
> --- a/tools/perf/util/bpf-loader.c
> +++ b/tools/perf/util/bpf-loader.c
> @@ -739,6 +739,251 @@ int bpf__foreach_tev(struct bpf_object *obj,
> return 0;
> }
>
> +enum bpf_map_op_type {
> + BPF_MAP_OP_SET_VALUE,
> +};
> +
> +enum bpf_map_key_type {
> + BPF_MAP_KEY_ALL,
> +};
> +
> +struct bpf_map_op {
> + struct list_head list;
> + enum bpf_map_op_type op_type;
> + enum bpf_map_key_type key_type;
> + union {
> + u64 value;
> + } v;
> +};
> +
> +struct bpf_map_priv {
> + struct list_head ops_list;
> +};
> +
> +static void
> +bpf_map_op__free(struct bpf_map_op *op)
> +{
> + struct list_head *list = &op->list;
> + /*
> + * bpf_map_op__free() needs to consider following cases:
> + * 1. When the op is created but not linked to any list:
> + * impossible. This only happen in bpf_map_op__alloc()
> + * and it would be freed directly;
> + * 2. Normal case, when the op is linked to a list;
> + * 3. After the op has already be removed.
> + * Thanks to list.h, if it has removed by list_del() then
> + * list->{next,prev} should have been set to LIST_POISON{1,2}.
> + */
> + if ((list->next != LIST_POISON1) && (list->prev != LIST_POISON2))
> + list_del(list);
> + free(op);
> +}
> +
> +static void
> +bpf_map_priv__clear(struct bpf_map *map __maybe_unused,
> + void *_priv)
> +{
> + struct bpf_map_priv *priv = _priv;
> + struct bpf_map_op *pos, *n;
> +
> + list_for_each_entry_safe(pos, n, &priv->ops_list, list)
> + bpf_map_op__free(pos);
> + free(priv);
> +}
> +
> +static struct bpf_map_op *
> +bpf_map_op__alloc(struct bpf_map *map)
> +{
> + struct bpf_map_op *op;
> + struct bpf_map_priv *priv;
> + const char *map_name;
> + int err;
> +
> + map_name = bpf_map__get_name(map);
> + err = bpf_map__get_private(map, (void **)&priv);
> + if (err) {
> + pr_debug("Failed to get private from map %s\n", map_name);
> + return ERR_PTR(err);
> + }
> +
> + if (!priv) {
> + priv = zalloc(sizeof(*priv));
> + if (!priv) {
> + pr_debug("No enough memory to alloc map private\n");
> + return ERR_PTR(-ENOMEM);
> + }
> + INIT_LIST_HEAD(&priv->ops_list);
> +
> + if (bpf_map__set_private(map, priv, bpf_map_priv__clear)) {
> + free(priv);
> + return ERR_PTR(-BPF_LOADER_ERRNO__INTERNAL);
> + }
> + }
> +
> + op = zalloc(sizeof(*op));
> + if (!op) {
> + pr_debug("Failed to alloc bpf_map_op\n");
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + op->key_type = BPF_MAP_KEY_ALL;
> + list_add_tail(&op->list, &priv->ops_list);
> + return op;
> +}
> +
> +static int
> +bpf__obj_config_map_array_value(struct bpf_map *map,
> + struct parse_events_term *term)
> +{
> + struct bpf_map_def def;
> + struct bpf_map_op *op;
> + const char *map_name;
> + int err;
> +
> + map_name = bpf_map__get_name(map);
> +
> + err = bpf_map__get_def(map, &def);
> + if (err) {
> + pr_debug("Unable to get map definition from '%s'\n",
> + map_name);
> + return -BPF_LOADER_ERRNO__INTERNAL;
> + }
> +
> + if (def.type != BPF_MAP_TYPE_ARRAY) {
> + pr_debug("Map %s type is not BPF_MAP_TYPE_ARRAY\n",
> + map_name);
> + return -BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE;
> + }
> + if (def.key_size < sizeof(unsigned int)) {
> + pr_debug("Map %s has incorrect key size\n", map_name);
> + return -BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE;
> + }
> + switch (def.value_size) {
> + case 1:
> + case 2:
> + case 4:
> + case 8:
> + break;
> + default:
> + pr_debug("Map %s has incorrect value size\n", map_name);
> + return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE;
> + }
> +
> + op = bpf_map_op__alloc(map);
> + if (IS_ERR(op))
> + return PTR_ERR(op);
> + op->op_type = BPF_MAP_OP_SET_VALUE;
> + op->v.value = term->val.num;
> + return 0;
> +}
> +
> +static int
> +bpf__obj_config_map_value(struct bpf_map *map,
> + struct parse_events_term *term,
> + struct perf_evlist *evlist __maybe_unused)
> +{
> + if (!term->err_val) {
> + pr_debug("Config value not set\n");
> + return -BPF_LOADER_ERRNO__OBJCONF_CONF;
> + }
> +
> + if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM)
> + return bpf__obj_config_map_array_value(map, term);
> +
> + pr_debug("ERROR: wrong value type\n");
> + return -BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE;
> +}
> +
> +struct bpf_obj_config_map_func {
> + const char *config_opt;
> + int (*config_func)(struct bpf_map *, struct parse_events_term *,
> + struct perf_evlist *);
> +};
> +
> +struct bpf_obj_config_map_func bpf_obj_config_map_funcs[] = {
> + {"value", bpf__obj_config_map_value},
> +};
> +
> +static int
> +bpf__obj_config_map(struct bpf_object *obj,
> + struct parse_events_term *term,
> + struct perf_evlist *evlist,
> + int *key_scan_pos)
> +{
> + /* key is "maps:<mapname>:<config opt>" */
> + char *map_name = strdup(term->config + sizeof("maps:") - 1);
> + struct bpf_map *map;
> + int err = -BPF_LOADER_ERRNO__OBJCONF_OPT;
> + char *map_opt;
> + size_t i;
> +
> + if (!map_name)
> + return -ENOMEM;
> +
> + map_opt = strchr(map_name, ':');
> + if (!map_opt) {
> + pr_debug("ERROR: Invalid map config: %s\n", map_name);
> + goto out;
> + }
> +
> + *map_opt++ = '\0';
> + if (*map_opt == '\0') {
> + pr_debug("ERROR: Invalid map option: %s\n", term->config);
> + goto out;
> + }
> +
> + map = bpf_object__get_map_by_name(obj, map_name);
> + if (!map) {
> + pr_debug("ERROR: Map %s is not exist\n", map_name);
> + err = -BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST;
> + goto out;
> + }
> +
> + *key_scan_pos += map_opt - map_name;
> + for (i = 0; i < ARRAY_SIZE(bpf_obj_config_map_funcs); i++) {
> + struct bpf_obj_config_map_func *func =
> + &bpf_obj_config_map_funcs[i];
> +
> + if (strcmp(map_opt, func->config_opt) == 0) {
> + err = func->config_func(map, term, evlist);
> + goto out;
> + }
> + }
> +
> + pr_debug("ERROR: invalid config option '%s' for maps\n",
> + map_opt);
> + err = -BPF_LOADER_ERRNO__OBJCONF_MAP_OPT;
> +out:
> + free(map_name);
> + if (!err)
> + key_scan_pos += strlen(map_opt);
> + return err;
> +}
> +
> +int bpf__config_obj(struct bpf_object *obj,
> + struct parse_events_term *term,
> + struct perf_evlist *evlist,
> + int *error_pos)
> +{
> + int key_scan_pos = 0;
> + int err;
> +
> + if (!obj || !term || !term->config)
> + return -EINVAL;
> +
> + if (!prefixcmp(term->config, "maps:")) {
> + key_scan_pos = sizeof("maps:") - 1;
> + err = bpf__obj_config_map(obj, term, evlist, &key_scan_pos);
> + goto out;
> + }
> + err = -BPF_LOADER_ERRNO__OBJCONF_OPT;
> +out:
> + if (error_pos)
> + *error_pos = key_scan_pos;
> + return err;
> +
> +}
> +
> #define ERRNO_OFFSET(e) ((e) - __BPF_LOADER_ERRNO__START)
> #define ERRCODE_OFFSET(c) ERRNO_OFFSET(BPF_LOADER_ERRNO__##c)
> #define NR_ERRNO (__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START)
> @@ -753,6 +998,14 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = {
> [ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue",
> [ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program",
> [ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue",
> + [ERRCODE_OFFSET(OBJCONF_OPT)] = "Invalid object config option",
> + [ERRCODE_OFFSET(OBJCONF_CONF)] = "Config value not set (lost '=')",
> + [ERRCODE_OFFSET(OBJCONF_MAP_OPT)] = "Invalid object maps config option",
> + [ERRCODE_OFFSET(OBJCONF_MAP_NOTEXIST)] = "Target map not exist",
> + [ERRCODE_OFFSET(OBJCONF_MAP_VALUE)] = "Incorrect value type for map",
> + [ERRCODE_OFFSET(OBJCONF_MAP_TYPE)] = "Incorrect map type",
> + [ERRCODE_OFFSET(OBJCONF_MAP_KEYSIZE)] = "Incorrect map key size",
> + [ERRCODE_OFFSET(OBJCONF_MAP_VALUESIZE)] = "Incorrect map value size",
> };
>
> static int
> @@ -872,3 +1125,16 @@ int bpf__strerror_load(struct bpf_object *obj,
> bpf__strerror_end(buf, size);
> return 0;
> }
> +
> +int bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused,
> + struct parse_events_term *term __maybe_unused,
> + struct perf_evlist *evlist __maybe_unused,
> + int *error_pos __maybe_unused, int err,
> + char *buf, size_t size)
> +{
> + bpf__strerror_head(err, buf, size);
> + bpf__strerror_entry(BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE,
> + "Can't use this config term to this type of map");
> + bpf__strerror_end(buf, size);
> + return 0;
> +}
> diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
> index 6fdc045..2464db9 100644
> --- a/tools/perf/util/bpf-loader.h
> +++ b/tools/perf/util/bpf-loader.h
> @@ -10,6 +10,7 @@
> #include <string.h>
> #include <bpf/libbpf.h>
> #include "probe-event.h"
> +#include "evlist.h"
> #include "debug.h"
>
> enum bpf_loader_errno {
> @@ -24,10 +25,19 @@ enum bpf_loader_errno {
> 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 */
> + BPF_LOADER_ERRNO__OBJCONF_OPT, /* Invalid object config option */
> + BPF_LOADER_ERRNO__OBJCONF_CONF, /* Config value not set (lost '=')) */
> + BPF_LOADER_ERRNO__OBJCONF_MAP_OPT, /* Invalid object maps config option */
> + BPF_LOADER_ERRNO__OBJCONF_MAP_NOTEXIST, /* Target map not exist */
> + BPF_LOADER_ERRNO__OBJCONF_MAP_VALUE, /* Incorrect value type for map */
> + BPF_LOADER_ERRNO__OBJCONF_MAP_TYPE, /* Incorrect map type */
> + BPF_LOADER_ERRNO__OBJCONF_MAP_KEYSIZE, /* Incorrect map key size */
> + BPF_LOADER_ERRNO__OBJCONF_MAP_VALUESIZE,/* Incorrect map value size */
> __BPF_LOADER_ERRNO__END,
> };
>
> struct bpf_object;
> +struct parse_events_term;
> #define PERF_BPF_PROBE_GROUP "perf_bpf_probe"
>
> typedef int (*bpf_prog_iter_callback_t)(struct probe_trace_event *tev,
> @@ -53,6 +63,14 @@ int bpf__strerror_load(struct bpf_object *obj, int err,
> char *buf, size_t size);
> int bpf__foreach_tev(struct bpf_object *obj,
> bpf_prog_iter_callback_t func, void *arg);
> +
> +int bpf__config_obj(struct bpf_object *obj, struct parse_events_term *term,
> + struct perf_evlist *evlist, int *error_pos);
> +int bpf__strerror_config_obj(struct bpf_object *obj,
> + struct parse_events_term *term,
> + struct perf_evlist *evlist,
> + int *error_pos, int err, char *buf,
> + size_t size);
> #else
> static inline struct bpf_object *
> bpf__prepare_load(const char *filename __maybe_unused,
> @@ -84,6 +102,15 @@ bpf__foreach_tev(struct bpf_object *obj __maybe_unused,
> }
>
> static inline int
> +bpf__config_obj(struct bpf_object *obj __maybe_unused,
> + struct parse_events_term *term __maybe_unused,
> + struct perf_evlist *evlist __maybe_unused,
> + int *error_pos __maybe_unused)
> +{
> + return 0;
> +}
> +
> +static inline int
> __bpf_strerror(char *buf, size_t size)
> {
> if (!size)
> @@ -118,5 +145,16 @@ static inline int bpf__strerror_load(struct bpf_object *obj __maybe_unused,
> {
> return __bpf_strerror(buf, size);
> }
> +
> +static inline int
> +bpf__strerror_config_obj(struct bpf_object *obj __maybe_unused,
> + struct parse_events_term *term __maybe_unused,
> + struct perf_evlist *evlist __maybe_unused,
> + int *error_pos __maybe_unused,
> + int err __maybe_unused,
> + char *buf, size_t size)
> +{
> + return __bpf_strerror(buf, size);
> +}
> #endif
> #endif
> --
> 1.8.3.4

2015-11-28 01:26:06

by Wang Nan

[permalink] [raw]
Subject: Re: RFC Re: [PATCH v2 04/13] perf tools: Add API to config maps in bpf object



On 2015/11/28 9:10, Arnaldo Carvalho de Melo wrote:
> Em Fri, Nov 27, 2015 at 08:47:38AM +0000, Wang Nan escreveu:
>> bpf__config_obj() is introduced as a core API to config BPF object
>> after loading. One configuration option of maps is introduced. After
>> this patch BPF object can accept configuration like:
>>
>> maps:my_map:value=1234
>>
>> (maps.my_map.value looks pretty. However, there's a small but hard
>> to fixed problem related to flex's greedy matching. Please see [1].
>> Choose ':' to avoid it in a simpler way.)
> Understood the issues, but I would like to hear from Ingo, Jiri,
> Namhyung, Brian and others here, since we're setting up syntax, and yes,
> using:
>
> maps.my_map.value[0,3...6]=1234;
>
> or even:
>
> maps->my_map->value[0,3...6]=1234;
>
> Looks more natural than:
>
> maps:my_map:value[0,3...6]=1234;

You don't mention maps::my_map::value. C++ use '::' this way, so I think
it is also a possible choice.

Or we can use another character for example '|' to replace '/' in case we
have a map starting with 'c' or 'o'. For example:

./mybpf.c|maps.channel.value=1234|

and in normal case still support '/':

./mybpf.c/maps.channel.value=1234/

like 'sed':

# sed 's/a/b/g'
a
b
# sed 's+/+|+g'
/
|

Thank you.

2015-11-28 01:22:38

by Wang Nan

[permalink] [raw]
Subject: Re: RFC Re: [PATCH v2 04/13] perf tools: Add API to config maps in bpf object



On 2015/11/28 9:20, Wangnan (F) wrote:
>
>
> On 2015/11/28 9:10, Arnaldo Carvalho de Melo wrote:
>> Em Fri, Nov 27, 2015 at 08:47:38AM +0000, Wang Nan escreveu:
>>> bpf__config_obj() is introduced as a core API to config BPF object
>>> after loading. One configuration option of maps is introduced. After
>>> this patch BPF object can accept configuration like:
>>>
>>> maps:my_map:value=1234
>>>
>>> (maps.my_map.value looks pretty. However, there's a small but hard
>>> to fixed problem related to flex's greedy matching. Please see [1].
>>> Choose ':' to avoid it in a simpler way.)
>> Understood the issues, but I would like to hear from Ingo, Jiri,
>> Namhyung, Brian and others here, since we're setting up syntax, and yes,
>> using:
>> maps.my_map.value[0,3...6]=1234;
>>
>> or even:
>>
>> maps->my_map->value[0,3...6]=1234;
>>
>> Looks more natural than:
>>
>> maps:my_map:value[0,3...6]=1234;
>
> You don't mention maps::my_map::value. C++ use '::' this way, so I think
> it is also a possible choice.
>
> Or we can use another character for example '|' to replace '/' in case we
> have a map starting with 'c' or 'o'. For example:
>
> ./mybpf.c|maps.channel.value=1234|
>
> and in normal case still support '/':
>
> ./mybpf.c/maps.channel.value=1234/

Should be:

./mybpf.c/maps.mymap.value=1234/

>
> like 'sed':
>
> # sed 's/a/b/g'
> a
> b
> # sed 's+/+|+g'
> /
> |
>
> Thank you.

Subject: [tip:perf/core] tools lib bpf: Collect map definition in bpf_object

Commit-ID: 9d759a9b4ac2690077d8b21258e6e95c3e34bfa9
Gitweb: http://git.kernel.org/tip/9d759a9b4ac2690077d8b21258e6e95c3e34bfa9
Author: Wang Nan <[email protected]>
AuthorDate: Fri, 27 Nov 2015 08:47:35 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 27 Nov 2015 21:57:09 -0300

tools lib bpf: Collect map definition in bpf_object

This patch collects more information from maps sections in BPF object
files into 'struct bpf_object', enables later patches access those
information (such as the type and size of the map).

In this patch, a new handler 'struct bpf_map' is extracted in parallel
with bpf_object and bpf_program. Its iterator and accessor is also
created.

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/lib/bpf/libbpf.c | 187 +++++++++++++++++++++++++++++++++----------------
tools/lib/bpf/libbpf.h | 21 ++++++
2 files changed, 148 insertions(+), 60 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index e3f4c33..f509825 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -163,22 +163,24 @@ struct bpf_program {
bpf_program_clear_priv_t clear_priv;
};

+struct bpf_map {
+ int fd;
+ 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,21 +491,38 @@ 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;
+
+ for (i = 0; i < nr_maps; i++) {
+ struct bpf_map_def *def = &obj->maps[i].def;

- obj->maps_buf_sz = size;
- memcpy(obj->maps_buf, data, size);
- pr_debug("maps in %s: %ld bytes\n", obj->path, (long)size);
+ /*
+ * 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;
}

@@ -688,37 +707,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;
- }

- 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);
+ for (i = 0; i < obj->nr_maps; i++) {
+ struct bpf_map_def *def = &obj->maps[i].def;
+ int *pfd = &obj->maps[i].fd;

- 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 +723,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 +753,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 +772,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 +796,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 +1041,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 +1085,15 @@ 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++) {
+ 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 +1248,73 @@ 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;
+}
+
+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];
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 949df4b..ef63125 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -165,4 +165,25 @@ 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_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);
+
+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

Subject: [tip:perf/core] tools lib bpf: Extract and collect map names from BPF object file

Commit-ID: 561bbccac72d08babafaa33fd7fa9100ec4c9fb6
Gitweb: http://git.kernel.org/tip/561bbccac72d08babafaa33fd7fa9100ec4c9fb6
Author: Wang Nan <[email protected]>
AuthorDate: Fri, 27 Nov 2015 08:47:36 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 27 Nov 2015 21:59:53 -0300

tools lib bpf: Extract and collect map names from BPF object file

This patch collects name of maps in BPF object files and saves them into
'maps' field in 'struct bpf_object'. 'bpf_object__get_map_by_name' is
introduced to retrive fd and definitions of a map through its name.

Signed-off-by: He Kuang <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Wang Nan <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/lib/bpf/libbpf.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++---
tools/lib/bpf/libbpf.h | 3 +++
2 files changed, 65 insertions(+), 3 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index f509825..a298614 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -165,6 +165,7 @@ struct bpf_program {

struct bpf_map {
int fd;
+ char *name;
struct bpf_map_def def;
void *priv;
bpf_map_clear_priv_t clear_priv;
@@ -526,12 +527,46 @@ bpf_object__init_maps(struct bpf_object *obj, void *data,
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)) {
@@ -581,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);
@@ -625,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;
}
@@ -1086,6 +1125,7 @@ void bpf_object__close(struct bpf_object *obj)
bpf_object__unload(obj);

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);
@@ -1266,6 +1306,13 @@ int bpf_map__get_def(struct bpf_map *map, struct bpf_map_def *pdef)
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)
{
@@ -1318,3 +1365,15 @@ bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
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;
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index ef63125..a51594c 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -170,6 +170,8 @@ struct bpf_map_def {
* 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);
@@ -180,6 +182,7 @@ bpf_map__next(struct bpf_map *map, struct bpf_object *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,

Subject: [tip:perf/core] perf bpf: Rename bpf config to program config

Commit-ID: 0bb93490170477224f8bd4cc9ce8920517461643
Gitweb: http://git.kernel.org/tip/0bb93490170477224f8bd4cc9ce8920517461643
Author: Wang Nan <[email protected]>
AuthorDate: Fri, 27 Nov 2015 08:47:37 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 27 Nov 2015 22:00:46 -0300

perf bpf: Rename bpf config to program config

Following patches are going to introduce BPF object level configuration
to enable setting values into BPF maps. To avoid confusion, this patch
renames existing 'config' in bpf-loader.c to 'program config'. Following
patches would introduce 'object config'.

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/bpf-loader.c | 65 ++++++++++++++++++++++----------------------
tools/perf/util/bpf-loader.h | 2 +-
2 files changed, 33 insertions(+), 34 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 36544e5..540a7ef 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -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;
}
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index a58740b..6fdc045 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -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 */

2015-11-29 16:15:09

by Namhyung Kim

[permalink] [raw]
Subject: Re: [PATCH v2 02/13] bpf tools: Extract and collect map names from BPF object file

Hi Wang,

On Fri, Nov 27, 2015 at 08:47:36AM +0000, Wang Nan wrote:
> This patch collects name of maps in BPF object files and saves them into
> 'maps' field in 'struct bpf_object'. 'bpf_object__get_map_by_name' is
> introduced to retrive fd and definitions of a map through its name.
>
> Signed-off-by: Wang Nan <[email protected]>
> Signed-off-by: He Kuang <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Masami Hiramatsu <[email protected]>
> Cc: Namhyung Kim <[email protected]>
> Cc: Zefan Li <[email protected]>
> Cc: [email protected]
> ---
> tools/lib/bpf/libbpf.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++---
> tools/lib/bpf/libbpf.h | 3 +++
> 2 files changed, 65 insertions(+), 3 deletions(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index f509825..a298614 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -165,6 +165,7 @@ struct bpf_program {
>
> struct bpf_map {
> int fd;
> + char *name;
> struct bpf_map_def def;
> void *priv;
> bpf_map_clear_priv_t clear_priv;
> @@ -526,12 +527,46 @@ bpf_object__init_maps(struct bpf_object *obj, void *data,
> 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);

It means that each map name is saved in section header string table?


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

You need to check the return value.

thanks,
Namhyung


> + pr_debug("map %zu is \"%s\"\n", map_idx,
> + obj->maps[map_idx].name);
> + }
> +}
> +

2015-11-29 16:22:31

by Namhyung Kim

[permalink] [raw]
Subject: Re: RFC Re: [PATCH v2 04/13] perf tools: Add API to config maps in bpf object

Hi Arnaldo,

On Fri, Nov 27, 2015 at 10:10:54PM -0300, Arnaldo Carvalho de Melo wrote:
> Em Fri, Nov 27, 2015 at 08:47:38AM +0000, Wang Nan escreveu:
> > bpf__config_obj() is introduced as a core API to config BPF object
> > after loading. One configuration option of maps is introduced. After
> > this patch BPF object can accept configuration like:
> >
> > maps:my_map:value=1234
> >
> > (maps.my_map.value looks pretty. However, there's a small but hard
> > to fixed problem related to flex's greedy matching. Please see [1].
> > Choose ':' to avoid it in a simpler way.)
>
> Understood the issues, but I would like to hear from Ingo, Jiri,
> Namhyung, Brian and others here, since we're setting up syntax, and yes,
> using:
>
> maps.my_map.value[0,3...6]=1234;
>
> or even:
>
> maps->my_map->value[0,3...6]=1234;
>
> Looks more natural than:
>
> maps:my_map:value[0,3...6]=1234;

Do we have to use only single separator? What about this?

maps:my_map.value[0,3...6]=1234;

Thanks,
Namhyung


>
> I'll think harder about this, maybe we can find a way to use the more
> familiar dot notation somehow.
>
> And I have to anyway test this more extensively, so I'll push the
> patches I have and try this as soon as I can.
>
> - Arnaldo
> >
> > This patch is more complex than the work it really does because the
> > consideration of extension. In designing of BPF map configuration,
> > following things should be considered:
> >
> > 1. Array indics selection: perf should allow user setting different
>
> indices :-)
>
> > value to different slots in an array, with syntax like:
> > maps:my_map:value[0,3...6]=1234;
> >
> > 2. A map can be config by different config terms, each for a part
> > of it. For example, set each slot to pid of a thread;
> >
> > 3. Type of value: integer is not the only valid value type. Perf
> > event can also be put into a map after commit 35578d7984003097af2b1e3
> > (bpf: Implement function bpf_perf_event_read() that get the selected
> > hardware PMU conuter);
> >
> > 4. For hash table, it is possible to use string or other as key;
> >
> > 5. It is possible that map configuration is unable to be setup
> > during parsing. Perf event is an example.
> >
> > Therefore, this patch does following:
> >
> > 1. Instead of updating map element during parsing, this patch stores
> > map config options in 'struct bpf_map_priv'. Following patches
> > would apply those configs at proper time;
> >
> > 2. Link map operations to a list so a map can have multiple config
> > terms attached, so different parts can be configured separately;
> >
> > 3. Make 'struct bpf_map_priv' extensible so following patches can
> > add new types of keys and operations;
> >
> > 4. Use bpf_config_map_funcs array to support more maps config options.
> >
> > Since the patch changing event parser to parse BPF object config is
> > relative large, I put in another commit. Code in this patch
> > could be tested after applying next patch.
> >
> > [1] http://lkml.kernel.org/g/[email protected]
>
> > Signed-off-by: Wang Nan <[email protected]>
> > Signed-off-by: He Kuang <[email protected]>
> > Cc: Alexei Starovoitov <[email protected]>
> > Cc: Arnaldo Carvalho de Melo <[email protected]>
> > Cc: Masami Hiramatsu <[email protected]>
> > Cc: Namhyung Kim <[email protected]>
> > Cc: Zefan Li <[email protected]>
> > Cc: [email protected]

2015-11-30 05:01:18

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH v2 02/13] bpf tools: Extract and collect map names from BPF object file



On 2015/11/30 0:14, Namhyung Kim wrote:
> Hi Wang,
>
> On Fri, Nov 27, 2015 at 08:47:36AM +0000, Wang Nan wrote:
>> This patch collects name of maps in BPF object files and saves them into
>> 'maps' field in 'struct bpf_object'. 'bpf_object__get_map_by_name' is
>> introduced to retrive fd and definitions of a map through its name.
>>
>> Signed-off-by: Wang Nan <[email protected]>
>> Signed-off-by: He Kuang <[email protected]>
>> Cc: Alexei Starovoitov <[email protected]>
>> Cc: Arnaldo Carvalho de Melo <[email protected]>
>> Cc: Masami Hiramatsu <[email protected]>
>> Cc: Namhyung Kim <[email protected]>
>> Cc: Zefan Li <[email protected]>
>> Cc: [email protected]
>> ---
>> tools/lib/bpf/libbpf.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++---
>> tools/lib/bpf/libbpf.h | 3 +++
>> 2 files changed, 65 insertions(+), 3 deletions(-)
>>
>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>> index f509825..a298614 100644
>> --- a/tools/lib/bpf/libbpf.c
>> +++ b/tools/lib/bpf/libbpf.c
>> @@ -165,6 +165,7 @@ struct bpf_program {
>>
>> struct bpf_map {
>> int fd;
>> + char *name;
>> struct bpf_map_def def;
>> void *priv;
>> bpf_map_clear_priv_t clear_priv;
>> @@ -526,12 +527,46 @@ bpf_object__init_maps(struct bpf_object *obj, void *data,
>> 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);
> It means that each map name is saved in section header string table?

According to elf format specification:

For an symbol table entry, the st_name field "holds an index
into the object file’s symbol string table, which holds the
character representations of the symbol names. If the value
is non-zero, it represents a string table index that gives
the symbol name. Otherwise, the symbol table entry has no
name."

And so called "object file’s symbol string table" is a
section in the object file which index is stored into
ehdr and be loaded during gelf_getehdr(), and its index
would be set to ehdr->e_shstrndx. So I think for each map
its name should be saved in that string table.

>
>> + 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);
> You need to check the return value.

Will send a patch for it.

Thank you.

2015-11-30 08:52:04

by Namhyung Kim

[permalink] [raw]
Subject: Re: [PATCH v2 02/13] bpf tools: Extract and collect map names from BPF object file

On Mon, Nov 30, 2015 at 01:00:46PM +0800, Wangnan (F) wrote:
>
>
> On 2015/11/30 0:14, Namhyung Kim wrote:
> >Hi Wang,
> >
> >On Fri, Nov 27, 2015 at 08:47:36AM +0000, Wang Nan wrote:
> >>This patch collects name of maps in BPF object files and saves them into
> >>'maps' field in 'struct bpf_object'. 'bpf_object__get_map_by_name' is
> >>introduced to retrive fd and definitions of a map through its name.
> >>
> >>Signed-off-by: Wang Nan <[email protected]>
> >>Signed-off-by: He Kuang <[email protected]>
> >>Cc: Alexei Starovoitov <[email protected]>
> >>Cc: Arnaldo Carvalho de Melo <[email protected]>
> >>Cc: Masami Hiramatsu <[email protected]>
> >>Cc: Namhyung Kim <[email protected]>
> >>Cc: Zefan Li <[email protected]>
> >>Cc: [email protected]
> >>---
> >> tools/lib/bpf/libbpf.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++---
> >> tools/lib/bpf/libbpf.h | 3 +++
> >> 2 files changed, 65 insertions(+), 3 deletions(-)
> >>
> >>diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> >>index f509825..a298614 100644
> >>--- a/tools/lib/bpf/libbpf.c
> >>+++ b/tools/lib/bpf/libbpf.c
> >>@@ -165,6 +165,7 @@ struct bpf_program {
> >> struct bpf_map {
> >> int fd;
> >>+ char *name;
> >> struct bpf_map_def def;
> >> void *priv;
> >> bpf_map_clear_priv_t clear_priv;
> >>@@ -526,12 +527,46 @@ bpf_object__init_maps(struct bpf_object *obj, void *data,
> >> 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);
> >It means that each map name is saved in section header string table?
>
> According to elf format specification:
>
> For an symbol table entry, the st_name field "holds an index
> into the object file’s symbol string table, which holds the
> character representations of the symbol names. If the value
> is non-zero, it represents a string table index that gives
> the symbol name. Otherwise, the symbol table entry has no
> name."
>
> And so called "object file’s symbol string table" is a
> section in the object file which index is stored into
> ehdr and be loaded during gelf_getehdr(), and its index
> would be set to ehdr->e_shstrndx. So I think for each map
> its name should be saved in that string table.

AFAIK there're two symbol string tables in a ELF file. One for
section headers (.shstrtab) and another for normal symbols (.strtab).
And ehdr->e_shstrndx is the index of section header string table so
your code assumes map names are saved in the section header string
table, right?

Thanks,
Namhyung


>
> >
> >>+ 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);
> >You need to check the return value.
>
> Will send a patch for it.
>
> Thank you.
>

2015-11-30 09:28:29

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH v2 02/13] bpf tools: Extract and collect map names from BPF object file



On 2015/11/30 16:51, Namhyung Kim wrote:
> On Mon, Nov 30, 2015 at 01:00:46PM +0800, Wangnan (F) wrote:
>>
>> On 2015/11/30 0:14, Namhyung Kim wrote:
>>> Hi Wang,
>>>
>>> On Fri, Nov 27, 2015 at 08:47:36AM +0000, Wang Nan wrote:
>>>> This patch collects name of maps in BPF object files and saves them into
>>>> 'maps' field in 'struct bpf_object'. 'bpf_object__get_map_by_name' is
>>>> introduced to retrive fd and definitions of a map through its name.
>>>>
>>>> Signed-off-by: Wang Nan <[email protected]>
>>>> Signed-off-by: He Kuang <[email protected]>
>>>> Cc: Alexei Starovoitov <[email protected]>
>>>> Cc: Arnaldo Carvalho de Melo <[email protected]>
>>>> Cc: Masami Hiramatsu <[email protected]>
>>>> Cc: Namhyung Kim <[email protected]>
>>>> Cc: Zefan Li <[email protected]>
>>>> Cc: [email protected]
>>>> ---
>>>> tools/lib/bpf/libbpf.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++---
>>>> tools/lib/bpf/libbpf.h | 3 +++
>>>> 2 files changed, 65 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>>>> index f509825..a298614 100644
>>>> --- a/tools/lib/bpf/libbpf.c
>>>> +++ b/tools/lib/bpf/libbpf.c
>>>> @@ -165,6 +165,7 @@ struct bpf_program {
>>>> struct bpf_map {
>>>> int fd;
>>>> + char *name;
>>>> struct bpf_map_def def;
>>>> void *priv;
>>>> bpf_map_clear_priv_t clear_priv;
>>>> @@ -526,12 +527,46 @@ bpf_object__init_maps(struct bpf_object *obj, void *data,
>>>> 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);
>>> It means that each map name is saved in section header string table?
>> According to elf format specification:
>>
>> For an symbol table entry, the st_name field "holds an index
>> into the object file’s symbol string table, which holds the
>> character representations of the symbol names. If the value
>> is non-zero, it represents a string table index that gives
>> the symbol name. Otherwise, the symbol table entry has no
>> name."
>>
>> And so called "object file’s symbol string table" is a
>> section in the object file which index is stored into
>> ehdr and be loaded during gelf_getehdr(), and its index
>> would be set to ehdr->e_shstrndx. So I think for each map
>> its name should be saved in that string table.
> AFAIK there're two symbol string tables in a ELF file. One for
> section headers (.shstrtab) and another for normal symbols (.strtab).
> And ehdr->e_shstrndx is the index of section header string table so
> your code assumes map names are saved in the section header string
> table, right?
>
> Thanks,
> Namhyung

In case of gcc:

$ echo 'int func() {return 0;}' | gcc -x c -c -o ./temp.o -
$ readelf -h ./temp.o
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 240 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 11
Section header string table index: 8

Let's see what is section 8:

$ readelf -S ./temp.o
...
[ 8] .shstrtab STRTAB 0000000000000000 00000098
0000000000000054 0000000000000000 0 0 1
...

Yes, in this case it is .shstrtab.

However, this is what I found when using llvm:

$ echo 'int func() {return 0;}' | x86_64-oe-linux-clang -x c -c -o
./temp.o -
ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
ABI Version: 0
Type: REL (Relocatable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 648 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 10
Section header string table index: 1

$ readelf -S ./temp.o
There are 10 section headers, starting at offset 0x288:

Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .strtab STRTAB 0000000000000000 00000230
0000000000000051 0000000000000000 0 0 1

This time it is strtab.

And here is the content of strtab:

$ readelf -p .strtab ./temp.o

String dump of section '.strtab':
[ 1] .text
[ 7] .comment
[ 10] .bss
[ 15] .note.GNU-stack
[ 25] .rela.eh_frame
[ 34] func
[ 39] .strtab
[ 41] .symtab
[ 49] .data
[ 4f] -


Note that I don't use BPF backend. This is a normal x86 compiling.

So seems it is the default behavior of LLVM.

Thank you.

2015-11-30 09:43:57

by Namhyung Kim

[permalink] [raw]
Subject: Re: [PATCH v2 02/13] bpf tools: Extract and collect map names from BPF object file

On November 30, 2015 6:27:57 PM GMT+09:00, "Wangnan (F)" <[email protected]> wrote:
>
>
>On 2015/11/30 16:51, Namhyung Kim wrote:
>> On Mon, Nov 30, 2015 at 01:00:46PM +0800, Wangnan (F) wrote:
>>>
>>> On 2015/11/30 0:14, Namhyung Kim wrote:
>>>> Hi Wang,
>>>>
>>>> On Fri, Nov 27, 2015 at 08:47:36AM +0000, Wang Nan wrote:
>>>>> This patch collects name of maps in BPF object files and saves
>them into
>>>>> 'maps' field in 'struct bpf_object'. 'bpf_object__get_map_by_name'
>is
>>>>> introduced to retrive fd and definitions of a map through its
>name.
>>>>>
>>>>> Signed-off-by: Wang Nan <[email protected]>
>>>>> Signed-off-by: He Kuang <[email protected]>
>>>>> Cc: Alexei Starovoitov <[email protected]>
>>>>> Cc: Arnaldo Carvalho de Melo <[email protected]>
>>>>> Cc: Masami Hiramatsu <[email protected]>
>>>>> Cc: Namhyung Kim <[email protected]>
>>>>> Cc: Zefan Li <[email protected]>
>>>>> Cc: [email protected]
>>>>> ---
>>>>> tools/lib/bpf/libbpf.c | 65
>+++++++++++++++++++++++++++++++++++++++++++++++---
>>>>> tools/lib/bpf/libbpf.h | 3 +++
>>>>> 2 files changed, 65 insertions(+), 3 deletions(-)
>>>>>
>>>>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>>>>> index f509825..a298614 100644
>>>>> --- a/tools/lib/bpf/libbpf.c
>>>>> +++ b/tools/lib/bpf/libbpf.c
>>>>> @@ -165,6 +165,7 @@ struct bpf_program {
>>>>> struct bpf_map {
>>>>> int fd;
>>>>> + char *name;
>>>>> struct bpf_map_def def;
>>>>> void *priv;
>>>>> bpf_map_clear_priv_t clear_priv;
>>>>> @@ -526,12 +527,46 @@ bpf_object__init_maps(struct bpf_object
>*obj, void *data,
>>>>> 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);
>>>> It means that each map name is saved in section header string
>table?
>>> According to elf format specification:
>>>
>>> For an symbol table entry, the st_name field "holds an index
>>> into the object file’s symbol string table, which holds the
>>> character representations of the symbol names. If the value
>>> is non-zero, it represents a string table index that gives
>>> the symbol name. Otherwise, the symbol table entry has no
>>> name."
>>>
>>> And so called "object file’s symbol string table" is a
>>> section in the object file which index is stored into
>>> ehdr and be loaded during gelf_getehdr(), and its index
>>> would be set to ehdr->e_shstrndx. So I think for each map
>>> its name should be saved in that string table.
>> AFAIK there're two symbol string tables in a ELF file. One for
>> section headers (.shstrtab) and another for normal symbols (.strtab).
>> And ehdr->e_shstrndx is the index of section header string table so
>> your code assumes map names are saved in the section header string
>> table, right?
>>
>> Thanks,
>> Namhyung
>
>In case of gcc:
>
>$ echo 'int func() {return 0;}' | gcc -x c -c -o ./temp.o -
>$ readelf -h ./temp.o
>ELF Header:
> Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
> Class: ELF64
> Data: 2's complement, little endian
> Version: 1 (current)
> OS/ABI: UNIX - System V
> ABI Version: 0
> Type: REL (Relocatable file)
> Machine: Advanced Micro Devices X86-64
> Version: 0x1
> Entry point address: 0x0
> Start of program headers: 0 (bytes into file)
> Start of section headers: 240 (bytes into file)
> Flags: 0x0
> Size of this header: 64 (bytes)
> Size of program headers: 0 (bytes)
> Number of program headers: 0
> Size of section headers: 64 (bytes)
> Number of section headers: 11
> Section header string table index: 8
>
>Let's see what is section 8:
>
>$ readelf -S ./temp.o
> ...
> [ 8] .shstrtab STRTAB 0000000000000000 00000098
> 0000000000000054 0000000000000000 0 0 1
> ...
>
>Yes, in this case it is .shstrtab.
>
>However, this is what I found when using llvm:
>
>$ echo 'int func() {return 0;}' | x86_64-oe-linux-clang -x c -c -o
>./temp.o -
>ELF Header:
> Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
> Class: ELF64
> Data: 2's complement, little endian
> Version: 1 (current)
> OS/ABI: UNIX - GNU
> ABI Version: 0
> Type: REL (Relocatable file)
> Machine: Advanced Micro Devices X86-64
> Version: 0x1
> Entry point address: 0x0
> Start of program headers: 0 (bytes into file)
> Start of section headers: 648 (bytes into file)
> Flags: 0x0
> Size of this header: 64 (bytes)
> Size of program headers: 0 (bytes)
> Number of program headers: 0
> Size of section headers: 64 (bytes)
> Number of section headers: 10
> Section header string table index: 1
>
>$ readelf -S ./temp.o
>There are 10 section headers, starting at offset 0x288:
>
>Section Headers:
> [Nr] Name Type Address Offset
> Size EntSize Flags Link Info Align
> [ 0] NULL 0000000000000000 00000000
> 0000000000000000 0000000000000000 0 0 0
> [ 1] .strtab STRTAB 0000000000000000 00000230
> 0000000000000051 0000000000000000 0 0 1
>
>This time it is strtab.
>
>And here is the content of strtab:
>
>$ readelf -p .strtab ./temp.o
>
>String dump of section '.strtab':
> [ 1] .text
> [ 7] .comment
> [ 10] .bss
> [ 15] .note.GNU-stack
> [ 25] .rela.eh_frame
> [ 34] func
> [ 39] .strtab
> [ 41] .symtab
> [ 49] .data
> [ 4f] -
>
>
>Note that I don't use BPF backend. This is a normal x86 compiling.
>
>So seems it is the default behavior of LLVM.

Ah, didn't know that. So strtab has section header strings as well as normal symbol strings when compiled with LLVM, right? It'd be great if you add comment about it.

Thanks,
Namhyung

--
Sent from my Android device with K-9 Mail. Please excuse my brevity.

2015-11-30 09:48:57

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH v2 02/13] bpf tools: Extract and collect map names from BPF object file



On 2015/11/30 17:43, Namhyung Kim wrote:
> On November 30, 2015 6:27:57 PM GMT+09:00, "Wangnan (F)" <[email protected]> wrote:
>>
>> On 2015/11/30 16:51, Namhyung Kim wrote:
>>> On Mon, Nov 30, 2015 at 01:00:46PM +0800, Wangnan (F) wrote:
>>>> On 2015/11/30 0:14, Namhyung Kim wrote:
>>>>> Hi Wang,
>>>>>
>>>>> On Fri, Nov 27, 2015 at 08:47:36AM +0000, Wang Nan wrote:
>>>>>> This patch collects name of maps in BPF object files and saves
>> them into
>>>>>> 'maps' field in 'struct bpf_object'. 'bpf_object__get_map_by_name'
>> is
>>>>>> introduced to retrive fd and definitions of a map through its
>> name.
>>>>>> Signed-off-by: Wang Nan <[email protected]>
>>>>>> Signed-off-by: He Kuang <[email protected]>
>>>>>> Cc: Alexei Starovoitov <[email protected]>
>>>>>> Cc: Arnaldo Carvalho de Melo <[email protected]>
>>>>>> Cc: Masami Hiramatsu <[email protected]>
>>>>>> Cc: Namhyung Kim <[email protected]>
>>>>>> Cc: Zefan Li <[email protected]>
>>>>>> Cc: [email protected]
>>>>>> ---
>>>>>> tools/lib/bpf/libbpf.c | 65
>> +++++++++++++++++++++++++++++++++++++++++++++++---
>>>>>> tools/lib/bpf/libbpf.h | 3 +++
>>>>>> 2 files changed, 65 insertions(+), 3 deletions(-)
>>>>>>
>>>>>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>>>>>> index f509825..a298614 100644
>>>>>> --- a/tools/lib/bpf/libbpf.c
>>>>>> +++ b/tools/lib/bpf/libbpf.c
>>>>>> @@ -165,6 +165,7 @@ struct bpf_program {
>>>>>> struct bpf_map {
>>>>>> int fd;
>>>>>> + char *name;
>>>>>> struct bpf_map_def def;
>>>>>> void *priv;
>>>>>> bpf_map_clear_priv_t clear_priv;
>>>>>> @@ -526,12 +527,46 @@ bpf_object__init_maps(struct bpf_object
>> *obj, void *data,
>>>>>> 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);
>>>>> It means that each map name is saved in section header string
>> table?
>>>> According to elf format specification:
>>>>
>>>> For an symbol table entry, the st_name field "holds an index
>>>> into the object file’s symbol string table, which holds the
>>>> character representations of the symbol names. If the value
>>>> is non-zero, it represents a string table index that gives
>>>> the symbol name. Otherwise, the symbol table entry has no
>>>> name."
>>>>
>>>> And so called "object file’s symbol string table" is a
>>>> section in the object file which index is stored into
>>>> ehdr and be loaded during gelf_getehdr(), and its index
>>>> would be set to ehdr->e_shstrndx. So I think for each map
>>>> its name should be saved in that string table.
>>> AFAIK there're two symbol string tables in a ELF file. One for
>>> section headers (.shstrtab) and another for normal symbols (.strtab).
>>> And ehdr->e_shstrndx is the index of section header string table so
>>> your code assumes map names are saved in the section header string
>>> table, right?
>>>
>>> Thanks,
>>> Namhyung
>> In case of gcc:
>>
>> $ echo 'int func() {return 0;}' | gcc -x c -c -o ./temp.o -
>> $ readelf -h ./temp.o
>> ELF Header:
>> Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
>> Class: ELF64
>> Data: 2's complement, little endian
>> Version: 1 (current)
>> OS/ABI: UNIX - System V
>> ABI Version: 0
>> Type: REL (Relocatable file)
>> Machine: Advanced Micro Devices X86-64
>> Version: 0x1
>> Entry point address: 0x0
>> Start of program headers: 0 (bytes into file)
>> Start of section headers: 240 (bytes into file)
>> Flags: 0x0
>> Size of this header: 64 (bytes)
>> Size of program headers: 0 (bytes)
>> Number of program headers: 0
>> Size of section headers: 64 (bytes)
>> Number of section headers: 11
>> Section header string table index: 8
>>
>> Let's see what is section 8:
>>
>> $ readelf -S ./temp.o
>> ...
>> [ 8] .shstrtab STRTAB 0000000000000000 00000098
>> 0000000000000054 0000000000000000 0 0 1
>> ...
>>
>> Yes, in this case it is .shstrtab.
>>
>> However, this is what I found when using llvm:
>>
>> $ echo 'int func() {return 0;}' | x86_64-oe-linux-clang -x c -c -o
>> ./temp.o -
>> ELF Header:
>> Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
>> Class: ELF64
>> Data: 2's complement, little endian
>> Version: 1 (current)
>> OS/ABI: UNIX - GNU
>> ABI Version: 0
>> Type: REL (Relocatable file)
>> Machine: Advanced Micro Devices X86-64
>> Version: 0x1
>> Entry point address: 0x0
>> Start of program headers: 0 (bytes into file)
>> Start of section headers: 648 (bytes into file)
>> Flags: 0x0
>> Size of this header: 64 (bytes)
>> Size of program headers: 0 (bytes)
>> Number of program headers: 0
>> Size of section headers: 64 (bytes)
>> Number of section headers: 10
>> Section header string table index: 1
>>
>> $ readelf -S ./temp.o
>> There are 10 section headers, starting at offset 0x288:
>>
>> Section Headers:
>> [Nr] Name Type Address Offset
>> Size EntSize Flags Link Info Align
>> [ 0] NULL 0000000000000000 00000000
>> 0000000000000000 0000000000000000 0 0 0
>> [ 1] .strtab STRTAB 0000000000000000 00000230
>> 0000000000000051 0000000000000000 0 0 1
>>
>> This time it is strtab.
>>
>> And here is the content of strtab:
>>
>> $ readelf -p .strtab ./temp.o
>>
>> String dump of section '.strtab':
>> [ 1] .text
>> [ 7] .comment
>> [ 10] .bss
>> [ 15] .note.GNU-stack
>> [ 25] .rela.eh_frame
>> [ 34] func
>> [ 39] .strtab
>> [ 41] .symtab
>> [ 49] .data
>> [ 4f] -
>>
>>
>> Note that I don't use BPF backend. This is a normal x86 compiling.
>>
>> So seems it is the default behavior of LLVM.
> Ah, didn't know that. So strtab has section header strings as well as normal symbol strings when compiled with LLVM, right? It'd be great if you add comment about it.

I think technically speaking you are right, because I haven't see any
documentation
about it, so I don't know the reason why LLVM behave like this, and
don't know
whether it would change it in future. And it is also possible that we
will have
other compiler to compile BPF source file. Now I'm reading readelf's
code and try to
find a canonical way for it.

Thank you for your review.

> Thanks,
> Namhyung
>

2015-11-30 10:39:47

by Wang Nan

[permalink] [raw]
Subject: [PATCH] tools lib bpf: Fetch map names from correct strtab

Namhyung Kim pointed out a potential problem in original code that it
fetches names of maps from section header string table, which is used
to store section names.

Original code doesn't cause error because of a LLVM behavior that, it
combines shstrtab into strtab. For example:

$ echo 'int func() {return 0;}' | x86_64-oe-linux-clang -x c -o temp.o -c -
$ readelf -h ./temp.o
ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
...
Section header string table index: 1
$ readelf -S ./temp.o
There are 10 section headers, starting at offset 0x288:

Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .strtab STRTAB 0000000000000000 00000230
0000000000000051 0000000000000000 0 0 1
...
$ readelf -p .strtab ./temp.o

String dump of section '.strtab':
[ 1] .text
[ 7] .comment
[ 10] .bss
[ 15] .note.GNU-stack
[ 25] .rela.eh_frame
[ 34] func
[ 39] .strtab
[ 41] .symtab
[ 49] .data
[ 4f] -

$ readelf -p .shstrtab ./temp.o
readelf: Warning: Section '.shstrtab' was not dumped because it does not exist!

Where, 'section header string table index' points to '.strtab', and
symbol names are also stored there.

However, in case of gcc:

$ echo 'int func() {return 0;}' | gcc -x c -o temp.o -c -
$ readelf -p .shstrtab ./temp.o

String dump of section '.shstrtab':
[ 1] .symtab
[ 9] .strtab
[ 11] .shstrtab
[ 1b] .text
[ 21] .data
[ 27] .bss
[ 2c] .comment
[ 35] .note.GNU-stack
[ 45] .rela.eh_frame
$ readelf -p .strtab ./temp.o

String dump of section '.strtab':
[ 1] func

They are separated sections.

Although original code doesn't cause error, we'd better use canonical
method for fetching symbol names to avoid potential behavior changing.
This patch learns from readelf's code, fetches string from sh_link
of .symbol section.

Reported-by: Namhyung Kim <[email protected]>
Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
---
tools/lib/bpf/libbpf.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 16485ab..8334a5a 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -195,6 +195,7 @@ struct bpf_object {
Elf *elf;
GElf_Ehdr ehdr;
Elf_Data *symbols;
+ size_t strtabidx;
struct {
GElf_Shdr shdr;
Elf_Data *data;
@@ -547,7 +548,7 @@ bpf_object__init_maps_name(struct bpf_object *obj, int maps_shndx)
continue;

map_name = elf_strptr(obj->efile.elf,
- obj->efile.ehdr.e_shstrndx,
+ obj->efile.strtabidx,
sym.st_name);
map_idx = sym.st_value / sizeof(struct bpf_map_def);
if (map_idx >= obj->nr_maps) {
@@ -630,8 +631,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
pr_warning("bpf: multiple SYMTAB in %s\n",
obj->path);
err = -LIBBPF_ERRNO__FORMAT;
- } else
+ } else {
obj->efile.symbols = data;
+ obj->efile.strtabidx = sh.sh_link;
+ }
} else if ((sh.sh_type == SHT_PROGBITS) &&
(sh.sh_flags & SHF_EXECINSTR) &&
(data->d_size > 0)) {
@@ -667,6 +670,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
goto out;
}

+ if (!obj->efile.strtabidx || obj->efile.strtabidx >= idx) {
+ pr_warning("Corrupted ELF file: index of strtab invalid\n");
+ return LIBBPF_ERRNO__FORMAT;
+ }
if (maps_shndx >= 0)
err = bpf_object__init_maps_name(obj, maps_shndx);
out:
--
1.8.3.4

2015-11-30 11:20:00

by Namhyung Kim

[permalink] [raw]
Subject: Re: [PATCH] tools lib bpf: Fetch map names from correct strtab

On Mon, Nov 30, 2015 at 10:39:10AM +0000, Wang Nan wrote:
> Namhyung Kim pointed out a potential problem in original code that it
> fetches names of maps from section header string table, which is used
> to store section names.
>
> Original code doesn't cause error because of a LLVM behavior that, it
> combines shstrtab into strtab. For example:
>
> $ echo 'int func() {return 0;}' | x86_64-oe-linux-clang -x c -o temp.o -c -
> $ readelf -h ./temp.o
> ELF Header:
> Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
> ...
> Section header string table index: 1
> $ readelf -S ./temp.o
> There are 10 section headers, starting at offset 0x288:
>
> Section Headers:
> [Nr] Name Type Address Offset
> Size EntSize Flags Link Info Align
> [ 0] NULL 0000000000000000 00000000
> 0000000000000000 0000000000000000 0 0 0
> [ 1] .strtab STRTAB 0000000000000000 00000230
> 0000000000000051 0000000000000000 0 0 1
> ...
> $ readelf -p .strtab ./temp.o
>
> String dump of section '.strtab':
> [ 1] .text
> [ 7] .comment
> [ 10] .bss
> [ 15] .note.GNU-stack
> [ 25] .rela.eh_frame
> [ 34] func
> [ 39] .strtab
> [ 41] .symtab
> [ 49] .data
> [ 4f] -
>
> $ readelf -p .shstrtab ./temp.o
> readelf: Warning: Section '.shstrtab' was not dumped because it does not exist!
>
> Where, 'section header string table index' points to '.strtab', and
> symbol names are also stored there.
>
> However, in case of gcc:
>
> $ echo 'int func() {return 0;}' | gcc -x c -o temp.o -c -
> $ readelf -p .shstrtab ./temp.o
>
> String dump of section '.shstrtab':
> [ 1] .symtab
> [ 9] .strtab
> [ 11] .shstrtab
> [ 1b] .text
> [ 21] .data
> [ 27] .bss
> [ 2c] .comment
> [ 35] .note.GNU-stack
> [ 45] .rela.eh_frame
> $ readelf -p .strtab ./temp.o
>
> String dump of section '.strtab':
> [ 1] func
>
> They are separated sections.
>
> Although original code doesn't cause error, we'd better use canonical
> method for fetching symbol names to avoid potential behavior changing.
> This patch learns from readelf's code, fetches string from sh_link
> of .symbol section.
>
> Reported-by: Namhyung Kim <[email protected]>
> Signed-off-by: Wang Nan <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>

Acked-by: Namhyung Kim <[email protected]>

Thanks,
Namhyung


> ---
> tools/lib/bpf/libbpf.c | 11 +++++++++--
> 1 file changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 16485ab..8334a5a 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -195,6 +195,7 @@ struct bpf_object {
> Elf *elf;
> GElf_Ehdr ehdr;
> Elf_Data *symbols;
> + size_t strtabidx;
> struct {
> GElf_Shdr shdr;
> Elf_Data *data;
> @@ -547,7 +548,7 @@ bpf_object__init_maps_name(struct bpf_object *obj, int maps_shndx)
> continue;
>
> map_name = elf_strptr(obj->efile.elf,
> - obj->efile.ehdr.e_shstrndx,
> + obj->efile.strtabidx,
> sym.st_name);
> map_idx = sym.st_value / sizeof(struct bpf_map_def);
> if (map_idx >= obj->nr_maps) {
> @@ -630,8 +631,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
> pr_warning("bpf: multiple SYMTAB in %s\n",
> obj->path);
> err = -LIBBPF_ERRNO__FORMAT;
> - } else
> + } else {
> obj->efile.symbols = data;
> + obj->efile.strtabidx = sh.sh_link;
> + }
> } else if ((sh.sh_type == SHT_PROGBITS) &&
> (sh.sh_flags & SHF_EXECINSTR) &&
> (data->d_size > 0)) {
> @@ -667,6 +670,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
> goto out;
> }
>
> + if (!obj->efile.strtabidx || obj->efile.strtabidx >= idx) {
> + pr_warning("Corrupted ELF file: index of strtab invalid\n");
> + return LIBBPF_ERRNO__FORMAT;
> + }
> if (maps_shndx >= 0)
> err = bpf_object__init_maps_name(obj, maps_shndx);
> out:
> --
> 1.8.3.4
>