2015-11-24 13:40:26

by Wang Nan

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

This patch set is based on perf/core.

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 this patch set:

Patch 1/16 - 2/16 are two small bugfix related to building;

Patch 3/16 - 13/16 add array filling support. In this 11 patches:

Patch 3/16 - 6/16 are preparation;
Patch 7/16 - 9/16 enables perf cmdline syntax to (basic) support
array filling
Patch 10/16 - 11/16 enables setting perf events to BPF event array
Patch 12/16 - 13/16 enables setting different slots in an array
with different values

Patch 14/16 - 16/16 support bpf_output_event() helper.

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

Cc: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[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]

He Kuang (2):
bpf tools: Add helper function for updating bpf maps elements
perf tools: Support perf event alias name

Wang Nan (14):
tools build: Clean CFLAGS and LDFLAGS for fixdep
tools lib bpf: Don't feature check when cleaning
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/build/Makefile.include | 2 +-
tools/lib/bpf/Makefile | 10 +
tools/lib/bpf/bpf.c | 14 +
tools/lib/bpf/bpf.h | 2 +
tools/lib/bpf/libbpf.c | 251 +++++++++----
tools/lib/bpf/libbpf.h | 24 ++
tools/perf/builtin-record.c | 11 +
tools/perf/util/bpf-loader.c | 753 ++++++++++++++++++++++++++++++++++++--
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 ++++++-
18 files changed, 1448 insertions(+), 113 deletions(-)

--
1.8.3.4


2015-11-24 13:40:35

by Wang Nan

[permalink] [raw]
Subject: [PATCH 01/16] tools build: Clean CFLAGS and LDFLAGS for fixdep

Sometimes variables passed to tools/build is dangerous. For example, on
my platform there is a gcc problem (gcc 4.8.1):

It passes stackprotector-all feature check:
$ gcc -fstack-protector-all./test.c
$ echo $?
0

