2015-07-09 12:38:40

by Wang Nan

[permalink] [raw]
Subject: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Hi Arnaldo,

The following changes since commit 3381a29cbec5447086c0f726ee9a88c02e60becc:

bpf tools: Collect map definitions from 'maps' section (2015-07-07 13:41:45 -0300)

are available in the git repository at:

https://github.com/WangNan0/linux.git perf/ebpf-for-acme

for you to fetch changes up to 072b826c5dc6b2031f4f21c59c57eb6ca1dfa7c0:

perf tools: Support attach BPF program on uprobe events (2015-07-09 12:20:13 +0000)

----------------------------------------------------------------

This pull request is based on commit 3381a29cb on your perf/ebpf branch
as your suggestion. Following changes are made:

1. Commit messages improvements, especially 'bpf tools: Collect symbol
table from SHT_SYMTAB section', which you have collected. Please
change them.

2. Update interface of bpf_program accessors as your suggestion.

He Kuang (3):
perf tools: Move linux/filter.h to tools/include
perf tools: Introduce arch_get_reg_info() for x86
perf record: Support custom vmlinux path

Wang Nan (36):
bpf tools: Collect symbol table from SHT_SYMTAB section
bpf tools: Collect eBPF programs from their own sections
bpf tools: Collect relocation sections from SHT_REL sections
bpf tools: Record map accessing instructions for each program
bpf tools: Add bpf.c/h for common bpf operations
bpf tools: Create eBPF maps defined in an object file
bpf tools: Relocate eBPF programs
bpf tools: Introduce bpf_load_program() to bpf.c
bpf tools: Load eBPF programs in object files into kernel
bpf tools: Introduce accessors for struct bpf_program
bpf tools: Link all bpf objects onto a list
perf tools: Introduce llvm config options
perf tools: Call clang to compile C source to object code
perf tools: Auto detecting kernel build directory
perf tools: Auto detecting kernel include options
perf tests: Add LLVM test for eBPF on-the-fly compiling
perf tools: Make perf depend on libbpf
perf record: Enable passing bpf object file to --event
perf record: Compile scriptlets if pass '.c' to --event
perf tools: Parse probe points of eBPF programs during preparation
perf probe: Attach trace_probe_event with perf_probe_event
perf record: Probe at kprobe points
perf record: Load all eBPF object into kernel
perf tools: Add bpf_fd field to evsel and config it
perf tools: Attach eBPF program to perf event
perf tools: Suppress probing messages when probing by BPF loading
perf record: Add clang options for compiling BPF scripts
bpf tools: Load a program with different instances using preprocessor
perf tools: Fix probe-event.h include
perf probe: Reset args and nargs for probe_trace_event when failure
perf tools: Add BPF_PROLOGUE config options for further patches
perf tools: Add prologue for BPF programs for fetching arguments
perf tools: Generate prologue for BPF programs
perf tools: Use same BPF program if arguments are identical
perf probe: Init symbol as kprobe if any event is kprobe
perf tools: Support attach BPF program on uprobe events

tools/build/Makefile.feature | 6 +-
tools/include/linux/filter.h | 237 +++++++++++
tools/lib/bpf/Build | 2 +-
tools/lib/bpf/bpf.c | 85 ++++
tools/lib/bpf/bpf.h | 23 ++
tools/lib/bpf/libbpf.c | 745 ++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 70 ++++
tools/perf/MANIFEST | 4 +
tools/perf/Makefile.perf | 19 +-
tools/perf/arch/x86/Makefile | 1 +
tools/perf/arch/x86/util/Build | 2 +
tools/perf/arch/x86/util/dwarf-regs.c | 104 +++--
tools/perf/builtin-probe.c | 4 +-
tools/perf/builtin-record.c | 47 ++-
tools/perf/config/Makefile | 31 +-
tools/perf/tests/Build | 1 +
tools/perf/tests/builtin-test.c | 4 +
tools/perf/tests/llvm.c | 85 ++++
tools/perf/tests/make | 4 +-
tools/perf/tests/tests.h | 1 +
tools/perf/util/Build | 3 +
tools/perf/util/bpf-loader.c | 631 ++++++++++++++++++++++++++++
tools/perf/util/bpf-loader.h | 46 +++
tools/perf/util/bpf-prologue.c | 442 ++++++++++++++++++++
tools/perf/util/bpf-prologue.h | 34 ++
tools/perf/util/config.c | 4 +
tools/perf/util/debug.c | 5 +
tools/perf/util/debug.h | 1 +
tools/perf/util/evlist.c | 41 ++
tools/perf/util/evlist.h | 1 +
tools/perf/util/evsel.c | 17 +
tools/perf/util/evsel.h | 1 +
tools/perf/util/include/dwarf-regs.h | 7 +
tools/perf/util/llvm-utils.c | 373 +++++++++++++++++
tools/perf/util/llvm-utils.h | 39 ++
tools/perf/util/parse-events.c | 16 +
tools/perf/util/parse-events.h | 2 +
tools/perf/util/parse-events.l | 6 +
tools/perf/util/parse-events.y | 29 +-
tools/perf/util/probe-event.c | 84 ++--
tools/perf/util/probe-event.h | 8 +-
tools/perf/util/probe-finder.c | 4 +
42 files changed, 3192 insertions(+), 77 deletions(-)
create mode 100644 tools/include/linux/filter.h
create mode 100644 tools/lib/bpf/bpf.c
create mode 100644 tools/lib/bpf/bpf.h
create mode 100644 tools/perf/tests/llvm.c
create mode 100644 tools/perf/util/bpf-loader.c
create mode 100644 tools/perf/util/bpf-loader.h
create mode 100644 tools/perf/util/bpf-prologue.c
create mode 100644 tools/perf/util/bpf-prologue.h
create mode 100644 tools/perf/util/llvm-utils.c
create mode 100644 tools/perf/util/llvm-utils.h
--
1.8.3.4


2015-07-09 12:36:25

by Wang Nan

[permalink] [raw]
Subject: [PATCH 01/39] bpf tools: Collect symbol table from SHT_SYMTAB section

This patch collects symbols section. This section is useful when linking
BPF maps.

What 'bpf_map_xxx()' functions actually require are map's file
descriptors (and the internal verifier converts fds into pointers to
'struct bpf_map'), which we don't know when compiling. Therefore, we
should make compiler generate a 'ldr_64 r1, <imm>' instruction, and
fill the 'imm' field with the actual file descriptor when loading in
libbpf.

BPF programs should be written in this way:

struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(unsigned long),
.value_size = sizeof(unsigned long),
.max_entries = 1000000,
};

SEC("my_func=sys_write")
int my_func(void *ctx)
{
...
bpf_map_update_elem(&my_map, &key, &value, BPF_ANY);
...
}

Compiler should convert '&my_map' into a 'ldr_64, r1, <imm>'
instruction, where imm should be the address of 'my_map'. According to
the address, libbpf knows which map it actually referenced, and then
fills the imm field with the 'fd' of that map created by it.

However, since we never really 'link' the object file, the imm field is
only a record in relocation section. Therefore libbpf should do the
relocation:

1. In relocation section (type == SHT_REL), positions of each such
'ld_64' instruction are recorded with a reference of an entry in
symbol table (SHT_SYMTAB);

2. From records in symbol table we can find the indics of map
variables.

Libbpf first record SHT_SYMTAB and positions of each instruction which
required bu such operation. Then create file descriptor. Finally, after
map creation complete, replace the imm field.

This is the first patch of BPF map related stuff. It records SHT_SYMTAB
into object's efile field for further use.

Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/lib/bpf/libbpf.c | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 87f5054..9b016c0 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -94,6 +94,7 @@ struct bpf_object {
size_t obj_buf_sz;
Elf *elf;
GElf_Ehdr ehdr;
+ Elf_Data *symbols;
} efile;
char path[];
};
@@ -135,6 +136,7 @@ static void bpf_object__elf_finish(struct bpf_object *obj)
elf_end(obj->efile.elf);
obj->efile.elf = NULL;
}
+ obj->efile.symbols = NULL;
zclose(obj->efile.fd);
obj->efile.obj_buf = NULL;
obj->efile.obj_buf_sz = 0;
@@ -333,6 +335,14 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
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) {
+ if (obj->efile.symbols) {
+ pr_warning("bpf: multiple SYMTAB in %s\n",
+ obj->path);
+ err = -EEXIST;
+ } else
+ obj->efile.symbols = data;
+ }
if (err)
goto out;
}
--
1.8.3.4

2015-07-09 12:46:52

by Wang Nan

[permalink] [raw]
Subject: [PATCH 02/39] bpf tools: Collect eBPF programs from their own sections

This patch collects all programs in an object file into an array of
'struct bpf_program' for further processing. That structure is for
representing each eBPF program. 'bpf_prog' should be a better name, but
it has been used by linux/filter.h. Although it is a kernel space name,
I still prefer to call it 'bpf_program' to prevent possible confusion.

bpf_program__new() creates a new 'struct bpf_program' object. It first
init a variable in stack using __bpf_program__new(), then if success,
enlarges obj->programs array and copy the new object in.

Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/lib/bpf/libbpf.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 117 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 9b016c0..3b717de 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -78,12 +78,27 @@ void libbpf_set_print(libbpf_print_fn_t warn,
# define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
#endif

+/*
+ * bpf_prog should be a better name but it has been used in
+ * linux/filter.h.
+ */
+struct bpf_program {
+ /* Index in elf obj file, for relocation use. */
+ int idx;
+ char *section_name;
+ struct bpf_insn *insns;
+ size_t insns_cnt;
+};
+
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;
+
/*
* Information when doing elf related work. Only valid if fd
* is valid.
@@ -100,6 +115,84 @@ struct bpf_object {
};
#define obj_elf_valid(o) ((o)->efile.elf)

+static void bpf_program__clear(struct bpf_program *prog)
+{
+ if (!prog)
+ return;
+
+ zfree(&prog->section_name);
+ zfree(&prog->insns);
+ prog->insns_cnt = 0;
+ prog->idx = -1;
+}
+
+static int
+__bpf_program__new(void *data, size_t size, char *name, int idx,
+ struct bpf_program *prog)
+{
+ if (size < sizeof(struct bpf_insn)) {
+ pr_warning("corrupted section '%s'\n", name);
+ return -EINVAL;
+ }
+
+ bzero(prog, sizeof(*prog));
+
+ prog->section_name = strdup(name);
+ if (!prog->section_name) {
+ pr_warning("failed to alloc name for prog %s\n",
+ name);
+ goto errout;
+ }
+
+ prog->insns = malloc(size);
+ if (!prog->insns) {
+ pr_warning("failed to alloc insns for %s\n", name);
+ goto errout;
+ }
+ prog->insns_cnt = size / sizeof(struct bpf_insn);
+ memcpy(prog->insns, data,
+ prog->insns_cnt * sizeof(struct bpf_insn));
+ prog->idx = idx;
+
+ return 0;
+errout:
+ bpf_program__clear(prog);
+ return -ENOMEM;
+}
+
+static struct bpf_program *
+bpf_program__new(struct bpf_object *obj, void *data, size_t size,
+ char *name, int idx)
+{
+ struct bpf_program prog, *progs;
+ int nr_progs, err;
+
+ err = __bpf_program__new(data, size, name, idx, &prog);
+ if (err)
+ return NULL;
+
+ progs = obj->programs;
+ nr_progs = obj->nr_programs;
+
+ progs = realloc(progs, sizeof(progs[0]) * (nr_progs + 1));
+ if (!progs) {
+ /*
+ * In this case the original obj->programs
+ * is still valid, so don't need special treat for
+ * bpf_close_object().
+ */
+ pr_warning("failed to alloc a new program '%s'\n",
+ name);
+ bpf_program__clear(&prog);
+ return NULL;
+ }
+
+ obj->programs = progs;
+ obj->nr_programs = nr_progs + 1;
+ progs[nr_progs] = prog;
+ return &progs[nr_progs];
+}
+
static struct bpf_object *bpf_object__new(const char *path,
void *obj_buf,
size_t obj_buf_sz)
@@ -342,6 +435,21 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
err = -EEXIST;
} else
obj->efile.symbols = data;
+ } else if ((sh.sh_type == SHT_PROGBITS) &&
+ (sh.sh_flags & SHF_EXECINSTR) &&
+ (data->d_size > 0)) {
+ struct bpf_program *prog;
+
+ prog = bpf_program__new(obj, data->d_buf,
+ data->d_size, name,
+ idx);
+ if (!prog) {
+ pr_warning("failed to alloc program %s (%s)",
+ name, obj->path);
+ err = -ENOMEM;
+ } else
+ pr_debug("found program %s\n",
+ prog->section_name);
}
if (err)
goto out;
@@ -415,11 +523,20 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,

void bpf_object__close(struct bpf_object *obj)
{
+ size_t i;
+
if (!obj)
return;

bpf_object__elf_finish(obj);

zfree(&obj->maps_buf);
+
+ if (obj->programs && obj->nr_programs) {
+ for (i = 0; i < obj->nr_programs; i++)
+ bpf_program__clear(&obj->programs[i]);
+ }
+ zfree(&obj->programs);
+
free(obj);
}
--
1.8.3.4

2015-07-09 12:38:34

by Wang Nan

[permalink] [raw]
Subject: [PATCH 03/39] bpf tools: Collect relocation sections from SHT_REL sections

This patch collects relocation sections into 'struct object'. Such
sections are used for connecting maps to bpf programs. 'reloc' field in
'struct bpf_object' is introduced for storing such information.

This patch simply store the data into 'reloc' field. Following patch
will parse them to know the exact instructions which are needed to be
relocated.

Note that the collected data will be invalid after ELF object file is
closed.

This is the second patch related to map relocation. The first one is
'bpf tools: Collect symbol table from SHT_SYMTAB section'. The
principle of map relocation is described in its commit message.

Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/lib/bpf/libbpf.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 3b717de..5f12fa6 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -110,6 +110,11 @@ struct bpf_object {
Elf *elf;
GElf_Ehdr ehdr;
Elf_Data *symbols;
+ struct {
+ GElf_Shdr shdr;
+ Elf_Data *data;
+ } *reloc;
+ int nr_reloc;
} efile;
char path[];
};
@@ -230,6 +235,9 @@ static void bpf_object__elf_finish(struct bpf_object *obj)
obj->efile.elf = NULL;
}
obj->efile.symbols = NULL;
+
+ zfree(&obj->efile.reloc);
+ obj->efile.nr_reloc = 0;
zclose(obj->efile.fd);
obj->efile.obj_buf = NULL;
obj->efile.obj_buf_sz = 0;
@@ -450,6 +458,24 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
} else
pr_debug("found program %s\n",
prog->section_name);
+ } else if (sh.sh_type == SHT_REL) {
+ void *reloc = obj->efile.reloc;
+ int nr_reloc = obj->efile.nr_reloc + 1;
+
+ reloc = realloc(reloc,
+ sizeof(*obj->efile.reloc) * nr_reloc);
+ if (!reloc) {
+ pr_warning("realloc failed\n");
+ err = -ENOMEM;
+ } else {
+ int n = nr_reloc - 1;
+
+ obj->efile.reloc = reloc;
+ obj->efile.nr_reloc = nr_reloc;
+
+ obj->efile.reloc[n].shdr = sh;
+ obj->efile.reloc[n].data = data;
+ }
}
if (err)
goto out;
--
1.8.3.4

2015-07-09 12:38:30

by Wang Nan

[permalink] [raw]
Subject: [PATCH 04/39] bpf tools: Record map accessing instructions for each program

This patch records the indices of instructions which are needed to be
relocated. That information is saved in the 'reloc_desc' field in
'struct bpf_program'. In the loading phase (this patch takes effect in
the opening phase), the collected instructions will be replaced by map
loading instructions.

Since we are going to close the ELF file and clear all data at the end
of the 'opening' phase, the ELF information will no longer be valid in
the 'loading' phase. We have to locate the instructions before maps are
loaded, instead of directly modifying the instruction.

'struct bpf_map_def' is introduced in this patch to let us know how many
maps are defined in the object.

This is the third part of map relocation. The principle of map relocation
is described in commit message of 'bpf tools: Collect symbol table from
SHT_SYMTAB section'.

Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/lib/bpf/libbpf.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 13 ++++++
2 files changed, 137 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 5f12fa6..4f13772 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
+#include <inttypes.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
@@ -88,6 +89,12 @@ struct bpf_program {
char *section_name;
struct bpf_insn *insns;
size_t insns_cnt;
+
+ struct {
+ int insn_idx;
+ int map_idx;
+ } *reloc_desc;
+ int nr_reloc;
};

struct bpf_object {
@@ -127,6 +134,9 @@ static void bpf_program__clear(struct bpf_program *prog)

zfree(&prog->section_name);
zfree(&prog->insns);
+ zfree(&prog->reloc_desc);
+
+ prog->nr_reloc = 0;
prog->insns_cnt = 0;
prog->idx = -1;
}
@@ -484,6 +494,118 @@ out:
return err;
}

+static struct bpf_program *
+bpf_object__find_prog_by_idx(struct bpf_object *obj, int idx)
+{
+ struct bpf_program *prog;
+ size_t i;
+
+ for (i = 0; i < obj->nr_programs; i++) {
+ prog = &obj->programs[i];
+ if (prog->idx == idx)
+ return prog;
+ }
+ return NULL;
+}
+
+static int
+bpf_program__collect_reloc(struct bpf_program *prog,
+ size_t nr_maps, GElf_Shdr *shdr,
+ Elf_Data *data, Elf_Data *symbols)
+{
+ int i, nrels;
+
+ pr_debug("collecting relocating info for: '%s'\n",
+ prog->section_name);
+ nrels = shdr->sh_size / shdr->sh_entsize;
+
+ prog->reloc_desc = malloc(sizeof(*prog->reloc_desc) * nrels);
+ if (!prog->reloc_desc) {
+ pr_warning("failed to alloc memory in relocation\n");
+ return -ENOMEM;
+ }
+ prog->nr_reloc = nrels;
+
+ for (i = 0; i < nrels; i++) {
+ GElf_Sym sym;
+ GElf_Rel rel;
+ unsigned int insn_idx;
+ struct bpf_insn *insns = prog->insns;
+ size_t map_idx;
+
+ if (!gelf_getrel(data, i, &rel)) {
+ pr_warning("relocation: failed to get %d reloc\n", i);
+ return -EINVAL;
+ }
+
+ insn_idx = rel.r_offset / sizeof(struct bpf_insn);
+ pr_debug("relocation: insn_idx=%u\n", insn_idx);
+
+ if (!gelf_getsym(symbols,
+ GELF_R_SYM(rel.r_info),
+ &sym)) {
+ pr_warning("relocation: symbol %"PRIx64" not found\n",
+ GELF_R_SYM(rel.r_info));
+ return -EINVAL;
+ }
+
+ if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) {
+ pr_warning("bpf: relocation: invalid relo for insns[%d].code 0x%x\n",
+ insn_idx, insns[insn_idx].code);
+ return -EINVAL;
+ }
+
+ map_idx = sym.st_value / sizeof(struct bpf_map_def);
+ if (map_idx >= nr_maps) {
+ pr_warning("bpf relocation: map_idx %d large than %d\n",
+ (int)map_idx, (int)nr_maps - 1);
+ return -EINVAL;
+ }
+
+ prog->reloc_desc[i].insn_idx = insn_idx;
+ prog->reloc_desc[i].map_idx = map_idx;
+ }
+ return 0;
+}
+
+static int bpf_object__collect_reloc(struct bpf_object *obj)
+{
+ int i, err;
+
+ if (!obj_elf_valid(obj)) {
+ pr_warning("Internal error: elf object is closed\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < obj->efile.nr_reloc; i++) {
+ GElf_Shdr *shdr = &obj->efile.reloc[i].shdr;
+ 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);
+
+ if (shdr->sh_type != SHT_REL) {
+ pr_warning("internal error at %d\n", __LINE__);
+ return -EINVAL;
+ }
+
+ prog = bpf_object__find_prog_by_idx(obj, idx);
+ if (!prog) {
+ pr_warning("relocation failed: no %d section\n",
+ idx);
+ return -ENOENT;
+ }
+
+ err = bpf_program__collect_reloc(prog, nr_maps,
+ shdr, data,
+ obj->efile.symbols);
+ if (err)
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int bpf_object__validate(struct bpf_object *obj)
{
if (obj->kern_version == 0) {
@@ -514,6 +636,8 @@ __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
goto out;
if (bpf_object__elf_collect(obj))
goto out;
+ if (bpf_object__collect_reloc(obj))
+ goto out;
if (bpf_object__validate(obj))
goto out;

diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index dc966dd..6e75acd 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -30,4 +30,17 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
size_t obj_buf_sz);
void bpf_object__close(struct bpf_object *object);

+/*
+ * We don't need __attribute__((packed)) now since it is
+ * unnecessary for 'bpf_map_def' because they are all aligned.
+ * In addition, using it will trigger -Wpacked warning message,
+ * and will be treated as an error due to -Werror.
+ */
+struct bpf_map_def {
+ unsigned int type;
+ unsigned int key_size;
+ unsigned int value_size;
+ unsigned int max_entries;
+};
+
#endif
--
1.8.3.4

2015-07-09 12:36:43

by Wang Nan

[permalink] [raw]
Subject: [PATCH 05/39] bpf tools: Add bpf.c/h for common bpf operations

This patch introduces bpf.c and bpf.h, which hold common functions
issuing bpf syscall. The goal of these two files is to hide syscall
completely from user. Note that bpf.c and bpf.h deal with kernel
interface only. Things like structure of 'map' section in the ELF object
is not cared by of bpf.[ch].

We first introduce bpf_create_map().

Note that, since functions in bpf.[ch] are wrapper of sys_bpf, they
don't use OO style naming.

Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[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/Build | 2 +-
tools/lib/bpf/bpf.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/bpf.h | 16 ++++++++++++++++
3 files changed, 68 insertions(+), 1 deletion(-)
create mode 100644 tools/lib/bpf/bpf.c
create mode 100644 tools/lib/bpf/bpf.h

diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build
index a316484..d874975 100644
--- a/tools/lib/bpf/Build
+++ b/tools/lib/bpf/Build
@@ -1 +1 @@
-libbpf-y := libbpf.o
+libbpf-y := libbpf.o bpf.o
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
new file mode 100644
index 0000000..208de7c3
--- /dev/null
+++ b/tools/lib/bpf/bpf.c
@@ -0,0 +1,51 @@
+/*
+ * common eBPF ELF operations.
+ *
+ * Copyright (C) 2013-2015 Alexei Starovoitov <[email protected]>
+ * Copyright (C) 2015 Wang Nan <[email protected]>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+
+#include <stdlib.h>
+#include <memory.h>
+#include <unistd.h>
+#include <asm/unistd.h>
+#include <linux/bpf.h>
+#include "bpf.h"
+
+/*
+ * When building perf, unistd.h is override. Define __NR_bpf is
+ * required to be defined.
+ */
+#ifndef __NR_bpf
+# if defined(__i386__)
+# define __NR_bpf 357
+# elif defined(__x86_64__)
+# define __NR_bpf 321
+# elif defined(__aarch64__)
+# define __NR_bpf 280
+# else
+# error __NR_bpf not defined. libbpf does not support your arch.
+# endif
+#endif
+
+static int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
+ unsigned int size)
+{
+ return syscall(__NR_bpf, cmd, attr, size);
+}
+
+int bpf_create_map(enum bpf_map_type map_type, int key_size,
+ int value_size, int max_entries)
+{
+ union bpf_attr attr;
+
+ memset(&attr, '\0', sizeof(attr));
+
+ attr.map_type = map_type;
+ attr.key_size = key_size;
+ attr.value_size = value_size;
+ attr.max_entries = max_entries;
+
+ return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
+}
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
new file mode 100644
index 0000000..28f7942
--- /dev/null
+++ b/tools/lib/bpf/bpf.h
@@ -0,0 +1,16 @@
+/*
+ * common eBPF ELF operations.
+ *
+ * Copyright (C) 2013-2015 Alexei Starovoitov <[email protected]>
+ * Copyright (C) 2015 Wang Nan <[email protected]>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+#ifndef __BPF_BPF_H
+#define __BPF_BPF_H
+
+#include <linux/bpf.h>
+
+int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
+ int max_entries);
+
+#endif
--
1.8.3.4

2015-07-09 12:46:44

by Wang Nan

[permalink] [raw]
Subject: [PATCH 06/39] bpf tools: Create eBPF maps defined in an object file

This patch creates maps based on 'map' section in object file using
bpf_create_map(), and stores the fds into an array in 'struct
bpf_object'.

Previous patches parse ELF object file and collects required data, but
doesn't play with the kernel. They belong to the 'opening' phase. This
patch is the first patch in 'loading' phase. The 'loaded' field is
introduced in 'struct bpf_object' to avoid loading an object twice,
because the loading phase clears resources collected during the opening
which becomes useless after loading. In this patch, maps_buf is cleared.

Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/lib/bpf/libbpf.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 4 ++
2 files changed, 106 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 4f13772..c214e1c 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -21,6 +21,7 @@
#include <gelf.h>

#include "libbpf.h"
+#include "bpf.h"

#define __printf(a, b) __attribute__((format(printf, a, b)))

@@ -105,6 +106,13 @@ struct bpf_object {

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;
+ bool loaded;

/*
* Information when doing elf related work. Only valid if fd
@@ -232,6 +240,7 @@ static struct bpf_object *bpf_object__new(const char *path,
obj->efile.obj_buf = obj_buf;
obj->efile.obj_buf_sz = obj_buf_sz;

+ obj->loaded = false;
return obj;
}

@@ -568,6 +577,62 @@ bpf_program__collect_reloc(struct bpf_program *prog,
return 0;
}

+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, 0xff, sizeof(int) * nr_maps);
+
+ pfd = obj->map_fds;
+ for (i = 0; i < nr_maps; i++) {
+ struct bpf_map_def def;
+
+ def = *(struct bpf_map_def *)(obj->maps_buf +
+ i * sizeof(struct bpf_map_def));
+
+ *pfd = bpf_create_map(def.type,
+ def.key_size,
+ def.value_size,
+ def.max_entries);
+ if (*pfd < 0) {
+ size_t j;
+ int err = *pfd;
+
+ 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);
+ 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_object__collect_reloc(struct bpf_object *obj)
{
int i, err;
@@ -671,6 +736,42 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
return __bpf_object__open("[buffer]", obj_buf, obj_buf_sz);
}

+int bpf_object__unload(struct bpf_object *obj)
+{
+ size_t i;
+
+ 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;
+
+ return 0;
+}
+
+int bpf_object__load(struct bpf_object *obj)
+{
+ if (!obj)
+ return -EINVAL;
+
+ if (obj->loaded) {
+ pr_warning("object should not be loaded twice\n");
+ return -EINVAL;
+ }
+
+ obj->loaded = true;
+ if (bpf_object__create_maps(obj))
+ goto out;
+
+ return 0;
+out:
+ bpf_object__unload(obj);
+ pr_warning("failed to load object '%s'\n", obj->path);
+ return -EINVAL;
+}
+
void bpf_object__close(struct bpf_object *obj)
{
size_t i;
@@ -679,6 +780,7 @@ void bpf_object__close(struct bpf_object *obj)
return;

bpf_object__elf_finish(obj);
+ bpf_object__unload(obj);

zfree(&obj->maps_buf);

diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 6e75acd..3e69600 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -30,6 +30,10 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
size_t obj_buf_sz);
void bpf_object__close(struct bpf_object *object);

+/* Load/unload object into/from kernel */
+int bpf_object__load(struct bpf_object *obj);
+int bpf_object__unload(struct bpf_object *obj);
+
/*
* We don't need __attribute__((packed)) now since it is
* unnecessary for 'bpf_map_def' because they are all aligned.
--
1.8.3.4

2015-07-09 12:36:34

by Wang Nan

[permalink] [raw]
Subject: [PATCH 07/39] bpf tools: Relocate eBPF programs

If an eBPF program accesses a map, LLVM generates a load instruction
which loads an absolute address into a register, like this:

ld_64 r1, <MCOperand Expr:(mymap)>
...
call 2

That ld_64 instruction will be recorded in relocation section.
To enable the usage of that map, relocation must be done by replacing
the immediate value by real map file descriptor so it can be found by
eBPF map functions.

This patch to the relocation work based on information collected by
patches:

'bpf tools: Collect symbol table from SHT_SYMTAB section',
'bpf tools: Collect relocation sections from SHT_REL sections'
and
'bpf tools: Record map accessing instructions for each program'.

For each instruction which needs relocation, it inject corresponding
file descriptor to imm field. As a part of protocol, src_reg is set to
BPF_PSEUDO_MAP_FD to notify kernel this is a map loading instruction.

This is the final part of map relocation patch. The principle of map
relocation is described in commit message of 'bpf tools: Collect symbol
table from SHT_SYMTAB section'.

Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/lib/bpf/libbpf.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index c214e1c..cd40ae0 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -633,6 +633,56 @@ bpf_object__create_maps(struct bpf_object *obj)
return 0;
}

+static int
+bpf_program__relocate(struct bpf_program *prog, int *map_fds)
+{
+ int i;
+
+ if (!prog || !prog->reloc_desc)
+ return 0;
+
+ for (i = 0; i < prog->nr_reloc; i++) {
+ int insn_idx, map_idx;
+ struct bpf_insn *insns = prog->insns;
+
+ insn_idx = prog->reloc_desc[i].insn_idx;
+ map_idx = prog->reloc_desc[i].map_idx;
+
+ if (insn_idx >= (int)prog->insns_cnt) {
+ pr_warning("relocation out of range: '%s'\n",
+ prog->section_name);
+ return -ERANGE;
+ }
+ insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
+ insns[insn_idx].imm = map_fds[map_idx];
+ }
+
+ zfree(&prog->reloc_desc);
+ prog->nr_reloc = 0;
+ return 0;
+}
+
+
+static int
+bpf_object__relocate(struct bpf_object *obj)
+{
+ struct bpf_program *prog;
+ size_t i;
+ int err;
+
+ for (i = 0; i < obj->nr_programs; i++) {
+ prog = &obj->programs[i];
+
+ err = bpf_program__relocate(prog, obj->map_fds);
+ if (err) {
+ pr_warning("failed to relocate '%s'\n",
+ prog->section_name);
+ return err;
+ }
+ }
+ return 0;
+}
+
static int bpf_object__collect_reloc(struct bpf_object *obj)
{
int i, err;
@@ -764,6 +814,8 @@ int bpf_object__load(struct bpf_object *obj)
obj->loaded = true;
if (bpf_object__create_maps(obj))
goto out;
+ if (bpf_object__relocate(obj))
+ goto out;

return 0;
out:
--
1.8.3.4

2015-07-09 12:38:47

by Wang Nan

[permalink] [raw]
Subject: [PATCH 08/39] bpf tools: Introduce bpf_load_program() to bpf.c

bpf_load_program() can be used to load bpf program into kernel. To make
loading faster, first try to load without logbuf. Try again with logbuf
if the first try failed.

Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[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/bpf.c | 34 ++++++++++++++++++++++++++++++++++
tools/lib/bpf/bpf.h | 7 +++++++
2 files changed, 41 insertions(+)

diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index 208de7c3..a633105 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -29,6 +29,11 @@
# endif
#endif

+static __u64 ptr_to_u64(void *ptr)
+{
+ return (__u64) (unsigned long) ptr;
+}
+
static int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
unsigned int size)
{
@@ -49,3 +54,32 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size,

return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
}
+
+int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
+ size_t insns_cnt, char *license,
+ u32 kern_version, char *log_buf, size_t log_buf_sz)
+{
+ int fd;
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.prog_type = type;
+ attr.insn_cnt = (__u32)insns_cnt;
+ attr.insns = ptr_to_u64(insns);
+ attr.license = ptr_to_u64(license);
+ attr.log_buf = ptr_to_u64(NULL);
+ attr.log_size = 0;
+ attr.log_level = 0;
+ attr.kern_version = kern_version;
+
+ fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+ if (fd >= 0 || !log_buf || !log_buf_sz)
+ return fd;
+
+ /* Try again with log */
+ attr.log_buf = ptr_to_u64(log_buf);
+ attr.log_size = log_buf_sz;
+ attr.log_level = 1;
+ log_buf[0] = 0;
+ return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+}
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index 28f7942..854b736 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -13,4 +13,11 @@
int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
int max_entries);

+/* Recommend log buffer size */
+#define BPF_LOG_BUF_SIZE 65536
+int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
+ size_t insns_cnt, char *license,
+ u32 kern_version, char *log_buf,
+ size_t log_buf_sz);
+
#endif
--
1.8.3.4

2015-07-09 12:40:21

by Wang Nan

[permalink] [raw]
Subject: [PATCH 09/39] bpf tools: Load eBPF programs in object files into kernel

This patch utilizes previous introduced bpf_load_program to load
programs in the ELF file into kernel. Result is stored in 'fd' field in
'struct bpf_program'.

During loading, it allocs a log buffer and free it before return. Note
that that buffer is not passed to bpf_load_program() if the first
loading try is successful. Doesn't use a statically allocated log buffer
to avoid potention multi-thread problem.

Instructions collected during opening is cleared after loading.

load_program() is created for loading a 'struct bpf_insn' array into
kernel, bpf_program__load() calls it. By this design we have a function
loads instructions into kernel. It will be used by further patches,
which creates different instances from a program and load them into
kernel.

Signed-off-by: Wang Nan <[email protected]>
Cc: <[email protected]>
Cc: <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Zefan Li <[email protected]>
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/lib/bpf/libbpf.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 90 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index cd40ae0..d826d5b 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -96,6 +96,8 @@ struct bpf_program {
int map_idx;
} *reloc_desc;
int nr_reloc;
+
+ int fd;
};

struct bpf_object {
@@ -135,11 +137,20 @@ struct bpf_object {
};
#define obj_elf_valid(o) ((o)->efile.elf)

+static void bpf_program__unload(struct bpf_program *prog)
+{
+ if (!prog)
+ return;
+
+ zclose(prog->fd);
+}
+
static void bpf_program__clear(struct bpf_program *prog)
{
if (!prog)
return;

+ bpf_program__unload(prog);
zfree(&prog->section_name);
zfree(&prog->insns);
zfree(&prog->reloc_desc);
@@ -176,6 +187,7 @@ __bpf_program__new(void *data, size_t size, char *name, int idx,
memcpy(prog->insns, data,
prog->insns_cnt * sizeof(struct bpf_insn));
prog->idx = idx;
+ prog->fd = -1;

return 0;
errout:
@@ -721,6 +733,79 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
return 0;
}

+static int
+load_program(struct bpf_insn *insns, int insns_cnt,
+ char *license, u32 kern_version, int *pfd)
+{
+ int ret;
+ char *log_buf;
+
+ if (!insns || !insns_cnt)
+ return -EINVAL;
+
+ log_buf = malloc(BPF_LOG_BUF_SIZE);
+ if (!log_buf)
+ pr_warning("Alloc log buffer for bpf loader error, continue without log\n");
+
+ ret = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns,
+ insns_cnt, license, kern_version,
+ log_buf, BPF_LOG_BUF_SIZE);
+
+ if (ret >= 0) {
+ *pfd = ret;
+ ret = 0;
+ goto out;
+ }
+
+ ret = -EINVAL;
+ pr_warning("load bpf program failed: %s\n", strerror(errno));
+
+ if (log_buf) {
+ pr_warning("-- BEGIN DUMP LOG ---\n");
+ pr_warning("\n%s\n", log_buf);
+ pr_warning("-- END LOG --\n");
+ }
+
+out:
+ free(log_buf);
+ return ret;
+}
+
+static int
+bpf_program__load(struct bpf_program *prog,
+ char *license, u32 kern_version)
+{
+ int err, fd;
+
+ err = load_program(prog->insns, prog->insns_cnt,
+ license, kern_version, &fd);
+ if (!err)
+ prog->fd = fd;
+
+ if (err)
+ pr_warning("failed to load program '%s'\n",
+ prog->section_name);
+ zfree(&prog->insns);
+ prog->insns_cnt = 0;
+ return err;
+}
+
+static int
+bpf_object__load_progs(struct bpf_object *obj)
+{
+ size_t i;
+ int err;
+
+ for (i = 0; i < obj->nr_programs; i++) {
+ err = bpf_program__load(&obj->programs[i],
+ obj->license,
+ obj->kern_version);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
static int bpf_object__validate(struct bpf_object *obj)
{
if (obj->kern_version == 0) {
@@ -798,6 +883,9 @@ int bpf_object__unload(struct bpf_object *obj)
zfree(&obj->map_fds);
obj->nr_map_fds = 0;

+ for (i = 0; i < obj->nr_programs; i++)
+ bpf_program__unload(&obj->programs[i]);
+
return 0;
}

@@ -816,6 +904,8 @@ int bpf_object__load(struct bpf_object *obj)
goto out;
if (bpf_object__relocate(obj))
goto out;
+ if (bpf_object__load_progs(obj))
+ goto out;

return 0;
out:
--
1.8.3.4

2015-07-09 12:38:25

by Wang Nan

[permalink] [raw]
Subject: [PATCH 10/39] bpf tools: Introduce accessors for struct bpf_program

This patch introduces accessors for user of libbpf to retrieve section
name and fd of a opened/loaded eBPF program. 'struct bpf_prog_handler'
is used for that purpose. Accessors of programs section name and file
descriptor are provided. Set/get private data are also impelmented.

Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/lib/bpf/libbpf.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 24 +++++++++++++++++
2 files changed, 96 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index d826d5b..ea6c1a6 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -98,6 +98,10 @@ struct bpf_program {
int nr_reloc;

int fd;
+
+ struct bpf_object *obj;
+ void *priv;
+ bpf_program_clear_priv_t clear_priv;
};

struct bpf_object {
@@ -150,6 +154,12 @@ static void bpf_program__clear(struct bpf_program *prog)
if (!prog)
return;

+ if (prog->clear_priv)
+ prog->clear_priv(prog, prog->priv);
+
+ prog->priv = NULL;
+ prog->clear_priv = NULL;
+
bpf_program__unload(prog);
zfree(&prog->section_name);
zfree(&prog->insns);
@@ -224,6 +234,7 @@ bpf_program__new(struct bpf_object *obj, void *data, size_t size,

obj->programs = progs;
obj->nr_programs = nr_progs + 1;
+ prog.obj = obj;
progs[nr_progs] = prog;
return &progs[nr_progs];
}
@@ -934,3 +945,64 @@ void bpf_object__close(struct bpf_object *obj)

free(obj);
}
+
+struct bpf_program *
+bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
+{
+ size_t idx;
+
+ if (!obj->programs)
+ return NULL;
+ /* First handler */
+ if (prev == NULL)
+ return &obj->programs[0];
+
+ if (prev->obj != obj) {
+ pr_warning("error: program handler doesn't match object\n");
+ return NULL;
+ }
+
+ idx = (prev - obj->programs) + 1;
+ if (idx >= obj->nr_programs)
+ return NULL;
+ return &obj->programs[idx];
+}
+
+int bpf_program__set_private(struct bpf_program *prog,
+ void *priv,
+ bpf_program_clear_priv_t clear_priv)
+{
+ if (prog->priv && prog->clear_priv)
+ prog->clear_priv(prog, prog->priv);
+
+ prog->priv = priv;
+ prog->clear_priv = clear_priv;
+ return 0;
+}
+
+int bpf_program__get_private(struct bpf_program *prog, void **ppriv)
+{
+ *ppriv = prog->priv;
+ return 0;
+}
+
+const char *bpf_program__title(struct bpf_program *prog, bool dup)
+{
+ const char *title;
+
+ title = prog->section_name;
+ if (dup) {
+ title = strdup(title);
+ if (!title) {
+ pr_warning("failed to strdup program title\n");
+ return NULL;
+ }
+ }
+
+ return title;
+}
+
+int bpf_program__fd(struct bpf_program *prog)
+{
+ return prog->fd;
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 3e69600..657e497 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -9,6 +9,7 @@
#define __BPF_LIBBPF_H

#include <stdio.h>
+#include <stdbool.h>

/*
* In include/linux/compiler-gcc.h, __printf is defined. However
@@ -34,6 +35,29 @@ void bpf_object__close(struct bpf_object *object);
int bpf_object__load(struct bpf_object *obj);
int bpf_object__unload(struct bpf_object *obj);

+/* Accessors of bpf_program. */
+struct bpf_program;
+struct bpf_program *bpf_program__next(struct bpf_program *prog,
+ struct bpf_object *obj);
+
+#define bpf_object__for_each_program(pos, obj) \
+ for ((pos) = bpf_program__next(NULL, (obj)); \
+ (pos) != NULL; \
+ (pos) = bpf_program__next((pos), (obj)))
+
+typedef void (*bpf_program_clear_priv_t)(struct bpf_program *,
+ void *);
+
+int bpf_program__set_private(struct bpf_program *prog, void *priv,
+ bpf_program_clear_priv_t clear_priv);
+
+int bpf_program__get_private(struct bpf_program *prog,
+ void **ppriv);
+
+const char *bpf_program__title(struct bpf_program *prog, bool dup);
+
+int bpf_program__fd(struct bpf_program *prog);
+
/*
* We don't need __attribute__((packed)) now since it is
* unnecessary for 'bpf_map_def' because they are all aligned.
--
1.8.3.4

2015-07-09 12:39:38

by Wang Nan

[permalink] [raw]
Subject: [PATCH 11/39] bpf tools: Link all bpf objects onto a list

To allow enumeration of all bpf_objects, keep them in a list (hidden to
caller). bpf_object__for_each_safe() is introduced to do this iteration.
It is safe even user close the object during iteration.

Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/lib/bpf/libbpf.c | 32 ++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 7 +++++++
2 files changed, 39 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index ea6c1a6..b37aab1 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -17,6 +17,7 @@
#include <asm/unistd.h>
#include <linux/kernel.h>
#include <linux/bpf.h>
+#include <linux/list.h>
#include <libelf.h>
#include <gelf.h>

@@ -104,6 +105,8 @@ struct bpf_program {
bpf_program_clear_priv_t clear_priv;
};

+static LIST_HEAD(bpf_objects_list);
+
struct bpf_object {
char license[64];
u32 kern_version;
@@ -137,6 +140,12 @@ struct bpf_object {
} *reloc;
int nr_reloc;
} efile;
+ /*
+ * All loaded bpf_object is linked in a list, which is
+ * hidden to caller. bpf_objects__<func> handlers deal with
+ * all objects.
+ */
+ struct list_head list;
char path[];
};
#define obj_elf_valid(o) ((o)->efile.elf)
@@ -264,6 +273,9 @@ static struct bpf_object *bpf_object__new(const char *path,
obj->efile.obj_buf_sz = obj_buf_sz;

obj->loaded = false;
+
+ INIT_LIST_HEAD(&obj->list);
+ list_add(&obj->list, &bpf_objects_list);
return obj;
}

@@ -943,9 +955,29 @@ void bpf_object__close(struct bpf_object *obj)
}
zfree(&obj->programs);

+ list_del(&obj->list);
free(obj);
}

+struct bpf_object *
+bpf_object__next(struct bpf_object *prev)
+{
+ struct bpf_object *next;
+
+ if (!prev)
+ next = list_first_entry(&bpf_objects_list,
+ struct bpf_object,
+ list);
+ else
+ next = list_next_entry(prev, list);
+
+ /* Empty list is noticed here so don't need checking on entry. */
+ if (&next->list == &bpf_objects_list)
+ return NULL;
+
+ return next;
+}
+
struct bpf_program *
bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
{
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 657e497..ea8adc2 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -35,6 +35,13 @@ void bpf_object__close(struct bpf_object *object);
int bpf_object__load(struct bpf_object *obj);
int bpf_object__unload(struct bpf_object *obj);

+struct bpf_object *bpf_object__next(struct bpf_object *prev);
+#define bpf_object__for_each_safe(pos, tmp) \
+ for ((pos) = bpf_object__next(NULL), \
+ (tmp) = bpf_object__next(pos); \
+ (pos) != NULL; \
+ (pos) = (tmp), (tmp) = bpf_object__next(tmp))
+
/* Accessors of bpf_program. */
struct bpf_program;
struct bpf_program *bpf_program__next(struct bpf_program *prog,
--
1.8.3.4

2015-07-09 12:38:09

by Wang Nan

[permalink] [raw]
Subject: [PATCH 12/39] perf tools: Introduce llvm config options

This patch introduces [llvm] config section with 5 options. Following
patches will use then to config llvm dynamica compiling.

'llvm-utils.[ch]' is introduced in this patch for holding all
llvm/clang related stuffs.

Example:

[llvm]
# Path to clang. If omit, search it from $PATH.
clang-path = "/path/to/clang"

# Cmdline template. Following line shows its default value.
# Environment variable is used to passing options.
clang-bpf-cmd-template = "$CLANG_EXEC $CLANG_OPTIONS \
$KERNEL_INC_OPTIONS -Wno-unused-value \
-Wno-pointer-sign -working-directory \
$WORKING_DIR -c $CLANG_SOURCE -target \
bpf -O2 -o -"

# Options passed to clang, will be passed to cmdline by
# $CLANG_OPTIONS.
clang-opt = "-Wno-unused-value -Wno-pointer-sign"

# kbuild directory. If not set, use /lib/modules/`uname -r`/build.
# If set to "" deliberately, skip kernel header auto-detector.
kbuild-dir = "/path/to/kernel/build"

# Options passed to 'make' when detecting kernel header options.
kbuild-opts = "ARCH=x86_64"

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/Build | 1 +
tools/perf/util/config.c | 4 ++++
tools/perf/util/llvm-utils.c | 45 ++++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/llvm-utils.h | 36 +++++++++++++++++++++++++++++++++++
4 files changed, 86 insertions(+)
create mode 100644 tools/perf/util/llvm-utils.c
create mode 100644 tools/perf/util/llvm-utils.h

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 601d114..bc24293 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -14,6 +14,7 @@ libperf-y += find_next_bit.o
libperf-y += help.o
libperf-y += kallsyms.o
libperf-y += levenshtein.o
+libperf-y += llvm-utils.o
libperf-y += parse-options.o
libperf-y += parse-events.o
libperf-y += path.o
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index e18f653..2e452ac 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -12,6 +12,7 @@
#include "cache.h"
#include "exec_cmd.h"
#include "util/hist.h" /* perf_hist_config */
+#include "util/llvm-utils.h" /* perf_llvm_config */

#define MAXNAME (256)

@@ -408,6 +409,9 @@ int perf_default_config(const char *var, const char *value,
if (!prefixcmp(var, "call-graph."))
return perf_callchain_config(var, value);

+ if (!prefixcmp(var, "llvm."))
+ return perf_llvm_config(var, value);
+
/* Add other config variables here. */
return 0;
}
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
new file mode 100644
index 0000000..fd5b1bc
--- /dev/null
+++ b/tools/perf/util/llvm-utils.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015, Wang Nan <[email protected]>
+ * Copyright (C) 2015, Huawei Inc.
+ */
+
+#include <stdio.h>
+#include "util.h"
+#include "debug.h"
+#include "llvm-utils.h"
+#include "cache.h"
+
+#define CLANG_BPF_CMD_DEFAULT_TEMPLATE \
+ "$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS " \
+ "-Wno-unused-value -Wno-pointer-sign " \
+ "-working-directory $WORKING_DIR " \
+ " -c \"$CLANG_SOURCE\" -target bpf -O2 -o -"
+
+struct llvm_param llvm_param = {
+ .clang_path = "clang",
+ .clang_bpf_cmd_template = CLANG_BPF_CMD_DEFAULT_TEMPLATE,
+ .clang_opt = NULL,
+ .kbuild_dir = NULL,
+ .kbuild_opts = NULL,
+};
+
+int perf_llvm_config(const char *var, const char *value)
+{
+ if (prefixcmp(var, "llvm."))
+ return 0;
+ var += sizeof("llvm.") - 1;
+
+ if (!strcmp(var, "clang-path"))
+ llvm_param.clang_path = strdup(value);
+ else if (!strcmp(var, "clang-bpf-cmd-template"))
+ llvm_param.clang_bpf_cmd_template = strdup(value);
+ else if (!strcmp(var, "clang-opt"))
+ llvm_param.clang_opt = strdup(value);
+ else if (!strcmp(var, "kbuild-dir"))
+ llvm_param.kbuild_dir = strdup(value);
+ else if (!strcmp(var, "kbuild-opts"))
+ llvm_param.kbuild_opts = strdup(value);
+ else
+ return -1;
+ return 0;
+}
diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h
new file mode 100644
index 0000000..504b799
--- /dev/null
+++ b/tools/perf/util/llvm-utils.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015, Wang Nan <[email protected]>
+ * Copyright (C) 2015, Huawei Inc.
+ */
+#ifndef __LLVM_UTILS_H
+#define __LLVM_UTILS_H
+
+#include "debug.h"
+
+struct llvm_param {
+ /* Path of clang executable */
+ const char *clang_path;
+ /*
+ * Template of clang bpf compiling. 5 env variables
+ * can be used:
+ * $CLANG_EXEC: Path to clang.
+ * $CLANG_OPTIONS: Extra options to clang.
+ * $KERNEL_INC_OPTIONS: Kernel include directories.
+ * $WORKING_DIR: Kernel source directory.
+ * $CLANG_SOURCE: Source file to be compiled.
+ */
+ const char *clang_bpf_cmd_template;
+ /* Will be filled in $CLANG_OPTIONS */
+ const char *clang_opt;
+ /* Where to find kbuild system */
+ const char *kbuild_dir;
+ /*
+ * Arguments passed to make, like 'ARCH=arm' if doing cross
+ * compiling. Should not be used for dynamic compiling.
+ */
+ const char *kbuild_opts;
+};
+
+extern struct llvm_param llvm_param;
+extern int perf_llvm_config(const char *var, const char *value);
+#endif
--
1.8.3.4

2015-07-09 12:48:28

by Wang Nan

[permalink] [raw]
Subject: [PATCH 13/39] perf tools: Call clang to compile C source to object code

This is the core patch for supporting eBPF on-the-fly compiling, does
the following work:

1. Search clang compiler using search_program().

2. Run command template defined in llvm-bpf-cmd-template option in
[llvm] config section using read_from_pipe(). Patch of clang and
source code path is injected into shell command using environment
variable using force_set_env().

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/llvm-utils.c | 225 +++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/llvm-utils.h | 3 +
2 files changed, 228 insertions(+)

diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
index fd5b1bc..dca16e7 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -43,3 +43,228 @@ int perf_llvm_config(const char *var, const char *value)
return -1;
return 0;
}
+
+static int
+search_program(const char *def, const char *name,
+ char *output)
+{
+ char *env, *path, *tmp;
+ char buf[PATH_MAX];
+ int ret;
+
+ output[0] = '\0';
+ if (def && def[0] != '\0') {
+ if (def[0] == '/') {
+ if (access(def, F_OK) == 0) {
+ strlcpy(output, def, PATH_MAX);
+ return 0;
+ }
+ } else if (def[0] != '\0')
+ name = def;
+ }
+
+ env = getenv("PATH");
+ if (!env)
+ return -1;
+ env = strdup(env);
+ if (!env)
+ return -1;
+
+ ret = -ENOENT;
+ path = strtok_r(env, ":", &tmp);
+ while (path) {
+ scnprintf(buf, sizeof(buf), "%s/%s", path, name);
+ if (access(buf, F_OK) == 0) {
+ strlcpy(output, buf, PATH_MAX);
+ ret = 0;
+ break;
+ }
+ path = strtok_r(NULL, ":", &tmp);
+ }
+
+ free(env);
+ return ret;
+}
+
+#define READ_SIZE 4096
+static int
+read_from_pipe(const char *cmd, void **p_buf, size_t *p_read_sz)
+{
+ int err = 0;
+ void *buf = NULL;
+ FILE *file = NULL;
+ size_t read_sz = 0, buf_sz = 0;
+
+ file = popen(cmd, "r");
+ if (!file) {
+ pr_err("ERROR: unable to popen cmd: %s\n",
+ strerror(errno));
+ return -EINVAL;
+ }
+
+ while (!feof(file) && !ferror(file)) {
+ /*
+ * Make buf_sz always have obe byte extra space so we
+ * can put '\0' there.
+ */
+ if (buf_sz - read_sz < READ_SIZE + 1) {
+ void *new_buf;
+
+ buf_sz = read_sz + READ_SIZE + 1;
+ new_buf = realloc(buf, buf_sz);
+
+ if (!new_buf) {
+ pr_err("ERROR: failed to realloc memory\n");
+ err = -ENOMEM;
+ goto errout;
+ }
+
+ buf = new_buf;
+ }
+ read_sz += fread(buf + read_sz, 1, READ_SIZE, file);
+ }
+
+ if (buf_sz - read_sz < 1) {
+ pr_err("ERROR: internal error\n");
+ err = -EINVAL;
+ goto errout;
+ }
+
+ if (ferror(file)) {
+ pr_err("ERROR: error occurred when reading from pipe: %s\n",
+ strerror(errno));
+ err = -EIO;
+ goto errout;
+ }
+
+ err = WEXITSTATUS(pclose(file));
+ file = NULL;
+ if (err) {
+ err = -EINVAL;
+ goto errout;
+ }
+
+ /*
+ * If buf is string, give it terminal '\0' to make our life
+ * easier. If buf is not string, that '\0' is out of space
+ * indicated by read_sz so caller won't even notice it.
+ */
+ ((char *)buf)[read_sz] = '\0';
+
+ if (!p_buf)
+ free(buf);
+ else
+ *p_buf = buf;
+
+ if (p_read_sz)
+ *p_read_sz = read_sz;
+ return 0;
+
+errout:
+ if (file)
+ pclose(file);
+ free(buf);
+ if (p_buf)
+ *p_buf = NULL;
+ if (p_read_sz)
+ *p_read_sz = 0;
+ return err;
+}
+
+static inline void
+force_set_env(const char *var, const char *value)
+{
+ if (value) {
+ setenv(var, value, 1);
+ pr_debug("set env: %s=%s\n", var, value);
+ } else {
+ unsetenv(var);
+ pr_debug("unset env: %s\n", var);
+ }
+}
+
+static void
+version_notice(void)
+{
+ pr_err(
+" \tLLVM 3.7 or newer is required. Which can be found from http://llvm.org\n"
+" \tYou may want to try git trunk:\n"
+" \t\tgit clone http://llvm.org/git/llvm.git\n"
+" \t\t and\n"
+" \t\tgit clone http://llvm.org/git/clang.git\n\n"
+" \tOr fetch the latest clang/llvm 3.7 from pre-built llvm packages for\n"
+" \tdebian/ubuntu:\n"
+" \t\thttp://llvm.org/apt\n\n"
+" \tIf you are using old version of clang, change 'clang-bpf-cmd-template'\n"
+" \toption in [llvm] section of ~/.perfconfig to:\n\n"
+" \t \"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS \\\n"
+" \t -working-directory $WORKING_DIR -c $CLANG_SOURCE \\\n"
+" \t -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -\"\n"
+" \t(Replace /path/to/llc with path to your llc)\n\n"
+);
+}
+
+int llvm__compile_bpf(const char *path, void **p_obj_buf,
+ size_t *p_obj_buf_sz)
+{
+ int err;
+ char clang_path[PATH_MAX];
+ const char *clang_opt = llvm_param.clang_opt;
+ const char *template = llvm_param.clang_bpf_cmd_template;
+ void *obj_buf = NULL;
+ size_t obj_buf_sz;
+
+ if (!template)
+ template = CLANG_BPF_CMD_DEFAULT_TEMPLATE;
+
+ err = search_program(llvm_param.clang_path,
+ "clang", clang_path);
+ if (err) {
+ pr_err(
+"ERROR:\tunable to find clang.\n"
+"Hint:\tTry to install latest clang/llvm to support BPF. Check your $PATH\n"
+" \tand 'clang-path' option in [llvm] section of ~/.perfconfig.\n");
+ version_notice();
+ return -ENOENT;
+ }
+
+ force_set_env("CLANG_EXEC", clang_path);
+ force_set_env("CLANG_OPTIONS", clang_opt);
+ force_set_env("KERNEL_INC_OPTIONS", NULL);
+ force_set_env("WORKING_DIR", ".");
+
+ /*
+ * Since we may reset clang's working dir, path of source file
+ * should be transferred into absolute path, except we want
+ * stdin to be source file (testing).
+ */
+ force_set_env("CLANG_SOURCE",
+ (path[0] == '-') ? path :
+ make_nonrelative_path(path));
+
+ pr_debug("llvm compiling command template: %s\n", template);
+ err = read_from_pipe(template, &obj_buf, &obj_buf_sz);
+ if (err) {
+ pr_err("ERROR:\tunable to compile %s\n", path);
+ pr_err("Hint:\tCheck error message shown above.\n");
+ version_notice();
+ pr_err("Hint:\tYou can also pre-compile it into .o\n");
+ goto errout;
+ }
+
+ if (!p_obj_buf)
+ free(obj_buf);
+ else
+ *p_obj_buf = obj_buf;
+
+ if (p_obj_buf_sz)
+ *p_obj_buf_sz = obj_buf_sz;
+ return 0;
+errout:
+ free(obj_buf);
+ if (p_obj_buf)
+ *p_obj_buf = NULL;
+ if (p_obj_buf_sz)
+ *p_obj_buf_sz = 0;
+ return err;
+}
diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h
index 504b799..d23adbc 100644
--- a/tools/perf/util/llvm-utils.h
+++ b/tools/perf/util/llvm-utils.h
@@ -33,4 +33,7 @@ struct llvm_param {

extern struct llvm_param llvm_param;
extern int perf_llvm_config(const char *var, const char *value);
+
+extern int llvm__compile_bpf(const char *path, void **p_obj_buf,
+ size_t *p_obj_buf_sz);
#endif
--
1.8.3.4

2015-07-09 12:45:04

by Wang Nan

[permalink] [raw]
Subject: [PATCH 14/39] perf tools: Auto detecting kernel build directory

This patch detects kernel build directory using a embedded shell
script 'kbuild_detector', which does this by checking existence of
include/generated/autoconf.h.

clang working directory is changed to kbuild directory if it is found,
to help user use relative include path. Following patch will detect
kernel include directory, which contains relative include patch so this
workdir changing is needed.

Users are allowed to set 'kbuild-dir = ""' manually to disable this
checking.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/llvm-utils.c | 56 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 55 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
index dca16e7..2ca2bd6 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -204,6 +204,51 @@ version_notice(void)
);
}

+static const char *kbuild_detector =
+"#!/usr/bin/env sh\n"
+"DEFAULT_KBUILD_DIR=/lib/modules/`uname -r`/build\n"
+"if test -z \"$KBUILD_DIR\"\n"
+"then\n"
+" KBUILD_DIR=$DEFAULT_KBUILD_DIR\n"
+"fi\n"
+"if test -f $KBUILD_DIR/include/generated/autoconf.h\n"
+"then\n"
+" echo -n $KBUILD_DIR\n"
+" exit 0\n"
+"fi\n"
+"exit -1\n";
+
+static inline void
+get_kbuild_opts(char **kbuild_dir)
+{
+ int err;
+
+ if (!kbuild_dir)
+ return;
+
+ *kbuild_dir = NULL;
+
+ if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) {
+ pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n");
+ pr_debug("Skip kbuild options detection.\n");
+ return;
+ }
+
+ force_set_env("KBUILD_DIR", llvm_param.kbuild_dir);
+ force_set_env("KBUILD_OPTS", llvm_param.kbuild_opts);
+ err = read_from_pipe(kbuild_detector,
+ ((void **)kbuild_dir),
+ NULL);
+ if (err) {
+ pr_warning(
+"WARNING:\tunable to get correct kernel building directory.\n"
+"Hint:\tSet correct kbuild directory using 'kbuild-dir' option in [llvm]\n"
+" \tsection of ~/.perfconfig or set it to \"\" to suppress kbuild\n"
+" \tdetection.\n\n");
+ return;
+ }
+}
+
int llvm__compile_bpf(const char *path, void **p_obj_buf,
size_t *p_obj_buf_sz)
{
@@ -211,6 +256,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
char clang_path[PATH_MAX];
const char *clang_opt = llvm_param.clang_opt;
const char *template = llvm_param.clang_bpf_cmd_template;
+ char *kbuild_dir = NULL;
void *obj_buf = NULL;
size_t obj_buf_sz;

@@ -228,10 +274,16 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
return -ENOENT;
}

+ /*
+ * This is an optional work. Even it fail we can continue our
+ * work. Needn't to check error return.
+ */
+ get_kbuild_opts(&kbuild_dir);
+
force_set_env("CLANG_EXEC", clang_path);
force_set_env("CLANG_OPTIONS", clang_opt);
force_set_env("KERNEL_INC_OPTIONS", NULL);
- force_set_env("WORKING_DIR", ".");
+ force_set_env("WORKING_DIR", kbuild_dir ? : ".");

/*
* Since we may reset clang's working dir, path of source file
@@ -252,6 +304,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
goto errout;
}

+ free(kbuild_dir);
if (!p_obj_buf)
free(obj_buf);
else
@@ -261,6 +314,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
*p_obj_buf_sz = obj_buf_sz;
return 0;
errout:
+ free(kbuild_dir);
free(obj_buf);
if (p_obj_buf)
*p_obj_buf = NULL;
--
1.8.3.4

2015-07-09 12:40:15

by Wang Nan

[permalink] [raw]
Subject: [PATCH 15/39] perf tools: Auto detecting kernel include options

To help user find correct kernel include options, this patch extracts
them from kbuild system by an embedded script kinc_fetch_script, which
creates a temporary directory, generates Makefile and an empty dummy.o
then use the Makefile to fetch $(NOSTDINC_FLAGS), $(LINUXINCLUDE) and
$(EXTRA_CFLAGS) options. The result is passed to compiler script using
'KERNEL_INC_OPTIONS' environment variable.

Because options from kbuild contains relative path like
'Iinclude/generated/uapi', the work directory must be changed. This is
done by previous patch.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/llvm-utils.c | 59 ++++++++++++++++++++++++++++++++++++++++----
1 file changed, 54 insertions(+), 5 deletions(-)

diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
index 2ca2bd6..b658896 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -218,15 +218,42 @@ static const char *kbuild_detector =
"fi\n"
"exit -1\n";

+static const char *kinc_fetch_script =
+"#!/usr/bin/env sh\n"
+"if ! test -d \"$KBUILD_DIR\"\n"
+"then\n"
+" exit -1\n"
+"fi\n"
+"if ! test -f \"$KBUILD_DIR/include/generated/autoconf.h\"\n"
+"then\n"
+" exit -1\n"
+"fi\n"
+"TMPDIR=`mktemp -d`\n"
+"if test -z \"$TMPDIR\"\n"
+"then\n"
+" exit -1\n"
+"fi\n"
+"cat << EOF > $TMPDIR/Makefile\n"
+"obj-y := dummy.o\n"
+"\\$(obj)/%.o: \\$(src)/%.c\n"
+"\t@echo -n \"\\$(NOSTDINC_FLAGS) \\$(LINUXINCLUDE) \\$(EXTRA_CFLAGS)\"\n"
+"EOF\n"
+"touch $TMPDIR/dummy.c\n"
+"make -s -C $KBUILD_DIR M=$TMPDIR $KBUILD_OPTS dummy.o 2>/dev/null\n"
+"RET=$?\n"
+"rm -rf $TMPDIR\n"
+"exit $RET\n";
+
static inline void
-get_kbuild_opts(char **kbuild_dir)
+get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts)
{
int err;

- if (!kbuild_dir)
+ if (!kbuild_dir || !kbuild_include_opts)
return;

*kbuild_dir = NULL;
+ *kbuild_include_opts = NULL;

if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) {
pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n");
@@ -247,6 +274,26 @@ get_kbuild_opts(char **kbuild_dir)
" \tdetection.\n\n");
return;
}
+
+ pr_debug("Kernel build dir is set to %s\n", *kbuild_dir);
+ force_set_env("KBUILD_DIR", *kbuild_dir);
+ err = read_from_pipe(kinc_fetch_script,
+ (void **)kbuild_include_opts,
+ NULL);
+ if (err) {
+ pr_warning(
+"WARNING:\tunable to get kernel include directories from '%s'\n"
+"Hint:\tTry set clang include options using 'clang-bpf-cmd-template'\n"
+" \toption in [llvm] section of ~/.perfconfig and set 'kbuild-dir'\n"
+" \toption in [llvm] to \"\" to suppress this detection.\n\n",
+ *kbuild_dir);
+
+ free(*kbuild_dir);
+ *kbuild_dir = NULL;
+ return;
+ }
+
+ pr_debug("include option is set to %s\n", *kbuild_include_opts);
}

int llvm__compile_bpf(const char *path, void **p_obj_buf,
@@ -256,7 +303,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
char clang_path[PATH_MAX];
const char *clang_opt = llvm_param.clang_opt;
const char *template = llvm_param.clang_bpf_cmd_template;
- char *kbuild_dir = NULL;
+ char *kbuild_dir = NULL, *kbuild_include_opts = NULL;
void *obj_buf = NULL;
size_t obj_buf_sz;

@@ -278,11 +325,11 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
* This is an optional work. Even it fail we can continue our
* work. Needn't to check error return.
*/
- get_kbuild_opts(&kbuild_dir);
+ get_kbuild_opts(&kbuild_dir, &kbuild_include_opts);

force_set_env("CLANG_EXEC", clang_path);
force_set_env("CLANG_OPTIONS", clang_opt);
- force_set_env("KERNEL_INC_OPTIONS", NULL);
+ force_set_env("KERNEL_INC_OPTIONS", kbuild_include_opts);
force_set_env("WORKING_DIR", kbuild_dir ? : ".");

/*
@@ -305,6 +352,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
}

free(kbuild_dir);
+ free(kbuild_include_opts);
if (!p_obj_buf)
free(obj_buf);
else
@@ -315,6 +363,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
return 0;
errout:
free(kbuild_dir);
+ free(kbuild_include_opts);
free(obj_buf);
if (p_obj_buf)
*p_obj_buf = NULL;
--
1.8.3.4

2015-07-09 12:38:15

by Wang Nan

[permalink] [raw]
Subject: [PATCH 16/39] perf tests: Add LLVM test for eBPF on-the-fly compiling

Previous patches introduce llvm__compile_bpf() to compile source file to
eBPF object. This patch adds testcase to test it. It also test libbpf
by opening generated object.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/tests/Build | 1 +
tools/perf/tests/builtin-test.c | 4 ++
tools/perf/tests/llvm.c | 85 +++++++++++++++++++++++++++++++++++++++++
tools/perf/tests/tests.h | 1 +
4 files changed, 91 insertions(+)
create mode 100644 tools/perf/tests/llvm.c

diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index d20d6e6..c1518bd 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -32,6 +32,7 @@ perf-y += sample-parsing.o
perf-y += parse-no-sample-id-all.o
perf-y += kmod-path.o
perf-y += thread-map.o
+perf-y += llvm.o

perf-$(CONFIG_X86) += perf-time-to-tsc.o

diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index c1dde73..136cd93 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -175,6 +175,10 @@ static struct test {
.func = test__thread_map,
},
{
+ .desc = "Test LLVM searching and compiling",
+ .func = test__llvm,
+ },
+ {
.func = NULL,
},
};
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
new file mode 100644
index 0000000..9984631
--- /dev/null
+++ b/tools/perf/tests/llvm.c
@@ -0,0 +1,85 @@
+#include <stdio.h>
+#include <bpf/libbpf.h>
+#include <util/llvm-utils.h>
+#include <util/cache.h>
+#include "tests.h"
+#include "debug.h"
+
+static int perf_config_cb(const char *var, const char *val,
+ void *arg __maybe_unused)
+{
+ return perf_default_config(var, val, arg);
+}
+
+/*
+ * Randomly give it a "version" section since we don't really load it
+ * into kernel
+ */
+static const char test_bpf_prog[] =
+ "__attribute__((section(\"do_fork\"), used)) "
+ "int fork(void *ctx) {return 0;} "
+ "char _license[] __attribute__((section(\"license\"), used)) = \"GPL\";"
+ "int _version __attribute__((section(\"version\"), used)) = 0x40100;";
+
+#ifdef HAVE_LIBBPF_SUPPORT
+static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz)
+{
+ struct bpf_object *obj;
+
+ obj = bpf_object__open_buffer(obj_buf, obj_buf_sz);
+ if (!obj)
+ return -1;
+ bpf_object__close(obj);
+ return 0;
+}
+#else
+static int test__bpf_parsing(void *obj_buf __maybe_unused,
+ size_t obj_buf_sz __maybe_unused)
+{
+ fprintf(stderr, " (skip bpf parsing)");
+ return 0;
+}
+#endif
+
+#define new_string(n, fmt, ...) \
+do { \
+ n##_sz = snprintf(NULL, 0, fmt, __VA_ARGS__);\
+ if (n##_sz == 0) \
+ return -1; \
+ n##_new = malloc(n##_sz + 1); \
+ if (!n##_new) \
+ return -1; \
+ snprintf(n##_new, n##_sz + 1, fmt, __VA_ARGS__);\
+ n##_new[n##_sz] = '\0'; \
+} while (0)
+
+int test__llvm(void)
+{
+ char *tmpl_new, *clang_opt_new;
+ size_t tmpl_sz, clang_opt_sz;
+ void *obj_buf;
+ size_t obj_buf_sz;
+ int err;
+
+ perf_config(perf_config_cb, NULL);
+
+ if (!llvm_param.clang_bpf_cmd_template)
+ return -1;
+
+ if (!llvm_param.clang_opt)
+ llvm_param.clang_opt = strdup("");
+
+ new_string(tmpl, "echo '%s' | %s", test_bpf_prog,
+ llvm_param.clang_bpf_cmd_template);
+ new_string(clang_opt, "-xc %s", llvm_param.clang_opt);
+
+ llvm_param.clang_bpf_cmd_template = tmpl_new;
+ llvm_param.clang_opt = clang_opt_new;
+ err = llvm__compile_bpf("-", &obj_buf, &obj_buf_sz);
+ if (err)
+ return -1;
+
+ err = test__bpf_parsing(obj_buf, obj_buf_sz);
+ free(obj_buf);
+ return err;
+}
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index ebb47d9..bf113a2 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -62,6 +62,7 @@ int test__fdarray__filter(void);
int test__fdarray__add(void);
int test__kmod_path__parse(void);
int test__thread_map(void);
+int test__llvm(void);

#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT
--
1.8.3.4

2015-07-09 12:37:59

by Wang Nan

[permalink] [raw]
Subject: [PATCH 17/39] perf tools: Make perf depend on libbpf

By adding libbpf into perf's Makefile, this patch enables perf to build
libbpf during building if libelf is found and neither NO_LIBELF nor
NO_LIBBPF is set. The newly introduced code is similar to libapi and
libtraceevent building in Makefile.perf.

MANIFEST is also updated for 'make perf-*-src-pkg'.

Append make_no_libbpf to tools/perf/tests/make.

'bpf' feature check is appended into default FEATURE_TESTS and
FEATURE_DISPLAY, so perf will check API version of bpf in
/path/to/kernel/include/uapi/linux/bpf.h. Which should not fail except
when we are trying to port this code to an old kernel.

Error messages are also updated to notify users about the disable of BPF
support of 'perf record' if libelf is missed or BPF API check failed.

tools/lib/bpf is added into TAG_FOLDERS to allow us to navigate on
libbpf files when working on perf using tools/perf/tags.

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[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.feature | 6 ++++--
tools/perf/MANIFEST | 3 +++
tools/perf/Makefile.perf | 19 +++++++++++++++++--
tools/perf/config/Makefile | 19 ++++++++++++++++++-
tools/perf/tests/make | 4 +++-
5 files changed, 45 insertions(+), 6 deletions(-)

diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 2975632..5ec6b37 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -51,7 +51,8 @@ FEATURE_TESTS ?= \
timerfd \
libdw-dwarf-unwind \
zlib \
- lzma
+ lzma \
+ bpf

FEATURE_DISPLAY ?= \
dwarf \
@@ -67,7 +68,8 @@ FEATURE_DISPLAY ?= \
libunwind \
libdw-dwarf-unwind \
zlib \
- lzma
+ lzma \
+ bpf

# Set FEATURE_CHECK_(C|LD)FLAGS-all for all FEATURE_TESTS features.
# If in the future we need per-feature checks/flags for features not
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST
index 09dc0aa..1b42f7c 100644
--- a/tools/perf/MANIFEST
+++ b/tools/perf/MANIFEST
@@ -17,6 +17,7 @@ tools/build
tools/arch/x86/include/asm/atomic.h
tools/arch/x86/include/asm/rmwcc.h
tools/lib/traceevent
+tools/lib/bpf
tools/lib/api
tools/lib/rbtree.c
tools/lib/symbol/kallsyms.c
@@ -67,6 +68,8 @@ arch/*/lib/memset*.S
include/linux/poison.h
include/linux/hw_breakpoint.h
include/uapi/linux/perf_event.h
+include/uapi/linux/bpf.h
+include/uapi/linux/bpf_common.h
include/uapi/linux/const.h
include/uapi/linux/swab.h
include/uapi/linux/hw_breakpoint.h
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 7a4b549..4857129 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -125,6 +125,7 @@ STRIP = strip

LIB_DIR = $(srctree)/tools/lib/api/
TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/
+BPF_DIR = $(srctree)/tools/lib/bpf/

# include config/Makefile by default and rule out
# non-config cases
@@ -160,6 +161,7 @@ strip-libs = $(filter-out -l%,$(1))

ifneq ($(OUTPUT),)
TE_PATH=$(OUTPUT)
+ BPF_PATH=$(OUTPUT)
ifneq ($(subdir),)
LIB_PATH=$(OUTPUT)/../lib/api/
else
@@ -168,6 +170,7 @@ endif
else
TE_PATH=$(TRACE_EVENT_DIR)
LIB_PATH=$(LIB_DIR)
+ BPF_PATH=$(BPF_DIR)
endif

LIBTRACEEVENT = $(TE_PATH)libtraceevent.a
@@ -179,6 +182,8 @@ LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS = -Xlinker --dynamic-list=$(LIBTRACEEVENT_DYN
LIBAPI = $(LIB_PATH)libapi.a
export LIBAPI

+LIBBPF = $(BPF_PATH)libbpf.a
+
# python extension build directories
PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/
PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/
@@ -231,6 +236,9 @@ export PERL_PATH
LIB_FILE=$(OUTPUT)libperf.a

PERFLIBS = $(LIB_FILE) $(LIBAPI) $(LIBTRACEEVENT)
+ifndef NO_LIBBPF
+ PERFLIBS += $(LIBBPF)
+endif

# We choose to avoid "if .. else if .. else .. endif endif"
# because maintaining the nesting to match is a pain. If
@@ -400,6 +408,13 @@ $(LIBAPI)-clean:
$(call QUIET_CLEAN, libapi)
$(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) clean >/dev/null

+$(LIBBPF): FORCE
+ $(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) $(OUTPUT)libbpf.a
+
+$(LIBBPF)-clean:
+ $(call QUIET_CLEAN, libbpf)
+ $(Q)$(MAKE) -C $(BPF_DIR) O=$(OUTPUT) clean >/dev/null
+
help:
@echo 'Perf make targets:'
@echo ' doc - make *all* documentation (see below)'
@@ -439,7 +454,7 @@ INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html
$(DOC_TARGETS):
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all)

-TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol
+TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol ../lib/bpf
TAG_FILES= ../../include/uapi/linux/perf_event.h

TAGS:
@@ -542,7 +557,7 @@ config-clean:
$(call QUIET_CLEAN, config)
$(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null

-clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean config-clean
+clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean config-clean
$(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS)
$(Q)find . -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
$(Q)$(RM) $(OUTPUT).config-detected
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 094ddae..52722cf 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -106,6 +106,7 @@ ifdef LIBBABELTRACE
FEATURE_CHECK_LDFLAGS-libbabeltrace := $(LIBBABELTRACE_LDFLAGS) -lbabeltrace-ctf
endif

+FEATURE_CHECK_CFLAGS-bpf = -I. -I$(srctree)/tools/include -I$(srctree)/arch/$(ARCH)/include/uapi -I$(srctree)/include/uapi
# include ARCH specific config
-include $(src-perf)/arch/$(ARCH)/Makefile

@@ -233,6 +234,7 @@ ifdef NO_LIBELF
NO_DEMANGLE := 1
NO_LIBUNWIND := 1
NO_LIBDW_DWARF_UNWIND := 1
+ NO_LIBBPF := 1
else
ifeq ($(feature-libelf), 0)
ifeq ($(feature-glibc), 1)
@@ -242,13 +244,14 @@ else
LIBC_SUPPORT := 1
endif
ifeq ($(LIBC_SUPPORT),1)
- msg := $(warning No libelf found, disables 'probe' tool, please install elfutils-libelf-devel/libelf-dev);
+ msg := $(warning No libelf found, disables 'probe' tool and BPF support in 'perf record', please install elfutils-libelf-devel/libelf-dev);

NO_LIBELF := 1
NO_DWARF := 1
NO_DEMANGLE := 1
NO_LIBUNWIND := 1
NO_LIBDW_DWARF_UNWIND := 1
+ NO_LIBBPF := 1
else
ifneq ($(filter s% -static%,$(LDFLAGS),),)
msg := $(error No static glibc found, please install glibc-static);
@@ -301,6 +304,13 @@ ifndef NO_LIBELF
$(call detected,CONFIG_DWARF)
endif # PERF_HAVE_DWARF_REGS
endif # NO_DWARF
+
+ ifndef NO_LIBBPF
+ ifeq ($(feature-bpf), 1)
+ CFLAGS += -DHAVE_LIBBPF_SUPPORT
+ $(call detected,CONFIG_LIBBPF)
+ endif
+ endif # NO_LIBBPF
endif # NO_LIBELF

ifeq ($(ARCH),powerpc)
@@ -316,6 +326,13 @@ ifndef NO_LIBUNWIND
endif
endif

+ifndef NO_LIBBPF
+ ifneq ($(feature-bpf), 1)
+ msg := $(warning BPF API too old. Please install recent kernel headers. BPF support in 'perf record' is disabled.)
+ NO_LIBBPF := 1
+ endif
+endif
+
dwarf-post-unwind := 1
dwarf-post-unwind-text := BUG

diff --git a/tools/perf/tests/make b/tools/perf/tests/make
index 729112f..89e0f3d 100644
--- a/tools/perf/tests/make
+++ b/tools/perf/tests/make
@@ -44,6 +44,7 @@ make_no_libnuma := NO_LIBNUMA=1
make_no_libaudit := NO_LIBAUDIT=1
make_no_libbionic := NO_LIBBIONIC=1
make_no_auxtrace := NO_AUXTRACE=1
+make_no_libbpf := NO_LIBBPF=1
make_tags := tags
make_cscope := cscope
make_help := help
@@ -65,7 +66,7 @@ make_static := LDFLAGS=-static
make_minimal := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1
make_minimal += NO_DEMANGLE=1 NO_LIBELF=1 NO_LIBUNWIND=1 NO_BACKTRACE=1
make_minimal += NO_LIBNUMA=1 NO_LIBAUDIT=1 NO_LIBBIONIC=1
-make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1
+make_minimal += NO_LIBDW_DWARF_UNWIND=1 NO_AUXTRACE=1 NO_LIBBPF=1

# $(run) contains all available tests
run := make_pure
@@ -93,6 +94,7 @@ run += make_no_libnuma
run += make_no_libaudit
run += make_no_libbionic
run += make_no_auxtrace
+run += make_no_libbpf
run += make_help
run += make_doc
run += make_perf_o
--
1.8.3.4

2015-07-09 12:44:35

by Wang Nan

[permalink] [raw]
Subject: [PATCH 18/39] perf record: Enable passing bpf object file to --event

By introducing new rules in tools/perf/util/parse-events.[ly], this
patch enables 'perf record --event bpf_file.o' to select events by
an eBPF object file. It calls parse_events_load_bpf() to load that
file, which uses bpf__prepare_load() and finally calls
bpf_object__open() for the object files.

Instead of introducing evsel to evlist during parsing, events
selected by eBPF object files are appended separately. The reason
is:

1. During parsing, the probing points have not been initialized.

2. Currently we are unable to call add_perf_probe_events() twice,
therefore we have to wait until all such events are collected,
then probe all points by one call.

The real probing and selecting is reside in following patches.

'bpf-loader.[ch]' are introduced in this patch. Which will be the
interface between perf and libbpf. bpf__prepare_load() resides in
bpf-loader.c. Dummy functions should be used because bpf-loader.c is
available only when CONFIG_LIBBPF is on.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/Build | 1 +
tools/perf/util/bpf-loader.c | 60 ++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/bpf-loader.h | 24 +++++++++++++++++
tools/perf/util/debug.c | 5 ++++
tools/perf/util/debug.h | 1 +
tools/perf/util/parse-events.c | 16 +++++++++++
tools/perf/util/parse-events.h | 2 ++
tools/perf/util/parse-events.l | 3 +++
tools/perf/util/parse-events.y | 18 ++++++++++++-
9 files changed, 129 insertions(+), 1 deletion(-)
create mode 100644 tools/perf/util/bpf-loader.c
create mode 100644 tools/perf/util/bpf-loader.h

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index bc24293..3357e5a 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -79,6 +79,7 @@ libperf-y += thread-stack.o
libperf-$(CONFIG_AUXTRACE) += auxtrace.o
libperf-y += parse-branch-options.o

+libperf-$(CONFIG_LIBBPF) += bpf-loader.o
libperf-$(CONFIG_LIBELF) += symbol-elf.o
libperf-$(CONFIG_LIBELF) += probe-event.o

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
new file mode 100644
index 0000000..7c750b6
--- /dev/null
+++ b/tools/perf/util/bpf-loader.c
@@ -0,0 +1,60 @@
+/*
+ * bpf-loader.c
+ *
+ * Copyright (C) 2015 Wang Nan <[email protected]>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+
+#include <bpf/libbpf.h>
+#include "perf.h"
+#include "debug.h"
+#include "bpf-loader.h"
+
+#define DEFINE_PRINT_FN(name, level) \
+static int libbpf_##name(const char *fmt, ...) \
+{ \
+ va_list args; \
+ int ret; \
+ \
+ va_start(args, fmt); \
+ ret = veprintf(level, verbose, pr_fmt(fmt), args);\
+ va_end(args); \
+ return ret; \
+}
+
+DEFINE_PRINT_FN(warning, 0)
+DEFINE_PRINT_FN(info, 0)
+DEFINE_PRINT_FN(debug, 1)
+
+static bool libbpf_initialized;
+
+int bpf__prepare_load(const char *filename)
+{
+ struct bpf_object *obj;
+
+ if (!libbpf_initialized)
+ libbpf_set_print(libbpf_warning,
+ libbpf_info,
+ libbpf_debug);
+
+ obj = bpf_object__open(filename);
+ if (!obj) {
+ pr_err("bpf: failed to load %s\n", filename);
+ return -EINVAL;
+ }
+
+ /*
+ * Throw object pointer away: it will be retrived using
+ * bpf_objects iterater.
+ */
+
+ return 0;
+}
+
+void bpf__clear(void)
+{
+ struct bpf_object *obj, *tmp;
+
+ bpf_object__for_each_safe(obj, tmp)
+ bpf_object__close(obj);
+}
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
new file mode 100644
index 0000000..39d8d1a
--- /dev/null
+++ b/tools/perf/util/bpf-loader.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2015, Wang Nan <[email protected]>
+ * Copyright (C) 2015, Huawei Inc.
+ */
+#ifndef __BPF_LOADER_H
+#define __BPF_LOADER_H
+
+#include <linux/compiler.h>
+#include "debug.h"
+
+#ifdef HAVE_LIBBPF_SUPPORT
+int bpf__prepare_load(const char *filename);
+
+void bpf__clear(void);
+#else
+static inline int bpf__prepare_load(const char *filename __maybe_unused)
+{
+ pr_err("ERROR: eBPF object loading is disabled during compiling.\n");
+ return -1;
+}
+
+static inline void bpf__clear(void) { }
+#endif
+#endif
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 2da5581..86d9c73 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -36,6 +36,11 @@ static int _eprintf(int level, int var, const char *fmt, va_list args)
return ret;
}

+int veprintf(int level, int var, const char *fmt, va_list args)
+{
+ return _eprintf(level, var, fmt, args);
+}
+
int eprintf(int level, int var, const char *fmt, ...)
{
va_list args;
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index caac2fd..8b9a088 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -50,6 +50,7 @@ void pr_stat(const char *fmt, ...);

int eprintf(int level, int var, const char *fmt, ...) __attribute__((format(printf, 3, 4)));
int eprintf_time(int level, int var, u64 t, const char *fmt, ...) __attribute__((format(printf, 4, 5)));
+int veprintf(int level, int var, const char *fmt, va_list args);

int perf_debug_option(const char *str);

diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index a71eeb2..a2829ef 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -19,6 +19,7 @@
#include "thread_map.h"
#include "cpumap.h"
#include "asm/bug.h"
+#include "bpf-loader.h"

#define MAX_NAME_LEN 100

@@ -475,6 +476,21 @@ int parse_events_add_tracepoint(struct list_head *list, int *idx,
return add_tracepoint_event(list, idx, sys, event);
}

+int parse_events_load_bpf(struct list_head *list __maybe_unused,
+ int *idx __maybe_unused,
+ char *bpf_file_name)
+{
+ /*
+ * Currently don't link any event to list. BPF object files
+ * should be saved to a seprated list and processed together.
+ *
+ * Things could be changed if we solve perf probe reentering
+ * problem. After that probe events file by file is possible.
+ * However, probing cost is still need to be considered.
+ */
+ return bpf__prepare_load(bpf_file_name);
+}
+
static int
parse_breakpoint_type(const char *type, struct perf_event_attr *attr)
{
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 131f29b..41b962a 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -114,6 +114,8 @@ int parse_events__modifier_group(struct list_head *list, char *event_mod);
int parse_events_name(struct list_head *list, char *name);
int parse_events_add_tracepoint(struct list_head *list, int *idx,
char *sys, char *event);
+int parse_events_load_bpf(struct list_head *list, int *idx,
+ char *bpf_file_name);
int parse_events_add_numeric(struct parse_events_evlist *data,
struct list_head *list,
u32 type, u64 config,
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 13cef3c..8328b28 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -115,6 +115,7 @@ do { \
group [^,{}/]*[{][^}]*[}][^,{}/]*
event_pmu [^,{}/]+[/][^/]*[/][^,{}/]*
event [^,{}/]+
+bpf_object .*\.(o|bpf)

num_dec [0-9]+
num_hex 0x[a-fA-F0-9]+
@@ -159,6 +160,7 @@ modifier_bp [rwx]{1,3}
}

{event_pmu} |
+{bpf_object} |
{event} {
BEGIN(INITIAL);
REWIND(1);
@@ -261,6 +263,7 @@ r{num_raw_hex} { return raw(yyscanner); }

{modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); }
{name} { return pmu_str_check(yyscanner); }
+{bpf_object} { return str(yyscanner, PE_BPF_OBJECT); }
"/" { BEGIN(config); return '/'; }
- { return '-'; }
, { BEGIN(event); return ','; }
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 591905a..481f3cd 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -42,6 +42,7 @@ static inc_group_count(struct list_head *list,
%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM
%token PE_EVENT_NAME
%token PE_NAME
+%token PE_BPF_OBJECT
%token PE_MODIFIER_EVENT PE_MODIFIER_BP
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
%token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP
@@ -53,6 +54,7 @@ static inc_group_count(struct list_head *list,
%type <num> PE_RAW
%type <num> PE_TERM
%type <str> PE_NAME
+%type <str> PE_BPF_OBJECT
%type <str> PE_NAME_CACHE_TYPE
%type <str> PE_NAME_CACHE_OP_RESULT
%type <str> PE_MODIFIER_EVENT
@@ -69,6 +71,7 @@ static inc_group_count(struct list_head *list,
%type <head> event_legacy_tracepoint
%type <head> event_legacy_numeric
%type <head> event_legacy_raw
+%type <head> event_bpf_file
%type <head> event_def
%type <head> event_mod
%type <head> event_name
@@ -198,7 +201,8 @@ event_def: event_pmu |
event_legacy_mem |
event_legacy_tracepoint sep_dc |
event_legacy_numeric sep_dc |
- event_legacy_raw sep_dc
+ event_legacy_raw sep_dc |
+ event_bpf_file

event_pmu:
PE_NAME '/' event_config '/'
@@ -420,6 +424,18 @@ PE_RAW
$$ = list;
}

+event_bpf_file:
+PE_BPF_OBJECT
+{
+ struct parse_events_evlist *data = _data;
+ struct list_head *list;
+
+ ALLOC_LIST(list);
+ ABORT_ON(parse_events_load_bpf(list, &data->idx, $1));
+ $$ = list;
+}
+
+
start_terms: event_config
{
struct parse_events_terms *data = _data;
--
1.8.3.4

2015-07-09 12:39:51

by Wang Nan

[permalink] [raw]
Subject: [PATCH 19/39] perf record: Compile scriptlets if pass '.c' to --event

This patch enables passing source files to --event directly using:

# perf record --event bpf-file.c command

This patch does following works:
1) Allow passing '.c' file to '--event'. parse_events_load_bpf() is
expanded to allow caller tell it whether the passed file is source
file or object.

2) llvm__compile_bpf() is called to compile the '.c' file, the result
is saved into memory. Use bpf_object__open_buffer() to load the
in-memory object.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/bpf-loader.c | 17 +++++++++++++++--
tools/perf/util/bpf-loader.h | 5 +++--
tools/perf/util/parse-events.c | 4 ++--
tools/perf/util/parse-events.h | 2 +-
tools/perf/util/parse-events.l | 3 +++
tools/perf/util/parse-events.y | 15 +++++++++++++--
6 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 7c750b6..61d3adf 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -9,6 +9,7 @@
#include "perf.h"
#include "debug.h"
#include "bpf-loader.h"
+#include "llvm-utils.h"

#define DEFINE_PRINT_FN(name, level) \
static int libbpf_##name(const char *fmt, ...) \
@@ -28,7 +29,7 @@ DEFINE_PRINT_FN(debug, 1)

static bool libbpf_initialized;

-int bpf__prepare_load(const char *filename)
+int bpf__prepare_load(const char *filename, bool source)
{
struct bpf_object *obj;

@@ -37,7 +38,19 @@ int bpf__prepare_load(const char *filename)
libbpf_info,
libbpf_debug);

- obj = bpf_object__open(filename);
+ if (source) {
+ void *obj_buf;
+ size_t obj_buf_sz;
+ int err;
+
+ err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz);
+ if (err)
+ return err;
+ obj = bpf_object__open_buffer(obj_buf, obj_buf_sz);
+ free(obj_buf);
+ } else
+ obj = bpf_object__open(filename);
+
if (!obj) {
pr_err("bpf: failed to load %s\n", filename);
return -EINVAL;
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 39d8d1a..5566be0 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -9,11 +9,12 @@
#include "debug.h"

#ifdef HAVE_LIBBPF_SUPPORT
-int bpf__prepare_load(const char *filename);
+int bpf__prepare_load(const char *filename, bool source);

void bpf__clear(void);
#else
-static inline int bpf__prepare_load(const char *filename __maybe_unused)
+static inline int bpf__prepare_load(const char *filename __maybe_unused,
+ bool source __maybe_unused)
{
pr_err("ERROR: eBPF object loading is disabled during compiling.\n");
return -1;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index a2829ef..8f3644f 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -478,7 +478,7 @@ int parse_events_add_tracepoint(struct list_head *list, int *idx,

int parse_events_load_bpf(struct list_head *list __maybe_unused,
int *idx __maybe_unused,
- char *bpf_file_name)
+ char *bpf_file_name, bool source)
{
/*
* Currently don't link any event to list. BPF object files
@@ -488,7 +488,7 @@ int parse_events_load_bpf(struct list_head *list __maybe_unused,
* problem. After that probe events file by file is possible.
* However, probing cost is still need to be considered.
*/
- return bpf__prepare_load(bpf_file_name);
+ return bpf__prepare_load(bpf_file_name, source);
}

static int
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 41b962a..5841d4f 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -115,7 +115,7 @@ int parse_events_name(struct list_head *list, char *name);
int parse_events_add_tracepoint(struct list_head *list, int *idx,
char *sys, char *event);
int parse_events_load_bpf(struct list_head *list, int *idx,
- char *bpf_file_name);
+ char *bpf_file_name, bool source);
int parse_events_add_numeric(struct parse_events_evlist *data,
struct list_head *list,
u32 type, u64 config,
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 8328b28..556fa21 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -116,6 +116,7 @@ group [^,{}/]*[{][^}]*[}][^,{}/]*
event_pmu [^,{}/]+[/][^/]*[/][^,{}/]*
event [^,{}/]+
bpf_object .*\.(o|bpf)
+bpf_source .*\.c

num_dec [0-9]+
num_hex 0x[a-fA-F0-9]+
@@ -161,6 +162,7 @@ modifier_bp [rwx]{1,3}

{event_pmu} |
{bpf_object} |
+{bpf_source} |
{event} {
BEGIN(INITIAL);
REWIND(1);
@@ -264,6 +266,7 @@ r{num_raw_hex} { return raw(yyscanner); }
{modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); }
{name} { return pmu_str_check(yyscanner); }
{bpf_object} { return str(yyscanner, PE_BPF_OBJECT); }
+{bpf_source} { return str(yyscanner, PE_BPF_SOURCE); }
"/" { BEGIN(config); return '/'; }
- { return '-'; }
, { BEGIN(event); return ','; }
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 481f3cd..eeb9768 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -42,7 +42,7 @@ static inc_group_count(struct list_head *list,
%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM
%token PE_EVENT_NAME
%token PE_NAME
-%token PE_BPF_OBJECT
+%token PE_BPF_OBJECT PE_BPF_SOURCE
%token PE_MODIFIER_EVENT PE_MODIFIER_BP
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
%token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP
@@ -55,6 +55,7 @@ static inc_group_count(struct list_head *list,
%type <num> PE_TERM
%type <str> PE_NAME
%type <str> PE_BPF_OBJECT
+%type <str> PE_BPF_SOURCE
%type <str> PE_NAME_CACHE_TYPE
%type <str> PE_NAME_CACHE_OP_RESULT
%type <str> PE_MODIFIER_EVENT
@@ -431,7 +432,17 @@ PE_BPF_OBJECT
struct list_head *list;

ALLOC_LIST(list);
- ABORT_ON(parse_events_load_bpf(list, &data->idx, $1));
+ ABORT_ON(parse_events_load_bpf(list, &data->idx, $1, false));
+ $$ = list;
+}
+|
+PE_BPF_SOURCE
+{
+ struct parse_events_evlist *data = _data;
+ struct list_head *list;
+
+ ALLOC_LIST(list);
+ ABORT_ON(parse_events_load_bpf(list, &data->idx, $1, true));
$$ = list;
}

--
1.8.3.4

2015-07-09 12:36:56

by Wang Nan

[permalink] [raw]
Subject: [PATCH 20/39] perf tools: Parse probe points of eBPF programs during preparation

This patch parses section name of each program, and creates
corresponding 'struct perf_probe_event' structure.

parse_perf_probe_command() is used to do the main parsing works.
Parsing result is stored into a global array. This is because
add_perf_probe_events() is non-reentrantable. In following patch,
add_perf_probe_events will be introduced to insert kprobes. It accepts
an array of 'struct perf_probe_event' and do all works in one call.

Define PERF_BPF_PROBE_GROUP as "perf_bpf_probe", which will be used
as group name of all eBPF probing points.

This patch utilizes bpf_program__set_private(), bind perf_probe_event
with bpf program by private field.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/bpf-loader.c | 126 ++++++++++++++++++++++++++++++++++++++++++-
tools/perf/util/bpf-loader.h | 2 +
2 files changed, 126 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 61d3adf..e33995d 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -10,6 +10,8 @@
#include "debug.h"
#include "bpf-loader.h"
#include "llvm-utils.h"
+#include "probe-event.h"
+#include "probe-finder.h"

#define DEFINE_PRINT_FN(name, level) \
static int libbpf_##name(const char *fmt, ...) \
@@ -29,9 +31,122 @@ DEFINE_PRINT_FN(debug, 1)

static bool libbpf_initialized;

+static struct perf_probe_event probe_event_array[MAX_PROBES];
+static size_t nr_probe_events;
+
+static struct perf_probe_event *
+alloc_perf_probe_event(void)
+{
+ struct perf_probe_event *pev;
+ int n = nr_probe_events;
+
+ if (n >= MAX_PROBES) {
+ pr_err("bpf: too many events, increase MAX_PROBES\n");
+ return NULL;
+ }
+
+ nr_probe_events = n + 1;
+ pev = &probe_event_array[n];
+ bzero(pev, sizeof(*pev));
+ return pev;
+}
+
+struct bpf_prog_priv {
+ struct perf_probe_event *pev;
+};
+
+static void
+bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
+ void *_priv)
+{
+ struct bpf_prog_priv *priv = _priv;
+
+ if (priv->pev)
+ clear_perf_probe_event(priv->pev);
+ free(priv);
+}
+
+static int
+config_bpf_program(struct bpf_program *prog)
+{
+ struct perf_probe_event *pev = alloc_perf_probe_event();
+ struct bpf_prog_priv *priv = NULL;
+ const char *config_str;
+ int err;
+
+ /* pr_err has been done by alloc_perf_probe_event */
+ if (!pev)
+ return -ENOMEM;
+
+ config_str = bpf_program__title(prog, false);
+ if (!config_str) {
+ pr_err("bpf: unable to get title for program\n");
+ return -EINVAL;
+ }
+
+ pr_debug("bpf: config program '%s'\n", config_str);
+ err = parse_perf_probe_command(config_str, pev);
+ if (err < 0) {
+ pr_err("bpf: '%s' is not a valid config string\n",
+ config_str);
+ /* parse failed, don't need clear pev. */
+ return -EINVAL;
+ }
+
+ if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) {
+ pr_err("bpf: '%s': group for event is set and not '%s'.\n",
+ config_str, PERF_BPF_PROBE_GROUP);
+ err = -EINVAL;
+ goto errout;
+ } else if (!pev->group)
+ pev->group = strdup(PERF_BPF_PROBE_GROUP);
+
+ if (!pev->group) {
+ pr_err("bpf: strdup failed\n");
+ err = -ENOMEM;
+ goto errout;
+ }
+
+ if (!pev->event) {
+ pr_err("bpf: '%s': event name is missing\n",
+ config_str);
+ err = -EINVAL;
+ goto errout;
+ }
+
+ pr_debug("bpf: config '%s' is ok\n", config_str);
+
+ priv = calloc(1, sizeof(*priv));
+ if (!priv) {
+ pr_err("bpf: failed to alloc memory\n");
+ err = -ENOMEM;
+ goto errout;
+ }
+
+ priv->pev = pev;
+
+ err = bpf_program__set_private(prog, priv,
+ bpf_prog_priv__clear);
+ if (err) {
+ pr_err("bpf: set program private failed\n");
+ err = -ENOMEM;
+ goto errout;
+ }
+ return 0;
+
+errout:
+ if (pev)
+ clear_perf_probe_event(pev);
+ if (priv)
+ free(priv);
+ return err;
+}
+
int bpf__prepare_load(const char *filename, bool source)
{
struct bpf_object *obj;
+ struct bpf_program *prog;
+ int err = 0;

if (!libbpf_initialized)
libbpf_set_print(libbpf_warning,
@@ -41,7 +156,6 @@ int bpf__prepare_load(const char *filename, bool source)
if (source) {
void *obj_buf;
size_t obj_buf_sz;
- int err;

err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz);
if (err)
@@ -56,12 +170,20 @@ int bpf__prepare_load(const char *filename, bool source)
return -EINVAL;
}

+ bpf_object__for_each_program(prog, obj) {
+ err = config_bpf_program(prog);
+ if (err)
+ goto errout;
+ }
+
/*
* Throw object pointer away: it will be retrived using
* bpf_objects iterater.
*/
-
return 0;
+errout:
+ bpf_object__close(obj);
+ return err;
}

void bpf__clear(void)
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 5566be0..5a3c954 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -8,6 +8,8 @@
#include <linux/compiler.h>
#include "debug.h"

+#define PERF_BPF_PROBE_GROUP "perf_bpf_probe"
+
#ifdef HAVE_LIBBPF_SUPPORT
int bpf__prepare_load(const char *filename, bool source);

--
1.8.3.4

2015-07-09 12:48:01

by Wang Nan

[permalink] [raw]
Subject: [PATCH 21/39] perf probe: Attach trace_probe_event with perf_probe_event

This patch drops struct __event_package structure. Instead, it adds
trace_probe_event into 'struct perf_probe_event'.

trace_probe_event information gives further patches a chance to access
actual probe points and actual arguments. Using them, bpf_loader will
be able to attach one bpf program to different probing points of a
inline functions (which has multiple probing points) and glob
functions. Moreover, by reading arguments information, bpf code for
reading those arguments can be generated.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/builtin-probe.c | 4 ++-
tools/perf/util/probe-event.c | 60 +++++++++++++++++++++----------------------
tools/perf/util/probe-event.h | 6 ++++-
3 files changed, 38 insertions(+), 32 deletions(-)

diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index b81cec3..826d452 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -496,7 +496,9 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
usage_with_options(probe_usage, options);
}

- ret = add_perf_probe_events(params.events, params.nevents);
+ ret = add_perf_probe_events(params.events,
+ params.nevents,
+ true);
if (ret < 0) {
pr_err_with_code(" Error: Failed to add events.", ret);
return ret;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 381f23a..083e8b4 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1930,6 +1930,9 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
struct perf_probe_arg_field *field, *next;
int i;

+ if (pev->ntevs)
+ cleanup_perf_probe_event(pev);
+
free(pev->event);
free(pev->group);
free(pev->target);
@@ -2778,61 +2781,58 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
return find_probe_trace_events_from_map(pev, tevs);
}

-struct __event_package {
- struct perf_probe_event *pev;
- struct probe_trace_event *tevs;
- int ntevs;
-};
-
-int add_perf_probe_events(struct perf_probe_event *pevs, int npevs)
+int cleanup_perf_probe_event(struct perf_probe_event *pev)
{
- int i, j, ret;
- struct __event_package *pkgs;
+ int i;

- ret = 0;
- pkgs = zalloc(sizeof(struct __event_package) * npevs);
+ if (!pev || !pev->ntevs)
+ return 0;

- if (pkgs == NULL)
- return -ENOMEM;
+ for (i = 0; i < pev->ntevs; i++)
+ clear_probe_trace_event(&pev->tevs[i]);
+
+ zfree(&pev->tevs);
+ pev->ntevs = 0;
+ return 0;
+}
+
+int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
+ bool cleanup)
+{
+ int i, ret;

ret = init_symbol_maps(pevs->uprobes);
- if (ret < 0) {
- free(pkgs);
+ if (ret < 0)
return ret;
- }

/* Loop 1: convert all events */
for (i = 0; i < npevs; i++) {
- pkgs[i].pev = &pevs[i];
/* Init kprobe blacklist if needed */
- if (!pkgs[i].pev->uprobes)
+ if (pevs[i].uprobes)
kprobe_blacklist__init();
/* Convert with or without debuginfo */
- ret = convert_to_probe_trace_events(pkgs[i].pev,
- &pkgs[i].tevs);
- if (ret < 0)
+ ret = convert_to_probe_trace_events(&pevs[i], &pevs[i].tevs);
+ if (ret < 0) {
+ cleanup = true;
goto end;
- pkgs[i].ntevs = ret;
+ }
+ pevs[i].ntevs = ret;
}
/* This just release blacklist only if allocated */
kprobe_blacklist__release();

/* Loop 2: add all events */
for (i = 0; i < npevs; i++) {
- ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs,
- pkgs[i].ntevs,
+ ret = __add_probe_trace_events(&pevs[i], pevs[i].tevs,
+ pevs[i].ntevs,
probe_conf.force_add);
if (ret < 0)
break;
}
end:
/* Loop 3: cleanup and free trace events */
- for (i = 0; i < npevs; i++) {
- for (j = 0; j < pkgs[i].ntevs; j++)
- clear_probe_trace_event(&pkgs[i].tevs[j]);
- zfree(&pkgs[i].tevs);
- }
- free(pkgs);
+ for (i = 0; cleanup && (i < npevs); i++)
+ cleanup_perf_probe_event(&pevs[i]);
exit_symbol_maps();

return ret;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 31db6ee..4b7a951 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -86,6 +86,8 @@ struct perf_probe_event {
bool uprobes; /* Uprobe event flag */
char *target; /* Target binary */
struct perf_probe_arg *args; /* Arguments */
+ struct probe_trace_event *tevs;
+ int ntevs;
};

/* Line range */
@@ -131,8 +133,10 @@ extern void line_range__clear(struct line_range *lr);
/* Initialize line range */
extern int line_range__init(struct line_range *lr);

-extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs);
+extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
+ bool cleanup);
extern int del_perf_probe_events(struct strfilter *filter);
+extern int cleanup_perf_probe_event(struct perf_probe_event *pev);
extern int show_perf_probe_events(struct strfilter *filter);
extern int show_line_range(struct line_range *lr, const char *module,
bool user);
--
1.8.3.4

2015-07-09 12:40:03

by Wang Nan

[permalink] [raw]
Subject: [PATCH 22/39] perf record: Probe at kprobe points

In this patch, kprobe points are created using add_perf_probe_events.
Since all events are already grouped together in an array, calling
add_perf_probe_events() creates all of them.

probe_conf.max_probes is set to MAX_PROBES to support glob matching.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/builtin-record.c | 18 ++++++++++++++++-
tools/perf/util/bpf-loader.c | 48 ++++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/bpf-loader.h | 4 ++++
3 files changed, 69 insertions(+), 1 deletion(-)

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 283fe96..33b213a 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -29,6 +29,7 @@
#include "util/data.h"
#include "util/auxtrace.h"
#include "util/parse-branch-options.h"
+#include "util/bpf-loader.h"

#include <unistd.h>
#include <sched.h>
@@ -1111,7 +1112,21 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
if (err)
return err;

- err = -ENOMEM;
+ /*
+ * bpf__probe must be called before symbol__init() because we
+ * need init_symbol_maps. If called after symbol__init,
+ * symbol_conf.sort_by_name won't take effect.
+ *
+ * bpf__unprobe() is safe even if bpf__probe() failed, and it
+ * also calls symbol__init. Therefore, goto out_symbol_exit
+ * is safe when probe failed.
+ */
+ err = bpf__probe();
+ if (err) {
+ pr_err("Probing at events in BPF object failed.\n");
+ pr_err("Try perf probe -d '*' to remove existing probe events.\n");
+ goto out_symbol_exit;
+ }

symbol__init(NULL);

@@ -1172,6 +1187,7 @@ out_symbol_exit:
perf_evlist__delete(rec->evlist);
symbol__exit();
auxtrace_record__free(rec->itr);
+ bpf__unprobe();
return err;
}

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index e33995d..10fe661 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -193,3 +193,51 @@ void bpf__clear(void)
bpf_object__for_each_safe(obj, tmp)
bpf_object__close(obj);
}
+
+static bool is_probing;
+
+int bpf__unprobe(void)
+{
+ struct strfilter *delfilter;
+ int ret;
+
+ if (!is_probing)
+ return 0;
+
+ delfilter = strfilter__new(PERF_BPF_PROBE_GROUP ":*", NULL);
+ if (!delfilter) {
+ pr_err("Failed to create delfilter when unprobing\n");
+ return -ENOMEM;
+ }
+
+ ret = del_perf_probe_events(delfilter);
+ strfilter__delete(delfilter);
+ if (ret < 0 && is_probing)
+ pr_err("Error: failed to delete events: %s\n",
+ strerror(-ret));
+ else
+ is_probing = false;
+ return ret < 0 ? ret : 0;
+}
+
+int bpf__probe(void)
+{
+ int err;
+
+ if (nr_probe_events <= 0)
+ return 0;
+
+ probe_conf.max_probes = MAX_PROBES;
+ /* Let add_perf_probe_events keeps probe_trace_event */
+ err = add_perf_probe_events(probe_event_array,
+ nr_probe_events,
+ false);
+
+ /* add_perf_probe_events return negative when fail */
+ if (err < 0)
+ pr_err("bpf probe: failed to probe events\n");
+ else
+ is_probing = true;
+
+ return err < 0 ? err : 0;
+}
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 5a3c954..374aec0 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -12,6 +12,8 @@

#ifdef HAVE_LIBBPF_SUPPORT
int bpf__prepare_load(const char *filename, bool source);
+int bpf__probe(void);
+int bpf__unprobe(void);

void bpf__clear(void);
#else
@@ -22,6 +24,8 @@ static inline int bpf__prepare_load(const char *filename __maybe_unused,
return -1;
}

+static inline int bpf__probe(void) { return 0; }
+static inline int bpf__unprobe(void) { return 0; }
static inline void bpf__clear(void) { }
#endif
#endif
--
1.8.3.4

2015-07-09 12:40:26

by Wang Nan

[permalink] [raw]
Subject: [PATCH 23/39] perf record: Load all eBPF object into kernel

This patch utilizes bpf_load_object() provided by libbpf to load all
objects into kernel.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/builtin-record.c | 12 ++++++++++++
tools/perf/util/bpf-loader.c | 19 +++++++++++++++++++
tools/perf/util/bpf-loader.h | 2 ++
3 files changed, 33 insertions(+)

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 33b213a..6d943a7 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1128,6 +1128,18 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
goto out_symbol_exit;
}

+ /*
+ * bpf__probe() also calls symbol__init() if there are probe
+ * events in bpf objects, so calling symbol_exit when failure
+ * is safe. If there is no probe event, bpf__load() always
+ * success.
+ */
+ err = bpf__load();
+ if (err) {
+ pr_err("Loading BPF programs failed\n");
+ goto out_symbol_exit;
+ }
+
symbol__init(NULL);

if (symbol_conf.kptr_restrict)
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 10fe661..6861560 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -241,3 +241,22 @@ int bpf__probe(void)

return err < 0 ? err : 0;
}
+
+int bpf__load(void)
+{
+ struct bpf_object *obj, *tmp;
+ int err = 0;
+
+ bpf_object__for_each_safe(obj, tmp) {
+ err = bpf_object__load(obj);
+ if (err) {
+ pr_err("bpf: load objects failed\n");
+ goto errout;
+ }
+ }
+ return 0;
+errout:
+ bpf_object__for_each_safe(obj, tmp)
+ bpf_object__unload(obj);
+ return err;
+}
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 374aec0..ae0dc9b 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -14,6 +14,7 @@
int bpf__prepare_load(const char *filename, bool source);
int bpf__probe(void);
int bpf__unprobe(void);
+int bpf__load(void);

void bpf__clear(void);
#else
@@ -26,6 +27,7 @@ static inline int bpf__prepare_load(const char *filename __maybe_unused,

static inline int bpf__probe(void) { return 0; }
static inline int bpf__unprobe(void) { return 0; }
+static inline int bpf__load(void) { return 0; }
static inline void bpf__clear(void) { }
#endif
#endif
--
1.8.3.4

2015-07-09 12:41:12

by Wang Nan

[permalink] [raw]
Subject: [PATCH 24/39] perf tools: Add bpf_fd field to evsel and config it

This patch adds a bpf_fd field to 'struct evsel' then introduces method
to config it. In bpf-loader, a bpf__foreach_tev() function is added,
Which calls the callback function for each 'struct probe_trace_event'
events for each bpf program with their file descriptors. In evlist.c,
perf_evlist__add_bpf() is introduced to add all bpf events into evlist.
The event names are found from probe_trace_event structure.
'perf record' calls perf_evlist__add_bpf().

Since bpf-loader.c will not be built if libbpf is turned off, an empty
bpf__foreach_tev() is defined in bpf-loader.h to avoid compiling
error.

This patch iterates over 'struct probe_trace_event' instead of
'struct probe_trace_event' during the loop for further patches, which
will generate multiple instances form one BPF program and install then
onto different 'struct probe_trace_event'.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/builtin-record.c | 6 ++++++
tools/perf/util/bpf-loader.c | 42 ++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/bpf-loader.h | 13 +++++++++++++
tools/perf/util/evlist.c | 41 +++++++++++++++++++++++++++++++++++++++++
tools/perf/util/evlist.h | 1 +
tools/perf/util/evsel.c | 1 +
tools/perf/util/evsel.h | 1 +
7 files changed, 105 insertions(+)

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 6d943a7..bd189b1 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1140,6 +1140,12 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
goto out_symbol_exit;
}

+ err = perf_evlist__add_bpf(rec->evlist);
+ if (err < 0) {
+ pr_err("Failed to add events from BPF object(s)\n");
+ goto out_symbol_exit;
+ }
+
symbol__init(NULL);

if (symbol_conf.kptr_restrict)
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 6861560..7a152ad 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -260,3 +260,45 @@ errout:
bpf_object__unload(obj);
return err;
}
+
+int bpf__foreach_tev(bpf_prog_iter_callback_t func, void *arg)
+{
+ struct bpf_object *obj, *tmp;
+ struct bpf_program *prog;
+ int err;
+
+ bpf_object__for_each_safe(obj, tmp) {
+ bpf_object__for_each_program(prog, obj) {
+ struct probe_trace_event *tev;
+ struct perf_probe_event *pev;
+ struct bpf_prog_priv *priv;
+ int i, fd;
+
+ err = bpf_program__get_private(prog,
+ (void **)&priv);
+ if (err || !priv) {
+ pr_err("bpf: failed to get private field\n");
+ return -EINVAL;
+ }
+
+ pev = priv->pev;
+ for (i = 0; i < pev->ntevs; i++) {
+ tev = &pev->tevs[i];
+
+ fd = bpf_program__fd(prog);
+ if (fd < 0) {
+ pr_err("bpf: failed to get file descriptor\n");
+ return fd;
+ }
+
+ err = func(tev, fd, arg);
+ if (err) {
+ pr_err("bpf: call back failed, stop iterate\n");
+ return err;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index ae0dc9b..ef9b3bb 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -6,10 +6,14 @@
#define __BPF_LOADER_H

#include <linux/compiler.h>
+#include "probe-event.h"
#include "debug.h"

#define PERF_BPF_PROBE_GROUP "perf_bpf_probe"

+typedef int (*bpf_prog_iter_callback_t)(struct probe_trace_event *tev,
+ int fd, void *arg);
+
#ifdef HAVE_LIBBPF_SUPPORT
int bpf__prepare_load(const char *filename, bool source);
int bpf__probe(void);
@@ -17,6 +21,8 @@ int bpf__unprobe(void);
int bpf__load(void);

void bpf__clear(void);
+
+int bpf__foreach_tev(bpf_prog_iter_callback_t func, void *arg);
#else
static inline int bpf__prepare_load(const char *filename __maybe_unused,
bool source __maybe_unused)
@@ -29,5 +35,12 @@ static inline int bpf__probe(void) { return 0; }
static inline int bpf__unprobe(void) { return 0; }
static inline int bpf__load(void) { return 0; }
static inline void bpf__clear(void) { }
+
+static inline int
+bpf__foreach_tev(bpf_prog_iter_callback_t func __maybe_unused,
+ void *arg __maybe_unused)
+{
+ return 0;
+}
#endif
#endif
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index f7d9c77..5a01c7f 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -14,6 +14,7 @@
#include "target.h"
#include "evlist.h"
#include "evsel.h"
+#include "bpf-loader.h"
#include "debug.h"
#include <unistd.h>

@@ -194,6 +195,46 @@ error:
return -ENOMEM;
}

+static int add_bpf_event(struct probe_trace_event *tev, int fd,
+ void *arg)
+{
+ struct perf_evlist *evlist = arg;
+ struct perf_evsel *pos;
+ struct list_head list;
+ int err, idx, entries;
+
+ pr_debug("add bpf event %s:%s and attach bpf program %d\n",
+ tev->group, tev->event, fd);
+ INIT_LIST_HEAD(&list);
+ idx = evlist->nr_entries;
+
+ pr_debug("adding %s:%s\n", tev->group, tev->event);
+ err = parse_events_add_tracepoint(&list, &idx, tev->group,
+ tev->event);
+ if (err) {
+ struct perf_evsel *evsel, *tmp;
+
+ pr_err("Failed to add BPF event %s:%s\n",
+ tev->group, tev->event);
+ list_for_each_entry_safe(evsel, tmp, &list, node) {
+ list_del(&evsel->node);
+ perf_evsel__delete(evsel);
+ }
+ return -EINVAL;
+ }
+
+ list_for_each_entry(pos, &list, node)
+ pos->bpf_fd = fd;
+ entries = idx - evlist->nr_entries;
+ perf_evlist__splice_list_tail(evlist, &list, entries);
+ return 0;
+}
+
+int perf_evlist__add_bpf(struct perf_evlist *evlist)
+{
+ return bpf__foreach_tev(add_bpf_event, evlist);
+}
+
static int perf_evlist__add_attrs(struct perf_evlist *evlist,
struct perf_event_attr *attrs, size_t nr_attrs)
{
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 037633c..4e3b6b0 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -72,6 +72,7 @@ void perf_evlist__delete(struct perf_evlist *evlist);

void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry);
int perf_evlist__add_default(struct perf_evlist *evlist);
+int perf_evlist__add_bpf(struct perf_evlist *evlist);
int __perf_evlist__add_default_attrs(struct perf_evlist *evlist,
struct perf_event_attr *attrs, size_t nr_attrs);

diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 83c0803..fe80047 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -206,6 +206,7 @@ void perf_evsel__init(struct perf_evsel *evsel,
evsel->leader = evsel;
evsel->unit = "";
evsel->scale = 1.0;
+ evsel->bpf_fd = -1;
INIT_LIST_HEAD(&evsel->node);
perf_evsel__object.init(evsel);
evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index fe9f327..4bcd629 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -86,6 +86,7 @@ struct perf_evsel {
unsigned long *per_pkg_mask;
struct perf_evsel *leader;
char *group_name;
+ int bpf_fd;
};

union u64_swap {
--
1.8.3.4

2015-07-09 12:45:11

by Wang Nan

[permalink] [raw]
Subject: [PATCH 25/39] perf tools: Attach eBPF program to perf event

This is the final patch which makes basic BPF filter work. After
applying this patch, users are allowed to use BPF filter like:

# perf record --event ./hello_world.c ls

In this patch PERF_EVENT_IOC_SET_BPF ioctl is used to attach eBPF
program to a newly created perf event. The file descriptor of the
eBPF program is passed to perf record using previous patches, and
stored into evsel->bpf_fd.

It is possible that different perf event are created for one kprobe
events for different CPUs. In this case, when trying to call the
ioctl, EEXIST will be return. This patch doesn't treat it as an error.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/evsel.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index fe80047..73c17f3 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1216,6 +1216,22 @@ retry_open:
err);
goto try_fallback;
}
+
+ if (evsel->bpf_fd >= 0) {
+ int evt_fd = FD(evsel, cpu, thread);
+ int bpf_fd = evsel->bpf_fd;
+
+ err = ioctl(evt_fd,
+ PERF_EVENT_IOC_SET_BPF,
+ bpf_fd);
+ if (err && errno != EEXIST) {
+ pr_err("failed to attach bpf fd %d: %s\n",
+ bpf_fd, strerror(errno));
+ err = -EINVAL;
+ goto out_close;
+ }
+ }
+
set_rlimit = NO_CHANGE;

/*
--
1.8.3.4

2015-07-09 12:44:03

by Wang Nan

[permalink] [raw]
Subject: [PATCH 26/39] perf tools: Suppress probing messages when probing by BPF loading

This patch suppress message output by add_perf_probe_events() and
del_perf_probe_events() if they are triggered by BPF loading. Before
this patch, when using 'perf record' with BPF object/source as event
selector, following message will be output:

Added new event:
perf_bpf_probe:lock_page_ret (on __lock_page%return)
You can now use it in all perf tools, such as:
perf record -e perf_bpf_probe:lock_page_ret -aR sleep 1
...
Removed event: perf_bpf_probe:lock_page_ret

Which is misleading, especially 'use it in all perf tools' because they
will be removed after 'pref record' exit.

In this patch, a 'silent' field is appended into probe_conf to control
output. bpf__{,un}probe() set it to true when calling
{add,del}_perf_probe_events().

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/bpf-loader.c | 6 ++++++
tools/perf/util/probe-event.c | 22 ++++++++++++++++------
tools/perf/util/probe-event.h | 1 +
3 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 7a152ad..bd2ebe1 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -199,6 +199,7 @@ static bool is_probing;
int bpf__unprobe(void)
{
struct strfilter *delfilter;
+ bool old_silent = probe_conf.silent;
int ret;

if (!is_probing)
@@ -210,7 +211,9 @@ int bpf__unprobe(void)
return -ENOMEM;
}

+ probe_conf.silent = true;
ret = del_perf_probe_events(delfilter);
+ probe_conf.silent = old_silent;
strfilter__delete(delfilter);
if (ret < 0 && is_probing)
pr_err("Error: failed to delete events: %s\n",
@@ -223,15 +226,18 @@ int bpf__unprobe(void)
int bpf__probe(void)
{
int err;
+ bool old_silent = probe_conf.silent;

if (nr_probe_events <= 0)
return 0;

+ probe_conf.silent = true;
probe_conf.max_probes = MAX_PROBES;
/* Let add_perf_probe_events keeps probe_trace_event */
err = add_perf_probe_events(probe_event_array,
nr_probe_events,
false);
+ probe_conf.silent = old_silent;

/* add_perf_probe_events return negative when fail */
if (err < 0)
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 083e8b4..b9573c5 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -51,7 +51,9 @@
#define PERFPROBE_GROUP "probe"

bool probe_event_dry_run; /* Dry run flag */
-struct probe_conf probe_conf;
+struct probe_conf probe_conf = {
+ .silent = false,
+};

#define semantic_error(msg ...) pr_err("Semantic error :" msg)

@@ -2250,10 +2252,12 @@ static int show_perf_probe_event(const char *group, const char *event,

ret = perf_probe_event__sprintf(group, event, pev, module, &buf);
if (ret >= 0) {
- if (use_stdout)
+ if (use_stdout && !probe_conf.silent)
printf("%s\n", buf.buf);
- else
+ else if (!probe_conf.silent)
pr_info("%s\n", buf.buf);
+ else
+ pr_debug("%s\n", buf.buf);
}
strbuf_release(&buf);

@@ -2512,7 +2516,10 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,

safename = (pev->point.function && !strisglob(pev->point.function));
ret = 0;
- pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
+ if (!probe_conf.silent)
+ pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
+ else
+ pr_debug("Added new event%s\n", (ntevs > 1) ? "s:" : ":");
for (i = 0; i < ntevs; i++) {
tev = &tevs[i];
/* Skip if the symbol is out of .text or blacklisted */
@@ -2569,7 +2576,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
warn_uprobe_event_compat(tev);

/* Note that it is possible to skip all events because of blacklist */
- if (ret >= 0 && event) {
+ if (ret >= 0 && event && !probe_conf.silent) {
/* Show how to use the event. */
pr_info("\nYou can now use it in all perf tools, such as:\n\n");
pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", group, event);
@@ -2865,7 +2872,10 @@ static int __del_trace_probe_event(int fd, struct str_node *ent)
goto error;
}

- pr_info("Removed event: %s\n", ent->s);
+ if (!probe_conf.silent)
+ pr_info("Removed event: %s\n", ent->s);
+ else
+ pr_debug("Removed event: %s\n", ent->s);
return 0;
error:
pr_warning("Failed to delete event: %s\n",
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 4b7a951..116b0aa 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -13,6 +13,7 @@ struct probe_conf {
bool force_add;
bool no_inlines;
int max_probes;
+ bool silent;
};
extern struct probe_conf probe_conf;
extern bool probe_event_dry_run;
--
1.8.3.4

2015-07-09 12:43:44

by Wang Nan

[permalink] [raw]
Subject: [PATCH 27/39] perf record: Add clang options for compiling BPF scripts

Although previous patch allows setting BPF compiler related options in
perfconfig, on some ad-hoc situation it still requires passing options
through cmdline. This patch introduces 2 options to 'perf record' for
this propose: --clang-path and --clang-opt.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/builtin-record.c | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index bd189b1..e89c045 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -30,6 +30,7 @@
#include "util/auxtrace.h"
#include "util/parse-branch-options.h"
#include "util/bpf-loader.h"
+#include "util/llvm-utils.h"

#include <unistd.h>
#include <sched.h>
@@ -1073,6 +1074,12 @@ struct option __record_options[] = {
"opts", "AUX area tracing Snapshot Mode", ""),
OPT_UINTEGER(0, "proc-map-timeout", &record.opts.proc_map_timeout,
"per thread proc mmap processing timeout in ms"),
+#ifdef HAVE_LIBBPF_SUPPORT
+ OPT_STRING(0, "clang-path", &llvm_param.clang_path, "clang path",
+ "clang binary to use for compiling BPF scriptlets"),
+ OPT_STRING(0, "clang-opt", &llvm_param.clang_opt, "clang options",
+ "options passed to clang when compiling BPF scriptlets"),
+#endif
OPT_END()
};

--
1.8.3.4

2015-07-09 12:41:18

by Wang Nan

[permalink] [raw]
Subject: [PATCH 28/39] bpf tools: Load a program with different instances using preprocessor

In this patch, caller of libbpf is able to control the loaded programs
by installing a preprocessor callback for a BPF program. With
preprocessor, different instances can be created from one BPF program.

This patch will be used by perf to generate different prologue for
different 'struct probe_trace_event' instances matched by one
'struct perf_probe_event'.

bpf_program__set_prep() is added to support this feature. Caller
should pass libbpf the number of instances should be created and a
preprocessor function which will be called when doing real loading.
The callback should return instructions arrays for each instances.

fd field in bpf_programs is replaced by instance, which has an nr field
and fds array. bpf_program__nth_fd() is introduced for read fd of
instances. Old interface bpf_program__fd() is reimplemented by
returning the first fd.

Signed-off-by: Wang Nan <[email protected]>
Signed-off-by: He Kuang <[email protected]>
---
tools/lib/bpf/libbpf.c | 138 +++++++++++++++++++++++++++++++++++++++++++++----
tools/lib/bpf/libbpf.h | 22 ++++++++
2 files changed, 151 insertions(+), 9 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index b37aab1..6fcd042 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -98,7 +98,11 @@ struct bpf_program {
} *reloc_desc;
int nr_reloc;

- int fd;
+ struct {
+ int nr;
+ int *fds;
+ } instance;
+ bpf_program_prep_t preprocessor;

struct bpf_object *obj;
void *priv;
@@ -152,10 +156,19 @@ struct bpf_object {

static void bpf_program__unload(struct bpf_program *prog)
{
+ int i;
+
if (!prog)
return;

- zclose(prog->fd);
+ if (prog->instance.nr < 0)
+ pr_warning("Internal error when unloading: instance is %d\n",
+ prog->instance.nr);
+ else
+ for (i = 0; i < prog->instance.nr; i++)
+ zclose(prog->instance.fds[i]);
+ prog->instance.nr = -1;
+ zfree(&prog->instance.fds);
}

static void bpf_program__clear(struct bpf_program *prog)
@@ -206,7 +219,8 @@ __bpf_program__new(void *data, size_t size, char *name, int idx,
memcpy(prog->insns, data,
prog->insns_cnt * sizeof(struct bpf_insn));
prog->idx = idx;
- prog->fd = -1;
+ prog->instance.fds = NULL;
+ prog->instance.nr = -1;

return 0;
errout:
@@ -798,13 +812,71 @@ static int
bpf_program__load(struct bpf_program *prog,
char *license, u32 kern_version)
{
- int err, fd;
+ int err = 0, fd, i;
+
+ if (prog->instance.nr < 0 || !prog->instance.fds) {
+ if (prog->preprocessor) {
+ pr_warning("Internal error: can't load program '%s'\n",
+ prog->section_name);
+ return -EINVAL;
+ }

- err = load_program(prog->insns, prog->insns_cnt,
- license, kern_version, &fd);
- if (!err)
- prog->fd = fd;
+ prog->instance.fds = malloc(sizeof(int));
+ if (prog->instance.fds) {
+ pr_warning("No enough memory for fds\n");
+ return -ENOMEM;
+ }
+ prog->instance.nr = 1;
+ prog->instance.fds[0] = -1;
+ }
+
+ if (!prog->preprocessor) {
+ if (prog->instance.nr != 1)
+ pr_warning("Program '%s' inconsistent: nr(%d) not 1\n",
+ prog->section_name, prog->instance.nr);
+
+ err = load_program(prog->insns, prog->insns_cnt,
+ license, kern_version, &fd);
+ if (!err)
+ prog->instance.fds[0] = fd;
+ goto out;
+ }
+
+ for (i = 0; i < prog->instance.nr; i++) {
+ struct bpf_prog_prep_result result;
+ bpf_program_prep_t preprocessor = prog->preprocessor;
+
+ bzero(&result, sizeof(result));
+ err = preprocessor(prog, i, prog->insns,
+ prog->insns_cnt, &result);
+ if (err) {
+ pr_warning("Preprocessing %dth instance of program '%s' failed\n",
+ i, prog->section_name);
+ goto out;
+ }
+
+ if (!result.new_insn_ptr || !result.new_insn_cnt) {
+ pr_debug("Skip loading %dth instance of program '%s'\n",
+ i, prog->section_name);
+ prog->instance.fds[i] = -1;
+ continue;
+ }
+
+ err = load_program(result.new_insn_ptr,
+ result.new_insn_cnt,
+ license, kern_version, &fd);
+
+ if (err) {
+ pr_warning("Loading %dth instance of program '%s' failed\n",
+ i, prog->section_name);
+ goto out;
+ }

+ if (result.pfd)
+ *result.pfd = fd;
+ prog->instance.fds[i] = fd;
+ }
+out:
if (err)
pr_warning("failed to load program '%s'\n",
prog->section_name);
@@ -1036,5 +1108,53 @@ const char *bpf_program__title(struct bpf_program *prog, bool dup)

int bpf_program__fd(struct bpf_program *prog)
{
- return prog->fd;
+ return bpf_program__nth_fd(prog, 0);
+}
+
+int bpf_program__set_prep(struct bpf_program *prog, int nr_instance,
+ bpf_program_prep_t prep)
+{
+ int *instance_fds;
+
+ if (nr_instance <= 0 || !prep)
+ return -EINVAL;
+
+ if (prog->instance.nr > 0 || prog->instance.fds) {
+ pr_warning("Can't set pre-processor after loading\n");
+ return -EINVAL;
+ }
+
+ instance_fds = malloc(sizeof(int) * nr_instance);
+ if (!instance_fds) {
+ pr_warning("alloc memory failed for instance of fds\n");
+ return -ENOMEM;
+ }
+
+ /* fill all fd with -1 */
+ memset(instance_fds, 0xff, sizeof(int) * nr_instance);
+
+ prog->instance.nr = nr_instance;
+ prog->instance.fds = instance_fds;
+ prog->preprocessor = prep;
+ return 0;
+}
+
+int bpf_program__nth_fd(struct bpf_program *prog, int n)
+{
+ int fd;
+
+ if (n >= prog->instance.nr || n < 0) {
+ pr_warning("Can't get the %dth fd from program %s: only %d instances\n",
+ n, prog->section_name, prog->instance.nr);
+ return -EINVAL;
+ }
+
+ fd = prog->instance.fds[n];
+ if (fd < 0) {
+ pr_warning("%dth instance of program '%s' is invalid\n",
+ n, prog->section_name);
+ return -ENOENT;
+ }
+
+ return fd;
}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index ea8adc2..9fa7b09 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -65,6 +65,28 @@ const char *bpf_program__title(struct bpf_program *prog, bool dup);

int bpf_program__fd(struct bpf_program *prog);

+struct bpf_insn;
+struct bpf_prog_prep_result {
+ /*
+ * If not NULL, load new instruction array.
+ * If set to NULL, don't load this instance.
+ */
+ struct bpf_insn *new_insn_ptr;
+ int new_insn_cnt;
+
+ /* If not NULL, result fd is set to it */
+ int *pfd;
+};
+
+typedef int (*bpf_program_prep_t)(struct bpf_program *, int n,
+ struct bpf_insn *, int insn_cnt,
+ struct bpf_prog_prep_result *res);
+
+int bpf_program__set_prep(struct bpf_program *prog, int nr_instance,
+ bpf_program_prep_t prep);
+
+int bpf_program__nth_fd(struct bpf_program *prog, int n);
+
/*
* We don't need __attribute__((packed)) now since it is
* unnecessary for 'bpf_map_def' because they are all aligned.
--
1.8.3.4

2015-07-09 12:44:50

by Wang Nan

[permalink] [raw]
Subject: [PATCH 29/39] perf tools: Fix probe-event.h include

Commit 7b6ff0bdbf4f7f429c2116cca92a6d171217449e ("perf probe ppc64le:
Fixup function entry if using kallsyms lookup") adds 'struct map' into
probe-event.h but not include "util/map.h" in it. This patch fixes it.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/probe-event.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 116b0aa..e9b7190 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -5,6 +5,7 @@
#include "intlist.h"
#include "strlist.h"
#include "strfilter.h"
+#include "map.h"

/* Probe related configurations */
struct probe_conf {
--
1.8.3.4

2015-07-09 12:42:31

by Wang Nan

[permalink] [raw]
Subject: [PATCH 30/39] perf probe: Reset args and nargs for probe_trace_event when failure

When failure occures in add_probe_trace_event(), args in
probe_trace_event is incomplete. Since information in it may be used
in further, this patch frees the allocated memory and set it to NULL
to avoid dangling pointer.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/probe-finder.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 2da65a7..f533d19 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -1243,6 +1243,10 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)

end:
free(args);
+ if (ret) {
+ tev->nargs = 0;
+ zfree(&tev->args);
+ }
return ret;
}

--
1.8.3.4

2015-07-09 12:44:10

by Wang Nan

[permalink] [raw]
Subject: [PATCH 31/39] perf tools: Move linux/filter.h to tools/include

From: He Kuang <[email protected]>

This patch moves filter.h from include/linux/kernel.h to
tools/include/linux/filter.h to enable other libraries use macros in
it, like libbpf which will be introduced by further patches. Currenty,
the moved filter.h only contains the useful macros needed by libbpf
for not introducing too much dependence.

MANIFEST is also updated for 'make perf-*-src-pkg'.

One change:
imm field of BPF_EMIT_CALL becomes ((FUNC) - BPF_FUNC_unspec) to
suit user space code generator.

Signed-off-by: He Kuang <[email protected]>
Signed-off-by: Wang Nan <[email protected]>
---
tools/include/linux/filter.h | 237 +++++++++++++++++++++++++++++++++++++++++++
tools/perf/MANIFEST | 1 +
2 files changed, 238 insertions(+)
create mode 100644 tools/include/linux/filter.h

diff --git a/tools/include/linux/filter.h b/tools/include/linux/filter.h
new file mode 100644
index 0000000..11d2b1c
--- /dev/null
+++ b/tools/include/linux/filter.h
@@ -0,0 +1,237 @@
+/*
+ * Linux Socket Filter Data Structures
+ */
+#ifndef __TOOLS_LINUX_FILTER_H
+#define __TOOLS_LINUX_FILTER_H
+
+#include <linux/bpf.h>
+
+/* ArgX, context and stack frame pointer register positions. Note,
+ * Arg1, Arg2, Arg3, etc are used as argument mappings of function
+ * calls in BPF_CALL instruction.
+ */
+#define BPF_REG_ARG1 BPF_REG_1
+#define BPF_REG_ARG2 BPF_REG_2
+#define BPF_REG_ARG3 BPF_REG_3
+#define BPF_REG_ARG4 BPF_REG_4
+#define BPF_REG_ARG5 BPF_REG_5
+#define BPF_REG_CTX BPF_REG_6
+#define BPF_REG_FP BPF_REG_10
+
+/* Additional register mappings for converted user programs. */
+#define BPF_REG_A BPF_REG_0
+#define BPF_REG_X BPF_REG_7
+#define BPF_REG_TMP BPF_REG_8
+
+/* BPF program can access up to 512 bytes of stack space. */
+#define MAX_BPF_STACK 512
+
+/* Helper macros for filter block array initializers. */
+
+/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */
+
+#define BPF_ALU64_REG(OP, DST, SRC) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = 0, \
+ .imm = 0 })
+
+#define BPF_ALU32_REG(OP, DST, SRC) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU | BPF_OP(OP) | BPF_X, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = 0, \
+ .imm = 0 })
+
+/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */
+
+#define BPF_ALU64_IMM(OP, DST, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = IMM })
+
+#define BPF_ALU32_IMM(OP, DST, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU | BPF_OP(OP) | BPF_K, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = IMM })
+
+/* Endianness conversion, cpu_to_{l,b}e(), {l,b}e_to_cpu() */
+
+#define BPF_ENDIAN(TYPE, DST, LEN) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU | BPF_END | BPF_SRC(TYPE), \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = LEN })
+
+/* Short form of mov, dst_reg = src_reg */
+
+#define BPF_MOV64_REG(DST, SRC) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU64 | BPF_MOV | BPF_X, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = 0, \
+ .imm = 0 })
+
+#define BPF_MOV32_REG(DST, SRC) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU | BPF_MOV | BPF_X, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = 0, \
+ .imm = 0 })
+
+/* Short form of mov, dst_reg = imm32 */
+
+#define BPF_MOV64_IMM(DST, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU64 | BPF_MOV | BPF_K, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = IMM })
+
+#define BPF_MOV32_IMM(DST, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU | BPF_MOV | BPF_K, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = IMM })
+
+/* Short form of mov based on type,
+ * BPF_X: dst_reg = src_reg, BPF_K: dst_reg = imm32
+ */
+
+#define BPF_MOV64_RAW(TYPE, DST, SRC, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU64 | BPF_MOV | BPF_SRC(TYPE), \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = 0, \
+ .imm = IMM })
+
+#define BPF_MOV32_RAW(TYPE, DST, SRC, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU | BPF_MOV | BPF_SRC(TYPE), \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = 0, \
+ .imm = IMM })
+
+/* Direct packet access, R0 = *(uint *) (skb->data + imm32) */
+
+#define BPF_LD_ABS(SIZE, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \
+ .dst_reg = 0, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = IMM })
+
+/* Indirect packet access, R0 = *(uint *) (skb->data + src_reg + imm32) */
+
+#define BPF_LD_IND(SIZE, SRC, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_LD | BPF_SIZE(SIZE) | BPF_IND, \
+ .dst_reg = 0, \
+ .src_reg = SRC, \
+ .off = 0, \
+ .imm = IMM })
+
+/* Memory load, dst_reg = *(uint *) (src_reg + off16) */
+
+#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \
+ ((struct bpf_insn) { \
+ .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = OFF, \
+ .imm = 0 })
+
+/* Memory store, *(uint *) (dst_reg + off16) = src_reg */
+
+#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \
+ ((struct bpf_insn) { \
+ .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = OFF, \
+ .imm = 0 })
+
+/* Memory store, *(uint *) (dst_reg + off16) = imm32 */
+
+#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = OFF, \
+ .imm = IMM })
+
+/* Conditional jumps against registers,
+ * if (dst_reg 'op' src_reg) goto pc + off16
+ */
+
+#define BPF_JMP_REG(OP, DST, SRC, OFF) \
+ ((struct bpf_insn) { \
+ .code = BPF_JMP | BPF_OP(OP) | BPF_X, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = OFF, \
+ .imm = 0 })
+
+/* Conditional jumps against immediates,
+ * if (dst_reg 'op' imm32) goto pc + off16
+ */
+
+#define BPF_JMP_IMM(OP, DST, IMM, OFF) \
+ ((struct bpf_insn) { \
+ .code = BPF_JMP | BPF_OP(OP) | BPF_K, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = OFF, \
+ .imm = IMM })
+
+/* Function call */
+
+#define BPF_EMIT_CALL(FUNC) \
+ ((struct bpf_insn) { \
+ .code = BPF_JMP | BPF_CALL, \
+ .dst_reg = 0, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = ((FUNC) - BPF_FUNC_unspec) })
+
+/* Raw code statement block */
+
+#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \
+ ((struct bpf_insn) { \
+ .code = CODE, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = OFF, \
+ .imm = IMM })
+
+/* Program exit */
+
+#define BPF_EXIT_INSN() \
+ ((struct bpf_insn) { \
+ .code = BPF_JMP | BPF_EXIT, \
+ .dst_reg = 0, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = 0 })
+
+#endif /* __TOOLS_LINUX_FILTER_H */
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST
index 1b42f7c..b0b3998 100644
--- a/tools/perf/MANIFEST
+++ b/tools/perf/MANIFEST
@@ -43,6 +43,7 @@ tools/include/linux/compiler.h
tools/include/linux/export.h
tools/include/linux/hash.h
tools/include/linux/kernel.h
+tools/include/linux/filter.h
tools/include/linux/list.h
tools/include/linux/log2.h
tools/include/linux/poison.h
--
1.8.3.4

2015-07-09 12:43:07

by Wang Nan

[permalink] [raw]
Subject: [PATCH 32/39] perf tools: Add BPF_PROLOGUE config options for further patches

If both LIBBPF and DWARF is detected, it is possible to create prologue
for eBPF programs to help them accessing kernel data. HAVE_BPF_PROLOGUE
and CONFIG_BPF_PROLOGUE is added as flags for this feature.

PERF_HAVE_ARCH_GET_REG_OFFSET indicates an architecture supports
converting name of a register to its offset in 'struct pt_regs'.
Without this support, BPF_PROLOGUE should be turned off.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/config/Makefile | 12 ++++++++++++
tools/perf/util/include/dwarf-regs.h | 7 +++++++
2 files changed, 19 insertions(+)

diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 52722cf..d776f32 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -310,6 +310,18 @@ ifndef NO_LIBELF
CFLAGS += -DHAVE_LIBBPF_SUPPORT
$(call detected,CONFIG_LIBBPF)
endif
+
+ ifndef NO_DWARF
+ ifneq ($(origin PERF_HAVE_ARCH_GET_REG_INFO), undefined)
+ CFLAGS += -DHAVE_BPF_PROLOGUE
+ $(call detected,CONFIG_BPF_PROLOGUE)
+ else
+ msg := $(warning BPF prologue is not supported by architecture $(ARCH));
+ endif
+ else
+ msg := $(warning DWARF support is off, BPF prologue is disabled);
+ endif
+
endif # NO_LIBBPF
endif # NO_LIBELF

diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index 8f14965..3dda083 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -5,4 +5,11 @@
const char *get_arch_regstr(unsigned int n);
#endif

+#ifdef HAVE_BPF_PROLOGUE
+/*
+ * Arch should support fetching the offset of a register in pt_regs
+ * by its name.
+ */
+int arch_get_reg_info(const char *name, int *offset);
+#endif
#endif
--
1.8.3.4

2015-07-09 12:37:10

by Wang Nan

[permalink] [raw]
Subject: [PATCH 33/39] perf tools: Introduce arch_get_reg_info() for x86

From: He Kuang <[email protected]>

arch_get_reg_info() is a helper function which converts register name
like "%rax" to offset of a register in 'struct pt_regs', which is
required by BPF prologue generator.

This patch replaces original string table by a 'struct reg_info' table,
which records offset of registers according to its name.

For x86, since there are two sub-archs (x86_32 and x86_64) but we can
only get pt_regs for the arch we are currently on, this patch fills
offset with '-1' for another sub-arch. This introduces a limitation to
perf prologue that, we are unable to generate prologue on a x86_32
compiled perf for BPF programs targeted on x86_64 kernel. This
limitation is acceptable, because this is a very rare usecase.

Signed-off-by: Wang Nan <[email protected]>
Signed-off-by: He Kuang <[email protected]>
---
tools/perf/arch/x86/Makefile | 1 +
tools/perf/arch/x86/util/Build | 2 +
tools/perf/arch/x86/util/dwarf-regs.c | 104 ++++++++++++++++++++++++----------
3 files changed, 78 insertions(+), 29 deletions(-)

diff --git a/tools/perf/arch/x86/Makefile b/tools/perf/arch/x86/Makefile
index 21322e0..a84a6f6f 100644
--- a/tools/perf/arch/x86/Makefile
+++ b/tools/perf/arch/x86/Makefile
@@ -2,3 +2,4 @@ ifndef NO_DWARF
PERF_HAVE_DWARF_REGS := 1
endif
HAVE_KVM_STAT_SUPPORT := 1
+PERF_HAVE_ARCH_GET_REG_INFO := 1
diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build
index cfbccc4..d6eeee8 100644
--- a/tools/perf/arch/x86/util/Build
+++ b/tools/perf/arch/x86/util/Build
@@ -2,6 +2,8 @@ libperf-y += header.o
libperf-y += tsc.o
libperf-y += kvm-stat.o

+# BPF_PROLOGUE also need dwarf-regs.o. However, if CONFIG_BPF_PROLOGUE
+# is true, CONFIG_DWARF must true.
libperf-$(CONFIG_DWARF) += dwarf-regs.o

libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
diff --git a/tools/perf/arch/x86/util/dwarf-regs.c b/tools/perf/arch/x86/util/dwarf-regs.c
index be22dd4..9928caf 100644
--- a/tools/perf/arch/x86/util/dwarf-regs.c
+++ b/tools/perf/arch/x86/util/dwarf-regs.c
@@ -22,44 +22,67 @@

#include <stddef.h>
#include <dwarf-regs.h>
+#include <string.h>
+#include <linux/ptrace.h>
+#include <linux/kernel.h> /* for offsetof */
+#include <util/bpf-loader.h>
+
+struct reg_info {
+ const char *name; /* Reg string in debuginfo */
+ int offset; /* Reg offset in struct pt_regs */
+};

/*
* Generic dwarf analysis helpers
*/
-
+/*
+ * x86_64 compiling can't access pt_regs for x86_32, so fill offset
+ * with -1.
+ */
+#ifdef __x86_64__
+# define REG_INFO(n, f) { .name = n, .offset = -1, }
+#else
+# define REG_INFO(n, f) { .name = n, .offset = offsetof(struct pt_regs, f), }
+#endif
#define X86_32_MAX_REGS 8
-const char *x86_32_regs_table[X86_32_MAX_REGS] = {
- "%ax",
- "%cx",
- "%dx",
- "%bx",
- "$stack", /* Stack address instead of %sp */
- "%bp",
- "%si",
- "%di",
+
+struct reg_info x86_32_regs_table[X86_32_MAX_REGS] = {
+ REG_INFO("%ax", eax),
+ REG_INFO("%cx", ecx),
+ REG_INFO("%dx", edx),
+ REG_INFO("%bx", ebx),
+ REG_INFO("$stack", esp), /* Stack address instead of %sp */
+ REG_INFO("%bp", ebp),
+ REG_INFO("%si", esi),
+ REG_INFO("%di", edi),
};

+#undef REG_INFO
+#ifdef __x86_64__
+# define REG_INFO(n, f) { .name = n, .offset = offsetof(struct pt_regs, f), }
+#else
+# define REG_INFO(n, f) { .name = n, .offset = -1, }
+#endif
#define X86_64_MAX_REGS 16
-const char *x86_64_regs_table[X86_64_MAX_REGS] = {
- "%ax",
- "%dx",
- "%cx",
- "%bx",
- "%si",
- "%di",
- "%bp",
- "%sp",
- "%r8",
- "%r9",
- "%r10",
- "%r11",
- "%r12",
- "%r13",
- "%r14",
- "%r15",
+struct reg_info x86_64_regs_table[X86_64_MAX_REGS] = {
+ REG_INFO("%ax", rax),
+ REG_INFO("%dx", rdx),
+ REG_INFO("%cx", rcx),
+ REG_INFO("%bx", rbx),
+ REG_INFO("%si", rsi),
+ REG_INFO("%di", rdi),
+ REG_INFO("%bp", rbp),
+ REG_INFO("%sp", rsp),
+ REG_INFO("%r8", r8),
+ REG_INFO("%r9", r9),
+ REG_INFO("%r10", r10),
+ REG_INFO("%r11", r11),
+ REG_INFO("%r12", r12),
+ REG_INFO("%r13", r13),
+ REG_INFO("%r14", r14),
+ REG_INFO("%r15", r15),
};

-/* TODO: switching by dwarf address size */
#ifdef __x86_64__
#define ARCH_MAX_REGS X86_64_MAX_REGS
#define arch_regs_table x86_64_regs_table
@@ -71,5 +94,28 @@ const char *x86_64_regs_table[X86_64_MAX_REGS] = {
/* Return architecture dependent register string (for kprobe-tracer) */
const char *get_arch_regstr(unsigned int n)
{
- return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
+ return (n <= ARCH_MAX_REGS) ? arch_regs_table[n].name : NULL;
}
+
+#ifdef HAVE_BPF_PROLOGUE
+int arch_get_reg_info(const char *name, int *offset)
+{
+ int i;
+ struct reg_info *info;
+
+ if (!name || !offset)
+ return -1;
+
+ for (i = 0; i < ARCH_MAX_REGS; i++) {
+ info = &arch_regs_table[i];
+ if (strcmp(info->name, name) == 0) {
+ if (info->offset < 0)
+ return -1;
+ *offset = info->offset;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+#endif
--
1.8.3.4

2015-07-09 12:45:54

by Wang Nan

[permalink] [raw]
Subject: [PATCH 34/39] perf tools: Add prologue for BPF programs for fetching arguments

This patch generates prologue for a BPF program which fetch arguments
for it. With this patch, the program can have arguments as follow:

SEC("lock_page=__lock_page page->flags")
int lock_page(struct pt_regs *ctx, int err, unsigned long flags)
{
return 1;
}

This patch passes at most 3 arguments from r3, r4 and r5. r1 is still
the ctx pointer. r2 is used to indicate the successfulness of
dereferencing.

This patch uses r6 to hold ctx (struct pt_regs) and r7 to hold stack
pointer for result. Result of each arguments first store on stack:

low address
BPF_REG_FP - 24 ARG3
BPF_REG_FP - 16 ARG2
BPF_REG_FP - 8 ARG1
BPF_REG_FP
high address

Then loaded into r3, r4 and r5.

The output prologue for offn(...off2(off1(reg)))) should be:

r6 <- r1 // save ctx into a callee saved register
r7 <- fp
r7 <- r7 - stack_offset // pointer to result slot
/* load r3 with the offset in pt_regs of 'reg' */
(r7) <- r3 // make slot valid
r3 <- r3 + off1 // prepare to read unsafe pointer
r2 <- 8
r1 <- r7 // result put onto stack
call probe_read // read unsafe pointer
jnei r0, 0, err // error checking
r3 <- (r7) // read result
r3 <- r3 + off2 // prepare to read unsafe pointer
r2 <- 8
r1 <- r7
call probe_read
jnei r0, 0, err
...
/* load r2, r3, r4 from stack */
goto success
err:
r2 <- 1
/* load r3, r4, r5 with 0 */
goto usercode
success:
r2 <- 0
usercode:
r1 <- r6 // restore ctx
// original user code

If all of arguments reside in register (dereferencing is not
required), gen_prologue_fastpath() will be used to create
fast prologue:

r3 <- (r1 + offset of reg1)
r4 <- (r1 + offset of reg2)
r5 <- (r1 + offset of reg3)
r2 <- 0

P.S.

eBPF calling convention is defined as:

* r0 - return value from in-kernel function, and exit value
for eBPF program
* r1 - r5 - arguments from eBPF program to in-kernel function
* r6 - r9 - callee saved registers that in-kernel function will
preserve
* r10 - read-only frame pointer to access stack

Signed-off-by: He Kuang <[email protected]>
Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/Build | 1 +
tools/perf/util/bpf-prologue.c | 442 +++++++++++++++++++++++++++++++++++++++++
tools/perf/util/bpf-prologue.h | 34 ++++
3 files changed, 477 insertions(+)
create mode 100644 tools/perf/util/bpf-prologue.c
create mode 100644 tools/perf/util/bpf-prologue.h

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 3357e5a..f83d070 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -80,6 +80,7 @@ libperf-$(CONFIG_AUXTRACE) += auxtrace.o
libperf-y += parse-branch-options.o

libperf-$(CONFIG_LIBBPF) += bpf-loader.o
+libperf-$(CONFIG_BPF_PROLOGUE) += bpf-prologue.o
libperf-$(CONFIG_LIBELF) += symbol-elf.o
libperf-$(CONFIG_LIBELF) += probe-event.o

diff --git a/tools/perf/util/bpf-prologue.c b/tools/perf/util/bpf-prologue.c
new file mode 100644
index 0000000..2a5f4c7
--- /dev/null
+++ b/tools/perf/util/bpf-prologue.c
@@ -0,0 +1,442 @@
+/*
+ * bpf-prologue.c
+ *
+ * Copyright (C) 2015 He Kuang <[email protected]>
+ * Copyright (C) 2015 Huawei Inc.
+ */
+
+#include <bpf/libbpf.h>
+#include "perf.h"
+#include "debug.h"
+#include "bpf-prologue.h"
+#include "probe-finder.h"
+#include <dwarf-regs.h>
+#include <linux/filter.h>
+
+#define BPF_REG_SIZE 8
+
+#define JMP_TO_ERROR_CODE -1
+#define JMP_TO_SUCCESS_CODE -2
+#define JMP_TO_USER_CODE -3
+
+struct bpf_insn_pos {
+ struct bpf_insn *begin;
+ struct bpf_insn *end;
+ struct bpf_insn *pos;
+};
+
+static inline int
+pos_get_cnt(struct bpf_insn_pos *pos)
+{
+ return pos->pos - pos->begin;
+}
+
+static int
+append_insn(struct bpf_insn new_insn, struct bpf_insn_pos *pos)
+{
+ if (!pos->pos)
+ return -ERANGE;
+
+ if (pos->pos + 1 >= pos->end) {
+ pr_err("bpf prologue: prologue too long\n");
+ pos->pos = NULL;
+ return -ERANGE;
+ }
+
+ *(pos->pos)++ = new_insn;
+ return 0;
+}
+
+static int
+check_pos(struct bpf_insn_pos *pos)
+{
+ if (!pos->pos || pos->pos >= pos->end)
+ return -ERANGE;
+ return 0;
+}
+
+/* Give it a shorter name */
+#define ins(i, p) append_insn((i), (p))
+
+/*
+ * Give a register name (in 'reg'), generate instruction to
+ * load register into an eBPF register rd:
+ * 'ldd target_reg, offset(ctx_reg)', where:
+ * ctx_reg is pre initialized to pointer of 'struct pt_regs'.
+ */
+static int
+gen_ldx_reg_from_ctx(struct bpf_insn_pos *pos, int ctx_reg,
+ const char *reg, int target_reg)
+{
+ int offset;
+
+ if (arch_get_reg_info(reg, &offset)) {
+ pr_err("bpf: prologue: failed to get register %s\n",
+ reg);
+ return -1;
+ }
+ ins(BPF_LDX_MEM(BPF_DW, target_reg, ctx_reg, offset), pos);
+
+ if (check_pos(pos))
+ return -ERANGE;
+ return 0;
+}
+
+/*
+ * Generate a BPF_FUNC_probe_read function call.
+ *
+ * src_base_addr_reg is a register holding base address,
+ * dst_addr_reg is a register holding dest address (on stack),
+ * result is:
+ *
+ * *[dst_addr_reg] = *([src_base_addr_reg] + offset)
+ *
+ * Arguments of BPF_FUNC_probe_read:
+ * ARG1: ptr to stack (dest)
+ * ARG2: size (8)
+ * ARG3: unsafe ptr (src)
+ */
+static int
+gen_read_mem(struct bpf_insn_pos *pos,
+ int src_base_addr_reg,
+ int dst_addr_reg,
+ long offset)
+{
+ /* mov arg3, src_base_addr_reg */
+ if (src_base_addr_reg != BPF_REG_ARG3)
+ ins(BPF_MOV64_REG(BPF_REG_ARG3, src_base_addr_reg), pos);
+ /* add arg3, #offset */
+ if (offset)
+ ins(BPF_ALU64_IMM(BPF_ADD, BPF_REG_ARG3, offset), pos);
+
+ /* mov arg2, #reg_size */
+ ins(BPF_ALU64_IMM(BPF_MOV, BPF_REG_ARG2, BPF_REG_SIZE), pos);
+
+ /* mov arg1, dst_addr_reg */
+ if (dst_addr_reg != BPF_REG_ARG1)
+ ins(BPF_MOV64_REG(BPF_REG_ARG1, dst_addr_reg), pos);
+
+ /* Call probe_read */
+ ins(BPF_EMIT_CALL(BPF_FUNC_probe_read), pos);
+ /*
+ * Error processing: if read fail, goto error code,
+ * will be relocated. Target should be the start of
+ * error processing code.
+ */
+ ins(BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, JMP_TO_ERROR_CODE),
+ pos);
+
+ if (check_pos(pos))
+ return -ERANGE;
+ return 0;
+}
+
+/*
+ * Each arg should be bare register. Fetch and save them into argument
+ * registers (r3 - r5).
+ *
+ * BPF_REG_1 should have been initialized with pointer to
+ * 'struct pt_regs'.
+ */
+static int
+gen_prologue_fastpath(struct bpf_insn_pos *pos,
+ struct probe_trace_arg *args, int nargs)
+{
+ int i;
+
+ for (i = 0; i < nargs; i++)
+ if (gen_ldx_reg_from_ctx(pos, BPF_REG_1, args[i].value,
+ BPF_PROLOGUE_START_ARG_REG + i))
+ goto errout;
+
+ if (check_pos(pos))
+ goto errout;
+ return 0;
+errout:
+ return -1;
+}
+
+/*
+ * Slow path:
+ * At least one argument has the form of 'offset($rx)'.
+ *
+ * Following code first stores them into stack, then loads all of then
+ * to r2 - r5.
+ * Before final loading, the final result should be:
+ *
+ * low address
+ * BPF_REG_FP - 24 ARG3
+ * BPF_REG_FP - 16 ARG2
+ * BPF_REG_FP - 8 ARG1
+ * BPF_REG_FP
+ * high address
+ *
+ * For each argument (described as: offn(...off2(off1(reg)))),
+ * generates following code:
+ *
+ * r7 <- fp
+ * r7 <- r7 - stack_offset // Ideal code should initialize r7 using
+ * // fp before generating args. However,
+ * // eBPF won't regard r7 as stack pointer
+ * // if it is generated by minus 8 from
+ * // another stack pointer except fp.
+ * // This is why we have to set r7
+ * // to fp for each variable.
+ * r3 <- value of 'reg'-> generated using gen_ldx_reg_from_ctx()
+ * (r7) <- r3 // skip following instructions for bare reg
+ * r3 <- r3 + off1 . // skip if off1 == 0
+ * r2 <- 8 \
+ * r1 <- r7 |-> generated by gen_read_mem()
+ * call probe_read /
+ * jnei r0, 0, err ./
+ * r3 <- (r7)
+ * r3 <- r3 + off2 . // skip if off2 == 0
+ * r2 <- 8 \ // r2 may be broken by probe_read, so set again
+ * r1 <- r7 |-> generated by gen_read_mem()
+ * call probe_read /
+ * jnei r0, 0, err ./
+ * ...
+ */
+static int
+gen_prologue_slowpath(struct bpf_insn_pos *pos,
+ struct probe_trace_arg *args, int nargs)
+{
+ int i;
+
+ for (i = 0; i < nargs; i++) {
+ struct probe_trace_arg *arg = &args[i];
+ const char *reg = arg->value;
+ struct probe_trace_arg_ref *ref = NULL;
+ int stack_offset = (i + 1) * -8;
+
+ pr_debug("prologue: fetch arg %d, base reg is %s\n",
+ i, reg);
+
+ /* value of base register is stored into ARG3 */
+ if (gen_ldx_reg_from_ctx(pos, BPF_REG_CTX, reg,
+ BPF_REG_ARG3)) {
+ pr_err("prologue: failed to get offset of register %s\n",
+ reg);
+ goto errout;
+ }
+
+ /* Make r7 the stack pointer. */
+ ins(BPF_MOV64_REG(BPF_REG_7, BPF_REG_FP), pos);
+ /* r7 += -8 */
+ ins(BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, stack_offset), pos);
+ /*
+ * Store r3 (base register) onto stack
+ * Ensure fp[offset] is set.
+ * fp is the only valid base register when storing
+ * into stack. We are not allowed to use r7 as base
+ * register here.
+ */
+ ins(BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_ARG3,
+ stack_offset), pos);
+
+ ref = arg->ref;
+ while (ref) {
+ pr_debug("prologue: arg %d: offset %ld\n",
+ i, ref->offset);
+ if (gen_read_mem(pos, BPF_REG_3, BPF_REG_7,
+ ref->offset)) {
+ pr_err("prologue: failed to generate probe_read function call\n");
+ goto errout;
+ }
+
+ ref = ref->next;
+ /*
+ * Load previous result into ARG3. Use
+ * BPF_REG_FP instead of r7 because verifier
+ * allows FP based addressing only.
+ */
+ if (ref)
+ ins(BPF_LDX_MEM(BPF_DW, BPF_REG_ARG3,
+ BPF_REG_FP, stack_offset), pos);
+ }
+ }
+
+ /* Final pass: read to registers */
+ for (i = 0; i < nargs; i++)
+ ins(BPF_LDX_MEM(BPF_DW, BPF_PROLOGUE_START_ARG_REG + i,
+ BPF_REG_FP, -BPF_REG_SIZE * (i + 1)), pos);
+
+ ins(BPF_JMP_IMM(BPF_JA, BPF_REG_0, 0, JMP_TO_SUCCESS_CODE), pos);
+
+ if (check_pos(pos))
+ goto errout;
+ return 0;
+errout:
+ return -1;
+}
+
+static int
+prologue_relocate(struct bpf_insn_pos *pos, struct bpf_insn *error_code,
+ struct bpf_insn *success_code, struct bpf_insn *user_code)
+{
+ struct bpf_insn *insn;
+
+ if (check_pos(pos))
+ return -ERANGE;
+
+ for (insn = pos->begin; insn < pos->pos; insn++) {
+ u8 class = BPF_CLASS(insn->code);
+ u8 opcode;
+
+ if (class != BPF_JMP)
+ continue;
+ opcode = BPF_OP(insn->code);
+ if (opcode == BPF_CALL)
+ continue;
+
+ switch (insn->off) {
+ case JMP_TO_ERROR_CODE:
+ insn->off = error_code - (insn + 1);
+ break;
+ case JMP_TO_SUCCESS_CODE:
+ insn->off = success_code - (insn + 1);
+ break;
+ case JMP_TO_USER_CODE:
+ insn->off = user_code - (insn + 1);
+ break;
+ default:
+ pr_err("bpf prologue: internal error: relocation failed\n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int bpf__gen_prologue(struct probe_trace_arg *args, int nargs,
+ struct bpf_insn *new_prog, size_t *new_cnt,
+ size_t cnt_space)
+{
+ struct bpf_insn *success_code = NULL;
+ struct bpf_insn *error_code = NULL;
+ struct bpf_insn *user_code = NULL;
+ struct bpf_insn_pos pos;
+ bool fastpath = true;
+ int i;
+
+ if (!new_prog || !new_cnt)
+ return -EINVAL;
+
+ pos.begin = new_prog;
+ pos.end = new_prog + cnt_space;
+ pos.pos = new_prog;
+
+ if (!nargs) {
+ ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 0),
+ &pos);
+
+ if (check_pos(&pos))
+ goto errout;
+
+ *new_cnt = pos_get_cnt(&pos);
+ return 0;
+ }
+
+ if (nargs > BPF_PROLOGUE_MAX_ARGS)
+ nargs = BPF_PROLOGUE_MAX_ARGS;
+ if (cnt_space > BPF_MAXINSNS)
+ cnt_space = BPF_MAXINSNS;
+
+ /* First pass: validation */
+ for (i = 0; i < nargs; i++) {
+ struct probe_trace_arg_ref *ref = args[i].ref;
+
+ if (args[i].value[0] == '@') {
+ /* TODO: fetch global variable */
+ pr_err("bpf: prologue: global %s%+ld not support\n",
+ args[i].value, ref ? ref->offset : 0);
+ return -ENOTSUP;
+ }
+
+ while (ref) {
+ /* fastpath is true if all args has ref == NULL */
+ fastpath = false;
+
+ /*
+ * Instruction encodes immediate value using
+ * s32, ref->offset is long. On systems which
+ * can't fill long in s32, refuse to process if
+ * ref->offset too large (or small).
+ */
+#ifdef __LP64__
+#define OFFSET_MAX ((1LL << 31) - 1)
+#define OFFSET_MIN ((1LL << 31) * -1)
+ if (ref->offset > OFFSET_MAX ||
+ ref->offset < OFFSET_MIN) {
+ pr_err("bpf: prologue: offset out of bound: %ld\n",
+ ref->offset);
+ return -E2BIG;
+ }
+#endif
+ ref = ref->next;
+ }
+ }
+ pr_debug("prologue: pass validation\n");
+
+ if (fastpath) {
+ /* If all variables are registers... */
+ pr_debug("prologue: fast path\n");
+ if (gen_prologue_fastpath(&pos, args, nargs))
+ goto errout;
+ } else {
+ pr_debug("prologue: slow path\n");
+
+ /* Initialization: move ctx to a callee saved register. */
+ ins(BPF_MOV64_REG(BPF_REG_CTX, BPF_REG_ARG1), &pos);
+
+ if (gen_prologue_slowpath(&pos, args, nargs))
+ goto errout;
+ /*
+ * start of ERROR_CODE (only slow pass needs error code)
+ * mov r2 <- 1
+ * goto usercode
+ */
+ error_code = pos.pos;
+ ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 1),
+ &pos);
+
+ for (i = 0; i < nargs; i++)
+ ins(BPF_ALU64_IMM(BPF_MOV,
+ BPF_PROLOGUE_START_ARG_REG + i,
+ 0),
+ &pos);
+ ins(BPF_JMP_IMM(BPF_JA, BPF_REG_0, 0, JMP_TO_USER_CODE),
+ &pos);
+ }
+
+ /*
+ * start of SUCCESS_CODE:
+ * mov r2 <- 0
+ * goto usercode // skip
+ */
+ success_code = pos.pos;
+ ins(BPF_ALU64_IMM(BPF_MOV, BPF_PROLOGUE_FETCH_RESULT_REG, 0), &pos);
+
+ /*
+ * start of USER_CODE:
+ * Restore ctx to r1
+ */
+ user_code = pos.pos;
+ if (!fastpath) {
+ /*
+ * Only slow path needs restoring of ctx. In fast path,
+ * register are loaded directly from r1.
+ */
+ ins(BPF_MOV64_REG(BPF_REG_ARG1, BPF_REG_CTX), &pos);
+ if (prologue_relocate(&pos, error_code, success_code,
+ user_code))
+ goto errout;
+ }
+
+ if (check_pos(&pos))
+ goto errout;
+
+ *new_cnt = pos_get_cnt(&pos);
+ return 0;
+errout:
+ return -ERANGE;
+}
diff --git a/tools/perf/util/bpf-prologue.h b/tools/perf/util/bpf-prologue.h
new file mode 100644
index 0000000..f1e4c5d
--- /dev/null
+++ b/tools/perf/util/bpf-prologue.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015, He Kuang <[email protected]>
+ * Copyright (C) 2015, Huawei Inc.
+ */
+#ifndef __BPF_PROLOGUE_H
+#define __BPF_PROLOGUE_H
+
+#include <linux/compiler.h>
+#include <linux/filter.h>
+#include "probe-event.h"
+
+#define BPF_PROLOGUE_MAX_ARGS 3
+#define BPF_PROLOGUE_START_ARG_REG BPF_REG_3
+#define BPF_PROLOGUE_FETCH_RESULT_REG BPF_REG_2
+
+#ifdef HAVE_BPF_PROLOGUE
+int bpf__gen_prologue(struct probe_trace_arg *args, int nargs,
+ struct bpf_insn *new_prog, size_t *new_cnt,
+ size_t cnt_space);
+#else
+static inline int
+bpf__gen_prologue(struct probe_trace_arg *args __maybe_unused,
+ int nargs __maybe_unused,
+ struct bpf_insn *new_prog __maybe_unused,
+ size_t *new_cnt,
+ size_t cnt_space __maybe_unused)
+{
+ if (!new_cnt)
+ return -EINVAL;
+ *new_cnt = 0;
+ return 0;
+}
+#endif
+#endif /* __BPF_PROLOGUE_H */
--
1.8.3.4

2015-07-09 12:41:45

by Wang Nan

[permalink] [raw]
Subject: [PATCH 35/39] perf tools: Generate prologue for BPF programs

This patch generates prologue for each 'struct probe_trace_event' for
fetching arguments for BPF programs.

After bpf__probe(), iterate over each programs to check whether
prologue is required. If none of 'struct perf_probe_event' a program
will attach to has at least one argument, simply skip preprocessor
hooking. For those who prologue is required, calls bpf__gen_prologue()
and paste original instruction after prologue.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/bpf-loader.c | 132 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 130 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index bd2ebe1..41bffdd 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -5,10 +5,12 @@
* Copyright (C) 2015 Huawei Inc.
*/

+#include <linux/bpf.h>
#include <bpf/libbpf.h>
#include "perf.h"
#include "debug.h"
#include "bpf-loader.h"
+#include "bpf-prologue.h"
#include "llvm-utils.h"
#include "probe-event.h"
#include "probe-finder.h"
@@ -53,6 +55,8 @@ alloc_perf_probe_event(void)

struct bpf_prog_priv {
struct perf_probe_event *pev;
+ bool need_prologue;
+ struct bpf_insn *insns_buf;
};

static void
@@ -63,6 +67,7 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,

if (priv->pev)
clear_perf_probe_event(priv->pev);
+ zfree(&priv->insns_buf);
free(priv);
}

@@ -223,10 +228,109 @@ int bpf__unprobe(void)
return ret < 0 ? ret : 0;
}

+static int
+preproc_gen_prologue(struct bpf_program *prog, int n,
+ struct bpf_insn *orig_insns, int orig_insns_cnt,
+ struct bpf_prog_prep_result *res)
+{
+ struct probe_trace_event *tev;
+ struct perf_probe_event *pev;
+ struct bpf_prog_priv *priv;
+ struct bpf_insn *buf;
+ size_t prologue_cnt = 0;
+ int err;
+
+ err = bpf_program__get_private(prog, (void **)&priv);
+ if (err || !priv)
+ goto errout;
+
+ pev = priv->pev;
+
+ if (n < 0 || n >= pev->ntevs)
+ goto errout;
+
+ tev = &pev->tevs[n];
+
+ buf = priv->insns_buf;
+ err = bpf__gen_prologue(tev->args, tev->nargs,
+ buf, &prologue_cnt,
+ BPF_MAXINSNS - orig_insns_cnt);
+ if (err) {
+ const char *title;
+
+ title = bpf_program__title(prog, false);
+ if (!title)
+ title = "??";
+
+ pr_err("Failed to generate prologue for program %s\n",
+ title);
+ return err;
+ }
+
+ memcpy(&buf[prologue_cnt], orig_insns,
+ sizeof(struct bpf_insn) * orig_insns_cnt);
+
+ res->new_insn_ptr = buf;
+ res->new_insn_cnt = prologue_cnt + orig_insns_cnt;
+ res->pfd = NULL;
+ return 0;
+
+errout:
+ pr_err("Internal error in preproc_gen_prologue\n");
+ return -EINVAL;
+}
+
+static int hook_load_preprocessor(struct bpf_program *prog)
+{
+ struct perf_probe_event *pev;
+ struct bpf_prog_priv *priv;
+ bool need_prologue = false;
+ int err, i;
+
+ err = bpf_program__get_private(prog, (void **)&priv);
+ if (err || !priv) {
+ pr_err("Internal error when hook preprocessor\n");
+ return -EINVAL;
+ }
+
+ pev = priv->pev;
+ for (i = 0; i < pev->ntevs; i++) {
+ struct probe_trace_event *tev = &pev->tevs[i];
+
+ if (tev->nargs > 0) {
+ need_prologue = true;
+ break;
+ }
+ }
+
+ /*
+ * Since all tev doesn't have argument, we don't need generate
+ * prologue.
+ */
+ if (!need_prologue) {
+ priv->need_prologue = false;
+ return 0;
+ }
+
+ priv->need_prologue = true;
+ priv->insns_buf = malloc(sizeof(struct bpf_insn) *
+ BPF_MAXINSNS);
+ if (!priv->insns_buf) {
+ pr_err("No enough memory: alloc insns_buf failed\n");
+ return -ENOMEM;
+ }
+
+ err = bpf_program__set_prep(prog, pev->ntevs,
+ preproc_gen_prologue);
+ return err;
+}
+
int bpf__probe(void)
{
int err;
bool old_silent = probe_conf.silent;
+ struct bpf_object *obj, *tmp;
+ struct bpf_program *prog;

if (nr_probe_events <= 0)
return 0;
@@ -245,7 +349,27 @@ int bpf__probe(void)
else
is_probing = true;

- return err < 0 ? err : 0;
+ err = err < 0 ? err : 0;
+ if (err)
+ return err;
+
+ /*
+ * After probing, let's consider prologue, which
+ * add program fetcher to BPF programs.
+ *
+ * hook_load_preprocessorr() hooks pre-processor to bpf_program,
+ * let it generate prologue dynamically during loading.
+ */
+
+ bpf_object__for_each_safe(obj, tmp) {
+ bpf_object__for_each_program(prog, obj) {
+ err = hook_load_preprocessor(prog);
+ if (err)
+ return err;
+ }
+ }
+
+ return err;
}

int bpf__load(void)
@@ -291,7 +415,11 @@ int bpf__foreach_tev(bpf_prog_iter_callback_t func, void *arg)
for (i = 0; i < pev->ntevs; i++) {
tev = &pev->tevs[i];

- fd = bpf_program__fd(prog);
+ if (priv->need_prologue)
+ fd = bpf_program__nth_fd(prog, i);
+ else
+ fd = bpf_program__fd(prog);
+
if (fd < 0) {
pr_err("bpf: failed to get file descriptor\n");
return fd;
--
1.8.3.4

2015-07-09 12:47:10

by Wang Nan

[permalink] [raw]
Subject: [PATCH 36/39] perf tools: Use same BPF program if arguments are identical

This patch allows creating only one BPF program for different
'probe_trace_event'(tev) generated by one 'perf_probe_event'(pev), if
their prologue are identical.

This is done by comparing argument list of different tev, and maps type
of prologue and tev using a mapping array. This patch utilizes qsort to
sort tevs. After sorting, tevs with identical argument list will group
together.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/bpf-loader.c | 133 ++++++++++++++++++++++++++++++++++++++++---
1 file changed, 126 insertions(+), 7 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 41bffdd..0a4e9ec 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -57,6 +57,8 @@ struct bpf_prog_priv {
struct perf_probe_event *pev;
bool need_prologue;
struct bpf_insn *insns_buf;
+ int nr_types;
+ int *type_mapping;
};

static void
@@ -68,6 +70,7 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
if (priv->pev)
clear_perf_probe_event(priv->pev);
zfree(&priv->insns_buf);
+ zfree(&priv->type_mapping);
free(priv);
}

@@ -238,7 +241,7 @@ preproc_gen_prologue(struct bpf_program *prog, int n,
struct bpf_prog_priv *priv;
struct bpf_insn *buf;
size_t prologue_cnt = 0;
- int err;
+ int i, err;

err = bpf_program__get_private(prog, (void **)&priv);
if (err || !priv)
@@ -246,10 +249,20 @@ preproc_gen_prologue(struct bpf_program *prog, int n,

pev = priv->pev;

- if (n < 0 || n >= pev->ntevs)
+ if (n < 0 || n >= priv->nr_types)
goto errout;

- tev = &pev->tevs[n];
+ /* Find a tev belongs to that type */
+ for (i = 0; i < pev->ntevs; i++)
+ if (priv->type_mapping[i] == n)
+ break;
+
+ if (i >= pev->ntevs) {
+ pr_err("Internal error: prologue type %d not found\n", n);
+ return -EEXIST;
+ }
+
+ tev = &pev->tevs[i];

buf = priv->insns_buf;
err = bpf__gen_prologue(tev->args, tev->nargs,
@@ -280,6 +293,98 @@ errout:
return -EINVAL;
}

+/*
+ * compare_tev_args is reflexive, transitive and antisymmetric.
+ * I can show that but this margin is too narrow to contain.
+ */
+static int compare_tev_args(const void *ptev1, const void *ptev2)
+{
+ int i, ret;
+ const struct probe_trace_event *tev1 =
+ *(const struct probe_trace_event **)ptev1;
+ const struct probe_trace_event *tev2 =
+ *(const struct probe_trace_event **)ptev2;
+
+ ret = tev2->nargs - tev1->nargs;
+ if (ret)
+ return ret;
+
+ for (i = 0; i < tev1->nargs; i++) {
+ struct probe_trace_arg *arg1, *arg2;
+ struct probe_trace_arg_ref *ref1, *ref2;
+
+ arg1 = &tev1->args[i];
+ arg2 = &tev2->args[i];
+
+ ret = strcmp(arg1->value, arg2->value);
+ if (ret)
+ return ret;
+
+ ref1 = arg1->ref;
+ ref2 = arg2->ref;
+
+ while (ref1 && ref2) {
+ ret = ref2->offset - ref1->offset;
+ if (ret)
+ return ret;
+
+ ref1 = ref1->next;
+ ref2 = ref2->next;
+ }
+
+ if (ref1 || ref2)
+ return ref2 ? 1 : -1;
+ }
+
+ return 0;
+}
+
+static int map_prologue(struct perf_probe_event *pev, int *mapping,
+ int *nr_types)
+{
+ int i, type = 0;
+ struct {
+ struct probe_trace_event *tev;
+ int idx;
+ } *stevs;
+ size_t array_sz = sizeof(*stevs) * pev->ntevs;
+
+ stevs = malloc(array_sz);
+ if (!stevs) {
+ pr_err("No ehough memory: alloc stevs failed\n");
+ return -ENOMEM;
+ }
+
+ pr_debug("In map_prologue, ntevs=%d\n", pev->ntevs);
+ for (i = 0; i < pev->ntevs; i++) {
+ stevs[i].tev = &pev->tevs[i];
+ stevs[i].idx = i;
+ }
+ qsort(stevs, pev->ntevs, sizeof(*stevs),
+ compare_tev_args);
+
+ for (i = 0; i < pev->ntevs; i++) {
+ if (i == 0) {
+ mapping[stevs[i].idx] = type;
+ pr_debug("mapping[%d]=%d\n", stevs[i].idx,
+ type);
+ continue;
+ }
+
+ if (compare_tev_args(stevs + i, stevs + i - 1) == 0)
+ mapping[stevs[i].idx] = type;
+ else
+ mapping[stevs[i].idx] = ++type;
+
+ pr_debug("mapping[%d]=%d\n", stevs[i].idx,
+ mapping[stevs[i].idx]);
+ }
+ free(stevs);
+ *nr_types = type + 1;
+
+ return 0;
+}
+
static int hook_load_preprocessor(struct bpf_program *prog)
{
struct perf_probe_event *pev;
@@ -320,7 +425,19 @@ static int hook_load_preprocessor(struct bpf_program *prog)
return -ENOMEM;
}

- err = bpf_program__set_prep(prog, pev->ntevs,
+ priv->type_mapping = malloc(sizeof(int) * pev->ntevs);
+ if (!priv->type_mapping) {
+ pr_err("No enough memory: alloc type_mapping failed\n");
+ return -ENOMEM;
+ }
+ memset(priv->type_mapping, 0xff,
+ sizeof(int) * pev->ntevs);
+
+ err = map_prologue(pev, priv->type_mapping, &priv->nr_types);
+ if (err)
+ return err;
+
+ err = bpf_program__set_prep(prog, priv->nr_types,
preproc_gen_prologue);
return err;
}
@@ -415,9 +532,11 @@ int bpf__foreach_tev(bpf_prog_iter_callback_t func, void *arg)
for (i = 0; i < pev->ntevs; i++) {
tev = &pev->tevs[i];

- if (priv->need_prologue)
- fd = bpf_program__nth_fd(prog, i);
- else
+ if (priv->need_prologue) {
+ int type = priv->type_mapping[i];
+
+ fd = bpf_program__nth_fd(prog, type);
+ } else
fd = bpf_program__fd(prog);

if (fd < 0) {
--
1.8.3.4

2015-07-09 12:40:37

by Wang Nan

[permalink] [raw]
Subject: [PATCH 37/39] perf record: Support custom vmlinux path

From: He Kuang <[email protected]>

Make perf-record command support --vmlinux option if BPF_PROLOGUE is on.

'perf record' needs vmlinux as the source of DWARF info to generate
prologue for BPF programs, so path of vmlinux should be specified.

Short name 'k' has been taken by 'clockid'. This patch skips the short
option name and use '--vmlinux' for vmlinux path.

Signed-off-by: He Kuang <[email protected]>
Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/builtin-record.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index e89c045..e613f2e 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1079,6 +1079,10 @@ struct option __record_options[] = {
"clang binary to use for compiling BPF scriptlets"),
OPT_STRING(0, "clang-opt", &llvm_param.clang_opt, "clang options",
"options passed to clang when compiling BPF scriptlets"),
+#ifdef HAVE_BPF_PROLOGUE
+ OPT_STRING(0, "vmlinux", &symbol_conf.vmlinux_name,
+ "file", "vmlinux pathname"),
+#endif
#endif
OPT_END()
};
--
1.8.3.4

2015-07-09 12:37:15

by Wang Nan

[permalink] [raw]
Subject: [PATCH 38/39] perf probe: Init symbol as kprobe if any event is kprobe

Before this patch, add_perf_probe_events() init symbol maps only for
uprobe if the first 'struct perf_probe_event' passed to it is a uprobe
event. This is a trick because 'perf probe''s command line syntax
constrains the first elements of the probe_event arrays must be kprobes
if there is one.

However, with the incoming BPF uprobe support, the constrain is not
hold since 'perf record' will also probe on k/u probes through BPF
object, and is possible to pass an array with kprobe but the first
element is uprobe.

This patch init symbol maps for kprobes even if all of events are
uprobes, because the extra cost should be small enough.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/probe-event.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index b9573c5..77650f4 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -2808,7 +2808,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
{
int i, ret;

- ret = init_symbol_maps(pevs->uprobes);
+ ret = init_symbol_maps(false);
if (ret < 0)
return ret;

--
1.8.3.4

2015-07-09 12:37:13

by Wang Nan

[permalink] [raw]
Subject: [PATCH 39/39] perf tools: Support attach BPF program on uprobe events

This patch appends new syntax to BPF object section name to support
probing at uprobe event. Now we can use BPF program like this:

SEC(
"target=/lib64/libc.so.6\n"
"libcwrite=__write"
)
int libcwrite(void *ctx)
{
return 1;
}

Where, in section name of a program, before the main config string,
we can use 'key=value' style options. Now the only option key "target"
is for uprobe probing.

Signed-off-by: Wang Nan <[email protected]>
---
tools/perf/util/bpf-loader.c | 88 ++++++++++++++++++++++++++++++++++++++++----
1 file changed, 81 insertions(+), 7 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 0a4e9ec..f5b2344 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -75,6 +75,84 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
}

static int
+do_config(const char *key, const char *value,
+ struct perf_probe_event *pev)
+{
+ pr_debug("config bpf program: %s=%s\n", key, value);
+ if (strcmp(key, "target") == 0) {
+ pev->uprobes = true;
+ pev->target = strdup(value);
+ return 0;
+ }
+
+ pr_warning("BPF: WARNING: invalid config option in object: %s=%s\n",
+ key, value);
+ pr_warning("\tHint: Currently only valid option is 'target=<file>'\n");
+ return 0;
+}
+
+static const char *
+parse_config_kvpair(const char *config_str, struct perf_probe_event *pev)
+{
+ char *text = strdup(config_str);
+ char *sep, *line;
+ const char *main_str = NULL;
+ int err = 0;
+
+ if (!text) {
+ pr_err("No enough memory: dup config_str failed\n");
+ return NULL;
+ }
+
+ line = text;
+ while ((sep = strchr(line, '\n'))) {
+ char *equ;
+
+ *sep = '\0';
+ equ = strchr(line, '=');
+ if (!equ) {
+ pr_warning("WARNING: invalid config in BPF object: %s\n",
+ line);
+ pr_warning("\tShould be 'key=value'.\n");
+ goto nextline;
+ }
+ *equ = '\0';
+
+ err = do_config(line, equ + 1, pev);
+ if (err)
+ break;
+nextline:
+ line = sep + 1;
+ }
+
+ if (!err)
+ main_str = config_str + (line - text);
+ free(text);
+
+ return main_str;
+}
+
+static int
+parse_config(const char *config_str, struct perf_probe_event *pev)
+{
+ const char *main_str;
+ int err;
+
+ main_str = parse_config_kvpair(config_str, pev);
+ if (!main_str)
+ return -EINVAL;
+
+ err = parse_perf_probe_command(main_str, pev);
+ if (err < 0) {
+ pr_err("bpf: '%s' is not a valid config string\n",
+ config_str);
+ /* parse failed, don't need clear pev. */
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int
config_bpf_program(struct bpf_program *prog)
{
struct perf_probe_event *pev = alloc_perf_probe_event();
@@ -93,13 +171,9 @@ config_bpf_program(struct bpf_program *prog)
}

pr_debug("bpf: config program '%s'\n", config_str);
- err = parse_perf_probe_command(config_str, pev);
- if (err < 0) {
- pr_err("bpf: '%s' is not a valid config string\n",
- config_str);
- /* parse failed, don't need clear pev. */
- return -EINVAL;
- }
+ err = parse_config(config_str, pev);
+ if (err)
+ return err;

if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) {
pr_err("bpf: '%s': group for event is set and not '%s'.\n",
--
1.8.3.4

2015-07-09 15:58:25

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 02/39] bpf tools: Collect eBPF programs from their own sections

Em Thu, Jul 09, 2015 at 12:35:05PM +0000, Wang Nan escreveu:
> This patch collects all programs in an object file into an array of
> 'struct bpf_program' for further processing. That structure is for
> representing each eBPF program. 'bpf_prog' should be a better name, but
> it has been used by linux/filter.h. Although it is a kernel space name,
> I still prefer to call it 'bpf_program' to prevent possible confusion.
>
> bpf_program__new() creates a new 'struct bpf_program' object. It first
> init a variable in stack using __bpf_program__new(), then if success,
> enlarges obj->programs array and copy the new object in.
>
> Signed-off-by: Wang Nan <[email protected]>
> Acked-by: Alexei Starovoitov <[email protected]>
> Cc: Brendan Gregg <[email protected]>
> Cc: Daniel Borkmann <[email protected]>
> Cc: David Ahern <[email protected]>
> Cc: He Kuang <[email protected]>
> Cc: Jiri Olsa <[email protected]>
> Cc: Kaixu Xia <[email protected]>
> Cc: Masami Hiramatsu <[email protected]>
> Cc: Namhyung Kim <[email protected]>
> Cc: Paul Mackerras <[email protected]>
> Cc: Peter Zijlstra <[email protected]>
> Cc: Zefan Li <[email protected]>
> Cc: [email protected]
> Link: http://lkml.kernel.org/r/[email protected]
> Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
> ---
> tools/lib/bpf/libbpf.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 117 insertions(+)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 9b016c0..3b717de 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -78,12 +78,27 @@ void libbpf_set_print(libbpf_print_fn_t warn,
> # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
> #endif
>
> +/*
> + * bpf_prog should be a better name but it has been used in
> + * linux/filter.h.
> + */
> +struct bpf_program {
> + /* Index in elf obj file, for relocation use. */
> + int idx;
> + char *section_name;
> + struct bpf_insn *insns;
> + size_t insns_cnt;
> +};
> +
> 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;
> +
> /*
> * Information when doing elf related work. Only valid if fd
> * is valid.
> @@ -100,6 +115,84 @@ struct bpf_object {
> };
> #define obj_elf_valid(o) ((o)->efile.elf)
>
> +static void bpf_program__clear(struct bpf_program *prog)
> +{
> + if (!prog)
> + return;
> +
> + zfree(&prog->section_name);
> + zfree(&prog->insns);
> + prog->insns_cnt = 0;
> + prog->idx = -1;
> +}

So in perf land we use 'bpf_program__exit()' as the counterpart of
bpf_program__init(), i.e. one just initializes fields, allocating
memory for 'struct bpf_program' members, but does not allocates the
struct bpf_program itself, because sometimes we embed it inside other
structs, or we have it in arrays, as you do.

So, to keep that convention, please rename bpf_program__clear() to
bpf_program__exit() and the next function, __bpf_program__new() to
bpf_program__init(), with 'struct bpf_program *prog' as the first
parameter.

To speed things up, from now on, when I see such stuff, I will do the
changes, put them in a branch with a commiter note, and wait for your
Ack (or not, if you disagree with something).

One more comment below.

> +
> +static int
> +__bpf_program__new(void *data, size_t size, char *name, int idx,
> + struct bpf_program *prog)
> +{
> + if (size < sizeof(struct bpf_insn)) {
> + pr_warning("corrupted section '%s'\n", name);
> + return -EINVAL;
> + }
> +
> + bzero(prog, sizeof(*prog));
> +
> + prog->section_name = strdup(name);
> + if (!prog->section_name) {
> + pr_warning("failed to alloc name for prog %s\n",
> + name);
> + goto errout;
> + }
> +
> + prog->insns = malloc(size);
> + if (!prog->insns) {
> + pr_warning("failed to alloc insns for %s\n", name);
> + goto errout;
> + }
> + prog->insns_cnt = size / sizeof(struct bpf_insn);
> + memcpy(prog->insns, data,
> + prog->insns_cnt * sizeof(struct bpf_insn));
> + prog->idx = idx;
> +
> + return 0;
> +errout:
> + bpf_program__clear(prog);
> + return -ENOMEM;
> +}
> +
> +static struct bpf_program *
> +bpf_program__new(struct bpf_object *obj, void *data, size_t size,
> + char *name, int idx)

This, as well, is not a 'bpf_program' method, it is a 'struct
bpf_object' one, that will manipulate 'struct bpf_object' internal
state, changing its struct members to get space for an extra bpf_program
that was initialized on the stack, if the initialization of such
bpf_program went well, or bail out otherwise.

So I suggest you rename this to:

int bpf_object__add_program(struct bpf_object *obj, void *data, size_t size, char *name, int idx)

And probably move that debug that uses prog->section_name to just after
the realloc, here in this function.

I will look at the other patches after lunch, thanks for providing the
git tree, I will try and use it before looking at the patches
individually, to get a feel of the whole thing.

Ah, I also noticed that you provided way more comments in other patches,
that really helps, keep it up!

Thanks,

- Arnaldo

> +{
> + struct bpf_program prog, *progs;
> + int nr_progs, err;
> +
> + err = __bpf_program__new(data, size, name, idx, &prog);
> + if (err)
> + return NULL;
> +
> + progs = obj->programs;
> + nr_progs = obj->nr_programs;
> +
> + progs = realloc(progs, sizeof(progs[0]) * (nr_progs + 1));
> + if (!progs) {
> + /*
> + * In this case the original obj->programs
> + * is still valid, so don't need special treat for
> + * bpf_close_object().
> + */
> + pr_warning("failed to alloc a new program '%s'\n",
> + name);
> + bpf_program__clear(&prog);
> + return NULL;
> + }
> +
> + obj->programs = progs;
> + obj->nr_programs = nr_progs + 1;
> + progs[nr_progs] = prog;
> + return &progs[nr_progs];
> +}
> +
> static struct bpf_object *bpf_object__new(const char *path,
> void *obj_buf,
> size_t obj_buf_sz)
> @@ -342,6 +435,21 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
> err = -EEXIST;
> } else
> obj->efile.symbols = data;
> + } else if ((sh.sh_type == SHT_PROGBITS) &&
> + (sh.sh_flags & SHF_EXECINSTR) &&
> + (data->d_size > 0)) {
> + struct bpf_program *prog;
> +
> + prog = bpf_program__new(obj, data->d_buf,
> + data->d_size, name,
> + idx);
> + if (!prog) {
> + pr_warning("failed to alloc program %s (%s)",
> + name, obj->path);
> + err = -ENOMEM;
> + } else
> + pr_debug("found program %s\n",
> + prog->section_name);
> }
> if (err)
> goto out;
> @@ -415,11 +523,20 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
>
> void bpf_object__close(struct bpf_object *obj)
> {
> + size_t i;
> +
> if (!obj)
> return;
>
> bpf_object__elf_finish(obj);
>
> zfree(&obj->maps_buf);
> +
> + if (obj->programs && obj->nr_programs) {
> + for (i = 0; i < obj->nr_programs; i++)
> + bpf_program__clear(&obj->programs[i]);
> + }
> + zfree(&obj->programs);
> +
> free(obj);
> }
> --
> 1.8.3.4

2015-07-10 03:16:57

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 02/39] bpf tools: Collect eBPF programs from their own sections



On 2015/7/9 23:58, Arnaldo Carvalho de Melo wrote:
> Em Thu, Jul 09, 2015 at 12:35:05PM +0000, Wang Nan escreveu:
>> This patch collects all programs in an object file into an array of
>> 'struct bpf_program' for further processing. That structure is for
>> representing each eBPF program. 'bpf_prog' should be a better name, but
>> it has been used by linux/filter.h. Although it is a kernel space name,
>> I still prefer to call it 'bpf_program' to prevent possible confusion.
>>
>> bpf_program__new() creates a new 'struct bpf_program' object. It first
>> init a variable in stack using __bpf_program__new(), then if success,
>> enlarges obj->programs array and copy the new object in.
>>
>> Signed-off-by: Wang Nan <[email protected]>
>> Acked-by: Alexei Starovoitov <[email protected]>
>> Cc: Brendan Gregg <[email protected]>
>> Cc: Daniel Borkmann <[email protected]>
>> Cc: David Ahern <[email protected]>
>> Cc: He Kuang <[email protected]>
>> Cc: Jiri Olsa <[email protected]>
>> Cc: Kaixu Xia <[email protected]>
>> Cc: Masami Hiramatsu <[email protected]>
>> Cc: Namhyung Kim <[email protected]>
>> Cc: Paul Mackerras <[email protected]>
>> Cc: Peter Zijlstra <[email protected]>
>> Cc: Zefan Li <[email protected]>
>> Cc: [email protected]
>> Link: http://lkml.kernel.org/r/[email protected]
>> Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
>> ---
>> tools/lib/bpf/libbpf.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 117 insertions(+)
>>
>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>> index 9b016c0..3b717de 100644
>> --- a/tools/lib/bpf/libbpf.c
>> +++ b/tools/lib/bpf/libbpf.c
>> @@ -78,12 +78,27 @@ void libbpf_set_print(libbpf_print_fn_t warn,
>> # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
>> #endif
>>
>> +/*
>> + * bpf_prog should be a better name but it has been used in
>> + * linux/filter.h.
>> + */
>> +struct bpf_program {
>> + /* Index in elf obj file, for relocation use. */
>> + int idx;
>> + char *section_name;
>> + struct bpf_insn *insns;
>> + size_t insns_cnt;
>> +};
>> +
>> 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;
>> +
>> /*
>> * Information when doing elf related work. Only valid if fd
>> * is valid.
>> @@ -100,6 +115,84 @@ struct bpf_object {
>> };
>> #define obj_elf_valid(o) ((o)->efile.elf)
>>
>> +static void bpf_program__clear(struct bpf_program *prog)
>> +{
>> + if (!prog)
>> + return;
>> +
>> + zfree(&prog->section_name);
>> + zfree(&prog->insns);
>> + prog->insns_cnt = 0;
>> + prog->idx = -1;
>> +}
> So in perf land we use 'bpf_program__exit()' as the counterpart of
> bpf_program__init(), i.e. one just initializes fields, allocating
> memory for 'struct bpf_program' members, but does not allocates the
> struct bpf_program itself, because sometimes we embed it inside other
> structs, or we have it in arrays, as you do.
>
> So, to keep that convention, please rename bpf_program__clear() to
> bpf_program__exit() and the next function, __bpf_program__new() to
> bpf_program__init(), with 'struct bpf_program *prog' as the first
> parameter.
>
> To speed things up, from now on, when I see such stuff, I will do the
> changes, put them in a branch with a commiter note, and wait for your
> Ack (or not, if you disagree with something).
>
> One more comment below.
>
>> +
>> +static int
>> +__bpf_program__new(void *data, size_t size, char *name, int idx,
>> + struct bpf_program *prog)
>> +{
>> + if (size < sizeof(struct bpf_insn)) {
>> + pr_warning("corrupted section '%s'\n", name);
>> + return -EINVAL;
>> + }
>> +
>> + bzero(prog, sizeof(*prog));
>> +
>> + prog->section_name = strdup(name);
>> + if (!prog->section_name) {
>> + pr_warning("failed to alloc name for prog %s\n",
>> + name);
>> + goto errout;
>> + }
>> +
>> + prog->insns = malloc(size);
>> + if (!prog->insns) {
>> + pr_warning("failed to alloc insns for %s\n", name);
>> + goto errout;
>> + }
>> + prog->insns_cnt = size / sizeof(struct bpf_insn);
>> + memcpy(prog->insns, data,
>> + prog->insns_cnt * sizeof(struct bpf_insn));
>> + prog->idx = idx;
>> +
>> + return 0;
>> +errout:
>> + bpf_program__clear(prog);
>> + return -ENOMEM;
>> +}
>> +
>> +static struct bpf_program *
>> +bpf_program__new(struct bpf_object *obj, void *data, size_t size,
>> + char *name, int idx)
> This, as well, is not a 'bpf_program' method, it is a 'struct
> bpf_object' one, that will manipulate 'struct bpf_object' internal
> state, changing its struct members to get space for an extra bpf_program
> that was initialized on the stack, if the initialization of such
> bpf_program went well, or bail out otherwise.
>
> So I suggest you rename this to:
>
> int bpf_object__add_program(struct bpf_object *obj, void *data, size_t size, char *name, int idx)
>
> And probably move that debug that uses prog->section_name to just after
> the realloc, here in this function.
>
> I will look at the other patches after lunch, thanks for providing the
> git tree, I will try and use it before looking at the patches
> individually, to get a feel of the whole thing.

I didn't find your code, so I updated my git tree. Please see:

https://github.com/WangNan0/linux/commit/e5ffa4f070ee36cce5130d08622dc305ad9cdb31

And I also resolved a confliction in the next patch.

I think you can comment on current patchset since the modification is
too small to
send again.

And following is new pull request:

The following changes since commit 3381a29cbec5447086c0f726ee9a88c02e60becc:

bpf tools: Collect map definitions from 'maps' section (2015-07-07
13:41:45 -0300)

are available in the git repository at:

https://github.com/WangNan0/linux.git perf/ebpf-for-acme

for you to fetch changes up to ecc453e27f60ff59e0c2d1cdf64fb595b72d2f68:

perf tools: Support attach BPF program on uprobe events (2015-07-10
02:52:57 +0000)

Thank you.

2015-07-13 05:52:24

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 28/39] bpf tools: Load a program with different instances using preprocessor



On 2015/7/9 20:35, Wang Nan wrote:
> In this patch, caller of libbpf is able to control the loaded programs
> by installing a preprocessor callback for a BPF program. With
> preprocessor, different instances can be created from one BPF program.
>
> This patch will be used by perf to generate different prologue for
> different 'struct probe_trace_event' instances matched by one
> 'struct perf_probe_event'.
>
> bpf_program__set_prep() is added to support this feature. Caller
> should pass libbpf the number of instances should be created and a
> preprocessor function which will be called when doing real loading.
> The callback should return instructions arrays for each instances.
>
> fd field in bpf_programs is replaced by instance, which has an nr field
> and fds array. bpf_program__nth_fd() is introduced for read fd of
> instances. Old interface bpf_program__fd() is reimplemented by
> returning the first fd.
>
> Signed-off-by: Wang Nan <[email protected]>
> Signed-off-by: He Kuang <[email protected]>
> ---
> tools/lib/bpf/libbpf.c | 138 +++++++++++++++++++++++++++++++++++++++++++++----
> tools/lib/bpf/libbpf.h | 22 ++++++++
> 2 files changed, 151 insertions(+), 9 deletions(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index b37aab1..6fcd042 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -98,7 +98,11 @@ struct bpf_program {
> } *reloc_desc;
> int nr_reloc;
>
> - int fd;
> + struct {
> + int nr;
> + int *fds;
> + } instance;
> + bpf_program_prep_t preprocessor;
>
> struct bpf_object *obj;
> void *priv;
> @@ -152,10 +156,19 @@ struct bpf_object {
>
> static void bpf_program__unload(struct bpf_program *prog)
> {
> + int i;
> +
> if (!prog)
> return;
>
> - zclose(prog->fd);
> + if (prog->instance.nr < 0)
> + pr_warning("Internal error when unloading: instance is %d\n",
> + prog->instance.nr);
> + else
> + for (i = 0; i < prog->instance.nr; i++)
> + zclose(prog->instance.fds[i]);
> + prog->instance.nr = -1;
> + zfree(&prog->instance.fds);
> }
>
> static void bpf_program__clear(struct bpf_program *prog)
> @@ -206,7 +219,8 @@ __bpf_program__new(void *data, size_t size, char *name, int idx,
> memcpy(prog->insns, data,
> prog->insns_cnt * sizeof(struct bpf_insn));
> prog->idx = idx;
> - prog->fd = -1;
> + prog->instance.fds = NULL;
> + prog->instance.nr = -1;
>
> return 0;
> errout:
> @@ -798,13 +812,71 @@ static int
> bpf_program__load(struct bpf_program *prog,
> char *license, u32 kern_version)
> {
> - int err, fd;
> + int err = 0, fd, i;
> +
> + if (prog->instance.nr < 0 || !prog->instance.fds) {
> + if (prog->preprocessor) {
> + pr_warning("Internal error: can't load program '%s'\n",
> + prog->section_name);
> + return -EINVAL;
> + }
>
> - err = load_program(prog->insns, prog->insns_cnt,
> - license, kern_version, &fd);
> - if (!err)
> - prog->fd = fd;
> + prog->instance.fds = malloc(sizeof(int));
> + if (prog->instance.fds) {

I lost a '!' here. Should be:

+ if (!prog->instance.fds) {
...

My github tree has updated. Please check:

https://github.com/WangNan0/linux.git perf/ebpf-for-acme


> + pr_warning("No enough memory for fds\n");
> + return -ENOMEM;
> + }
> + prog->instance.nr = 1;
> + prog->instance.fds[0] = -1;
> + }
> +
> + if (!prog->preprocessor) {
> + if (prog->instance.nr != 1)
> + pr_warning("Program '%s' inconsistent: nr(%d) not 1\n",
> + prog->section_name, prog->instance.nr);
> +
> + err = load_program(prog->insns, prog->insns_cnt,
> + license, kern_version, &fd);
> + if (!err)
> + prog->instance.fds[0] = fd;
> + goto out;
> + }
> +
> + for (i = 0; i < prog->instance.nr; i++) {
> + struct bpf_prog_prep_result result;
> + bpf_program_prep_t preprocessor = prog->preprocessor;
> +
> + bzero(&result, sizeof(result));
> + err = preprocessor(prog, i, prog->insns,
> + prog->insns_cnt, &result);
> + if (err) {
> + pr_warning("Preprocessing %dth instance of program '%s' failed\n",
> + i, prog->section_name);
> + goto out;
> + }
> +
> + if (!result.new_insn_ptr || !result.new_insn_cnt) {
> + pr_debug("Skip loading %dth instance of program '%s'\n",
> + i, prog->section_name);
> + prog->instance.fds[i] = -1;
> + continue;
> + }
> +
> + err = load_program(result.new_insn_ptr,
> + result.new_insn_cnt,
> + license, kern_version, &fd);
> +
> + if (err) {
> + pr_warning("Loading %dth instance of program '%s' failed\n",
> + i, prog->section_name);
> + goto out;
> + }
>
> + if (result.pfd)
> + *result.pfd = fd;
> + prog->instance.fds[i] = fd;
> + }
> +out:
> if (err)
> pr_warning("failed to load program '%s'\n",
> prog->section_name);
> @@ -1036,5 +1108,53 @@ const char *bpf_program__title(struct bpf_program *prog, bool dup)
>
> int bpf_program__fd(struct bpf_program *prog)
> {
> - return prog->fd;
> + return bpf_program__nth_fd(prog, 0);
> +}
> +
> +int bpf_program__set_prep(struct bpf_program *prog, int nr_instance,
> + bpf_program_prep_t prep)
> +{
> + int *instance_fds;
> +
> + if (nr_instance <= 0 || !prep)
> + return -EINVAL;
> +
> + if (prog->instance.nr > 0 || prog->instance.fds) {
> + pr_warning("Can't set pre-processor after loading\n");
> + return -EINVAL;
> + }
> +
> + instance_fds = malloc(sizeof(int) * nr_instance);
> + if (!instance_fds) {
> + pr_warning("alloc memory failed for instance of fds\n");
> + return -ENOMEM;
> + }
> +
> + /* fill all fd with -1 */
> + memset(instance_fds, 0xff, sizeof(int) * nr_instance);
> +
> + prog->instance.nr = nr_instance;
> + prog->instance.fds = instance_fds;
> + prog->preprocessor = prep;
> + return 0;
> +}
> +
> +int bpf_program__nth_fd(struct bpf_program *prog, int n)
> +{
> + int fd;
> +
> + if (n >= prog->instance.nr || n < 0) {
> + pr_warning("Can't get the %dth fd from program %s: only %d instances\n",
> + n, prog->section_name, prog->instance.nr);
> + return -EINVAL;
> + }
> +
> + fd = prog->instance.fds[n];
> + if (fd < 0) {
> + pr_warning("%dth instance of program '%s' is invalid\n",
> + n, prog->section_name);
> + return -ENOENT;
> + }
> +
> + return fd;
> }
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index ea8adc2..9fa7b09 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -65,6 +65,28 @@ const char *bpf_program__title(struct bpf_program *prog, bool dup);
>
> int bpf_program__fd(struct bpf_program *prog);
>
> +struct bpf_insn;
> +struct bpf_prog_prep_result {
> + /*
> + * If not NULL, load new instruction array.
> + * If set to NULL, don't load this instance.
> + */
> + struct bpf_insn *new_insn_ptr;
> + int new_insn_cnt;
> +
> + /* If not NULL, result fd is set to it */
> + int *pfd;
> +};
> +
> +typedef int (*bpf_program_prep_t)(struct bpf_program *, int n,
> + struct bpf_insn *, int insn_cnt,
> + struct bpf_prog_prep_result *res);
> +
> +int bpf_program__set_prep(struct bpf_program *prog, int nr_instance,
> + bpf_program_prep_t prep);
> +
> +int bpf_program__nth_fd(struct bpf_program *prog, int n);
> +
> /*
> * We don't need __attribute__((packed)) now since it is
> * unnecessary for 'bpf_map_def' because they are all aligned.

2015-07-13 19:51:32

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 02/39] bpf tools: Collect eBPF programs from their own sections

Em Fri, Jul 10, 2015 at 11:07:53AM +0800, Wangnan (F) escreveu:
> On 2015/7/9 23:58, Arnaldo Carvalho de Melo wrote:
> >Em Thu, Jul 09, 2015 at 12:35:05PM +0000, Wang Nan escreveu:
> >>This patch collects all programs in an object file into an array of
> >>'struct bpf_program' for further processing. That structure is for
> >>representing each eBPF program. 'bpf_prog' should be a better name, but
> >>it has been used by linux/filter.h. Although it is a kernel space name,
> >>I still prefer to call it 'bpf_program' to prevent possible confusion.
> >>
> >>bpf_program__new() creates a new 'struct bpf_program' object. It first
> >>init a variable in stack using __bpf_program__new(), then if success,
> >>enlarges obj->programs array and copy the new object in.
> >>
> >>Signed-off-by: Wang Nan <[email protected]>
> >>Acked-by: Alexei Starovoitov <[email protected]>
> >>Cc: Brendan Gregg <[email protected]>
> >>Cc: Daniel Borkmann <[email protected]>
> >>Cc: David Ahern <[email protected]>
> >>Cc: He Kuang <[email protected]>
> >>Cc: Jiri Olsa <[email protected]>
> >>Cc: Kaixu Xia <[email protected]>
> >>Cc: Masami Hiramatsu <[email protected]>
> >>Cc: Namhyung Kim <[email protected]>
> >>Cc: Paul Mackerras <[email protected]>
> >>Cc: Peter Zijlstra <[email protected]>
> >>Cc: Zefan Li <[email protected]>
> >>Cc: [email protected]
> >>Link: http://lkml.kernel.org/r/[email protected]
> >>Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
> >>---
> >> tools/lib/bpf/libbpf.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++
> >> 1 file changed, 117 insertions(+)
> >>
> >>diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> >>index 9b016c0..3b717de 100644
> >>--- a/tools/lib/bpf/libbpf.c
> >>+++ b/tools/lib/bpf/libbpf.c
> >>@@ -78,12 +78,27 @@ void libbpf_set_print(libbpf_print_fn_t warn,
> >> # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
> >> #endif
> >>+/*
> >>+ * bpf_prog should be a better name but it has been used in
> >>+ * linux/filter.h.
> >>+ */
> >>+struct bpf_program {
> >>+ /* Index in elf obj file, for relocation use. */
> >>+ int idx;
> >>+ char *section_name;
> >>+ struct bpf_insn *insns;
> >>+ size_t insns_cnt;
> >>+};
> >>+
> >> 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;
> >>+
> >> /*
> >> * Information when doing elf related work. Only valid if fd
> >> * is valid.
> >>@@ -100,6 +115,84 @@ struct bpf_object {
> >> };
> >> #define obj_elf_valid(o) ((o)->efile.elf)
> >>+static void bpf_program__clear(struct bpf_program *prog)
> >>+{
> >>+ if (!prog)
> >>+ return;
> >>+
> >>+ zfree(&prog->section_name);
> >>+ zfree(&prog->insns);
> >>+ prog->insns_cnt = 0;
> >>+ prog->idx = -1;
> >>+}
> >So in perf land we use 'bpf_program__exit()' as the counterpart of
> >bpf_program__init(), i.e. one just initializes fields, allocating
> >memory for 'struct bpf_program' members, but does not allocates the
> >struct bpf_program itself, because sometimes we embed it inside other
> >structs, or we have it in arrays, as you do.
> >
> >So, to keep that convention, please rename bpf_program__clear() to
> >bpf_program__exit() and the next function, __bpf_program__new() to
> >bpf_program__init(), with 'struct bpf_program *prog' as the first
> >parameter.
> >
> >To speed things up, from now on, when I see such stuff, I will do the
> >changes, put them in a branch with a commiter note, and wait for your
> >Ack (or not, if you disagree with something).
> >
> >One more comment below.
> >
> >>+
> >>+static int
> >>+__bpf_program__new(void *data, size_t size, char *name, int idx,
> >>+ struct bpf_program *prog)
> >>+{
> >>+ if (size < sizeof(struct bpf_insn)) {
> >>+ pr_warning("corrupted section '%s'\n", name);
> >>+ return -EINVAL;
> >>+ }
> >>+
> >>+ bzero(prog, sizeof(*prog));
> >>+
> >>+ prog->section_name = strdup(name);
> >>+ if (!prog->section_name) {
> >>+ pr_warning("failed to alloc name for prog %s\n",
> >>+ name);
> >>+ goto errout;
> >>+ }
> >>+
> >>+ prog->insns = malloc(size);
> >>+ if (!prog->insns) {
> >>+ pr_warning("failed to alloc insns for %s\n", name);
> >>+ goto errout;
> >>+ }
> >>+ prog->insns_cnt = size / sizeof(struct bpf_insn);
> >>+ memcpy(prog->insns, data,
> >>+ prog->insns_cnt * sizeof(struct bpf_insn));
> >>+ prog->idx = idx;
> >>+
> >>+ return 0;
> >>+errout:
> >>+ bpf_program__clear(prog);
> >>+ return -ENOMEM;
> >>+}
> >>+
> >>+static struct bpf_program *
> >>+bpf_program__new(struct bpf_object *obj, void *data, size_t size,
> >>+ char *name, int idx)
> >This, as well, is not a 'bpf_program' method, it is a 'struct
> >bpf_object' one, that will manipulate 'struct bpf_object' internal
> >state, changing its struct members to get space for an extra bpf_program
> >that was initialized on the stack, if the initialization of such
> >bpf_program went well, or bail out otherwise.
> >
> >So I suggest you rename this to:
> >
> >int bpf_object__add_program(struct bpf_object *obj, void *data, size_t size, char *name, int idx)
> >
> >And probably move that debug that uses prog->section_name to just after
> >the realloc, here in this function.
> >
> >I will look at the other patches after lunch, thanks for providing the
> >git tree, I will try and use it before looking at the patches
> >individually, to get a feel of the whole thing.
>
> I didn't find your code, so I updated my git tree. Please see:
>
> https://github.com/WangNan0/linux/commit/e5ffa4f070ee36cce5130d08622dc305ad9cdb31

Ok, so used bpf_object__add_program, but you still return a bpf_program
pointer, that you do not use for anything, i.e. the failure of
bpf_object__add_program is reported only via a NULL return and you then
assume this was because ENOMEM was the reason, when there are multiple
errors that can cause bpf_object__add_program to fail.

Noted that with a comment on that patch, checked that no later patches
use that return, etc.

- Arnaldo

2015-07-13 19:54:54

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 06/39] bpf tools: Create eBPF maps defined in an object file

Em Thu, Jul 09, 2015 at 12:35:09PM +0000, Wang Nan escreveu:

<SNIP>

> +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, 0xff, sizeof(int) * nr_maps);

Huh? Each entry is an int... If you want to set it all to -1, why don't
you do it as:

memset(obj->map_fds, -1, sizeof(int) * nr_maps);

That way we can even remove that comment :) Doing it, please let me know
if this has some magic twist that makes it be right as-is, if so, please
provide a better comment.

- Arnaldo

> +
> + 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);
> + if (*pfd < 0) {
> + size_t j;
> + int err = *pfd;
> +
> + 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);
> + 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_object__collect_reloc(struct bpf_object *obj)
> {
> int i, err;
> @@ -671,6 +736,42 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
> return __bpf_object__open("[buffer]", obj_buf, obj_buf_sz);
> }
>
> +int bpf_object__unload(struct bpf_object *obj)
> +{
> + size_t i;
> +
> + 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;
> +
> + return 0;
> +}
> +
> +int bpf_object__load(struct bpf_object *obj)
> +{
> + if (!obj)
> + return -EINVAL;
> +
> + if (obj->loaded) {
> + pr_warning("object should not be loaded twice\n");
> + return -EINVAL;
> + }
> +
> + obj->loaded = true;
> + if (bpf_object__create_maps(obj))
> + goto out;
> +
> + return 0;
> +out:
> + bpf_object__unload(obj);
> + pr_warning("failed to load object '%s'\n", obj->path);
> + return -EINVAL;
> +}
> +
> void bpf_object__close(struct bpf_object *obj)
> {
> size_t i;
> @@ -679,6 +780,7 @@ void bpf_object__close(struct bpf_object *obj)
> return;
>
> bpf_object__elf_finish(obj);
> + bpf_object__unload(obj);
>
> zfree(&obj->maps_buf);
>
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index 6e75acd..3e69600 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -30,6 +30,10 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
> size_t obj_buf_sz);
> void bpf_object__close(struct bpf_object *object);
>
> +/* Load/unload object into/from kernel */
> +int bpf_object__load(struct bpf_object *obj);
> +int bpf_object__unload(struct bpf_object *obj);
> +
> /*
> * We don't need __attribute__((packed)) now since it is
> * unnecessary for 'bpf_map_def' because they are all aligned.
> --
> 1.8.3.4

2015-07-13 21:46:09

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 14/39] perf tools: Auto detecting kernel build directory

Em Thu, Jul 09, 2015 at 12:35:17PM +0000, Wang Nan escreveu:
> This patch detects kernel build directory using a embedded shell
> script 'kbuild_detector', which does this by checking existence of
> include/generated/autoconf.h.
>
> clang working directory is changed to kbuild directory if it is found,
> to help user use relative include path. Following patch will detect
> kernel include directory, which contains relative include patch so this
> workdir changing is needed.
>
> Users are allowed to set 'kbuild-dir = ""' manually to disable this
> checking.

What is the advantage of doing this using a hardcoded shell script,
pipes, etc? Why not plain use some asprintf() for the pathnames to be
tested, then check those, etc in plain C?

I have applied everything up to 13/39, provided some coments to the
others, will continue later today/tomorrow.

Pushing what I have to perf/ebpf in my tree,

Ah, please try, in your csets, to add the CC entries for the people you
CC when posting the patches to lkml, so that, when I pull from your
tree and then push to Ingo, people get notified, i.e. to follow the
style we use in tip/ branches.

Right now I am cherry-picking, but at some point I would like to do
direct pulls.

Thanks,

- Arnaldo

> Signed-off-by: Wang Nan <[email protected]>
> ---
> tools/perf/util/llvm-utils.c | 56 +++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 55 insertions(+), 1 deletion(-)
>
> diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
> index dca16e7..2ca2bd6 100644
> --- a/tools/perf/util/llvm-utils.c
> +++ b/tools/perf/util/llvm-utils.c
> @@ -204,6 +204,51 @@ version_notice(void)
> );
> }
>
> +static const char *kbuild_detector =
> +"#!/usr/bin/env sh\n"
> +"DEFAULT_KBUILD_DIR=/lib/modules/`uname -r`/build\n"
> +"if test -z \"$KBUILD_DIR\"\n"
> +"then\n"
> +" KBUILD_DIR=$DEFAULT_KBUILD_DIR\n"
> +"fi\n"
> +"if test -f $KBUILD_DIR/include/generated/autoconf.h\n"
> +"then\n"
> +" echo -n $KBUILD_DIR\n"
> +" exit 0\n"
> +"fi\n"
> +"exit -1\n";
> +
> +static inline void
> +get_kbuild_opts(char **kbuild_dir)
> +{
> + int err;
> +
> + if (!kbuild_dir)
> + return;
> +
> + *kbuild_dir = NULL;
> +
> + if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) {
> + pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n");
> + pr_debug("Skip kbuild options detection.\n");
> + return;
> + }
> +
> + force_set_env("KBUILD_DIR", llvm_param.kbuild_dir);
> + force_set_env("KBUILD_OPTS", llvm_param.kbuild_opts);
> + err = read_from_pipe(kbuild_detector,
> + ((void **)kbuild_dir),
> + NULL);
> + if (err) {
> + pr_warning(
> +"WARNING:\tunable to get correct kernel building directory.\n"
> +"Hint:\tSet correct kbuild directory using 'kbuild-dir' option in [llvm]\n"
> +" \tsection of ~/.perfconfig or set it to \"\" to suppress kbuild\n"
> +" \tdetection.\n\n");
> + return;
> + }
> +}
> +
> int llvm__compile_bpf(const char *path, void **p_obj_buf,
> size_t *p_obj_buf_sz)
> {
> @@ -211,6 +256,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
> char clang_path[PATH_MAX];
> const char *clang_opt = llvm_param.clang_opt;
> const char *template = llvm_param.clang_bpf_cmd_template;
> + char *kbuild_dir = NULL;
> void *obj_buf = NULL;
> size_t obj_buf_sz;
>
> @@ -228,10 +274,16 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
> return -ENOENT;
> }
>
> + /*
> + * This is an optional work. Even it fail we can continue our
> + * work. Needn't to check error return.
> + */
> + get_kbuild_opts(&kbuild_dir);
> +
> force_set_env("CLANG_EXEC", clang_path);
> force_set_env("CLANG_OPTIONS", clang_opt);
> force_set_env("KERNEL_INC_OPTIONS", NULL);
> - force_set_env("WORKING_DIR", ".");
> + force_set_env("WORKING_DIR", kbuild_dir ? : ".");
>
> /*
> * Since we may reset clang's working dir, path of source file
> @@ -252,6 +304,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
> goto errout;
> }
>
> + free(kbuild_dir);
> if (!p_obj_buf)
> free(obj_buf);
> else
> @@ -261,6 +314,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
> *p_obj_buf_sz = obj_buf_sz;
> return 0;
> errout:
> + free(kbuild_dir);
> free(obj_buf);
> if (p_obj_buf)
> *p_obj_buf = NULL;
> --
> 1.8.3.4

2015-07-14 04:03:10

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 02/39] bpf tools: Collect eBPF programs from their own sections



On 2015/7/14 3:51, Arnaldo Carvalho de Melo wrote:
> Em Fri, Jul 10, 2015 at 11:07:53AM +0800, Wangnan (F) escreveu:
>> On 2015/7/9 23:58, Arnaldo Carvalho de Melo wrote:
>>> Em Thu, Jul 09, 2015 at 12:35:05PM +0000, Wang Nan escreveu:
>>>> This patch collects all programs in an object file into an array of
>>>> 'struct bpf_program' for further processing. That structure is for
>>>> representing each eBPF program. 'bpf_prog' should be a better name, but
>>>> it has been used by linux/filter.h. Although it is a kernel space name,
>>>> I still prefer to call it 'bpf_program' to prevent possible confusion.
>>>>
>>>> bpf_program__new() creates a new 'struct bpf_program' object. It first
>>>> init a variable in stack using __bpf_program__new(), then if success,
>>>> enlarges obj->programs array and copy the new object in.
>>>>
>>>> Signed-off-by: Wang Nan <[email protected]>
>>>> Acked-by: Alexei Starovoitov <[email protected]>
>>>> Cc: Brendan Gregg <[email protected]>
>>>> Cc: Daniel Borkmann <[email protected]>
>>>> Cc: David Ahern <[email protected]>
>>>> Cc: He Kuang <[email protected]>
>>>> Cc: Jiri Olsa <[email protected]>
>>>> Cc: Kaixu Xia <[email protected]>
>>>> Cc: Masami Hiramatsu <[email protected]>
>>>> Cc: Namhyung Kim <[email protected]>
>>>> Cc: Paul Mackerras <[email protected]>
>>>> Cc: Peter Zijlstra <[email protected]>
>>>> Cc: Zefan Li <[email protected]>
>>>> Cc: [email protected]
>>>> Link: http://lkml.kernel.org/r/[email protected]
>>>> Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
>>>> ---
>>>> tools/lib/bpf/libbpf.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>> 1 file changed, 117 insertions(+)
>>>>
>>>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>>>> index 9b016c0..3b717de 100644
>>>> --- a/tools/lib/bpf/libbpf.c
>>>> +++ b/tools/lib/bpf/libbpf.c
>>>> @@ -78,12 +78,27 @@ void libbpf_set_print(libbpf_print_fn_t warn,
>>>> # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
>>>> #endif
>>>> +/*
>>>> + * bpf_prog should be a better name but it has been used in
>>>> + * linux/filter.h.
>>>> + */
>>>> +struct bpf_program {
>>>> + /* Index in elf obj file, for relocation use. */
>>>> + int idx;
>>>> + char *section_name;
>>>> + struct bpf_insn *insns;
>>>> + size_t insns_cnt;
>>>> +};
>>>> +
>>>> 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;
>>>> +
>>>> /*
>>>> * Information when doing elf related work. Only valid if fd
>>>> * is valid.
>>>> @@ -100,6 +115,84 @@ struct bpf_object {
>>>> };
>>>> #define obj_elf_valid(o) ((o)->efile.elf)
>>>> +static void bpf_program__clear(struct bpf_program *prog)
>>>> +{
>>>> + if (!prog)
>>>> + return;
>>>> +
>>>> + zfree(&prog->section_name);
>>>> + zfree(&prog->insns);
>>>> + prog->insns_cnt = 0;
>>>> + prog->idx = -1;
>>>> +}
>>> So in perf land we use 'bpf_program__exit()' as the counterpart of
>>> bpf_program__init(), i.e. one just initializes fields, allocating
>>> memory for 'struct bpf_program' members, but does not allocates the
>>> struct bpf_program itself, because sometimes we embed it inside other
>>> structs, or we have it in arrays, as you do.
>>>
>>> So, to keep that convention, please rename bpf_program__clear() to
>>> bpf_program__exit() and the next function, __bpf_program__new() to
>>> bpf_program__init(), with 'struct bpf_program *prog' as the first
>>> parameter.
>>>
>>> To speed things up, from now on, when I see such stuff, I will do the
>>> changes, put them in a branch with a commiter note, and wait for your
>>> Ack (or not, if you disagree with something).
>>>
>>> One more comment below.
>>>
>>>> +
>>>> +static int
>>>> +__bpf_program__new(void *data, size_t size, char *name, int idx,
>>>> + struct bpf_program *prog)
>>>> +{
>>>> + if (size < sizeof(struct bpf_insn)) {
>>>> + pr_warning("corrupted section '%s'\n", name);
>>>> + return -EINVAL;
>>>> + }
>>>> +
>>>> + bzero(prog, sizeof(*prog));
>>>> +
>>>> + prog->section_name = strdup(name);
>>>> + if (!prog->section_name) {
>>>> + pr_warning("failed to alloc name for prog %s\n",
>>>> + name);
>>>> + goto errout;
>>>> + }
>>>> +
>>>> + prog->insns = malloc(size);
>>>> + if (!prog->insns) {
>>>> + pr_warning("failed to alloc insns for %s\n", name);
>>>> + goto errout;
>>>> + }
>>>> + prog->insns_cnt = size / sizeof(struct bpf_insn);
>>>> + memcpy(prog->insns, data,
>>>> + prog->insns_cnt * sizeof(struct bpf_insn));
>>>> + prog->idx = idx;
>>>> +
>>>> + return 0;
>>>> +errout:
>>>> + bpf_program__clear(prog);
>>>> + return -ENOMEM;
>>>> +}
>>>> +
>>>> +static struct bpf_program *
>>>> +bpf_program__new(struct bpf_object *obj, void *data, size_t size,
>>>> + char *name, int idx)
>>> This, as well, is not a 'bpf_program' method, it is a 'struct
>>> bpf_object' one, that will manipulate 'struct bpf_object' internal
>>> state, changing its struct members to get space for an extra bpf_program
>>> that was initialized on the stack, if the initialization of such
>>> bpf_program went well, or bail out otherwise.
>>>
>>> So I suggest you rename this to:
>>>
>>> int bpf_object__add_program(struct bpf_object *obj, void *data, size_t size, char *name, int idx)
>>>
>>> And probably move that debug that uses prog->section_name to just after
>>> the realloc, here in this function.
>>>
>>> I will look at the other patches after lunch, thanks for providing the
>>> git tree, I will try and use it before looking at the patches
>>> individually, to get a feel of the whole thing.
>> I didn't find your code, so I updated my git tree. Please see:
>>
>> https://github.com/WangNan0/linux/commit/e5ffa4f070ee36cce5130d08622dc305ad9cdb31
> Ok, so used bpf_object__add_program, but you still return a bpf_program
> pointer, that you do not use for anything, i.e. the failure of
> bpf_object__add_program is reported only via a NULL return and you then
> assume this was because ENOMEM was the reason, when there are multiple
> errors that can cause bpf_object__add_program to fail.
>
> Noted that with a comment on that patch, checked that no later patches
> use that return, etc.

I saw your modification ann it looks good to me. I'll collect it into my
patchset.

Thank you.

> - Arnaldo

2015-07-14 04:03:00

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 06/39] bpf tools: Create eBPF maps defined in an object file



On 2015/7/14 3:54, Arnaldo Carvalho de Melo wrote:
> Em Thu, Jul 09, 2015 at 12:35:09PM +0000, Wang Nan escreveu:
>
> <SNIP>
>
>> +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, 0xff, sizeof(int) * nr_maps);
> Huh? Each entry is an int... If you want to set it all to -1, why don't
> you do it as:
>
> memset(obj->map_fds, -1, sizeof(int) * nr_maps);
>
> That way we can even remove that comment :) Doing it, please let me know
> if this has some magic twist that makes it be right as-is, if so, please
> provide a better comment.

Yes, it should be better.

Thank you,

> - Arnaldo
>
>> +
>> + 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);
>> + if (*pfd < 0) {
>> + size_t j;
>> + int err = *pfd;
>> +
>> + 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);
>> + 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_object__collect_reloc(struct bpf_object *obj)
>> {
>> int i, err;
>> @@ -671,6 +736,42 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
>> return __bpf_object__open("[buffer]", obj_buf, obj_buf_sz);
>> }
>>
>> +int bpf_object__unload(struct bpf_object *obj)
>> +{
>> + size_t i;
>> +
>> + 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;
>> +
>> + return 0;
>> +}
>> +
>> +int bpf_object__load(struct bpf_object *obj)
>> +{
>> + if (!obj)
>> + return -EINVAL;
>> +
>> + if (obj->loaded) {
>> + pr_warning("object should not be loaded twice\n");
>> + return -EINVAL;
>> + }
>> +
>> + obj->loaded = true;
>> + if (bpf_object__create_maps(obj))
>> + goto out;
>> +
>> + return 0;
>> +out:
>> + bpf_object__unload(obj);
>> + pr_warning("failed to load object '%s'\n", obj->path);
>> + return -EINVAL;
>> +}
>> +
>> void bpf_object__close(struct bpf_object *obj)
>> {
>> size_t i;
>> @@ -679,6 +780,7 @@ void bpf_object__close(struct bpf_object *obj)
>> return;
>>
>> bpf_object__elf_finish(obj);
>> + bpf_object__unload(obj);
>>
>> zfree(&obj->maps_buf);
>>
>> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
>> index 6e75acd..3e69600 100644
>> --- a/tools/lib/bpf/libbpf.h
>> +++ b/tools/lib/bpf/libbpf.h
>> @@ -30,6 +30,10 @@ struct bpf_object *bpf_object__open_buffer(void *obj_buf,
>> size_t obj_buf_sz);
>> void bpf_object__close(struct bpf_object *object);
>>
>> +/* Load/unload object into/from kernel */
>> +int bpf_object__load(struct bpf_object *obj);
>> +int bpf_object__unload(struct bpf_object *obj);
>> +
>> /*
>> * We don't need __attribute__((packed)) now since it is
>> * unnecessary for 'bpf_map_def' because they are all aligned.
>> --
>> 1.8.3.4

2015-07-14 06:57:44

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 14/39] perf tools: Auto detecting kernel build directory



On 2015/7/14 5:46, Arnaldo Carvalho de Melo wrote:
> Em Thu, Jul 09, 2015 at 12:35:17PM +0000, Wang Nan escreveu:
>> This patch detects kernel build directory using a embedded shell
>> script 'kbuild_detector', which does this by checking existence of
>> include/generated/autoconf.h.
>>
>> clang working directory is changed to kbuild directory if it is found,
>> to help user use relative include path. Following patch will detect
>> kernel include directory, which contains relative include patch so this
>> workdir changing is needed.
>>
>> Users are allowed to set 'kbuild-dir = ""' manually to disable this
>> checking.
> What is the advantage of doing this using a hardcoded shell script,
> pipes, etc? Why not plain use some asprintf() for the pathnames to be
> tested, then check those, etc in plain C?
>
> I have applied everything up to 13/39, provided some coments to the
> others, will continue later today/tomorrow.
>
> Pushing what I have to perf/ebpf in my tree,
>
> Ah, please try, in your csets, to add the CC entries for the people you
> CC when posting the patches to lkml, so that, when I pull from your
> tree and then push to Ingo, people get notified, i.e. to follow the
> style we use in tip/ branches.
>
> Right now I am cherry-picking, but at some point I would like to do
> direct pulls.
>
> Thanks,
>
> - Arnaldo
>

Thanks to your reply. I have accepted all of your advise and updated my
csets:

1. Don't use script to check kbuild dir. Instead, use asprintf() +
access();

2. Add CCs in each patches. Commit message of the top two patches in
your perf/ebpf
is updated.

Other changes including:

3. Simplify test__llvm using asprintf();

4. Bugfix: 'bpf tools: Load a program with different instances using
preprocessor':
1. Add missing '!';
2. Allows bpf_program__unload() before the program is loaded to
suppress
warning message in 'perf test 38'.

The following changes since commit 101ef9b4f5c70dbfffa0186102c1014bd81a4ec7:

bpf tools: Link all bpf objects onto a list (2015-07-13 18:48:17 -0300)

are available in the git repository at:

https://github.com/WangNan0/linux.git perf/ebpf-for-acme

for you to fetch changes up to d7425110530a9457f55a44ac01ab81558a5a5cca:

perf tools: Support attach BPF program on uprobe events (2015-07-14
06:40:11 +0000)


Thank you.

2015-07-14 07:25:30

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 14/39] perf tools: Auto detecting kernel build directory



On 2015/7/14 14:56, Wangnan (F) wrote:
>
>
> On 2015/7/14 5:46, Arnaldo Carvalho de Melo wrote:
>> Em Thu, Jul 09, 2015 at 12:35:17PM +0000, Wang Nan escreveu:
>>> This patch detects kernel build directory using a embedded shell
>>> script 'kbuild_detector', which does this by checking existence of
>>> include/generated/autoconf.h.
>>>
>>> clang working directory is changed to kbuild directory if it is found,
>>> to help user use relative include path. Following patch will detect
>>> kernel include directory, which contains relative include patch so this
>>> workdir changing is needed.
>>>
>>> Users are allowed to set 'kbuild-dir = ""' manually to disable this
>>> checking.
>> What is the advantage of doing this using a hardcoded shell script,
>> pipes, etc? Why not plain use some asprintf() for the pathnames to be
>> tested, then check those, etc in plain C?
>>
>> I have applied everything up to 13/39, provided some coments to the
>> others, will continue later today/tomorrow.
>>
>> Pushing what I have to perf/ebpf in my tree,
>>
>> Ah, please try, in your csets, to add the CC entries for the people you
>> CC when posting the patches to lkml, so that, when I pull from your
>> tree and then push to Ingo, people get notified, i.e. to follow the
>> style we use in tip/ branches.
>>
>> Right now I am cherry-picking, but at some point I would like to do
>> direct pulls.
>>
>> Thanks,
>>
>> - Arnaldo
>
> Thanks to your reply. I have accepted all of your advise and updated
> my csets:
>
> 1. Don't use script to check kbuild dir. Instead, use asprintf() +
> access();
>
> 2. Add CCs in each patches. Commit message of the top two patches in
> your perf/ebpf
> is updated.
>
> Other changes including:
>
> 3. Simplify test__llvm using asprintf();
>
> 4. Bugfix: 'bpf tools: Load a program with different instances using
> preprocessor':
> 1. Add missing '!';
> 2. Allows bpf_program__unload() before the program is loaded to
> suppress
> warning message in 'perf test 38'.
>
> The following changes since commit
> 101ef9b4f5c70dbfffa0186102c1014bd81a4ec7:
>
> bpf tools: Link all bpf objects onto a list (2015-07-13 18:48:17 -0300)
>
> are available in the git repository at:
>
> https://github.com/WangNan0/linux.git perf/ebpf-for-acme
>
> for you to fetch changes up to d7425110530a9457f55a44ac01ab81558a5a5cca:
>
up to 7df09f02cba5ded215908e7a4db648966dddec2b.

I added 'Link' fields after CCs.

Thank you.


> perf tools: Support attach BPF program on uprobe events (2015-07-14
> 06:40:11 +0000)
>
>
> Thank you.

2015-07-14 15:37:03

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Em Thu, Jul 09, 2015 at 12:35:03PM +0000, Wang Nan escreveu:
> Hi Arnaldo,
>
> The following changes since commit 3381a29cbec5447086c0f726ee9a88c02e60becc:
>
> bpf tools: Collect map definitions from 'maps' section (2015-07-07 13:41:45 -0300)
>
> are available in the git repository at:
>
> https://github.com/WangNan0/linux.git perf/ebpf-for-acme
>
> for you to fetch changes up to 072b826c5dc6b2031f4f21c59c57eb6ca1dfa7c0:

So, this is really nice:

38: Test LLVM searching and compiling :ERROR: unable to find clang.
Hint: Try to install latest clang/llvm to support BPF. Check your $PATH
and 'clang-path' option in [llvm] section of ~/.perfconfig.
LLVM 3.7 or newer is required. Which can be found from http://llvm.org
You may want to try git trunk:
git clone http://llvm.org/git/llvm.git
and
git clone http://llvm.org/git/clang.git

Or fetch the latest clang/llvm 3.7 from pre-built llvm packages for
debian/ubuntu:
http://llvm.org/apt

If you are using old version of clang, change 'clang-bpf-cmd-template'
option in [llvm] section of ~/.perfconfig to:

"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS \
-working-directory $WORKING_DIR -c $CLANG_SOURCE \
-emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -"
(Replace /path/to/llc with path to your llc)

FAILED!
[acme@zoo linux]$

I have the trees cloned and plan to follow these instructions to have this test
passing, to then move on to the next patches, but one request, please change
the above to produce:

38: Test LLVM searching and compiling :(no clang) Ok run 'perf test -v LLVM' for instructions on having a clang environment

'perf test' will accept either '38' or any substring in the test description and
will run just the ones specified, and with -v we will spare people not interested
in this eBPF support of these nice verbose instructions :-)

If we find clang but then fail to build, ok, then this is a hard error, I think,
and 'perf test' should FAIL that test.

Regards,

- Arnaldo

2015-07-15 11:08:36

by Wang Nan

[permalink] [raw]
Subject: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs



On 2015/7/14 23:36, Arnaldo Carvalho de Melo wrote:
> Em Thu, Jul 09, 2015 at 12:35:03PM +0000, Wang Nan escreveu:
>> Hi Arnaldo,
>>
>> The following changes since commit 3381a29cbec5447086c0f726ee9a88c02e60becc:
>>
>> bpf tools: Collect map definitions from 'maps' section (2015-07-07 13:41:45 -0300)
>>
>> are available in the git repository at:
>>
>> https://github.com/WangNan0/linux.git perf/ebpf-for-acme
>>
>> for you to fetch changes up to 072b826c5dc6b2031f4f21c59c57eb6ca1dfa7c0:
> So, this is really nice:
>
> 38: Test LLVM searching and compiling :ERROR: unable to find clang.
> Hint: Try to install latest clang/llvm to support BPF. Check your $PATH
> and 'clang-path' option in [llvm] section of ~/.perfconfig.
> LLVM 3.7 or newer is required. Which can be found from http://llvm.org
> You may want to try git trunk:
> git clone http://llvm.org/git/llvm.git
> and
> git clone http://llvm.org/git/clang.git
>
> Or fetch the latest clang/llvm 3.7 from pre-built llvm packages for
> debian/ubuntu:
> http://llvm.org/apt
>
> If you are using old version of clang, change 'clang-bpf-cmd-template'
> option in [llvm] section of ~/.perfconfig to:
>
> "$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS \
> -working-directory $WORKING_DIR -c $CLANG_SOURCE \
> -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -"
> (Replace /path/to/llc with path to your llc)
>
> FAILED!
> [acme@zoo linux]$
>
> I have the trees cloned and plan to follow these instructions to have this test
> passing, to then move on to the next patches, but one request, please change
> the above to produce:
>
> 38: Test LLVM searching and compiling :(no clang) Ok run 'perf test -v LLVM' for instructions on having a clang environment

The output format you desire is hard to implement, because 'Ok' is printed
by the parent process. We have to find a way to allow child process to pass
'run 'perf test -v LLVM' for ...' to parent, then parent can print it
after 'Ok'.

I change this test as follow:


*Case 1*
When user don't set any [llvm] option in his/her ~/.perfconfig, and 'clang'
is not found in $PATH:

# perf test 'LLVM'
38: Test LLVM searching and compiling : (no
clang, try 'perf test -v LLVM') Skip

and:

# perf test -v 'LLVM'
38: Test LLVM searching and compiling :
--- start ---
test child forked, pid 163505
ERROR: unable to find clang.
Hint: Try to install latest clang/llvm to support BPF. Check your $PATH
...

test child finished with -1
---- end ----
Test LLVM searching and compiling: FAILED!

*Case 2*

If clang can be found or user set something in his/her ~/.perfconfig:
# cat << EOF > ~/.perfconfig
> [llvm]
> clang-path = "/usr/bin/false"
> EOF
# perf test 'LLVM'
38: Test LLVM searching and compiling : (use -v
to see error message) FAILED!

# perf test -v 'LLVM'
38: Test LLVM searching and compiling :
--- start ---
test child forked, pid 20712
Kernel build dir is set to /lib/modules/3.12.28-4-default/build
set env: KBUILD_DIR=/lib/modules/3.12.28-4-default/build
...
ERROR: unable to compile -
Hint: Check error message shown above.
LLVM 3.7 or newer is required. Which can be found from
http://llvm.org
...
test child finished with -1
---- end ----
Test LLVM searching and compiling: FAILED!

*Case 3*
If everything is okay:
# perf test 'LLVM'
38: Test LLVM searching and compiling : Ok

Here is the newest pull request:

The following changes since commit 101ef9b4f5c70dbfffa0186102c1014bd81a4ec7:

bpf tools: Link all bpf objects onto a list (2015-07-13 18:48:17 -0300)

are available in the git repository at:

https://github.com/WangNan0/linux.git perf/ebpf-for-acme

for you to fetch changes up to a20f387a54aa38c3cae70775273ff9d69a16d0c6:

perf tools: Support attach BPF program on uprobe events (2015-07-15
10:39:24 +0000)

----------------------------------------------------------------

I'll posted a new 'perf tests: Add LLVM test for eBPF on-the-fly
compiling' by replying
the old one, so you can leave your comment message there if you want.

Thank you.

> 'perf test' will accept either '38' or any substring in the test description and
> will run just the ones specified, and with -v we will spare people not interested
> in this eBPF support of these nice verbose instructions :-)
>
> If we find clang but then fail to build, ok, then this is a hard error, I think,
> and 'perf test' should FAIL that test.
>
> Regards,
>
> - Arnaldo

2015-07-15 11:02:59

by Wang Nan

[permalink] [raw]
Subject: [PATCH updated 16/39] perf tests: Add LLVM test for eBPF on-the-fly compiling

Previous patches introduce llvm__compile_bpf() to compile source file to
eBPF object. This patch adds testcase to test it. It also tests libbpf
by opening generated object after applying next patch which introduces
HAVE_LIBBPF_SUPPORT option.

Since llvm__compile_bpf() prints long messages which users who don't
explicitly test llvm doesn't care, this patch set verbose to -1 to
suppress all debug, warning and error message, and hint user use
'perf test -v' to see the full output.

For the same reason, if clang is not found in PATH and there's no [llvm]
section in .perfconfig, skip this test.

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Cc: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/tests/Build | 1 +
tools/perf/tests/builtin-test.c | 4 ++
tools/perf/tests/llvm.c | 98 +++++++++++++++++++++++++++++++++++++++++
tools/perf/tests/tests.h | 1 +
tools/perf/util/llvm-utils.c | 9 ++++
tools/perf/util/llvm-utils.h | 10 +++++
6 files changed, 123 insertions(+)
create mode 100644 tools/perf/tests/llvm.c

diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index d20d6e6..c1518bd 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -32,6 +32,7 @@ perf-y += sample-parsing.o
perf-y += parse-no-sample-id-all.o
perf-y += kmod-path.o
perf-y += thread-map.o
+perf-y += llvm.o

perf-$(CONFIG_X86) += perf-time-to-tsc.o

diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index c1dde73..136cd93 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -175,6 +175,10 @@ static struct test {
.func = test__thread_map,
},
{
+ .desc = "Test LLVM searching and compiling",
+ .func = test__llvm,
+ },
+ {
.func = NULL,
},
};
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
new file mode 100644
index 0000000..a337356
--- /dev/null
+++ b/tools/perf/tests/llvm.c
@@ -0,0 +1,98 @@
+#include <stdio.h>
+#include <bpf/libbpf.h>
+#include <util/llvm-utils.h>
+#include <util/cache.h>
+#include "tests.h"
+#include "debug.h"
+
+static int perf_config_cb(const char *var, const char *val,
+ void *arg __maybe_unused)
+{
+ return perf_default_config(var, val, arg);
+}
+
+/*
+ * Randomly give it a "version" section since we don't really load it
+ * into kernel
+ */
+static const char test_bpf_prog[] =
+ "__attribute__((section(\"do_fork\"), used)) "
+ "int fork(void *ctx) {return 0;} "
+ "char _license[] __attribute__((section(\"license\"), used)) = \"GPL\";"
+ "int _version __attribute__((section(\"version\"), used)) = 0x40100;";
+
+#ifdef HAVE_LIBBPF_SUPPORT
+static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz)
+{
+ struct bpf_object *obj;
+
+ obj = bpf_object__open_buffer(obj_buf, obj_buf_sz);
+ if (!obj)
+ return -1;
+ bpf_object__close(obj);
+ return 0;
+}
+#else
+static int test__bpf_parsing(void *obj_buf __maybe_unused,
+ size_t obj_buf_sz __maybe_unused)
+{
+ fprintf(stderr, " (skip bpf parsing)");
+ return 0;
+}
+#endif
+
+int test__llvm(void)
+{
+ char *tmpl_new, *clang_opt_new;
+ void *obj_buf;
+ size_t obj_buf_sz;
+ int err, old_verbose;
+
+ perf_config(perf_config_cb, NULL);
+
+ /*
+ * Skip this test if user's .perfconfig doesn't set [llvm] section
+ * and clang is not found in $PATH, and this is not perf test -v
+ */
+ if (verbose == 0 && !llvm_param.user_set_param && llvm__search_clang()) {
+ fprintf(stderr, " (no clang, try 'perf test -v LLVM')");
+ return TEST_SKIP;
+ }
+
+ old_verbose = verbose;
+ /*
+ * llvm is verbosity when error. Suppress all error output if
+ * not 'perf test -v'.
+ */
+ if (verbose == 0)
+ verbose = -1;
+
+ if (!llvm_param.clang_bpf_cmd_template)
+ return -1;
+
+ if (!llvm_param.clang_opt)
+ llvm_param.clang_opt = strdup("");
+
+ err = asprintf(&tmpl_new, "echo '%s' | %s", test_bpf_prog,
+ llvm_param.clang_bpf_cmd_template);
+ if (err < 0)
+ return -1;
+ err = asprintf(&clang_opt_new, "-xc %s", llvm_param.clang_opt);
+ if (err < 0)
+ return -1;
+
+ llvm_param.clang_bpf_cmd_template = tmpl_new;
+ llvm_param.clang_opt = clang_opt_new;
+ err = llvm__compile_bpf("-", &obj_buf, &obj_buf_sz);
+
+ verbose = old_verbose;
+ if (err) {
+ if (!verbose)
+ fprintf(stderr, " (use -v to see error message)");
+ return -1;
+ }
+
+ err = test__bpf_parsing(obj_buf, obj_buf_sz);
+ free(obj_buf);
+ return err;
+}
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index ebb47d9..bf113a2 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -62,6 +62,7 @@ int test__fdarray__filter(void);
int test__fdarray__add(void);
int test__kmod_path__parse(void);
int test__thread_map(void);
+int test__llvm(void);

#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
index b2fa0b7..22fd69d 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -22,6 +22,7 @@ struct llvm_param llvm_param = {
.clang_opt = NULL,
.kbuild_dir = NULL,
.kbuild_opts = NULL,
+ .user_set_param = false,
};

int perf_llvm_config(const char *var, const char *value)
@@ -42,6 +43,7 @@ int perf_llvm_config(const char *var, const char *value)
llvm_param.kbuild_opts = strdup(value);
else
return -1;
+ llvm_param.user_set_param = true;
return 0;
}

@@ -396,3 +398,10 @@ errout:
*p_obj_buf_sz = 0;
return err;
}
+
+int llvm__search_clang(void)
+{
+ char clang_path[PATH_MAX];
+
+ return search_program(llvm_param.clang_path, "clang", clang_path);
+}
diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h
index d23adbc..5b3cf1c 100644
--- a/tools/perf/util/llvm-utils.h
+++ b/tools/perf/util/llvm-utils.h
@@ -29,6 +29,13 @@ struct llvm_param {
* compiling. Should not be used for dynamic compiling.
*/
const char *kbuild_opts;
+ /*
+ * Default is false. If one of the above fields is set by user
+ * explicitly then user_set_llvm is set to true. This is used
+ * for perf test. If user doesn't set anything in .perfconfig
+ * and clang is not found, don't trigger llvm test.
+ */
+ bool user_set_param;
};

extern struct llvm_param llvm_param;
@@ -36,4 +43,7 @@ extern int perf_llvm_config(const char *var, const char *value);

extern int llvm__compile_bpf(const char *path, void **p_obj_buf,
size_t *p_obj_buf_sz);
+
+/* This function is for test__llvm() use only */
+extern int llvm__search_clang(void);
#endif
--
1.8.3.4

2015-07-15 11:20:22

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Em Wed, Jul 15, 2015 at 06:49:40PM +0800, Wangnan (F) escreveu:
> On 2015/7/14 23:36, Arnaldo Carvalho de Melo wrote:
> >I have the trees cloned and plan to follow these instructions to have this test
> >passing, to then move on to the next patches, but one request, please change
> >the above to produce:

> >38: Test LLVM searching and compiling :(no clang) Ok run 'perf test -v LLVM' for instructions on having a clang environment

> The output format you desire is hard to implement, because 'Ok' is printed
> by the parent process. We have to find a way to allow child process to pass
> 'run 'perf test -v LLVM' for ...' to parent, then parent can print it
> after 'Ok'.

> I change this test as follow:

>From a quick look: excellent, thanks for improving this, I downloaded
llvm and clang, built it, and the test passes, will apply this new patch
and continue from there,

- Arnaldo

2015-07-17 02:34:44

by Wang Nan

[permalink] [raw]
Subject: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs



On 2015/7/15 19:20, Arnaldo Carvalho de Melo wrote:
> Em Wed, Jul 15, 2015 at 06:49:40PM +0800, Wangnan (F) escreveu:
>> On 2015/7/14 23:36, Arnaldo Carvalho de Melo wrote:
>>> I have the trees cloned and plan to follow these instructions to have this test
>>> passing, to then move on to the next patches, but one request, please change
>>> the above to produce:
>>> 38: Test LLVM searching and compiling :(no clang) Ok run 'perf test -v LLVM' for instructions on having a clang environment
>> The output format you desire is hard to implement, because 'Ok' is printed
>> by the parent process. We have to find a way to allow child process to pass
>> 'run 'perf test -v LLVM' for ...' to parent, then parent can print it
>> after 'Ok'.
>
>> I change this test as follow:
> From a quick look: excellent, thanks for improving this, I downloaded
> llvm and clang, built it, and the test passes, will apply this new patch
> and continue from there,
>
> - Arnaldo

Hi Arnaldo,

What's the reviewing going on?

I collected some more patches on my git tree to avoid losting, and will
collect more on it.
I'll keep this patchset at the bottom so I think you shoud not be affected.

Here is the new branch:

https://github.com/WangNan0/linux.git ebpf

Do you want me to keep the original perf/ebpf-for-acme branch?

Thank you.

2015-07-21 11:09:43

by Wang Nan

[permalink] [raw]
Subject: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Hi Arnaldo,

Have you recovered from your cold?

Could you please repick patch "perf tools: Introduce llvm config options"
from my github:

https://github.com/WangNan0/linux/commit/f9fb25c518c7e52a47e98c36ce49f51051bbccf4

I made a small change that introduce '-D__KERNEL__' in front of
$CLANG_OPTIONS,
so samples in 'samples/ebpf' should be compilable with perf now.

I'll repost a patch by replying the original one also.

The following changes since commit 101ef9b4f5c70dbfffa0186102c1014bd81a4ec7:

bpf tools: Link all bpf objects onto a list (2015-07-13 18:48:17 -0300)

are available in the git repository at:

https://github.com/WangNan0/linux.git ebpf

for you to fetch changes up to 51f169df1d11ea842a808d751f9c620371c55e74:

bpf: Introduce function for outputing data to perf event (2015-07-21
10:55:48 +0000)

----------------------------------------------------------------

Thank you.


On 2015/7/17 11:48, Arnaldo Carvalho de Melo wrote:
>
> On Jul 16, 2015 11:34 PM, "Wangnan (F)" <[email protected]
> <mailto:[email protected]>> wrote:
> > On 2015/7/15 19:20, Arnaldo Carvalho de Melo wrote:
> >>
> >> Em Wed, Jul 15, 2015 at 06:49:40PM +0800, Wangnan (F) escreveu:
> >>>
> >>> On 2015/7/14 23:36, Arnaldo Carvalho de Melo wrote:
> >>>>
> >>>> I have the trees cloned and plan to follow these instructions to
> have this test
> >>>> passing, to then move on to the next patches, but one request,
> please change
> >>>> the above to produce:
> >>>> 38: Test LLVM searching and compiling :(no clang)
> Ok run 'perf test -v LLVM' for instructions on having a clang
> environment
> >>>
> >>> The output format you desire is hard to implement, because 'Ok' is
> printed
> >>> by the parent process. We have to find a way to allow child
> process to pass
> >>> 'run 'perf test -v LLVM' for ...' to parent, then parent can print it
> >>> after 'Ok'.
> >>
> >>
> >>>
> >>> I change this test as follow:
> >>
> >> From a quick look: excellent, thanks for improving this, I downloaded
> >> llvm and clang, built it, and the test passes, will apply this new
> patch
> >> and continue from there,
> >>
> >> - Arnaldo
> >
> >
> > Hi Arnaldo,
> >
> > What's the reviewing going on?
>
> Was sidetracked with some other stuff and was a bit slow due to a
> cold, hope to get back to work on ebpf+perf in 8 hours ir so.
>
> Thanks,
>
> Arnaldo
>
> > I collected some more patches on my git tree to avoid losting, and
> will collect more on it.
> > I'll keep this patchset at the bottom so I think you shoud not be
> affected.
> >
> > Here is the new branch:
> >
> > https://github.com/WangNan0/linux.git ebpf
> >
> > Do you want me to keep the original perf/ebpf-for-acme branch?
> >
> > Thank you.
> >
> >
>

2015-07-21 11:14:10

by Wang Nan

[permalink] [raw]
Subject: [PATCH 12/39 update] perf tools: Introduce llvm config options

This patch introduces [llvm] config section with 5 options. Following
patches will use then to config llvm dynamica compiling.

'llvm-utils.[ch]' is introduced in this patch for holding all
llvm/clang related stuffs.

Example:

[llvm]
# Path to clang. If omit, search it from $PATH.
clang-path = "/path/to/clang"

# Cmdline template. Following line shows its default value.
# Environment variable is used to passing options.
#
# *NOTE*: -D__KERNEL__ MUST appears before $CLANG_OPTIONS,
# so user have a chance to use -U__KERNEL__ in $CLANG_OPTIONS
# to cancel it.
clang-bpf-cmd-template = "$CLANG_EXEC -D__KERNEL__ $CLANG_OPTIONS \
$KERNEL_INC_OPTIONS -Wno-unused-value \
-Wno-pointer-sign -working-directory \
$WORKING_DIR -c $CLANG_SOURCE -target \
bpf -O2 -o -"

# Options passed to clang, will be passed to cmdline by
# $CLANG_OPTIONS.
clang-opt = "-Wno-unused-value -Wno-pointer-sign"

# kbuild directory. If not set, use /lib/modules/`uname -r`/build.
# If set to "" deliberately, skip kernel header auto-detector.
kbuild-dir = "/path/to/kernel/build"

# Options passed to 'make' when detecting kernel header options.
kbuild-opts = "ARCH=x86_64"

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/n/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---

Introduce -D__KERNEL__, put it in front of $CLANG_OPTIONS. code in samples/bpf
should be built by perf after this change.

---
tools/perf/util/Build | 1 +
tools/perf/util/config.c | 4 ++++
tools/perf/util/llvm-utils.c | 45 ++++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/llvm-utils.h | 36 +++++++++++++++++++++++++++++++++++
4 files changed, 86 insertions(+)
create mode 100644 tools/perf/util/llvm-utils.c
create mode 100644 tools/perf/util/llvm-utils.h

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 601d114..bc24293 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -14,6 +14,7 @@ libperf-y += find_next_bit.o
libperf-y += help.o
libperf-y += kallsyms.o
libperf-y += levenshtein.o
+libperf-y += llvm-utils.o
libperf-y += parse-options.o
libperf-y += parse-events.o
libperf-y += path.o
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index e18f653..2e452ac 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -12,6 +12,7 @@
#include "cache.h"
#include "exec_cmd.h"
#include "util/hist.h" /* perf_hist_config */
+#include "util/llvm-utils.h" /* perf_llvm_config */

#define MAXNAME (256)

@@ -408,6 +409,9 @@ int perf_default_config(const char *var, const char *value,
if (!prefixcmp(var, "call-graph."))
return perf_callchain_config(var, value);

+ if (!prefixcmp(var, "llvm."))
+ return perf_llvm_config(var, value);
+
/* Add other config variables here. */
return 0;
}
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
new file mode 100644
index 0000000..472e8cd
--- /dev/null
+++ b/tools/perf/util/llvm-utils.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015, Wang Nan <[email protected]>
+ * Copyright (C) 2015, Huawei Inc.
+ */
+
+#include <stdio.h>
+#include "util.h"
+#include "debug.h"
+#include "llvm-utils.h"
+#include "cache.h"
+
+#define CLANG_BPF_CMD_DEFAULT_TEMPLATE \
+ "$CLANG_EXEC -D__KERNEL__ $CLANG_OPTIONS " \
+ "$KERNEL_INC_OPTIONS -Wno-unused-value " \
+ "-Wno-pointer-sign -working-directory " \
+ "$WORKING_DIR -c \"$CLANG_SOURCE\" -target bpf -O2 -o -"
+
+struct llvm_param llvm_param = {
+ .clang_path = "clang",
+ .clang_bpf_cmd_template = CLANG_BPF_CMD_DEFAULT_TEMPLATE,
+ .clang_opt = NULL,
+ .kbuild_dir = NULL,
+ .kbuild_opts = NULL,
+};
+
+int perf_llvm_config(const char *var, const char *value)
+{
+ if (prefixcmp(var, "llvm."))
+ return 0;
+ var += sizeof("llvm.") - 1;
+
+ if (!strcmp(var, "clang-path"))
+ llvm_param.clang_path = strdup(value);
+ else if (!strcmp(var, "clang-bpf-cmd-template"))
+ llvm_param.clang_bpf_cmd_template = strdup(value);
+ else if (!strcmp(var, "clang-opt"))
+ llvm_param.clang_opt = strdup(value);
+ else if (!strcmp(var, "kbuild-dir"))
+ llvm_param.kbuild_dir = strdup(value);
+ else if (!strcmp(var, "kbuild-opts"))
+ llvm_param.kbuild_opts = strdup(value);
+ else
+ return -1;
+ return 0;
+}
diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h
new file mode 100644
index 0000000..504b799
--- /dev/null
+++ b/tools/perf/util/llvm-utils.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015, Wang Nan <[email protected]>
+ * Copyright (C) 2015, Huawei Inc.
+ */
+#ifndef __LLVM_UTILS_H
+#define __LLVM_UTILS_H
+
+#include "debug.h"
+
+struct llvm_param {
+ /* Path of clang executable */
+ const char *clang_path;
+ /*
+ * Template of clang bpf compiling. 5 env variables
+ * can be used:
+ * $CLANG_EXEC: Path to clang.
+ * $CLANG_OPTIONS: Extra options to clang.
+ * $KERNEL_INC_OPTIONS: Kernel include directories.
+ * $WORKING_DIR: Kernel source directory.
+ * $CLANG_SOURCE: Source file to be compiled.
+ */
+ const char *clang_bpf_cmd_template;
+ /* Will be filled in $CLANG_OPTIONS */
+ const char *clang_opt;
+ /* Where to find kbuild system */
+ const char *kbuild_dir;
+ /*
+ * Arguments passed to make, like 'ARCH=arm' if doing cross
+ * compiling. Should not be used for dynamic compiling.
+ */
+ const char *kbuild_opts;
+};
+
+extern struct llvm_param llvm_param;
+extern int perf_llvm_config(const char *var, const char *value);
+#endif
--
1.8.3.4

2015-07-21 11:42:18

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Em Tue, Jul 21, 2015 at 07:09:12PM +0800, Wangnan (F) escreveu:
> Hi Arnaldo,
>
> Have you recovered from your cold?

Much better now, thanks.

> Could you please repick patch "perf tools: Introduce llvm config options"
> from my github:
>
> https://github.com/WangNan0/linux/commit/f9fb25c518c7e52a47e98c36ce49f51051bbccf4
>
> I made a small change that introduce '-D__KERNEL__' in front of
> $CLANG_OPTIONS,
> so samples in 'samples/ebpf' should be compilable with perf now.
>
> I'll repost a patch by replying the original one also.

Ok, saw that 12/39 repost, will pick it

> The following changes since commit 101ef9b4f5c70dbfffa0186102c1014bd81a4ec7:
>
> bpf tools: Link all bpf objects onto a list (2015-07-13 18:48:17 -0300)
>
> are available in the git repository at:
>
> https://github.com/WangNan0/linux.git ebpf
>
> for you to fetch changes up to 51f169df1d11ea842a808d751f9c620371c55e74:
>
> bpf: Introduce function for outputing data to perf event (2015-07-21
> 10:55:48 +0000)
>
> ----------------------------------------------------------------
>
> Thank you.
>
>
> On 2015/7/17 11:48, Arnaldo Carvalho de Melo wrote:
> >
> >On Jul 16, 2015 11:34 PM, "Wangnan (F)" <[email protected]
> ><mailto:[email protected]>> wrote:
> >> On 2015/7/15 19:20, Arnaldo Carvalho de Melo wrote:
> >>>
> >>> Em Wed, Jul 15, 2015 at 06:49:40PM +0800, Wangnan (F) escreveu:
> >>>>
> >>>> On 2015/7/14 23:36, Arnaldo Carvalho de Melo wrote:
> >>>>>
> >>>>> I have the trees cloned and plan to follow these instructions to have
> >this test
> >>>>> passing, to then move on to the next patches, but one request, please
> >change
> >>>>> the above to produce:
> >>>>> 38: Test LLVM searching and compiling :(no clang) Ok
> >run 'perf test -v LLVM' for instructions on having a clang environment
> >>>>
> >>>> The output format you desire is hard to implement, because 'Ok' is
> >printed
> >>>> by the parent process. We have to find a way to allow child process to
> >pass
> >>>> 'run 'perf test -v LLVM' for ...' to parent, then parent can print it
> >>>> after 'Ok'.
> >>>
> >>>
> >>>>
> >>>> I change this test as follow:
> >>>
> >>> From a quick look: excellent, thanks for improving this, I downloaded
> >>> llvm and clang, built it, and the test passes, will apply this new
> >patch
> >>> and continue from there,
> >>>
> >>> - Arnaldo
> >>
> >>
> >> Hi Arnaldo,
> >>
> >> What's the reviewing going on?
> >
> >Was sidetracked with some other stuff and was a bit slow due to a cold,
> >hope to get back to work on ebpf+perf in 8 hours ir so.
> >
> >Thanks,
> >
> >Arnaldo
> >
> >> I collected some more patches on my git tree to avoid losting, and will
> >collect more on it.
> >> I'll keep this patchset at the bottom so I think you shoud not be
> >affected.
> >>
> >> Here is the new branch:
> >>
> >> https://github.com/WangNan0/linux.git ebpf
> >>
> >> Do you want me to keep the original perf/ebpf-for-acme branch?
> >>
> >> Thank you.
> >>
> >>
> >
>

2015-07-22 04:40:25

by Wang Nan

[permalink] [raw]
Subject: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Hi Arnaldo,

I noticed that you have rebase your perf/ebpf branch to 4.2-rc3. I have also
done it in my tree. Please see:

https://github.com/WangNan0/linux.git ebpf

Also, please recollect patch "perf tests: Add LLVM test for eBPF on-the-fly
compiling", which adds verbose control. I'll also post it again.

Thank you.

On 2015/7/21 19:41, Arnaldo Carvalho de Melo wrote:
> Em Tue, Jul 21, 2015 at 07:09:12PM +0800, Wangnan (F) escreveu:
>> Hi Arnaldo,
>>
>> Have you recovered from your cold?
> Much better now, thanks.
>
>> Could you please repick patch "perf tools: Introduce llvm config options"
>> from my github:
>>
>> https://github.com/WangNan0/linux/commit/f9fb25c518c7e52a47e98c36ce49f51051bbccf4
>>
>> I made a small change that introduce '-D__KERNEL__' in front of
>> $CLANG_OPTIONS,
>> so samples in 'samples/ebpf' should be compilable with perf now.
>>
>> I'll repost a patch by replying the original one also.
> Ok, saw that 12/39 repost, will pick it
>
>> The following changes since commit 101ef9b4f5c70dbfffa0186102c1014bd81a4ec7:
>>
>> bpf tools: Link all bpf objects onto a list (2015-07-13 18:48:17 -0300)
>>
>> are available in the git repository at:
>>
>> https://github.com/WangNan0/linux.git ebpf
>>
>> for you to fetch changes up to 51f169df1d11ea842a808d751f9c620371c55e74:
>>
>> bpf: Introduce function for outputing data to perf event (2015-07-21
>> 10:55:48 +0000)
>>
>> ----------------------------------------------------------------
>>
>> Thank you.
>>
>>
>> On 2015/7/17 11:48, Arnaldo Carvalho de Melo wrote:
>>> On Jul 16, 2015 11:34 PM, "Wangnan (F)" <[email protected]
>>> <mailto:[email protected]>> wrote:
>>>> On 2015/7/15 19:20, Arnaldo Carvalho de Melo wrote:
>>>>> Em Wed, Jul 15, 2015 at 06:49:40PM +0800, Wangnan (F) escreveu:
>>>>>> On 2015/7/14 23:36, Arnaldo Carvalho de Melo wrote:
>>>>>>> I have the trees cloned and plan to follow these instructions to have
>>> this test
>>>>>>> passing, to then move on to the next patches, but one request, please
>>> change
>>>>>>> the above to produce:
>>>>>>> 38: Test LLVM searching and compiling :(no clang) Ok
>>> run 'perf test -v LLVM' for instructions on having a clang environment
>>>>>> The output format you desire is hard to implement, because 'Ok' is
>>> printed
>>>>>> by the parent process. We have to find a way to allow child process to
>>> pass
>>>>>> 'run 'perf test -v LLVM' for ...' to parent, then parent can print it
>>>>>> after 'Ok'.
>>>>>
>>>>>> I change this test as follow:
>>>>> From a quick look: excellent, thanks for improving this, I downloaded
>>>>> llvm and clang, built it, and the test passes, will apply this new
>>> patch
>>>>> and continue from there,
>>>>>
>>>>> - Arnaldo
>>>>
>>>> Hi Arnaldo,
>>>>
>>>> What's the reviewing going on?
>>> Was sidetracked with some other stuff and was a bit slow due to a cold,
>>> hope to get back to work on ebpf+perf in 8 hours ir so.
>>>
>>> Thanks,
>>>
>>> Arnaldo
>>>
>>>> I collected some more patches on my git tree to avoid losting, and will
>>> collect more on it.
>>>> I'll keep this patchset at the bottom so I think you shoud not be
>>> affected.
>>>> Here is the new branch:
>>>>
>>>> https://github.com/WangNan0/linux.git ebpf
>>>>
>>>> Do you want me to keep the original perf/ebpf-for-acme branch?
>>>>
>>>> Thank you.
>>>>
>>>>

2015-07-22 04:46:33

by Wang Nan

[permalink] [raw]
Subject: [PATCH 16/39 RESEND] perf tests: Add LLVM test for eBPF on-the-fly compiling

Previous patches introduce llvm__compile_bpf() to compile source file to
eBPF object. This patch adds testcase to test it. It also tests libbpf
by opening generated object after applying next patch which introduces
HAVE_LIBBPF_SUPPORT option.

Since llvm__compile_bpf() prints long messages which users who don't
explicitly test llvm doesn't care, this patch set verbose to -1 to
suppress all debug, warning and error message, and hint user use
'perf test -v' to see the full output.

For the same reason, if clang is not found in PATH and there's no [llvm]
section in .perfconfig, skip this test.

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Cc: Arnaldo Carvalho de Melo <[email protected]>
Link: http://lkml.kernel.org/n/[email protected]
---
Suppress error message if clang not found.
---
tools/perf/tests/Build | 1 +
tools/perf/tests/builtin-test.c | 4 ++
tools/perf/tests/llvm.c | 98 +++++++++++++++++++++++++++++++++++++++++
tools/perf/tests/tests.h | 1 +
tools/perf/util/llvm-utils.c | 9 ++++
tools/perf/util/llvm-utils.h | 10 +++++
6 files changed, 123 insertions(+)
create mode 100644 tools/perf/tests/llvm.c

diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index d20d6e6..c1518bd 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -32,6 +32,7 @@ perf-y += sample-parsing.o
perf-y += parse-no-sample-id-all.o
perf-y += kmod-path.o
perf-y += thread-map.o
+perf-y += llvm.o

perf-$(CONFIG_X86) += perf-time-to-tsc.o

diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index c1dde73..136cd93 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -175,6 +175,10 @@ static struct test {
.func = test__thread_map,
},
{
+ .desc = "Test LLVM searching and compiling",
+ .func = test__llvm,
+ },
+ {
.func = NULL,
},
};
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
new file mode 100644
index 0000000..a337356
--- /dev/null
+++ b/tools/perf/tests/llvm.c
@@ -0,0 +1,98 @@
+#include <stdio.h>
+#include <bpf/libbpf.h>
+#include <util/llvm-utils.h>
+#include <util/cache.h>
+#include "tests.h"
+#include "debug.h"
+
+static int perf_config_cb(const char *var, const char *val,
+ void *arg __maybe_unused)
+{
+ return perf_default_config(var, val, arg);
+}
+
+/*
+ * Randomly give it a "version" section since we don't really load it
+ * into kernel
+ */
+static const char test_bpf_prog[] =
+ "__attribute__((section(\"do_fork\"), used)) "
+ "int fork(void *ctx) {return 0;} "
+ "char _license[] __attribute__((section(\"license\"), used)) = \"GPL\";"
+ "int _version __attribute__((section(\"version\"), used)) = 0x40100;";
+
+#ifdef HAVE_LIBBPF_SUPPORT
+static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz)
+{
+ struct bpf_object *obj;
+
+ obj = bpf_object__open_buffer(obj_buf, obj_buf_sz);
+ if (!obj)
+ return -1;
+ bpf_object__close(obj);
+ return 0;
+}
+#else
+static int test__bpf_parsing(void *obj_buf __maybe_unused,
+ size_t obj_buf_sz __maybe_unused)
+{
+ fprintf(stderr, " (skip bpf parsing)");
+ return 0;
+}
+#endif
+
+int test__llvm(void)
+{
+ char *tmpl_new, *clang_opt_new;
+ void *obj_buf;
+ size_t obj_buf_sz;
+ int err, old_verbose;
+
+ perf_config(perf_config_cb, NULL);
+
+ /*
+ * Skip this test if user's .perfconfig doesn't set [llvm] section
+ * and clang is not found in $PATH, and this is not perf test -v
+ */
+ if (verbose == 0 && !llvm_param.user_set_param && llvm__search_clang()) {
+ fprintf(stderr, " (no clang, try 'perf test -v LLVM')");
+ return TEST_SKIP;
+ }
+
+ old_verbose = verbose;
+ /*
+ * llvm is verbosity when error. Suppress all error output if
+ * not 'perf test -v'.
+ */
+ if (verbose == 0)
+ verbose = -1;
+
+ if (!llvm_param.clang_bpf_cmd_template)
+ return -1;
+
+ if (!llvm_param.clang_opt)
+ llvm_param.clang_opt = strdup("");
+
+ err = asprintf(&tmpl_new, "echo '%s' | %s", test_bpf_prog,
+ llvm_param.clang_bpf_cmd_template);
+ if (err < 0)
+ return -1;
+ err = asprintf(&clang_opt_new, "-xc %s", llvm_param.clang_opt);
+ if (err < 0)
+ return -1;
+
+ llvm_param.clang_bpf_cmd_template = tmpl_new;
+ llvm_param.clang_opt = clang_opt_new;
+ err = llvm__compile_bpf("-", &obj_buf, &obj_buf_sz);
+
+ verbose = old_verbose;
+ if (err) {
+ if (!verbose)
+ fprintf(stderr, " (use -v to see error message)");
+ return -1;
+ }
+
+ err = test__bpf_parsing(obj_buf, obj_buf_sz);
+ free(obj_buf);
+ return err;
+}
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index ebb47d9..bf113a2 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -62,6 +62,7 @@ int test__fdarray__filter(void);
int test__fdarray__add(void);
int test__kmod_path__parse(void);
int test__thread_map(void);
+int test__llvm(void);

#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
index b34411b..cdf036e 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -22,6 +22,7 @@ struct llvm_param llvm_param = {
.clang_opt = NULL,
.kbuild_dir = NULL,
.kbuild_opts = NULL,
+ .user_set_param = false,
};

int perf_llvm_config(const char *var, const char *value)
@@ -42,6 +43,7 @@ int perf_llvm_config(const char *var, const char *value)
llvm_param.kbuild_opts = strdup(value);
else
return -1;
+ llvm_param.user_set_param = true;
return 0;
}

@@ -396,3 +398,10 @@ errout:
*p_obj_buf_sz = 0;
return err;
}
+
+int llvm__search_clang(void)
+{
+ char clang_path[PATH_MAX];
+
+ return search_program(llvm_param.clang_path, "clang", clang_path);
+}
diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h
index d23adbc..5b3cf1c 100644
--- a/tools/perf/util/llvm-utils.h
+++ b/tools/perf/util/llvm-utils.h
@@ -29,6 +29,13 @@ struct llvm_param {
* compiling. Should not be used for dynamic compiling.
*/
const char *kbuild_opts;
+ /*
+ * Default is false. If one of the above fields is set by user
+ * explicitly then user_set_llvm is set to true. This is used
+ * for perf test. If user doesn't set anything in .perfconfig
+ * and clang is not found, don't trigger llvm test.
+ */
+ bool user_set_param;
};

extern struct llvm_param llvm_param;
@@ -36,4 +43,7 @@ extern int perf_llvm_config(const char *var, const char *value);

extern int llvm__compile_bpf(const char *path, void **p_obj_buf,
size_t *p_obj_buf_sz);
+
+/* This function is for test__llvm() use only */
+extern int llvm__search_clang(void);
#endif
--
1.8.3.4

2015-07-31 15:36:39

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Em Wed, Jul 22, 2015 at 12:40:05PM +0800, Wangnan (F) escreveu:
> I noticed that you have rebase your perf/ebpf branch to 4.2-rc3. I have also
> done it in my tree. Please see:
>
> https://github.com/WangNan0/linux.git ebpf
>
> Also, please recollect patch "perf tests: Add LLVM test for eBPF on-the-fly
> compiling", which adds verbose control. I'll also post it again.

I did it and some other rebases, expect more, but now I've got to:

commit f0f99c45295e67787ad532492d9bebd6d9b56d3a
Author: Wang Nan <[email protected]>
Date: Wed Jul 8 11:50:57 2015 +0000

perf record: Enable passing bpf object file to --event

Which I split in 3 patches, no changes to the code, just avoiding doing
multiple things per patch, but point here is, when I see "enable passing
bpf object file to --event" I apply the patch and expect to be able to
go straight away and do:

perf record -e foo.o sleep 1

And get some informative message as to hey, yes, I managed to do what
you asked or something is wrong, but I get:

[root@felicio ~]# perf record --event foo.o sleep
event syntax error: 'foo.o'
\___ parser error
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
[root@felicio ~]#

So, is it enabled or is it not? Doesn't look like. I.e. I think the best
thing is to only expose this when it can be used. Till that time, we can
go on adding infrastructure to enable that and test that infrastructure
via 'perf test', like you did with 'perf test LLVM', thanks for doing
that:

[root@felicio ~]# perf test LLVM
38: Test LLVM searching and compiling : Ok

And in verbose mode we can see that it is finding everything it needs on this
RHEL7.1 test machine, after I got all the pieces from the llvm project git
repos and built it:

[root@felicio ~]# perf test -v LLVM
38: Test LLVM searching and compiling :
--- start ---
test child forked, pid 24457
Kernel build dir is set to /lib/modules/4.2.0-rc3+/build
set env: KBUILD_DIR=/lib/modules/4.2.0-rc3+/build
unset env: KBUILD_OPTS
include option is set to -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.8.3/include -I/home/acme/git/linux/arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated -I/home/acme/git/linux/include -Iinclude -I/home/acme/git/linux/arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I/home/acme/git/linux/include/uapi -Iinclude/generated/uapi -include /home/acme/git/linux/include/linux/kconfig.h
set env: CLANG_EXEC=/usr/local/bin/clang
set env: CLANG_OPTIONS=-xc
set env: KERNEL_INC_OPTIONS= -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.8.3/include -I/home/acme/git/linux/arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated -I/home/acme/git/linux/include -Iinclude -I/home/acme/git/linux/arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I/home/acme/git/linux/include/uapi -Iinclude/generated/uapi -include /home/acme/git/linux/include/linux/kconfig.h
set env: WORKING_DIR=/lib/modules/4.2.0-rc3+/build
set env: CLANG_SOURCE=-
llvm compiling command template: echo '__attribute__((section("do_fork"), used)) int fork(void *ctx) {return 0;} char _license[] __attribute__((section("license"), used)) = "GPL";int _version __attribute__((section("version"), used)) = 0x40100;' | $CLANG_EXEC -D__KERNEL__ $CLANG_OPTIONS $KERNEL_INC_OPTIONS -Wno-unused-value -Wno-pointer-sign -working-directory $WORKING_DIR -c "$CLANG_SOURCE" -target bpf -O2 -o -
test child finished with 0
---- end ----
Test LLVM searching and compiling: Ok
[root@felicio ~]#

Then, the next patch is even more exciting:

perf record: Compile scriptlets if pass '.c' to --event

But its changelog doesn't come with a "hello_bpf.c" for me to use nor
the output of using it... bummer, I expected to use this new feature
right now :-)

Also, originally it had this name:

[acme@felicio linux]$ git show --oneline f0f99c45295e67787ad532492d9bebd6d9b56d3a | head -1
f0f99c45295e perf record: Enable passing bpf object file to --event

But it doesn't even touch 'perf record':

[acme@felicio linux]$ git show f0f99c45295e67787ad532492d9bebd6d9b56d3a | grep builtin-record
[acme@felicio linux]$

So perhaps this was an artifact of refactoring previous patchkits?

I.e. I haven't looked at the other patches, but conceptually I can't think
_why_ would we expose a new feature that would only later be wired up and
then _really_ work.

Please reorder the patches to only expose --event foo.[oc] to when we can use
it.

Please continue from what I have in my perf/ebpf branch, it is all rebased to
latest tip/perf/core and passes most of the tests I put it thru (make -C
tools/perf build-test, 'perf test', fedora 21, RHEL 7.1).

Thanks!

- Arnaldo

2015-07-31 20:32:16

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Em Fri, Jul 31, 2015 at 12:35:22PM -0300, Arnaldo Carvalho de Melo escreveu:
> But point here is, when I see "enable passing
> bpf object file to --event" I apply the patch and expect to be able to
> go straight away and do:

> perf record -e foo.o sleep 1

> And get some informative message as to hey, yes, I managed to do what
> you asked or something is wrong, but I get:

> [root@felicio ~]# perf record --event foo.o sleep
> event syntax error: 'foo.o'
> \___ parser error
> 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
> [root@felicio ~]#

> So, is it enabled or is it not? Doesn't look like. I.e. I think the best
> thing is to only expose this when it can be used. Till that time, we can

I just tried gdb'ing this with a breakpoint on parse_events_load_bpf(),
but when I run:

(gdb) b parse_events_load_bpf
Breakpoint 1 at 0x48b11b: file util/parse-events.c, line 488.
(gdb) run record -e foo.o sleep 1
Starting program: /root/bin/perf record -e foo.o sleep 1
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
event syntax error: 'foo.o'
\___ parser error
Run 'perf list' for a list of valid events

Somehow it is not calling what the changeset says it would call when passing
"-e foo.o", investigating...

- Arnaldo

2015-08-03 02:38:49

by Wang Nan

[permalink] [raw]
Subject: Re: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs



On 2015/8/1 4:31, Arnaldo Carvalho de Melo wrote:
> Em Fri, Jul 31, 2015 at 12:35:22PM -0300, Arnaldo Carvalho de Melo escreveu:
>> But point here is, when I see "enable passing
>> bpf object file to --event" I apply the patch and expect to be able to
>> go straight away and do:
>
>> perf record -e foo.o sleep 1
>
>> And get some informative message as to hey, yes, I managed to do what
>> you asked or something is wrong, but I get:
>
>> [root@felicio ~]# perf record --event foo.o sleep
>> event syntax error: 'foo.o'
>> \___ parser error
>> 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
>> [root@felicio ~]#
>
>> So, is it enabled or is it not? Doesn't look like. I.e. I think the best
>> thing is to only expose this when it can be used. Till that time, we can
> I just tried gdb'ing this with a breakpoint on parse_events_load_bpf(),
> but when I run:
>
> (gdb) b parse_events_load_bpf
> Breakpoint 1 at 0x48b11b: file util/parse-events.c, line 488.
> (gdb) run record -e foo.o sleep 1
> Starting program: /root/bin/perf record -e foo.o sleep 1
> [Thread debugging using libthread_db enabled]
> Using host libthread_db library "/lib64/libthread_db.so.1".
> event syntax error: 'foo.o'
> \___ parser error
> Run 'perf list' for a list of valid events
>
> Somehow it is not calling what the changeset says it would call when passing
> "-e foo.o", investigating...


Have you tried

/root/bin/perf record -e ./foo.o sleep 1

The key is './'.

I have reproduced the problem. Haven't noticed that because I always use './' name.

Will look into it.

Thank you, and glad to see you start looking at this cset.


> - Arnaldo

2015-08-03 15:07:45

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Em Mon, Aug 03, 2015 at 10:37:59AM +0800, Wangnan (F) escreveu:
> On 2015/8/1 4:31, Arnaldo Carvalho de Melo wrote:
> >Somehow it is not calling what the changeset says it would call when passing
> >"-e foo.o", investigating...

> Have you tried

> /root/bin/perf record -e ./foo.o sleep 1

> The key is './'.

Nope, trying it now I get:

[root@felicio ~]# perf record -e ./foo.o sleep 1
libbpf: failed to open ./foo.o: No such file or directory
bpf: failed to load ./foo.o
invalid or unsupported event: './foo.o'
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
[root@felicio ~]#

Way too much verbosity, I think this should be just:

[root@felicio ~]# perf record -e ./foo.o sleep 1
perf: Failed to open ./foo.o: No such file or directory

I.e. Avoid messages coming directly from a library, as you don't know
what kind of UI tools using such library will use, it maybe GTK, for
instance, and then sending stuff to stderr or stdout is inappropriate.

> I have reproduced the problem. Haven't noticed that because I always use './' name.

Right, right now this is required, it should not be.

> Will look into it.

Thanks, I will try checkint the other patches, taking this current
limitation into account, so that I can comment on them.

- Arnaldo

2015-08-03 15:19:19

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Em Mon, Aug 03, 2015 at 12:07:28PM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Mon, Aug 03, 2015 at 10:37:59AM +0800, Wangnan (F) escreveu:
> > On 2015/8/1 4:31, Arnaldo Carvalho de Melo wrote:
> > >Somehow it is not calling what the changeset says it would call when passing
> > >"-e foo.o", investigating...
>
> > Have you tried
>
> > /root/bin/perf record -e ./foo.o sleep 1
>
> > The key is './'.
>
> Nope, trying it now I get:
>
> [root@felicio ~]# perf record -e ./foo.o sleep 1
> libbpf: failed to open ./foo.o: No such file or directory
> bpf: failed to load ./foo.o

And with a .c suffix:

[root@felicio ~]# perf record -e ./foo.c sleep 1
clang-3.8: error: no such file or directory: '/root/./foo.c'
clang-3.8: error: no input files
ERROR: unable to compile ./foo.c
Hint: Check error message shown above.
LLVM 3.7 or newer is required. Which can be found from http://llvm.org
You may want to try git trunk:
git clone http://llvm.org/git/llvm.git
and
git clone http://llvm.org/git/clang.git

Or fetch the latest clang/llvm 3.7 from pre-built llvm packages for
debian/ubuntu:
http://llvm.org/apt

If you are using old version of clang, change 'clang-bpf-cmd-template'
option in [llvm] section of ~/.perfconfig to:

"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS \
-working-directory $WORKING_DIR -c $CLANG_SOURCE \
-emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -"
(Replace /path/to/llc with path to your llc)

Hint: You can also pre-compile it into .o
invalid or unsupported event: './foo.c'
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
[root@felicio ~]#

Now to find a hello.c BPF scriptlet...

- Arnaldo

2015-08-03 15:53:37

by Wang Nan

[permalink] [raw]
Subject: Re: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs



?????ҵ? iPhone

> ?? 2015??8??3?գ?????11:19??Arnaldo Carvalho de Melo <[email protected]> д????
>
> Em Mon, Aug 03, 2015 at 12:07:28PM -0300, Arnaldo Carvalho de Melo escreveu:
>> Em Mon, Aug 03, 2015 at 10:37:59AM +0800, Wangnan (F) escreveu:
>>> On 2015/8/1 4:31, Arnaldo Carvalho de Melo wrote:
>>>> Somehow it is not calling what the changeset says it would call when passing
>>>> "-e foo.o", investigating...
>>
>>> Have you tried
>>
>>> /root/bin/perf record -e ./foo.o sleep 1
>>
>>> The key is './'.
>>
>> Nope, trying it now I get:
>>
>> [root@felicio ~]# perf record -e ./foo.o sleep 1
>> libbpf: failed to open ./foo.o: No such file or directory
>> bpf: failed to load ./foo.o
>
> And with a .c suffix:
>
> [root@felicio ~]# perf record -e ./foo.c sleep 1
> clang-3.8: error: no such file or directory: '/root/./foo.c'
> clang-3.8: error: no input files

What's the content of your foo.c? Could you please have a look?

I considered moving .o enabler down so when you see that patch the filter will be
workable. However , like the llvm support, doing so will enable a bulk of code by
one patch, that's not good. Moving .c enabler down is easy.

Now I'm working on a BPF testcase. Tomorrow you will see a new test:

# perf test BPF

It enforces test__llvm, and forks a 'perf record' to do the filtering, then checks the
result using perf report -D. The patch of the new test is a mark which indicates the
basic function of BPF filter is done. What do you think about this idea?

Thank you.

> ERROR: unable to compile ./foo.c
> Hint: Check error message shown above.
> LLVM 3.7 or newer is required. Which can be found from http://llvm.org
> You may want to try git trunk:
> git clone http://llvm.org/git/llvm.git
> and
> git clone http://llvm.org/git/clang.git
>
> Or fetch the latest clang/llvm 3.7 from pre-built llvm packages for
> debian/ubuntu:
> http://llvm.org/apt
>
> If you are using old version of clang, change 'clang-bpf-cmd-template'
> option in [llvm] section of ~/.perfconfig to:
>
> "$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS \
> -working-directory $WORKING_DIR -c $CLANG_SOURCE \
> -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -"
> (Replace /path/to/llc with path to your llc)
>
> Hint: You can also pre-compile it into .o
> invalid or unsupported event: './foo.c'
> 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
> [root@felicio ~]#
>
> Now to find a hello.c BPF scriptlet...
>
> - Arnaldo

2015-08-03 16:11:27

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Em Mon, Aug 03, 2015 at 11:53:05PM +0800, pi3orama escreveu:
>
>
> 发自我的 iPhone
>
> > 在 2015年8月3日,下午11:19,Arnaldo Carvalho de Melo <[email protected]> 写道:
> >
> > Em Mon, Aug 03, 2015 at 12:07:28PM -0300, Arnaldo Carvalho de Melo escreveu:
> >> Em Mon, Aug 03, 2015 at 10:37:59AM +0800, Wangnan (F) escreveu:
> >>> On 2015/8/1 4:31, Arnaldo Carvalho de Melo wrote:
> >>>> Somehow it is not calling what the changeset says it would call when passing
> >>>> "-e foo.o", investigating...
> >>
> >>> Have you tried
> >>
> >>> /root/bin/perf record -e ./foo.o sleep 1
> >>
> >>> The key is './'.
> >>
> >> Nope, trying it now I get:
> >>
> >> [root@felicio ~]# perf record -e ./foo.o sleep 1
> >> libbpf: failed to open ./foo.o: No such file or directory
> >> bpf: failed to load ./foo.o
> >
> > And with a .c suffix:
> >
> > [root@felicio ~]# perf record -e ./foo.c sleep 1
> > clang-3.8: error: no such file or directory: '/root/./foo.c'
> > clang-3.8: error: no input files
>
> What's the content of your foo.c? Could you please have a look?

I said:

> > Now to find a hello.c BPF scriptlet...

Because, as clang says: "error: no such file or directory: '/root/./foo.c'"

It doesn't exists :-)

> I considered moving .o enabler down so when you see that patch the filter will be
> workable. However , like the llvm support, doing so will enable a bulk of code by
> one patch, that's not good. Moving .c enabler down is easy.

Right, probably leaving the .c enabler to be the last one, together with
a "hello_bpf.c" that we can use in testing it is best.

> Now I'm working on a BPF testcase. Tomorrow you will see a new test:

> # perf test BPF

> It enforces test__llvm, and forks a 'perf record' to do the filtering, then checks the
> result using perf report -D. The patch of the new test is a mark which indicates the
> basic function of BPF filter is done. What do you think about this idea?

Excellent idea, you could even save the intermediary .o file and use it
in another test :-)

Sometimes we can't straight away test some infrastructure because the
patch at hand is just glue, that will later be used by a tool, 'perf
record', say, in those cases we can test this glue by using it in a
'perf test' entry.

Thanks!

- Arnaldo

> Thank you.
>
> > ERROR: unable to compile ./foo.c
> > Hint: Check error message shown above.
> > LLVM 3.7 or newer is required. Which can be found from http://llvm.org
> > You may want to try git trunk:
> > git clone http://llvm.org/git/llvm.git
> > and
> > git clone http://llvm.org/git/clang.git
> >
> > Or fetch the latest clang/llvm 3.7 from pre-built llvm packages for
> > debian/ubuntu:
> > http://llvm.org/apt
> >
> > If you are using old version of clang, change 'clang-bpf-cmd-template'
> > option in [llvm] section of ~/.perfconfig to:
> >
> > "$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS \
> > -working-directory $WORKING_DIR -c $CLANG_SOURCE \
> > -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -"
> > (Replace /path/to/llc with path to your llc)
> >
> > Hint: You can also pre-compile it into .o
> > invalid or unsupported event: './foo.c'
> > 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
> > [root@felicio ~]#
> >
> > Now to find a hello.c BPF scriptlet...
> >
> > - Arnaldo

2015-08-03 19:49:24

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Em Mon, Aug 03, 2015 at 01:11:16PM -0300, Arnaldo Carvalho de Melo escreveu:
> > > ERROR: unable to compile ./foo.c
> > > Hint: Check error message shown above.
> > > LLVM 3.7 or newer is required. Which can be found from http://llvm.org
> > > You may want to try git trunk:
> > > git clone http://llvm.org/git/llvm.git
> > > and
<SNIP>
> > > or: perf record [<options>] -- <command> [<options>]

> > > -e, --event <event> event selector. use 'perf list' to list available events
> > > [root@felicio ~]#

> > > Now to find a hello.c BPF scriptlet...

So, we do not need to provide all this LLVM environment installation
hints when we get to any error, i.e. the one above was just becasuse
"./foo.c" doesn't exist, clang ran successfully, so no need for telling
the user how to install it.

The following error also shouldn't emit those hints:

[root@felicio ~]# perf record -e ./lock_page.bpf.c sleep 1
/root/./lock_page.bpf.c:1:5: error: expected parameter declarator
SEC("lock_page=__lock_page page->flags")
^
/root/./lock_page.bpf.c:1:5: error: expected ')'
/root/./lock_page.bpf.c:1:4: note: to match this '('
SEC("lock_page=__lock_page page->flags")
^
/root/./lock_page.bpf.c:1:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int]
SEC("lock_page=__lock_page page->flags")
^
/root/./lock_page.bpf.c:1:41: error: expected ';' after top level declarator
SEC("lock_page=__lock_page page->flags")
^
;
/root/./lock_page.bpf.c:2:22: warning: declaration of 'struct pt_regs' will not be visible outside of this function [-Wvisibility]
int lock_page(struct pt_regs *ctx, int err, unsigned long flags)
^
2 warnings and 3 errors generated.
ERROR: unable to compile ./lock_page.bpf.c
Hint: Check error message shown above.
LLVM 3.7 or newer is required. Which can be found from http://llvm.org
You may want to try git trunk:
git clone http://llvm.org/git/llvm.git
and
git clone http://llvm.org/git/clang.git

Or fetch the latest clang/llvm 3.7 from pre-built llvm packages for
debian/ubuntu:
http://llvm.org/apt

If you are using old version of clang, change 'clang-bpf-cmd-template'
option in [llvm] section of ~/.perfconfig to:

"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS \
-working-directory $WORKING_DIR -c $CLANG_SOURCE \
-emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -"
(Replace /path/to/llc with path to your llc)

Hint: You can also pre-compile it into .o
invalid or unsupported event: './lock_page.bpf.c'
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
[root@felicio ~]#

And I am interested in that "Hint: You can also pre-compile it into .o", seems
like a useful one, if it was on the screen, i.e. how can I pre-compile this
into a .o, its some random piece of eBPF "scriptlet" I found in one of your
csets, after this cset in your patchkit:

[root@felicio ~]# cat lock_page.bpf.c
SEC("lock_page=__lock_page page->flags")
int lock_page(struct pt_regs *ctx, int err, unsigned long flags)
{
return 1;
}
[root@felicio ~]#

Also it would be nice to tell the user, when compilation fails, where to look
for a eBPF scriptlet primer, i.e. where can I find some documentation on how
to write such scriptlets?

At this point I expect it to build the .c into a .o, then, as the wiring up is
not there, tell the user that all went well, but more infrastructure is needed,
apply the following patches from Wang Nan! 8-)

- Arnaldo

2015-08-04 05:28:47

by Wang Nan

[permalink] [raw]
Subject: Re: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Hi Arnaldo,

The following changes since commit 922cc21746202956acb41c89a6190bb50805fa31:

perf tools: Introduce llvm config options (2015-07-31 12:17:50 -0300)

are available in the git repository at:

https://github.com/WangNan0/linux.git ebpf

for you to fetch changes up to d85bf4b6470b8d860bbae25418e5ae3ccd9711e8:

perf tools: Support attach BPF program on uprobe events (2015-08-04
04:59:20 +0000)

----------------------------------------------------------------

The new cset has following improvements:

1. Improve error message: now don't dump LLVM environment setting messages
if clang is found. Also, describe how to pre-compile .c file into .o.

See: perf tools: Call clang to compile C source to object code
https://github.com/WangNan0/linux/commit/264676a5b922aaf1e9be3800fe06d5b67b06cd12

2. Reorder patches, so when 'perf record' is able to accept '--event
file.c', the BPF filter
should work. Also, an example BPF script file is provided, and the
compilation
method is described in commit message.

See:
perf tools: Infrastructure for compiling scriptlets when
passing '.c' to --event
https://github.com/WangNan0/linux/commit/eca622f4a88e1a791fc2405c398256ad572eba54

3. Introduce 'perf test BPF', which uses previous introduced scriptlet,
fork a
'perf record' to utilise it and uses 'perf report' to check the result.

See: perf tests: Enforce LLVM test for BPF test
https://github.com/WangNan0/linux/commit/a7cdab453863c580446dc2c3a3f3a86f21b770ce

perf test: Enable 'perf test' run as test targets
https://github.com/WangNan0/linux/commit/b14f2627e95d348be5ec19bd24a5117e8c2ffe46
and
perf test: Add 'perf test BPF'
https://github.com/WangNan0/linux/commit/8414217dbfa57df4dbb55642dc26205e1c7cbdf1

4. Fix a bug that if the filename doesn't contain '/' it is recongnised
as event name then
failed to be applied by order adjusting in parse-events.l: bring
{bpf_object} and
{bpf_source} ahead.

You need to pop 9 patches from your perf/ebpf tree and rebase my tree.
However, until
patch "perf tools: Enable passing bpf object file to --event" the
changes is tiny. Please
check.

Thank you.

On 2015/8/4 3:49, Arnaldo Carvalho de Melo wrote:
> Em Mon, Aug 03, 2015 at 01:11:16PM -0300, Arnaldo Carvalho de Melo escreveu:
>>>> ERROR: unable to compile ./foo.c
>>>> Hint: Check error message shown above.
>>>> LLVM 3.7 or newer is required. Which can be found from http://llvm.org
>>>> You may want to try git trunk:
>>>> git clone http://llvm.org/git/llvm.git
>>>> and
> <SNIP>
>>>> or: perf record [<options>] -- <command> [<options>]
>>>> -e, --event <event> event selector. use 'perf list' to list available events
>>>> [root@felicio ~]#
>>>> Now to find a hello.c BPF scriptlet...
> So, we do not need to provide all this LLVM environment installation
> hints when we get to any error, i.e. the one above was just becasuse
> "./foo.c" doesn't exist, clang ran successfully, so no need for telling
> the user how to install it.
>
> The following error also shouldn't emit those hints:
>
> [root@felicio ~]# perf record -e ./lock_page.bpf.c sleep 1
> /root/./lock_page.bpf.c:1:5: error: expected parameter declarator
> SEC("lock_page=__lock_page page->flags")
> ^
> /root/./lock_page.bpf.c:1:5: error: expected ')'
> /root/./lock_page.bpf.c:1:4: note: to match this '('
> SEC("lock_page=__lock_page page->flags")
> ^
> /root/./lock_page.bpf.c:1:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int]
> SEC("lock_page=__lock_page page->flags")
> ^
> /root/./lock_page.bpf.c:1:41: error: expected ';' after top level declarator
> SEC("lock_page=__lock_page page->flags")
> ^
> ;
> /root/./lock_page.bpf.c:2:22: warning: declaration of 'struct pt_regs' will not be visible outside of this function [-Wvisibility]
> int lock_page(struct pt_regs *ctx, int err, unsigned long flags)
> ^
> 2 warnings and 3 errors generated.
> ERROR: unable to compile ./lock_page.bpf.c
> Hint: Check error message shown above.
> LLVM 3.7 or newer is required. Which can be found from http://llvm.org
> You may want to try git trunk:
> git clone http://llvm.org/git/llvm.git
> and
> git clone http://llvm.org/git/clang.git
>
> Or fetch the latest clang/llvm 3.7 from pre-built llvm packages for
> debian/ubuntu:
> http://llvm.org/apt
>
> If you are using old version of clang, change 'clang-bpf-cmd-template'
> option in [llvm] section of ~/.perfconfig to:
>
> "$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS \
> -working-directory $WORKING_DIR -c $CLANG_SOURCE \
> -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -"
> (Replace /path/to/llc with path to your llc)
>
> Hint: You can also pre-compile it into .o
> invalid or unsupported event: './lock_page.bpf.c'
> 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
> [root@felicio ~]#
>
> And I am interested in that "Hint: You can also pre-compile it into .o", seems
> like a useful one, if it was on the screen, i.e. how can I pre-compile this
> into a .o, its some random piece of eBPF "scriptlet" I found in one of your
> csets, after this cset in your patchkit:
>
> [root@felicio ~]# cat lock_page.bpf.c
> SEC("lock_page=__lock_page page->flags")
> int lock_page(struct pt_regs *ctx, int err, unsigned long flags)
> {
> return 1;
> }
> [root@felicio ~]#
>
> Also it would be nice to tell the user, when compilation fails, where to look
> for a eBPF scriptlet primer, i.e. where can I find some documentation on how
> to write such scriptlets?
>
> At this point I expect it to build the .c into a .o, then, as the wiring up is
> not there, tell the user that all went well, but more infrastructure is needed,
> apply the following patches from Wang Nan! 8-)
>
> - Arnaldo

2015-08-04 10:43:37

by Wang Nan

[permalink] [raw]
Subject: Re: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs



On 2015/8/4 13:28, Wangnan (F) wrote:
> Hi Arnaldo,
>
> The following changes since commit
> 922cc21746202956acb41c89a6190bb50805fa31:
>
> perf tools: Introduce llvm config options (2015-07-31 12:17:50 -0300)
>
> are available in the git repository at:
>
> https://github.com/WangNan0/linux.git ebpf
>
> for you to fetch changes up to d85bf4b6470b8d860bbae25418e5ae3ccd9711e8:
>
> perf tools: Support attach BPF program on uprobe events (2015-08-04
> 04:59:20 +0000)
>
> ----------------------------------------------------------------
>
> The new cset has following improvements:
>
> 1. Improve error message: now don't dump LLVM environment setting
> messages
> if clang is found. Also, describe how to pre-compile .c file into .o.
>
> See: perf tools: Call clang to compile C source to object code
> https://github.com/WangNan0/linux/commit/264676a5b922aaf1e9be3800fe06d5b67b06cd12
>
>
> 2. Reorder patches, so when 'perf record' is able to accept '--event
> file.c', the BPF filter
> should work. Also, an example BPF script file is provided, and the
> compilation
> method is described in commit message.
>
> See:
> perf tools: Infrastructure for compiling scriptlets when
> passing '.c' to --event
> https://github.com/WangNan0/linux/commit/eca622f4a88e1a791fc2405c398256ad572eba54
>
>
> 3. Introduce 'perf test BPF', which uses previous introduced
> scriptlet, fork a
> 'perf record' to utilise it and uses 'perf report' to check the
> result.
>
> See: perf tests: Enforce LLVM test for BPF test
> https://github.com/WangNan0/linux/commit/a7cdab453863c580446dc2c3a3f3a86f21b770ce
>
>
> perf test: Enable 'perf test' run as test targets
> https://github.com/WangNan0/linux/commit/b14f2627e95d348be5ec19bd24a5117e8c2ffe46
>
> and
> perf test: Add 'perf test BPF'
> https://github.com/WangNan0/linux/commit/8414217dbfa57df4dbb55642dc26205e1c7cbdf1
>

Just realized that 'tempnam' is not safe. Switch to mkdtemp. Please see
the new version:

https://github.com/WangNan0/linux/commit/bd02aed760c4a35fec4483d24c158799f05f6f9e

and pull request update:

The following changes since commit 922cc21746202956acb41c89a6190bb50805fa31:

perf tools: Introduce llvm config options (2015-07-31 12:17:50 -0300)

are available in the git repository at:

https://github.com/WangNan0/linux.git ebpf

for you to fetch changes up to 47ef1d2a64bd0e7931835e1c40caed21f793c8b8:

perf tools: Support attach BPF program on uprobe events (2015-08-04
10:30:50 +0000)

Thank you.


2015-08-04 15:56:08

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Em Tue, Aug 04, 2015 at 06:39:39PM +0800, Wangnan (F) escreveu:
> Just realized that 'tempnam' is not safe. Switch to mkdtemp. Please see the
> new version:
>
> https://github.com/WangNan0/linux/commit/bd02aed760c4a35fec4483d24c158799f05f6f9e
>
> and pull request update:
>
> The following changes since commit 922cc21746202956acb41c89a6190bb50805fa31:
>
> perf tools: Introduce llvm config options (2015-07-31 12:17:50 -0300)
>
> are available in the git repository at:
>
> https://github.com/WangNan0/linux.git ebpf

Ok, but I am checking patch by patch to see if the changes I made are
kept, and for the first one, it wasn't (the second one is the cset in
your newest github tree: cceb9fed5722dc78730e7ed739b75ece4476740f, the
other is for my tree, that was rebased, but the contents should be the
same as what is in my perf/ebpf branch in git.kernel.org), I'm keeping
my version, will check the others:

[acme@felicio linux]$ gdiff f696ecb01b519ae8273b2876b45678b920767f8a cceb9fed5722dc78730e7ed739b75ece4476740f
--- /tmp/gdiff.f696ecb01b519ae8273b2876b45678b920767f8a.y3ojwYFV0g 2015-08-04 12:53:29.898525129 -0300
+++ /tmp/gdiff.cceb9fed5722dc78730e7ed739b75ece4476740f.1ximzKA8ST 2015-08-04 12:53:29.900525095 -0300
@@ -1,4 +1,4 @@
-commit f696ecb01b519ae8273b2876b45678b920767f8a
+commit cceb9fed5722dc78730e7ed739b75ece4476740f
Author: Wang Nan <[email protected]>
Date: Thu Jun 11 10:31:09 2015 +0000

@@ -14,20 +14,8 @@
source code path is injected into shell command using environment
variable using force_set_env().

- Commiter notice:
-
- When building with DEBUG=1 we get a compiler error that gets fixed with
- the same approach described in commit b236512280fb:
-
- perf kmem: Fix compiler warning about may be accessing uninitialized variable
-
- The last argument to strtok_r doesn't need to be initialized, its
- just a placeholder to make this routine reentrant, but gcc doesn't know
- about that and complains, breaking the build, fix it by setting it to
- NULL.
-
Signed-off-by: Wang Nan <[email protected]>
- Acked-by: Alexei Starovoitov <[email protected]>
+ Cc: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
@@ -43,7 +32,7 @@
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>

diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
-index 472e8cd69361..7d01fcc23aa8 100644
+index 472e8cd69361..08e2eb1354e3 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -43,3 +43,228 @@ int perf_llvm_config(const char *var, const char *value)
@@ -55,7 +44,7 @@
+search_program(const char *def, const char *name,
+ char *output)
+{
-+ char *env, *path, *tmp = NULL;
++ char *env, *path, *tmp;
+ char buf[PATH_MAX];
+ int ret;
+

2015-08-04 16:11:27

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Em Tue, Aug 04, 2015 at 12:55:57PM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Tue, Aug 04, 2015 at 06:39:39PM +0800, Wangnan (F) escreveu:

> > https://github.com/WangNan0/linux.git ebpf

> Ok, but I am checking patch by patch to see if the changes I made are
> kept, and for the first one, it wasn't (the second one is the cset in

ouch, you now use a branch named "ebpf" whereas before you used
"perf/ebpf-for-acme" before, that was what I was looking at now :-\

After I realised that, yes, you kept my changes in the "ebpf" branch,
looking there now.

- Arnaldo

2015-08-04 16:14:33

by Wang Nan

[permalink] [raw]
Subject: Re: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs



?????ҵ? iPhone

> ?? 2015??8??5?գ?????12:11??Arnaldo Carvalho de Melo <[email protected]> д????
>
> Em Tue, Aug 04, 2015 at 12:55:57PM -0300, Arnaldo Carvalho de Melo escreveu:
>> Em Tue, Aug 04, 2015 at 06:39:39PM +0800, Wangnan (F) escreveu:
>
>>> https://github.com/WangNan0/linux.git ebpf
>
>> Ok, but I am checking patch by patch to see if the changes I made are
>> kept, and for the first one, it wasn't (the second one is the cset in
>
> ouch, you now use a branch named "ebpf" whereas before you used
> "perf/ebpf-for-acme" before, that was what I was looking at now :-\
>
> After I realised that, yes, you kept my changes in the "ebpf" branch,
> looking there now.
>

I'll remove that branch tomorrow.

Thank you.

> - Arnaldo

2015-08-06 01:50:04

by Wang Nan

[permalink] [raw]
Subject: Re: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Hi Arnaldo,

Have you tried 'perf test BPF'? Is that okay on your environment?

Thank you.

On 2015/8/5 0:11, Arnaldo Carvalho de Melo wrote:
> Em Tue, Aug 04, 2015 at 12:55:57PM -0300, Arnaldo Carvalho de Melo escreveu:
>> Em Tue, Aug 04, 2015 at 06:39:39PM +0800, Wangnan (F) escreveu:
>>> https://github.com/WangNan0/linux.git ebpf
>
>> Ok, but I am checking patch by patch to see if the changes I made are
>> kept, and for the first one, it wasn't (the second one is the cset in
> ouch, you now use a branch named "ebpf" whereas before you used
> "perf/ebpf-for-acme" before, that was what I was looking at now :-\
>
> After I realised that, yes, you kept my changes in the "ebpf" branch,
> looking there now.
>
> - Arnaldo

2015-08-07 02:00:59

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Em Thu, Aug 06, 2015 at 09:44:33AM +0800, Wangnan (F) escreveu:
> Hi Arnaldo,
>
> Have you tried 'perf test BPF'? Is that okay on your environment?

Freshly checked out from your github repo, 'ebpf' branch, looks good, see
output below, back to reading patches...

37: Test thread map : Ok
38: Test LLVM searching and compiling : Ok
39: Test BPF filter : Ok
[root@felicio ~]# set -o vi
[root@felicio ~]# perf test -v BPF
39: Test BPF filter :
--- start ---
test child forked, pid 9746
Kernel build dir is set to /lib/modules/4.2.0-rc3+/build
set env: KBUILD_DIR=/lib/modules/4.2.0-rc3+/build
unset env: KBUILD_OPTS
include option is set to -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.8.3/include -I/home/acme/git/linux/arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated -I/home/acme/git/linux/include -Iinclude -I/home/acme/git/linux/arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I/home/acme/git/linux/include/uapi -Iinclude/generated/uapi -include /home/acme/git/linux/include/linux/kconfig.h
set env: CLANG_EXEC=/usr/local/bin/clang
set env: CLANG_OPTIONS=-xc
set env: KERNEL_INC_OPTIONS= -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.8.3/include -I/home/acme/git/linux/arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated -I/home/acme/git/linux/include -Iinclude -I/home/acme/git/linux/arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I/home/acme/git/linux/include/uapi -Iinclude/generated/uapi -include /home/acme/git/linux/include/linux/kconfig.h
set env: WORKING_DIR=/lib/modules/4.2.0-rc3+/build
set env: CLANG_SOURCE=-
llvm compiling command template: cat << '__EOF__' | $CLANG_EXEC -D__KERNEL__ $CLANG_OPTIONS $KERNEL_INC_OPTIONS -Wno-unused-value -Wno-pointer-sign -working-directory $WORKING_DIR -c "$CLANG_SOURCE" -target bpf -O2 -o -
#define LINUX_VERSION_CODE 0x00040200;
#ifndef LINUX_VERSION_CODE
# error Need LINUX_VERSION_CODE
# error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig'
#endif
#define BPF_ANY 0
#define BPF_MAP_TYPE_ARRAY 2
#define BPF_FUNC_map_lookup_elem 1
#define BPF_FUNC_map_update_elem 2

static void *(*bpf_map_lookup_elem)(void *map, void *key) =
(void *) BPF_FUNC_map_lookup_elem;
static void *(*bpf_map_update_elem)(void *map, void *key, void *value, int flags) =
(void *) BPF_FUNC_map_update_elem;

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))
struct bpf_map_def SEC("maps") flip_table = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = 1,
};

SEC("func=sys_epoll_pwait")
int bpf_func__sys_epoll_pwait(void *ctx)
{
int ind =0;
int *flag = bpf_map_lookup_elem(&flip_table, &ind);
int new_flag;
if (!flag)
return 0;
/* flip flag and store back */
new_flag = !*flag;
bpf_map_update_elem(&flip_table, &ind, &new_flag, BPF_ANY);
return new_flag;
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;

__EOF__

[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.018 MB /tmp/perf-bpf-test-7EbF3J/perf.data (56 samples) ]
test child finished with 0
---- end ----
Test BPF filter: Ok
[root@felicio ~]#

2015-08-07 02:08:41

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: perf eBPF patch ordering. was: Re: perf test LLVM was: Re: [GIT PULL 00/39] perf tools: filtering events using eBPF programs

Em Thu, Aug 06, 2015 at 11:46:52AM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Thu, Aug 06, 2015 at 09:44:33AM +0800, Wangnan (F) escreveu:
> > Have you tried 'perf test BPF'? Is that okay on your environment?

> Freshly checked out from your github repo, 'ebpf' branch, looks good, see
> output below, back to reading patches...

I have replaced the contents of my perf/ebpf branch with what I have so
far read/edited-changelog-comments/compile-tested/tested-to-some-degree,
will continue.

https://git.kernel.org/cgit/linux/kernel/git/acme/linux.git/log/?h=perf/ebpf

- Arnaldo

Subject: [tip:perf/core] perf tools: Introduce llvm config options

Commit-ID: aa61fd05ca79666c973d5b15e0f91ecdc7dcfa21
Gitweb: http://git.kernel.org/tip/aa61fd05ca79666c973d5b15e0f91ecdc7dcfa21
Author: Wang Nan <[email protected]>
AuthorDate: Tue, 21 Jul 2015 11:13:34 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 7 Aug 2015 10:16:59 -0300

perf tools: Introduce llvm config options

This patch introduces [llvm] config section with 5 options. Following
patches will use then to config llvm dynamica compiling.

'llvm-utils.[ch]' is introduced in this patch for holding all
llvm/clang related stuffs.

Example:

[llvm]
# Path to clang. If omit, search it from $PATH.
clang-path = "/path/to/clang"

# Cmdline template. Following line shows its default value.
# Environment variable is used to passing options.
#
# *NOTE*: -D__KERNEL__ MUST appears before $CLANG_OPTIONS,
# so user have a chance to use -U__KERNEL__ in $CLANG_OPTIONS
# to cancel it.
clang-bpf-cmd-template = "$CLANG_EXEC -D__KERNEL__ $CLANG_OPTIONS \
$KERNEL_INC_OPTIONS -Wno-unused-value \
-Wno-pointer-sign -working-directory \
$WORKING_DIR -c $CLANG_SOURCE -target \
bpf -O2 -o -"

# Options passed to clang, will be passed to cmdline by
# $CLANG_OPTIONS.
clang-opt = "-Wno-unused-value -Wno-pointer-sign"

# kbuild directory. If not set, use /lib/modules/`uname -r`/build.
# If set to "" deliberately, skip kernel header auto-detector.
kbuild-dir = "/path/to/kernel/build"

# Options passed to 'make' when detecting kernel header options.
kbuild-opts = "ARCH=x86_64"

Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/Build | 1 +
tools/perf/util/config.c | 4 ++++
tools/perf/util/llvm-utils.c | 45 ++++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/llvm-utils.h | 36 +++++++++++++++++++++++++++++++++++
4 files changed, 86 insertions(+)

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index a1e5168..2ee81d7 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -14,6 +14,7 @@ libperf-y += find_next_bit.o
libperf-y += help.o
libperf-y += kallsyms.o
libperf-y += levenshtein.o
+libperf-y += llvm-utils.o
libperf-y += parse-options.o
libperf-y += parse-events.o
libperf-y += path.o
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index e18f653..2e452ac 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -12,6 +12,7 @@
#include "cache.h"
#include "exec_cmd.h"
#include "util/hist.h" /* perf_hist_config */
+#include "util/llvm-utils.h" /* perf_llvm_config */

#define MAXNAME (256)

@@ -408,6 +409,9 @@ int perf_default_config(const char *var, const char *value,
if (!prefixcmp(var, "call-graph."))
return perf_callchain_config(var, value);

+ if (!prefixcmp(var, "llvm."))
+ return perf_llvm_config(var, value);
+
/* Add other config variables here. */
return 0;
}
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
new file mode 100644
index 0000000..472e8cd
--- /dev/null
+++ b/tools/perf/util/llvm-utils.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015, Wang Nan <[email protected]>
+ * Copyright (C) 2015, Huawei Inc.
+ */
+
+#include <stdio.h>
+#include "util.h"
+#include "debug.h"
+#include "llvm-utils.h"
+#include "cache.h"
+
+#define CLANG_BPF_CMD_DEFAULT_TEMPLATE \
+ "$CLANG_EXEC -D__KERNEL__ $CLANG_OPTIONS " \
+ "$KERNEL_INC_OPTIONS -Wno-unused-value " \
+ "-Wno-pointer-sign -working-directory " \
+ "$WORKING_DIR -c \"$CLANG_SOURCE\" -target bpf -O2 -o -"
+
+struct llvm_param llvm_param = {
+ .clang_path = "clang",
+ .clang_bpf_cmd_template = CLANG_BPF_CMD_DEFAULT_TEMPLATE,
+ .clang_opt = NULL,
+ .kbuild_dir = NULL,
+ .kbuild_opts = NULL,
+};
+
+int perf_llvm_config(const char *var, const char *value)
+{
+ if (prefixcmp(var, "llvm."))
+ return 0;
+ var += sizeof("llvm.") - 1;
+
+ if (!strcmp(var, "clang-path"))
+ llvm_param.clang_path = strdup(value);
+ else if (!strcmp(var, "clang-bpf-cmd-template"))
+ llvm_param.clang_bpf_cmd_template = strdup(value);
+ else if (!strcmp(var, "clang-opt"))
+ llvm_param.clang_opt = strdup(value);
+ else if (!strcmp(var, "kbuild-dir"))
+ llvm_param.kbuild_dir = strdup(value);
+ else if (!strcmp(var, "kbuild-opts"))
+ llvm_param.kbuild_opts = strdup(value);
+ else
+ return -1;
+ return 0;
+}
diff --git a/tools/perf/util/llvm-utils.h b/tools/perf/util/llvm-utils.h
new file mode 100644
index 0000000..504b799
--- /dev/null
+++ b/tools/perf/util/llvm-utils.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015, Wang Nan <[email protected]>
+ * Copyright (C) 2015, Huawei Inc.
+ */
+#ifndef __LLVM_UTILS_H
+#define __LLVM_UTILS_H
+
+#include "debug.h"
+
+struct llvm_param {
+ /* Path of clang executable */
+ const char *clang_path;
+ /*
+ * Template of clang bpf compiling. 5 env variables
+ * can be used:
+ * $CLANG_EXEC: Path to clang.
+ * $CLANG_OPTIONS: Extra options to clang.
+ * $KERNEL_INC_OPTIONS: Kernel include directories.
+ * $WORKING_DIR: Kernel source directory.
+ * $CLANG_SOURCE: Source file to be compiled.
+ */
+ const char *clang_bpf_cmd_template;
+ /* Will be filled in $CLANG_OPTIONS */
+ const char *clang_opt;
+ /* Where to find kbuild system */
+ const char *kbuild_dir;
+ /*
+ * Arguments passed to make, like 'ARCH=arm' if doing cross
+ * compiling. Should not be used for dynamic compiling.
+ */
+ const char *kbuild_opts;
+};
+
+extern struct llvm_param llvm_param;
+extern int perf_llvm_config(const char *var, const char *value);
+#endif

Subject: [tip:perf/core] perf tools: Auto detecting kernel include options

Commit-ID: 0c6d18bfd551622b438e216e4863155f47907b0d
Gitweb: http://git.kernel.org/tip/0c6d18bfd551622b438e216e4863155f47907b0d
Author: Wang Nan <[email protected]>
AuthorDate: Thu, 11 Jun 2015 11:25:49 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Fri, 7 Aug 2015 10:57:23 -0300

perf tools: Auto detecting kernel include options

To help user find correct kernel include options, this patch extracts
them from kbuild system by an embedded script kinc_fetch_script, which
creates a temporary directory, generates Makefile and an empty dummy.o
then use the Makefile to fetch $(NOSTDINC_FLAGS), $(LINUXINCLUDE) and
$(EXTRA_CFLAGS) options. The result is passed to compiler script using
'KERNEL_INC_OPTIONS' environment variable.

Because options from kbuild contains relative path like
'Iinclude/generated/uapi', the work directory must be changed. This is
done by previous patch.

Signed-off-by: Wang Nan <[email protected]>
Acked-by: Alexei Starovoitov <[email protected]>
Cc: Brendan Gregg <[email protected]>
Cc: Daniel Borkmann <[email protected]>
Cc: David Ahern <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Kaixu Xia <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/llvm-utils.c | 60 ++++++++++++++++++++++++++++++++++++++++----
1 file changed, 55 insertions(+), 5 deletions(-)

diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
index 6bfcb2d..5887bb8 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -246,15 +246,42 @@ static int detect_kbuild_dir(char **kbuild_dir)
return -ENOENT;
}

+static const char *kinc_fetch_script =
+"#!/usr/bin/env sh\n"
+"if ! test -d \"$KBUILD_DIR\"\n"
+"then\n"
+" exit -1\n"
+"fi\n"
+"if ! test -f \"$KBUILD_DIR/include/generated/autoconf.h\"\n"
+"then\n"
+" exit -1\n"
+"fi\n"
+"TMPDIR=`mktemp -d`\n"
+"if test -z \"$TMPDIR\"\n"
+"then\n"
+" exit -1\n"
+"fi\n"
+"cat << EOF > $TMPDIR/Makefile\n"
+"obj-y := dummy.o\n"
+"\\$(obj)/%.o: \\$(src)/%.c\n"
+"\t@echo -n \"\\$(NOSTDINC_FLAGS) \\$(LINUXINCLUDE) \\$(EXTRA_CFLAGS)\"\n"
+"EOF\n"
+"touch $TMPDIR/dummy.c\n"
+"make -s -C $KBUILD_DIR M=$TMPDIR $KBUILD_OPTS dummy.o 2>/dev/null\n"
+"RET=$?\n"
+"rm -rf $TMPDIR\n"
+"exit $RET\n";
+
static inline void
-get_kbuild_opts(char **kbuild_dir)
+get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts)
{
int err;

- if (!kbuild_dir)
+ if (!kbuild_dir || !kbuild_include_opts)
return;

*kbuild_dir = NULL;
+ *kbuild_include_opts = NULL;

if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) {
pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n");
@@ -271,6 +298,27 @@ get_kbuild_opts(char **kbuild_dir)
" \tdetection.\n\n");
return;
}
+
+ pr_debug("Kernel build dir is set to %s\n", *kbuild_dir);
+ force_set_env("KBUILD_DIR", *kbuild_dir);
+ force_set_env("KBUILD_OPTS", llvm_param.kbuild_opts);
+ err = read_from_pipe(kinc_fetch_script,
+ (void **)kbuild_include_opts,
+ NULL);
+ if (err) {
+ pr_warning(
+"WARNING:\tunable to get kernel include directories from '%s'\n"
+"Hint:\tTry set clang include options using 'clang-bpf-cmd-template'\n"
+" \toption in [llvm] section of ~/.perfconfig and set 'kbuild-dir'\n"
+" \toption in [llvm] to \"\" to suppress this detection.\n\n",
+ *kbuild_dir);
+
+ free(*kbuild_dir);
+ *kbuild_dir = NULL;
+ return;
+ }
+
+ pr_debug("include option is set to %s\n", *kbuild_include_opts);
}

int llvm__compile_bpf(const char *path, void **p_obj_buf,
@@ -280,7 +328,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
char clang_path[PATH_MAX];
const char *clang_opt = llvm_param.clang_opt;
const char *template = llvm_param.clang_bpf_cmd_template;
- char *kbuild_dir = NULL;
+ char *kbuild_dir = NULL, *kbuild_include_opts = NULL;
void *obj_buf = NULL;
size_t obj_buf_sz;

@@ -302,11 +350,11 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
* This is an optional work. Even it fail we can continue our
* work. Needn't to check error return.
*/
- get_kbuild_opts(&kbuild_dir);
+ get_kbuild_opts(&kbuild_dir, &kbuild_include_opts);

force_set_env("CLANG_EXEC", clang_path);
force_set_env("CLANG_OPTIONS", clang_opt);
- force_set_env("KERNEL_INC_OPTIONS", NULL);
+ force_set_env("KERNEL_INC_OPTIONS", kbuild_include_opts);
force_set_env("WORKING_DIR", kbuild_dir ? : ".");

/*
@@ -330,6 +378,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
}

free(kbuild_dir);
+ free(kbuild_include_opts);
if (!p_obj_buf)
free(obj_buf);
else
@@ -340,6 +389,7 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
return 0;
errout:
free(kbuild_dir);
+ free(kbuild_include_opts);
free(obj_buf);
if (p_obj_buf)
*p_obj_buf = NULL;