But requires LDFLAGS support if separete compiling and linking:
$ gcc -fstack-protector-all -c ./test.c
$ gcc ./test.o
./test.o: In function `main':
test.c:(.text+0xb): undefined reference to `__stack_chk_guard'
test.c:(.text+0x21): undefined reference to `__stack_chk_guard'
collect2: error: ld returned 1 exit status
$ gcc -fstack-protector-all ./test.o
$ echo $?
0
$ gcc ./test.o -lssp
$ echo $?
0

In this environment perf building throws an error:

$ make
BUILD: Doing 'make -j24' parallel build
config/Makefile:344: No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR
config/Makefile:403: No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev
config/Makefile:418: slang not found, disables TUI support. Please install slang-devel or libslang-dev
config/Makefile:432: GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev
config/Makefile:564: No bfd.h/libbfd found, please install binutils-dev[el]/zlib-static/libiberty-dev to gain symbol demangling
config/Makefile:606: No numa.h found, disables 'perf bench numa mem' benchmark, please install numactl-devel/libnuma-devel/libnuma-dev
CC fixdep.o
LD fixdep-in.o
LINK fixdep
fixdep-in.o: In function `parse_dep_file':
/kernel/tools/build/fixdep.c:47: undefined reference to `__stack_chk_guard'
/kernel/tools/build/fixdep.c:117: undefined reference to `__stack_chk_guard'
fixdep-in.o: In function `main':
/kernel-hydrogen/tools/build/fixdep.c:156: undefined reference to `__stack_chk_guard'
/kernel/tools/build/fixdep.c:168: undefined reference to `__stack_chk_guard'
collect2: error: ld returned 1 exit status
make[2]: *** [fixdep] Error 1
make[1]: *** [fixdep] Error 2
make: *** [all] Error 2

This is because CFLAGS in perf building pollutes CFLAGS used for fixdep,
passes -fstack-protector-all to fixdep builder which is obviously not
required. Since fixdep is a small host side tool, we should keep its
CFLAGS/LDFLAGS simple anc clean.

This patch clear CFLAGS and LDFLAGS passed to fixdep building, so such
gcc problem won't block perf building.

Signed-off-by: Wang Nan <[email protected]>
Acked-by: Jiri Olsa <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
---
tools/build/Makefile.include | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/build/Makefile.include b/tools/build/Makefile.include
index 4e09ad6..6254760 100644
--- a/tools/build/Makefile.include
+++ b/tools/build/Makefile.include
@@ -4,7 +4,7 @@ ifdef CROSS_COMPILE
fixdep:
else
fixdep:
- $(Q)$(MAKE) -C $(srctree)/tools/build fixdep
+ $(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= fixdep
endif

.PHONY: fixdep
--
1.8.3.4

2015-11-24 13:42:23

by Wang Nan

[permalink] [raw]
Subject: [PATCH 02/16] tools lib bpf: Don't feature check when cleaning

Before this patch libbpf always do feature check even when cleaning.
For example:

$ cd kernel/tools/lib/bpf
$ make

Auto-detecting system features:
... libelf: [ on ]
... bpf: [ on ]

CC libbpf.o
CC bpf.o
LD libbpf-in.o
LINK libbpf.a
LINK libbpf.so
$ make clean
CLEAN libbpf
CLEAN core-gen
$ make clean

Auto-detecting system features:
... libelf: [ on ]
... bpf: [ on ]

CLEAN libbpf
CLEAN core-gen
$

Although the first 'make clean' doesn't show feature check result,
it still does the check. No output because check result is similar
to FEATURE-DUMP.libbpf.

This patch uses same method as perf to turn off feature checking
when 'make clean'.

Reported-by: Arnaldo Carvalho de Melo <[email protected]>
Signed-off-by: Wang Nan <[email protected]>
Cc: Jiri Olsa <[email protected]>
---
tools/lib/bpf/Makefile | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile
index a3caaf3..636e3dd 100644
--- a/tools/lib/bpf/Makefile
+++ b/tools/lib/bpf/Makefile
@@ -71,7 +71,17 @@ FEATURE_DISPLAY = libelf bpf
INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi
FEATURE_CHECK_CFLAGS-bpf = $(INCLUDES)

+check_feat := 1
+NON_CHECK_FEAT_TARGETS := clean TAGS tags cscope help
+ifdef MAKECMDGOALS
+ifeq ($(filter-out $(NON_CHECK_FEAT_TARGETS),$(MAKECMDGOALS)),)
+ check_feat := 0
+endif
+endif
+
+ifeq ($(check_feat),1)
include $(srctree)/tools/build/Makefile.feature
+endif

export prefix libdir src obj

--
1.8.3.4

2015-11-24 13:40:30

by Wang Nan

[permalink] [raw]
Subject: [PATCH 03/16] bpf tools: Add helper function for updating bpf maps elements

From: He Kuang <[email protected]>

Add helper function bpf_map_update_elem() which calls sys_bpf syscall
to update elements in bpf maps. Following patches would use it to
adjust data in map through perf cmdline.

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/bpf.c | 14 ++++++++++++++
tools/lib/bpf/bpf.h | 2 ++
2 files changed, 16 insertions(+)

diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index a633105..5bdc6ea 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -83,3 +83,17 @@ int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
log_buf[0] = 0;
return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
}
+
+int bpf_map_update_elem(int fd, void *key, void *value,
+ u64 flags)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+ attr.value = ptr_to_u64(value);
+ attr.flags = flags;
+
+ return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
+}
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index 854b736..a764655 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -20,4 +20,6 @@ int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
u32 kern_version, char *log_buf,
size_t log_buf_sz);

+int bpf_map_update_elem(int fd, void *key, void *value,
+ u64 flags);
#endif
--
1.8.3.4

2015-11-24 13:40:34

by Wang Nan

[permalink] [raw]
Subject: [PATCH 04/16] 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 | 186 +++++++++++++++++++++++++++++++++----------------
tools/lib/bpf/libbpf.h | 21 ++++++
2 files changed, 147 insertions(+), 60 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index e3f4c33..61c9f40 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: %ld bytes\n", obj->path, (long)size);
+
+ obj->maps = calloc(1, sizeof(obj->maps[0]) * nr_maps);
+ if (!obj->maps) {
+ pr_warning("alloc maps for object failed\n");
return -ENOMEM;
}
+ obj->nr_maps = nr_maps;

- obj->maps_buf_sz = size;
- memcpy(obj->maps_buf, data, size);
- pr_debug("maps in %s: %ld bytes\n", obj->path, (long)size);
+ for (i = 0; i < nr_maps; i++) {
+ struct bpf_map_def *def = &obj->maps[i].def;
+
+ /*
+ * fill all fd with -1 so won't close incorrect
+ * fd (0, stdin) when failure.
+ */
+ obj->maps[i].fd = -1;
+
+ /* Save map definition into obj->maps */
+ *def = *(struct bpf_map_def *)(data +
+ i * sizeof(struct bpf_map_def));
+ }
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,72 @@ 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: map handler doesn't belong to object\n");
+ 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..709d2fa 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 confliction.
+ */
+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-24 13:37:04

by Wang Nan

[permalink] [raw]
Subject: [PATCH 05/16] bpf tools: Extract and collect map names from BPF object file

This patch collect name of map in BPF object files and saves them into
'maps' field in 'struct bpf_object'. 'bpf_object__get_map_by_name' is
introduced to retrive map fd and definitions 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 61c9f40..79b1dc2 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)
{
@@ -1317,3 +1364,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 709d2fa..9bc0a56 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 confliction.
*/
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-24 13:40:28

by Wang Nan

[permalink] [raw]
Subject: [PATCH 06/16] 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 | 63 ++++++++++++++++++++++----------------------
tools/perf/util/bpf-loader.h | 2 +-
2 files changed, 32 insertions(+), 33 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 36544e5..e8325a6 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 in object: %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);
+ 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-24 13:40:32

by Wang Nan

[permalink] [raw]
Subject: [PATCH 07/16] 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 | 255 +++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/bpf-loader.h | 38 +++++++
2 files changed, 293 insertions(+)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index e8325a6..0cb94bc 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -739,6 +739,240 @@ 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)
+{
+ if (!list_is_singular(&op->list))
+ list_del(&op->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 +987,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 +1114,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-24 13:37:11

by Wang Nan

[permalink] [raw]
Subject: [PATCH 08/16] 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-24 13:39:18

by Wang Nan

[permalink] [raw]
Subject: [PATCH 09/16] 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 0cb94bc..ac0110c 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"
@@ -973,6 +974,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)
@@ -1127,3 +1300,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-24 13:37:14

by Wang Nan

[permalink] [raw]
Subject: [PATCH 10/16] 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 397fb4e..de02b8d 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-24 13:37:21

by Wang Nan

[permalink] [raw]
Subject: [PATCH 11/16] 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 ac0110c..a6e4bde 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;
};

@@ -880,10 +882,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 *,
@@ -892,6 +957,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
@@ -1036,6 +1102,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,
@@ -1090,6 +1157,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,
@@ -1103,6 +1224,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;
@@ -1168,6 +1293,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
@@ -1304,6 +1434,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-24 13:37:59

by Wang Nan

[permalink] [raw]
Subject: [PATCH 12/16] 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 | 133 ++++++++++++++++++++++++++++++++++++++---
tools/perf/util/bpf-loader.h | 1 +
tools/perf/util/parse-events.c | 33 +++++++++-
tools/perf/util/parse-events.h | 12 ++++
4 files changed, 171 insertions(+), 8 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index a6e4bde..185d2cf 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;
@@ -768,6 +773,8 @@ bpf_map_op__free(struct bpf_map_op *op)
{
if (!list_is_singular(&op->list))
list_del(&op->list);
+ if (op->key_type == BPF_MAP_KEY_RANGES)
+ parse_events__clear_array(&op->k.array);
free(op);
}

@@ -783,8 +790,31 @@ 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 = malloc(memsz);
+ if (!op->k.array.ranges) {
+ pr_debug("No enough memory to alloc indices for %s\n",
+ map_name);
+ return -ENOMEM;
+ }
+ memcpy(op->k.array.ranges, term->array.ranges, memsz);
+ 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;
@@ -818,7 +848,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;
}
@@ -861,7 +896,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;
@@ -922,7 +957,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);

@@ -961,6 +996,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,
@@ -996,6 +1069,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];
@@ -1066,6 +1146,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)
@@ -1105,13 +1212,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;
@@ -1298,6 +1416,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-24 13:37:33

by Wang Nan

[permalink] [raw]
Subject: [PATCH 13/16] 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.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.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.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.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.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-24 13:37:41

by Wang Nan

[permalink] [raw]
Subject: [PATCH 14/16] 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 de02b8d..1ecd6c9 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-24 13:37:24

by Wang Nan

[permalink] [raw]
Subject: [PATCH 15/16] 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-24 13:38:47

by Wang Nan

[permalink] [raw]
Subject: [PATCH 16/16] 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__,
};

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)
# perf data convert --to-ctf ./out.ctf
# babeltrace ./out.ctf
[18:48:34.229365067] (+?.?????????) evt=bpf-output/no-inherit/: { cpu_id = 6 }, { perf_ip = 0xFFFFFFFF811ED5F1, perf_tid = 3861, perf_pid = 3861, perf_id = 38693, raw_len = 3, raw_data = [ [0] = 0xC9529965, [1] = 0x9C1C1, [2] = 0x6 ] }
[18:48:34.229368152] (+0.000003085) evt=bpf-output/no-inherit/: { cpu_id = 6 }, { perf_ip = 0xFFFFFFFF811ED5F1, perf_tid = 3861, perf_pid = 3861, perf_id = 38693, raw_len = 3, raw_data = [ [0] = 0xC952A6D7, [1] = 0x9C1C1, [2] = 0x6 ] }
...

# cat ./test_bpf_output.py
from babeltrace import TraceCollection

tc = TraceCollection(
tc.add_trace('./out.ctf', 'ctf')

for event in tc.events:
if not event.name.startswith('evt='):
continue
raw_data = event['raw_data']
print(raw_data[0] + raw_data[1] << 32, raw_data[2]));

# python3 ./test_bpf_output.py
14509572318247780352 6
14509587101525213184 6
14509649971256492032 6
14509668422435995648 6
...

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

Subject: [tip:perf/core] tools build: Clean CFLAGS and LDFLAGS for fixdep

Commit-ID: 5725dd8fa888b4dcdff58241f9d3d3ac42a048e2
Gitweb: http://git.kernel.org/tip/5725dd8fa888b4dcdff58241f9d3d3ac42a048e2
Author: Wang Nan <[email protected]>
AuthorDate: Tue, 24 Nov 2015 13:36:06 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 25 Nov 2015 16:36:36 -0300

tools build: Clean CFLAGS and LDFLAGS for fixdep

Sometimes passing variables to tools/build is dangerous. For example, on
my platform there is a gcc problem (gcc 4.8.1):

It passes the stackprotector-all feature check:

$ gcc -fstack-protector-all -c ./test.c
$ echo $?
0

But requires LDFLAGS support if separate compiling and linking:
$ gcc -fstack-protector-all -c ./test.c
$ gcc ./test.o
./test.o: In function `main':
test.c:(.text+0xb): undefined reference to `__stack_chk_guard'
test.c:(.text+0x21): undefined reference to `__stack_chk_guard'
collect2: error: ld returned 1 exit status
$ gcc -fstack-protector-all ./test.o
$ echo $?
0
$ gcc ./test.o -lssp
$ echo $?
0
$

In this environment building perf throws an error:

$ make
BUILD: Doing 'make -j24' parallel build
config/Makefile:344: No libunwind found. Please install libunwind-dev[el] >= 1.1 and/or set LIBUNWIND_DIR
config/Makefile:403: No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev
config/Makefile:418: slang not found, disables TUI support. Please install slang-devel or libslang-dev
config/Makefile:432: GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev
config/Makefile:564: No bfd.h/libbfd found, please install binutils-dev[el]/zlib-static/libiberty-dev to gain symbol demangling
config/Makefile:606: No numa.h found, disables 'perf bench numa mem' benchmark, please install numactl-devel/libnuma-devel/libnuma-dev
CC fixdep.o
LD fixdep-in.o
LINK fixdep
fixdep-in.o: In function `parse_dep_file':
/kernel/tools/build/fixdep.c:47: undefined reference to `__stack_chk_guard'
/kernel/tools/build/fixdep.c:117: undefined reference to `__stack_chk_guard'
fixdep-in.o: In function `main':
/kernel-hydrogen/tools/build/fixdep.c:156: undefined reference to `__stack_chk_guard'
/kernel/tools/build/fixdep.c:168: undefined reference to `__stack_chk_guard'
collect2: error: ld returned 1 exit status
make[2]: *** [fixdep] Error 1
make[1]: *** [fixdep] Error 2
make: *** [all] Error 2

This is because the CFLAGS used in building perf pollutes the CFLAGS
used for fixdep, passing -fstack-protector-all to buiold fixdep which is
obviously not required. Since fixdep is a small host side tool, we
should keep its CFLAGS/LDFLAGS simple and clean.

This patch clears the CFLAGS and LDFLAGS passed when building fixdep, so
such gcc problem won't block the perf build process.

Signed-off-by: Wang Nan <[email protected]>
Acked-by: Jiri Olsa <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[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/build/Makefile.include | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/build/Makefile.include b/tools/build/Makefile.include
index 4e09ad6..6254760 100644
--- a/tools/build/Makefile.include
+++ b/tools/build/Makefile.include
@@ -4,7 +4,7 @@ ifdef CROSS_COMPILE
fixdep:
else
fixdep:
- $(Q)$(MAKE) -C $(srctree)/tools/build fixdep
+ $(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= fixdep
endif

.PHONY: fixdep

Subject: [tip:perf/core] tools lib bpf: Don' t do a feature check when cleaning

Commit-ID: d8ad6a15cc3a364de6c8010378adc3fb06ce3ff1
Gitweb: http://git.kernel.org/tip/d8ad6a15cc3a364de6c8010378adc3fb06ce3ff1
Author: Wang Nan <[email protected]>
AuthorDate: Tue, 24 Nov 2015 13:36:07 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 25 Nov 2015 16:38:13 -0300

tools lib bpf: Don't do a feature check when cleaning

Before this patch libbpf always do feature check even when cleaning.

For example:

$ cd kernel/tools/lib/bpf
$ make

Auto-detecting system features:
... libelf: [ on ]
... bpf: [ on ]

CC libbpf.o
CC bpf.o
LD libbpf-in.o
LINK libbpf.a
LINK libbpf.so
$ make clean
CLEAN libbpf
CLEAN core-gen
$ make clean

Auto-detecting system features:
... libelf: [ on ]
... bpf: [ on ]

CLEAN libbpf
CLEAN core-gen
$

Although the first 'make clean' doesn't show feature check result, it
still does the check. No output because check result is similar to
FEATURE-DUMP.libbpf.

This patch uses same method as perf to turn off feature checking when
'make clean'.

Reported-and-Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Masami Hiramatsu <[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/Makefile | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile
index a3caaf3..636e3dd 100644
--- a/tools/lib/bpf/Makefile
+++ b/tools/lib/bpf/Makefile
@@ -71,7 +71,17 @@ FEATURE_DISPLAY = libelf bpf
INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi
FEATURE_CHECK_CFLAGS-bpf = $(INCLUDES)

+check_feat := 1
+NON_CHECK_FEAT_TARGETS := clean TAGS tags cscope help
+ifdef MAKECMDGOALS
+ifeq ($(filter-out $(NON_CHECK_FEAT_TARGETS),$(MAKECMDGOALS)),)
+ check_feat := 0
+endif
+endif
+
+ifeq ($(check_feat),1)
include $(srctree)/tools/build/Makefile.feature
+endif

export prefix libdir src obj

2015-11-26 20:57:08

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 04/16] bpf tools: Collect map definition in bpf_object

Em Tue, Nov 24, 2015 at 01:36:09PM +0000, Wang Nan escreveu:
> 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: %ld bytes\n", obj->path, (long)size);

We have %zd for these cases, please avoid using cast unnecessarily.

> +
> + obj->maps = calloc(1, sizeof(obj->maps[0]) * nr_maps);

Hey, calloc does more than just zeroing the allocated memory, that first
member is the number of entries, so you could do it as:

obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));

To use that 'nmemb' first parameter and to make the whole thing a bit
more compact.

> + if (!obj->maps) {
> + pr_warning("alloc maps for object failed\n");
> return -ENOMEM;
> }
> + obj->nr_maps = nr_maps;
>
> - obj->maps_buf_sz = size;
> - memcpy(obj->maps_buf, data, size);
> - pr_debug("maps in %s: %ld bytes\n", obj->path, (long)size);
> + for (i = 0; i < nr_maps; i++) {
> + struct bpf_map_def *def = &obj->maps[i].def;
> +
> + /*
> + * fill all fd with -1 so won't close incorrect
> + * fd (0, stdin) when failure.
> + */
> + obj->maps[i].fd = -1;
> +
> + /* Save map definition into obj->maps */
> + *def = *(struct bpf_map_def *)(data +
> + i * sizeof(struct bpf_map_def));

This gets more clear/compact as:

*def = ((struct bpf_map_def *)data)[i];

Testing:

[acme@zoo c]$ cat void_long.c
#include <stdio.h>

void main(void)
{
long a[] = { 0, 21, 332, 4443, 5554 };
void *v = &a;
int i;

for (i = 0; i < 5; ++i)
printf("++((long *)v)[%d]=%ld\n", i, ++((long *)v)[i]);
}
[acme@zoo c]$ make void_long
cc void_long.c -o void_long
[acme@zoo c]$ ./void_long
++((long *)v)[0]=1
++((long *)v)[1]=22
++((long *)v)[2]=333
++((long *)v)[3]=4444
++((long *)v)[4]=5555
[acme@zoo c]$

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

perhaps calloc?

> - 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,72 @@ 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: map handler doesn't belong to object\n");

I wonder if this shouldn't be made pr_debug, and as well have a function
prefix, otherwise we may think this is related to some other kind of
map, so I suggest:
pr_debug("%s: error: map handler doesn't belong to object\n", __func__);

Or at least:

pr_debug("BPF error: map handler doesn't belong to object\n");

> + 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..709d2fa 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 confliction.

s/confliction/conflict/g

But I would use "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 06:17:06

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 04/16] bpf tools: Collect map definition in bpf_object



On 2015/11/27 4:56, Arnaldo Carvalho de Melo wrote:
> Em Tue, Nov 24, 2015 at 01:36:09PM +0000, Wang Nan escreveu:

[SNIP]

>> + }
>> 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);
> perhaps calloc?

This line is being removed, so you'll still see this in my next version...

>
>> - 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,72 @@ 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: map handler doesn't belong to object\n");
> I wonder if this shouldn't be made pr_debug, and as well have a function
> prefix, otherwise we may think this is related to some other kind of
> map, so I suggest:
> pr_debug("%s: error: map handler doesn't belong to object\n", __func__);
>
> Or at least:
>
> pr_debug("BPF error: map handler doesn't belong to object\n");

We always have a libbpf prefix before debug info from libbpf. Please see

static __printf(1, 2) libbpf_print_fn_t __pr_warning = __base_pr;
static __printf(1, 2) libbpf_print_fn_t __pr_info = __base_pr;
static __printf(1, 2) libbpf_print_fn_t __pr_debug;
#define __pr(func, fmt, ...) \
do { \
if ((func)) \
(func)("libbpf: " fmt, ##__VA_ARGS__); \
} while (0)

#define pr_warning(fmt, ...) __pr(__pr_warning, fmt, ##__VA_ARGS__)
#define pr_info(fmt, ...) __pr(__pr_info, fmt, ##__VA_ARGS__)
#define pr_debug(fmt, ...) __pr(__pr_debug, fmt, ##__VA_ARGS__)

We allow __pr_{warning,info,debug} be overwritten but the 'libbpf:'
prefix is
always there.

Also, don't forget in perf's context libbpf is muted.

>> + 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..709d2fa 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 confliction.
> s/confliction/conflict/g
>
> But I would use "name clash".

Good word.

Thank you.

2015-11-27 06:21:54

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 04/16] bpf tools: Collect map definition in bpf_object



On 2015/11/27 14:16, Wangnan (F) wrote:
>
>
> On 2015/11/27 4:56, Arnaldo Carvalho de Melo wrote:
>> Em Tue, Nov 24, 2015 at 01:36:09PM +0000, Wang Nan escreveu:
>
> [SNIP]

[SNIP]

>>> +
>>> + if ((prev < s) || (prev >= e)) {
>>> + pr_warning("error: map handler doesn't belong to object\n");
>> I wonder if this shouldn't be made pr_debug, and as well have a function
>> prefix, otherwise we may think this is related to some other kind of
>> map, so I suggest:
>> pr_debug("%s: error: map handler doesn't belong to object\n",
>> __func__);
>>
>> Or at least:
>>
>> pr_debug("BPF error: map handler doesn't belong to object\n");
>
> We always have a libbpf prefix before debug info from libbpf. Please see
>
> static __printf(1, 2) libbpf_print_fn_t __pr_warning = __base_pr;
> static __printf(1, 2) libbpf_print_fn_t __pr_info = __base_pr;
> static __printf(1, 2) libbpf_print_fn_t __pr_debug;
> #define __pr(func, fmt, ...) \
> do { \
> if ((func)) \
> (func)("libbpf: " fmt, ##__VA_ARGS__); \
> } while (0)
>
> #define pr_warning(fmt, ...) __pr(__pr_warning, fmt, ##__VA_ARGS__)
> #define pr_info(fmt, ...) __pr(__pr_info, fmt, ##__VA_ARGS__)
> #define pr_debug(fmt, ...) __pr(__pr_debug, fmt, ##__VA_ARGS__)
>
> We allow __pr_{warning,info,debug} be overwritten but the 'libbpf:'
> prefix is
> always there.
>
> Also, don't forget in perf's context libbpf is muted.
>
Sorry, Shoule be "muted in most case"...

Here's an example:

$ sudo ./perf record -v -e ./test_bpf_base.c ls
...
ude/uapi -Iinclude/generated/uapi -include
/home/w00229757/linux-hydrogen/include/linux/kconfig.h
set env: WORKING_DIR=/lib/modules/4.3.0-rc4+/build
set env: CLANG_SOURCE=/home/w00229757/./test_bpf_base.c
llvm compiling command template: $CLANG_EXEC -D__KERNEL__
-D__NR_CPUS__=$NR_CPUS -DLINUX_VERSION_CODE=$LINUX_VERSION_CODE
$CLANG_OPTIONS $KERNEL_INC_OPTIONS -Wno-unused-value -Wno-pointer-sign
-working-directory $WORKING_DIR -c "$CLANG_SOURCE" -target bpf -O2 -o -
libbpf: loading object './test_bpf_base.c' from buffer
libbpf: section .strtab, size 112, link 0, flags 0, type=3
libbpf: section .text, size 0, link 0, flags 6, type=1
libbpf: section .data, size 0, link 0, flags 3, type=1
...
libbpf: kernel version of ./test_bpf_base.c is 40300
libbpf: section .symtab, size 96, link 1, flags 0, type=2
bpf: config program 'func_write=vfs_write file->f_mode'
symbol:vfs_write file:(null) line:0 offset:0 return:0 lazy:(null)
parsing arg: file->f_mode into file, f_mode(1)
bpf: config 'func_write=vfs_write file->f_mode' is ok

If the error you pointed out is triggered, it should be:

libbpf: error: map handler doesn't belong to object ...

So we can always find which error message is belone to libbpf
and which is belone to perf.

2015-11-27 07:32:33

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 12/16] perf tools: Support setting different slots in a BPF map separately



On 2015/11/24 21:36, Wang Nan wrote:
> 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 | 133 ++++++++++++++++++++++++++++++++++++++---
> tools/perf/util/bpf-loader.h | 1 +
> tools/perf/util/parse-events.c | 33 +++++++++-
> tools/perf/util/parse-events.h | 12 ++++
> 4 files changed, 171 insertions(+), 8 deletions(-)
>
> diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
> index a6e4bde..185d2cf 100644
> --- a/tools/perf/util/bpf-loader.c
> +++ b/tools/perf/util/bpf-loader.c

[SNIP]

> +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 = malloc(memsz);
> + if (!op->k.array.ranges) {
> + pr_debug("No enough memory to alloc indices for %s\n",
> + map_name);
> + return -ENOMEM;
> + }
> + memcpy(op->k.array.ranges, term->array.ranges, memsz);

Here can use memdup.

> + op->key_type = BPF_MAP_KEY_RANGES;
> + op->k.array.nr_ranges = term->array.nr_ranges;
> + }
> + return 0;
> +}
> +

2015-11-27 07:33:13

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 07/16] perf tools: Add API to config maps in bpf object



On 2015/11/24 21:36, Wang Nan wrote:
> 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 | 255 +++++++++++++++++++++++++++++++++++++++++++
> tools/perf/util/bpf-loader.h | 38 +++++++
> 2 files changed, 293 insertions(+)
>
> diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
> index e8325a6..0cb94bc 100644
> --- a/tools/perf/util/bpf-loader.c
> +++ b/tools/perf/util/bpf-loader.c
> @@ -739,6 +739,240 @@ 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)
> +{
> + if (!list_is_singular(&op->list))

This seems incorrect. bpf_map_op__free() should consider
following cases:

1. When the op is created but not linked to any list:
impossible. In bpf_map_op__alloc() it would be freed
directly;
2. Normal case, when the op is linked to a list;
3. After the op has already be removed.

list_is_singular() tests whether a list has only one entry. This is not
belone to any of the above cases.

Will change to:

if ((op->list.next != LIST_POISON1) && (op->list.prev !=
LIST_POISON2))
list_del(&op->list);

> + list_del(&op->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 +987,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 +1114,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

Subject: [tip:perf/core] bpf tools: Add helper function for updating bpf maps elements

Commit-ID: 43798bf37215fe242e592fd4605d804e2da0781b
Gitweb: http://git.kernel.org/tip/43798bf37215fe242e592fd4605d804e2da0781b
Author: He Kuang <[email protected]>
AuthorDate: Tue, 24 Nov 2015 13:36:08 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Thu, 26 Nov 2015 17:21:24 -0300

bpf tools: Add helper function for updating bpf maps elements

Add bpf_map_update_elem() helper function which calls the sys_bpf
syscall to update elements in bpf maps. Upcoming patches will use it to
adjust data in map through the perf command line.

Signed-off-by: He Kuang <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Wang Nan <[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/bpf.c | 14 ++++++++++++++
tools/lib/bpf/bpf.h | 2 ++
2 files changed, 16 insertions(+)

diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index a633105..5bdc6ea 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -83,3 +83,17 @@ int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
log_buf[0] = 0;
return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
}
+
+int bpf_map_update_elem(int fd, void *key, void *value,
+ u64 flags)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+ attr.value = ptr_to_u64(value);
+ attr.flags = flags;
+
+ return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
+}
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index 854b736..a764655 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -20,4 +20,6 @@ int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
u32 kern_version, char *log_buf,
size_t log_buf_sz);

+int bpf_map_update_elem(int fd, void *key, void *value,
+ u64 flags);
#endif