2015-11-16 12:48:39

by Wang Nan

[permalink] [raw]
Subject: [PATCH 00/13] perf tools: bpf: Improve BPF program ability

Resend patch set in [1] with sereval improvements based on Arnaldo's
suggestions. This patch set is based on Arnaldo's perf/core.

Patch 1/13 - 2/13 should have already been collected by Arnaldo but
I can't find them in his repository, so I resend them.

Patch 3/13 - 6/13 are improved according to Arnaldo's suggestion: Some
lines are made compat, return values of strdup() are checked, use
named initializers. In addition, error messages are improved to reduce
confusion.

Patch 7/13 - 13/13 are identical to previous version, only cc-lists are
shrinked.

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

He Kuang (1):
perf tools: Add prologue for BPF programs for fetching arguments

Masami Hiramatsu (1):
perf probe: Fix memory leaking on faiulre by clearing all
probe_trace_events

Wang Nan (11):
perf probe: Clear probe_trace_event when add_probe_trace_event() fails
perf tools: Allow BPF program attach to uprobe events
perf tools: Allow BPF program attach to modules
perf tools: Introduce strtobool() to string.c
perf tools: Allow BPF program config probing options
bpf tools: Load a program with different instances using preprocessor
perf tools: Add BPF_PROLOGUE config options for further patches
perf tools: Compile dwarf-regs.c if CONFIG_BPF_PROLOGUE is on
perf tools: Generate prologue for BPF programs
perf test: Test BPF prologue
perf tools: Use same BPF program if arguments are identical

tools/lib/bpf/libbpf.c | 145 ++++++++-
tools/lib/bpf/libbpf.h | 64 ++++
tools/perf/arch/x86/util/Build | 1 +
tools/perf/config/Makefile | 12 +
tools/perf/tests/Build | 9 +-
tools/perf/tests/bpf-script-test-prologue.c | 35 +++
tools/perf/tests/bpf.c | 34 +++
tools/perf/tests/llvm.c | 4 +
tools/perf/tests/llvm.h | 2 +
tools/perf/util/Build | 1 +
tools/perf/util/bpf-loader.c | 432 +++++++++++++++++++++++++-
tools/perf/util/bpf-loader.h | 4 +
tools/perf/util/bpf-prologue.c | 455 ++++++++++++++++++++++++++++
tools/perf/util/bpf-prologue.h | 34 +++
tools/perf/util/include/linux/string.h | 5 +
tools/perf/util/probe-event.c | 7 +-
tools/perf/util/probe-finder.c | 24 +-
tools/perf/util/string.c | 28 ++
18 files changed, 1269 insertions(+), 27 deletions(-)
create mode 100644 tools/perf/tests/bpf-script-test-prologue.c
create mode 100644 tools/perf/util/bpf-prologue.c
create mode 100644 tools/perf/util/bpf-prologue.h

--
1.8.3.4


2015-11-16 12:48:57

by Wang Nan

[permalink] [raw]
Subject: [PATCH 01/13] perf probe: Fix memory leaking on faiulre by clearing all probe_trace_events

From: Masami Hiramatsu <[email protected]>

Fix memory leaking on failure path in debuginfo__find_trace_events()
which frees an array of probe_trace_events but doesn't clear all
allocated sub-structures and strings.
So, before doing zfree(tevs), clear all the array elements which
can have allocated resources.

Signed-off-by: Masami Hiramatsu <[email protected]>
Reported-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/probe-finder.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index bd8f03d..63993d7 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -1246,7 +1246,7 @@ int debuginfo__find_trace_events(struct debuginfo *dbg,
struct trace_event_finder tf = {
.pf = {.pev = pev, .callback = add_probe_trace_event},
.max_tevs = probe_conf.max_probes, .mod = dbg->mod};
- int ret;
+ int ret, i;

/* Allocate result tevs array */
*tevs = zalloc(sizeof(struct probe_trace_event) * tf.max_tevs);
@@ -1258,6 +1258,8 @@ int debuginfo__find_trace_events(struct debuginfo *dbg,

ret = debuginfo__find_probes(dbg, &tf.pf);
if (ret < 0) {
+ for (i = 0; i < tf.ntevs; i++)
+ clear_probe_trace_event(&tf.tevs[i]);
zfree(tevs);
return ret;
}
--
1.8.3.4

2015-11-16 12:52:35

by Wang Nan

[permalink] [raw]
Subject: [PATCH 02/13] perf probe: Clear probe_trace_event when add_probe_trace_event() fails

When probe with glob, error in add_probe_trace_event() won't be passed
to debuginfo__find_trace_events() because it whould be modified by
probe_point_search_cb(). It causes segfault if perf failed to find
argument for one probing point matched by the glob. For example:

# ./perf probe -v -n 'SyS_dup? oldfd'
probe-definition(0): SyS_dup? oldfd
symbol:SyS_dup? file:(null) line:0 offset:0 return:0 lazy:(null)
parsing arg: oldfd into oldfd
1 arguments
Looking at the vmlinux_path (7 entries long)
Using /lib/modules/4.3.0-rc4+/build/vmlinux for symbols
Open Debuginfo file: /lib/modules/4.3.0-rc4+/build/vmlinux
Try to find probe point from debuginfo.
Matched function: SyS_dup3
found inline addr: 0xffffffff812095c0
Probe point found: SyS_dup3+0
Searching 'oldfd' variable in context.
Converting variable oldfd into trace event.
oldfd type is long int.
found inline addr: 0xffffffff812096d4
Probe point found: SyS_dup2+36
Searching 'oldfd' variable in context.
Failed to find 'oldfd' in this function.
Matched function: SyS_dup3
Probe point found: SyS_dup3+0
Searching 'oldfd' variable in context.
Converting variable oldfd into trace event.
oldfd type is long int.
Matched function: SyS_dup2
Probe point found: SyS_dup2+0
Searching 'oldfd' variable in context.
Converting variable oldfd into trace event.
oldfd type is long int.
Found 4 probe_trace_events.
Opening /sys/kernel/debug/tracing//kprobe_events write=1
Writing event: p:probe/SyS_dup3 _text+2135488 oldfd=%di:s64
Segmentation fault (core dumped)

This patch ensures add_probe_trace_event() not touch tf->ntevs and
tf->tevs if it returns failure.

Here is testing result:

# perf probe 'SyS_dup? oldfd'
Failed to find 'oldfd' in this function.
Added new events:
probe:SyS_dup3 (on SyS_dup? with oldfd)
probe:SyS_dup3_1 (on SyS_dup? with oldfd)
probe:SyS_dup2 (on SyS_dup? with oldfd)

You can now use it in all perf tools, such as:

perf record -e probe:SyS_dup2 -aR sleep 1

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/probe-finder.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 63993d7..05012bb 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -1183,7 +1183,7 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
container_of(pf, struct trace_event_finder, pf);
struct perf_probe_point *pp = &pf->pev->point;
struct probe_trace_event *tev;
- struct perf_probe_arg *args;
+ struct perf_probe_arg *args = NULL;
int ret, i;

/* Check number of tevs */
@@ -1198,19 +1198,23 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
ret = convert_to_trace_point(&pf->sp_die, tf->mod, pf->addr,
pp->retprobe, pp->function, &tev->point);
if (ret < 0)
- return ret;
+ goto end;

tev->point.realname = strdup(dwarf_diename(sc_die));
- if (!tev->point.realname)
- return -ENOMEM;
+ if (!tev->point.realname) {
+ ret = -ENOMEM;
+ goto end;
+ }

pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
tev->point.offset);

/* Expand special probe argument if exist */
args = zalloc(sizeof(struct perf_probe_arg) * MAX_PROBE_ARGS);
- if (args == NULL)
- return -ENOMEM;
+ if (args == NULL) {
+ ret = -ENOMEM;
+ goto end;
+ }

ret = expand_probe_args(sc_die, pf, args);
if (ret < 0)
@@ -1234,6 +1238,10 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
}

end:
+ if (ret) {
+ clear_probe_trace_event(tev);
+ tf->ntevs--;
+ }
free(args);
return ret;
}
--
1.8.3.4

2015-11-16 12:48:52

by Wang Nan

[permalink] [raw]
Subject: [PATCH 03/13] perf tools: Allow BPF program attach to 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(
"exec=/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 "exec"
is for uprobe probing.

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

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 4c50411..54bfe62 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -110,6 +110,113 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
}

static int
+config__exec(const char *value, struct perf_probe_event *pev)
+{
+ pev->uprobes = true;
+ pev->target = strdup(value);
+ if (!pev->target)
+ return -ENOMEM;
+ return 0;
+}
+
+static struct {
+ const char *key;
+ const char *usage;
+ const char *desc;
+ int (*func)(const char *, struct perf_probe_event *);
+} bpf_config_terms[] = {
+ {
+ .key = "exec",
+ .usage = "exec=<full path of file>",
+ .desc = "Set uprobe target",
+ .func = config__exec,
+ },
+};
+
+static int
+do_config(const char *key, const char *value,
+ struct perf_probe_event *pev)
+{
+ unsigned int i;
+
+ pr_debug("config bpf program: %s=%s\n", key, value);
+ for (i = 0; i < ARRAY_SIZE(bpf_config_terms); i++)
+ if (strcmp(key, bpf_config_terms[i].key) == 0)
+ return bpf_config_terms[i].func(value, pev);
+
+ pr_debug("BPF: ERROR: invalid config option in object: %s=%s\n",
+ key, value);
+
+ pr_debug("\nHint: Currently valid options are:\n");
+ for (i = 0; i < ARRAY_SIZE(bpf_config_terms); i++)
+ pr_debug("\t%s:\t%s\n", bpf_config_terms[i].usage,
+ bpf_config_terms[i].desc);
+ pr_debug("\n");
+
+ return -BPF_LOADER_ERRNO__CONFIG_TERM;
+}
+
+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_debug("No enough memory: dup config_str failed\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ 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 err ? ERR_PTR(err) : main_str;
+}
+
+static int
+parse_config(const char *config_str, struct perf_probe_event *pev)
+{
+ int err;
+ const char *main_str = parse_config_kvpair(config_str, pev);
+
+ if (IS_ERR(main_str))
+ return PTR_ERR(main_str);
+
+ err = parse_perf_probe_command(main_str, pev);
+ if (err < 0) {
+ pr_debug("bpf: '%s' is not a valid config string\n",
+ config_str);
+ /* parse failed, don't need clear pev. */
+ return -BPF_LOADER_ERRNO__CONFIG;
+ }
+ return 0;
+}
+
+static int
config_bpf_program(struct bpf_program *prog)
{
struct perf_probe_event *pev = NULL;
@@ -131,13 +238,9 @@ config_bpf_program(struct bpf_program *prog)
pev = &priv->pev;

pr_debug("bpf: config program '%s'\n", config_str);
- err = parse_perf_probe_command(config_str, pev);
- if (err < 0) {
- pr_debug("bpf: '%s' is not a valid config string\n",
- config_str);
- err = -BPF_LOADER_ERRNO__CONFIG;
+ err = parse_config(config_str, pev);
+ if (err)
goto errout;
- }

if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) {
pr_debug("bpf: '%s': group for event is set and not '%s'.\n",
@@ -340,6 +443,7 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = {
[ERRCODE_OFFSET(EVENTNAME)] = "No event name found in config string",
[ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error",
[ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet",
+ [ERRCODE_OFFSET(CONFIG_TERM)] = "Invalid config term in config string",
};

static int
@@ -420,6 +524,10 @@ int bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
int err, char *buf, size_t size)
{
bpf__strerror_head(err, buf, size);
+ case BPF_LOADER_ERRNO__CONFIG_TERM: {
+ scnprintf(buf, size, "%s (add -v to see detail)", emsg);
+ break;
+ }
bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'");
bpf__strerror_entry(EACCES, "You need to be root");
bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0");
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 9caf3ae..d19f5c5 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -20,6 +20,7 @@ enum bpf_loader_errno {
BPF_LOADER_ERRNO__EVENTNAME, /* Event name is missing */
BPF_LOADER_ERRNO__INTERNAL, /* BPF loader internal error */
BPF_LOADER_ERRNO__COMPILE, /* Error when compiling BPF scriptlet */
+ BPF_LOADER_ERRNO__CONFIG_TERM, /* Invalid config term in config term */
__BPF_LOADER_ERRNO__END,
};

--
1.8.3.4

2015-11-16 12:49:29

by Wang Nan

[permalink] [raw]
Subject: [PATCH 04/13] perf tools: Allow BPF program attach to modules

By extending the syntax of BPF object section names, this patch allows
user to attach BPF programs to symbol in modules. For example:

SEC("module=i915\n"
"parse_cmds=i915_parse_cmds")
int parse_cmds(void *ctx)
{
return 1;
}

Implementation is very simple: like what 'perf probe' does, for module,
fill 'uprobe' field in 'struct perf_probe_event'. Other parts would be
done automatically.

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]
---
tools/perf/util/bpf-loader.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 54bfe62..aeac08c 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -119,6 +119,16 @@ config__exec(const char *value, struct perf_probe_event *pev)
return 0;
}

+static int
+config__module(const char *value, struct perf_probe_event *pev)
+{
+ pev->uprobes = false;
+ pev->target = strdup(value);
+ if (!pev->target)
+ return -ENOMEM;
+ return 0;
+}
+
static struct {
const char *key;
const char *usage;
@@ -131,6 +141,12 @@ static struct {
.desc = "Set uprobe target",
.func = config__exec,
},
+ {
+ .key = "module",
+ .usage = "module=<module name> ",
+ .desc = "Set kprobe module",
+ .func = config__module,
+ }
};

static int
--
1.8.3.4

2015-11-16 12:48:53

by Wang Nan

[permalink] [raw]
Subject: [PATCH 05/13] perf tools: Introduce strtobool() to string.c

This patch clones strtobool() from kernel's string.c to perf's string.c,
then add the function entry to tools/perf/util/include/linux/string.h.

string.h in perf utils doesn't have #ifdef guard. This patch fixes it.

This is preparation for enforcing BPF program configuration, which
would allow config string like 'inlines=yes'.

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/include/linux/string.h | 5 +++++
tools/perf/util/string.c | 28 ++++++++++++++++++++++++++++
2 files changed, 33 insertions(+)

diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h
index 6f19c54..2bb0057 100644
--- a/tools/perf/util/include/linux/string.h
+++ b/tools/perf/util/include/linux/string.h
@@ -1,3 +1,8 @@
+#ifndef PERF_LINUX_STRING_H_
+#define PERF_LINUX_STRING_H_
#include <string.h>

void *memdup(const void *src, size_t len);
+
+int strtobool(const char *s, bool *res);
+#endif
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index fc8781d..c7fd44d 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -396,3 +396,31 @@ out_err_overflow:
free(expr);
return NULL;
}
+
+/**
+ * strtobool - convert common user inputs into boolean values
+ * @s: input string
+ * @res: result
+ *
+ * This routine returns 0 iff the first character is one of 'Yy1Nn0'.
+ * Otherwise it will return -EINVAL. Value pointed to by res is
+ * updated upon finding a match.
+ */
+int strtobool(const char *s, bool *res)
+{
+ switch (s[0]) {
+ case 'y':
+ case 'Y':
+ case '1':
+ *res = true;
+ break;
+ case 'n':
+ case 'N':
+ case '0':
+ *res = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
--
1.8.3.4

2015-11-16 12:50:48

by Wang Nan

[permalink] [raw]
Subject: [PATCH 06/13] perf tools: Allow BPF program config probing options

By extending the syntax of BPF object section names, this patch allows
user to config probing options like what they can do in 'perf probe'.

Error message in perf probe is also updated.

Test result:

For following BPF file bpf.c:

SEC("inlines=no\n"
"func=SyS_dup?")
int func(void *ctx)
{
return 1;
}

Cmdline:

# ./perf record -e ./test_probe_glob.c ls /
...
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.013 MB perf.data ]
# ./perf evlist
perf_bpf_probe:func_1
perf_bpf_probe:func

After changing "inlines=no" to "inlines=yes":

Cmdline:

# ./perf record -e ./test_probe_glob.c ls /
...
[ perf record: Woken up 2 times to write data ]
[ perf record: Captured and wrote 0.013 MB perf.data ]
# ./perf evlist
perf_bpf_probe:func_3
perf_bpf_probe:func_2
perf_bpf_probe:func_1
perf_bpf_probe:func

Then test 'force':

use following program:

SEC("func=sys_read")
int funca(void *ctx)
{
return 1;
}

SEC("force=no\n"
"func=sys_write")
int funcb(void *ctx)
{
return 1;
}

Error: event "func" already exists.
Hint: Remove existing event by 'perf probe -d'
or force duplicates by 'perf probe -f'
or set 'force=yes' in BPF source.
event syntax error: './test_force.c'
\___ Probe point exist. Try 'perf probe -d "*"' and set 'force=yes'

(add -v to see detail)
...

Then replace 'force=no' to 'force=yes':

# ./perf record -e ./test_force.c ls /
...
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.013 MB perf.data (4 samples) ]
# ./perf evlist
perf_bpf_probe:func_1
perf_bpf_probe:func

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/bpf-loader.c | 53 +++++++++++++++++++++++++++++++++++++++++--
tools/perf/util/probe-event.c | 7 ++++--
2 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index aeac08c..3694e16 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -7,6 +7,7 @@

#include <bpf/libbpf.h>
#include <linux/err.h>
+#include <linux/string.h>
#include "perf.h"
#include "debug.h"
#include "bpf-loader.h"
@@ -129,6 +130,38 @@ config__module(const char *value, struct perf_probe_event *pev)
return 0;
}

+static int
+config__bool(const char *value,
+ bool *pbool, bool invert)
+{
+ int err;
+ bool bool_value;
+
+ if (!pbool)
+ return -EINVAL;
+
+ err = strtobool(value, &bool_value);
+ if (err)
+ return err;
+
+ *pbool = invert ? !bool_value : bool_value;
+ return 0;
+}
+
+static int
+config__inlines(const char *value,
+ struct perf_probe_event *pev __maybe_unused)
+{
+ return config__bool(value, &probe_conf.no_inlines, true);
+}
+
+static int
+config__force(const char *value,
+ struct perf_probe_event *pev __maybe_unused)
+{
+ return config__bool(value, &probe_conf.force_add, false);
+}
+
static struct {
const char *key;
const char *usage;
@@ -146,7 +179,19 @@ static struct {
.usage = "module=<module name> ",
.desc = "Set kprobe module",
.func = config__module,
- }
+ },
+ {
+ .key = "inlines",
+ .usage = "inlines=[yes|no] ",
+ .desc = "Probe at inline symbol",
+ .func = config__inlines,
+ },
+ {
+ .key = "force",
+ .usage = "force=[yes|no] ",
+ .desc = "Forcibly add events with existing name",
+ .func = config__force,
+ },
};

static int
@@ -240,6 +285,10 @@ config_bpf_program(struct bpf_program *prog)
const char *config_str;
int err;

+ /* Initialize per-program probing setting */
+ probe_conf.no_inlines = false;
+ probe_conf.force_add = false;
+
config_str = bpf_program__title(prog, false);
if (IS_ERR(config_str)) {
pr_debug("bpf: unable to get title for program\n");
@@ -544,7 +593,7 @@ int bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
scnprintf(buf, size, "%s (add -v to see detail)", emsg);
break;
}
- bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'");
+ bpf__strerror_entry(EEXIST, "Probe point exist. Try 'perf probe -d \"*\"' and set 'force=yes'");
bpf__strerror_entry(EACCES, "You need to be root");
bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0");
bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file");
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 03875f9..93996ec 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -2326,8 +2326,11 @@ static int get_new_event_name(char *buf, size_t len, const char *base,
goto out;

if (!allow_suffix) {
- pr_warning("Error: event \"%s\" already exists. "
- "(Use -f to force duplicates.)\n", buf);
+ pr_warning("Error: event \"%s\" already exists.\n"
+ " Hint: Remove existing event by 'perf probe -d'\n"
+ " or force duplicates by 'perf probe -f'\n"
+ " or set 'force=yes' in BPF source.\n",
+ buf);
ret = -EEXIST;
goto out;
}
--
1.8.3.4

2015-11-16 12:49:19

by Wang Nan

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

This patch is a preparation for BPF prologue support which allows
generating a series of BPF bytecode for fetching kernel data before
calling program code. With the newly introduced multiple instances
support, perf is able to create different prologues for different
kprobe points.

Before this patch, a bpf_program can be loaded into kernel only once,
and get the only resuling fd. What this patch done is to allow creating
and loading different variants of one bpf_program, then fetching their
fds.

Here describe the basic idea in this patch. The detail description of
the newly introduced APIs can be found in comment in the patch body.

The key of this patch is the new mechanism in bpf_program__load().
Instead of loading BPF program into kernel directly, it calls a
'pre-processor' to generate program instances which would be final
loaded into kernel based on the original code. To enable multiple
instances generation, libbpf passes an index to the pre-processor
so it know which instance is being loaded.

Pre-processor should be passed from libbpf's user (perf) using
bpf_program__set_prep(). The number of instances and the relationship
between indics and the target instance should be clear when calling
bpf_program__set_prep().

To retrive fd for a specific instance of a program,
bpf_program__nth_fd() is introduced. It return the resuling fd
according to index.

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

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

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

struct bpf_object *obj;
void *priv;
@@ -206,10 +210,24 @@ struct bpf_object {

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

- zclose(prog->fd);
+ /*
+ * If the object is opened but the program is never loaded,
+ * it is possible that prog->instances.nr == -1.
+ */
+ if (prog->instances.nr > 0) {
+ for (i = 0; i < prog->instances.nr; i++)
+ zclose(prog->instances.fds[i]);
+ } else if (prog->instances.nr != -1)
+ pr_warning("Internal error: instances.nr is %d\n",
+ prog->instances.nr);
+
+ prog->instances.nr = -1;
+ zfree(&prog->instances.fds);
}

static void bpf_program__exit(struct bpf_program *prog)
@@ -260,7 +278,8 @@ bpf_program__init(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->instances.fds = NULL;
+ prog->instances.nr = -1;

return 0;
errout:
@@ -860,13 +879,73 @@ static int
bpf_program__load(struct bpf_program *prog,
char *license, u32 kern_version)
{
- int err, fd;
+ int err = 0, fd, i;
+
+ if (prog->instances.nr < 0 || !prog->instances.fds) {
+ if (prog->preprocessor) {
+ pr_warning("Internal error: can't load program '%s'\n",
+ prog->section_name);
+ return -LIBBPF_ERRNO__INTERNAL;
+ }
+
+ prog->instances.fds = malloc(sizeof(int));
+ if (!prog->instances.fds) {
+ pr_warning("No enough memory for fds\n");
+ return -ENOMEM;
+ }
+ prog->instances.nr = 1;
+ prog->instances.fds[0] = -1;
+ }
+
+ if (!prog->preprocessor) {
+ if (prog->instances.nr != 1)
+ pr_warning("Program '%s' inconsistent: nr(%d) not 1\n",
+ prog->section_name, prog->instances.nr);

- err = load_program(prog->insns, prog->insns_cnt,
- license, kern_version, &fd);
- if (!err)
- prog->fd = fd;
+ err = load_program(prog->insns, prog->insns_cnt,
+ license, kern_version, &fd);
+ if (!err)
+ prog->instances.fds[0] = fd;
+ goto out;
+ }
+
+ for (i = 0; i < prog->instances.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->instances.fds[i] = -1;
+ if (result.pfd)
+ *result.pfd = -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->instances.fds[i] = fd;
+ }
+out:
if (err)
pr_warning("failed to load program '%s'\n",
prog->section_name);
@@ -1121,5 +1200,53 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy)

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_instances,
+ bpf_program_prep_t prep)
+{
+ int *instances_fds;
+
+ if (nr_instances <= 0 || !prep)
+ return -EINVAL;
+
+ if (prog->instances.nr > 0 || prog->instances.fds) {
+ pr_warning("Can't set pre-processor after loading\n");
+ return -EINVAL;
+ }
+
+ instances_fds = malloc(sizeof(int) * nr_instances);
+ if (!instances_fds) {
+ pr_warning("alloc memory failed for fds\n");
+ return -ENOMEM;
+ }
+
+ /* fill all fd with -1 */
+ memset(instances_fds, 0xff, sizeof(int) * nr_instances);
+
+ prog->instances.nr = nr_instances;
+ prog->instances.fds = instances_fds;
+ prog->preprocessor = prep;
+ return 0;
+}
+
+int bpf_program__nth_fd(struct bpf_program *prog, int n)
+{
+ int fd;
+
+ if (n >= prog->instances.nr || n < 0) {
+ pr_warning("Can't get the %dth fd from program %s: only %d instances\n",
+ n, prog->section_name, prog->instances.nr);
+ return -EINVAL;
+ }
+
+ fd = prog->instances.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 c9a9aef..949df4b 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -88,6 +88,70 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy);

int bpf_program__fd(struct bpf_program *prog);

+struct bpf_insn;
+
+/*
+ * Libbpf allows callers to adjust BPF programs before being loaded
+ * into kernel. One program in an object file can be transform into
+ * multiple variants to be attached to different code.
+ *
+ * bpf_program_prep_t, bpf_program__set_prep and bpf_program__nth_fd
+ * are APIs for this propose.
+ *
+ * - bpf_program_prep_t:
+ * It defines 'preprocessor', which is a caller defined function
+ * passed to libbpf through bpf_program__set_prep(), and will be
+ * called before program is loaded. The processor should adjust
+ * the program one time for each instances according to the number
+ * passed to it.
+ *
+ * - bpf_program__set_prep:
+ * Attachs a preprocessor to a BPF program. The number of instances
+ * whould be created is also passed through this function.
+ *
+ * - bpf_program__nth_fd:
+ * After the program is loaded, get resuling fds from bpf program for
+ * each instances.
+ *
+ * If bpf_program__set_prep() is not used, the program whould be loaded
+ * without adjustment during bpf_object__load(). The program has only
+ * one instance. In this case bpf_program__fd(prog) is equal to
+ * bpf_program__nth_fd(prog, 0).
+ */
+
+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;
+};
+
+/*
+ * Parameters of bpf_program_prep_t:
+ * - prog: The bpf_program being loaded.
+ * - n: Index of instance being generated.
+ * - insns: BPF instructions array.
+ * - insns_cnt:Number of instructions in insns.
+ * - res: Output parameter, result of transformation.
+ *
+ * Return value:
+ * - Zero: pre-processing success.
+ * - Non-zero: pre-processing, stop loading.
+ */
+typedef int (*bpf_program_prep_t)(struct bpf_program *prog, int n,
+ struct bpf_insn *insns, int insns_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-11-16 12:52:04

by Wang Nan

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

If both LIBBPF and DWARF are 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_REGS_QUERY_REGISTER_OFFSET is introduced in commit
63ab024a5b6f295ca17a293ad81b7c728f49a89a ("perf tools:
regs_query_register_offset() infrastructure"), which 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]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/config/Makefile | 12 ++++++++++++
1 file changed, 12 insertions(+)

diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index de89ec5..6eb9a95 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -318,6 +318,18 @@ ifndef NO_LIBELF
CFLAGS += -DHAVE_LIBBPF_SUPPORT
$(call detected,CONFIG_LIBBPF)
endif
+
+ ifndef NO_DWARF
+ ifdef PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET
+ CFLAGS += -DHAVE_BPF_PROLOGUE
+ $(call detected,CONFIG_BPF_PROLOGUE)
+ else
+ msg := $(warning BPF prologue is not supported by architecture $(ARCH), missing regs_query_register_offset());
+ endif
+ else
+ msg := $(warning DWARF support is off, BPF prologue is disabled);
+ endif
+
endif # NO_LIBBPF
endif # NO_LIBELF

--
1.8.3.4

2015-11-16 12:49:13

by Wang Nan

[permalink] [raw]
Subject: [PATCH 09/13] perf tools: Compile dwarf-regs.c if CONFIG_BPF_PROLOGUE is on

regs_query_register_offset() in dwarf-regs.c is required by BPF prologue.
This patch compiles it if CONFIG_BPF_PROLOGUE is on to avoid buildin
failure when CONFIG_BPF_PROLOGUE is on but CONFIG_DWARF is not set.

Signed-off-by: Wang Nan <[email protected]>
Signed-off-by: He Kuang <[email protected]>
Acked-by: Masami Hiramatsu <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/arch/x86/util/Build | 1 +
1 file changed, 1 insertion(+)

diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build
index ff63649..4659703 100644
--- a/tools/perf/arch/x86/util/Build
+++ b/tools/perf/arch/x86/util/Build
@@ -5,6 +5,7 @@ libperf-y += kvm-stat.o
libperf-y += perf_regs.o

libperf-$(CONFIG_DWARF) += dwarf-regs.o
+libperf-$(CONFIG_BPF_PROLOGUE) += dwarf-regs.o

libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
--
1.8.3.4

2015-11-16 12:49:51

by Wang Nan

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

From: He Kuang <[email protected]>

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]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/Build | 1 +
tools/perf/util/bpf-loader.c | 3 +
tools/perf/util/bpf-loader.h | 3 +
tools/perf/util/bpf-prologue.c | 455 +++++++++++++++++++++++++++++++++++++++++
tools/perf/util/bpf-prologue.h | 34 +++
5 files changed, 496 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 591b3fe..b9d56f2 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -88,6 +88,7 @@ libperf-y += parse-branch-options.o
libperf-y += parse-regs-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-file.o
libperf-$(CONFIG_LIBELF) += probe-event.o
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 3694e16..e0045c3 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -509,6 +509,9 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = {
[ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error",
[ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet",
[ERRCODE_OFFSET(CONFIG_TERM)] = "Invalid config term in config string",
+ [ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue",
+ [ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program",
+ [ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue",
};

static int
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index d19f5c5..a58740b 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -21,6 +21,9 @@ enum bpf_loader_errno {
BPF_LOADER_ERRNO__INTERNAL, /* BPF loader internal error */
BPF_LOADER_ERRNO__COMPILE, /* Error when compiling BPF scriptlet */
BPF_LOADER_ERRNO__CONFIG_TERM, /* Invalid config term in config term */
+ BPF_LOADER_ERRNO__PROLOGUE, /* Failed to generate prologue */
+ BPF_LOADER_ERRNO__PROLOGUE2BIG, /* Prologue too big for program */
+ BPF_LOADER_ERRNO__PROLOGUEOOB, /* Offset out of bound for prologue */
__BPF_LOADER_ERRNO__END,
};

diff --git a/tools/perf/util/bpf-prologue.c b/tools/perf/util/bpf-prologue.c
new file mode 100644
index 0000000..6cdbee1
--- /dev/null
+++ b/tools/perf/util/bpf-prologue.c
@@ -0,0 +1,455 @@
+/*
+ * bpf-prologue.c
+ *
+ * Copyright (C) 2015 He Kuang <[email protected]>
+ * 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"
+#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 -BPF_LOADER_ERRNO__PROLOGUE2BIG;
+
+ if (pos->pos + 1 >= pos->end) {
+ pr_err("bpf prologue: prologue too long\n");
+ pos->pos = NULL;
+ return -BPF_LOADER_ERRNO__PROLOGUE2BIG;
+ }
+
+ *(pos->pos)++ = new_insn;
+ return 0;
+}
+
+static int
+check_pos(struct bpf_insn_pos *pos)
+{
+ if (!pos->pos || pos->pos >= pos->end)
+ return -BPF_LOADER_ERRNO__PROLOGUE2BIG;
+ 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 = regs_query_register_offset(reg);
+
+ if (offset < 0) {
+ pr_err("bpf: prologue: failed to get register %s\n",
+ reg);
+ return offset;
+ }
+ ins(BPF_LDX_MEM(BPF_DW, target_reg, ctx_reg, offset), pos);
+
+ return check_pos(pos);
+}
+
+/*
+ * 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);
+
+ return check_pos(pos);
+}
+
+/*
+ * 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, err = 0;
+
+ for (i = 0; i < nargs; i++) {
+ err = gen_ldx_reg_from_ctx(pos, BPF_REG_1, args[i].value,
+ BPF_PROLOGUE_START_ARG_REG + i);
+ if (err)
+ goto errout;
+ }
+
+ return check_pos(pos);
+errout:
+ return err;
+}
+
+/*
+ * 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 err, 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 */
+ err = gen_ldx_reg_from_ctx(pos, BPF_REG_CTX, reg,
+ BPF_REG_ARG3);
+ if (err) {
+ 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);
+ err = gen_read_mem(pos, BPF_REG_3, BPF_REG_7,
+ ref->offset);
+ if (err) {
+ 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);
+
+ return check_pos(pos);
+errout:
+ return err;
+}
+
+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 -BPF_LOADER_ERRNO__PROLOGUE2BIG;
+
+ for (insn = pos->begin; insn < pos->pos; insn++) {
+ struct bpf_insn *target;
+ 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:
+ target = error_code;
+ break;
+ case JMP_TO_SUCCESS_CODE:
+ target = success_code;
+ break;
+ case JMP_TO_USER_CODE:
+ target = user_code;
+ break;
+ default:
+ pr_err("bpf prologue: internal error: relocation failed\n");
+ return -BPF_LOADER_ERRNO__PROLOGUE;
+ }
+
+ insn->off = target - (insn + 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 err = 0, i;
+
+ if (!new_prog || !new_cnt)
+ return -EINVAL;
+
+ if (cnt_space > BPF_MAXINSNS)
+ cnt_space = BPF_MAXINSNS;
+
+ 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) {
+ pr_warning("bpf: prologue: %d arguments are dropped\n",
+ nargs - BPF_PROLOGUE_MAX_ARGS);
+ nargs = BPF_PROLOGUE_MAX_ARGS;
+ }
+
+ /* 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 -BPF_LOADER_ERRNO__PROLOGUEOOB;
+ }
+#endif
+ ref = ref->next;
+ }
+ }
+ pr_debug("prologue: pass validation\n");
+
+ if (fastpath) {
+ /* If all variables are registers... */
+ pr_debug("prologue: fast path\n");
+ err = gen_prologue_fastpath(&pos, args, nargs);
+ if (err)
+ 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);
+
+ err = gen_prologue_slowpath(&pos, args, nargs);
+ if (err)
+ goto errout;
+ /*
+ * start of ERROR_CODE (only slow pass needs error code)
+ * mov r2 <- 1 // r2 is error number
+ * mov r3 <- 0 // r3, r4... should be touched or
+ * // verifier would complain
+ * mov r4 <- 0
+ * ...
+ * 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);
+ err = prologue_relocate(&pos, error_code, success_code,
+ user_code);
+ if (err)
+ goto errout;
+ }
+
+ err = check_pos(&pos);
+ if (err)
+ goto errout;
+
+ *new_cnt = pos_get_cnt(&pos);
+ return 0;
+errout:
+ return err;
+}
diff --git a/tools/perf/util/bpf-prologue.h b/tools/perf/util/bpf-prologue.h
new file mode 100644
index 0000000..d94cbea
--- /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 -ENOTSUP;
+}
+#endif
+#endif /* __BPF_PROLOGUE_H */
--
1.8.3.4

2015-11-16 12:50:16

by Wang Nan

[permalink] [raw]
Subject: [PATCH 11/13] 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]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/bpf-loader.c | 120 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 119 insertions(+), 1 deletion(-)

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

+#include <linux/bpf.h>
#include <bpf/libbpf.h>
#include <linux/err.h>
#include <linux/string.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" // for MAX_PROBES
#include "llvm-utils.h"
@@ -33,6 +36,8 @@ DEFINE_PRINT_FN(debug, 1)

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

static bool libbpf_initialized;
@@ -107,6 +112,7 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
struct bpf_prog_priv *priv = _priv;

cleanup_perf_probe_events(&priv->pev, 1);
+ zfree(&priv->insns_buf);
free(priv);
}

@@ -365,6 +371,102 @@ static int bpf__prepare_probe(void)
return err;
}

+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 = "[unknown]";
+
+ pr_debug("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_debug("Internal error in preproc_gen_prologue\n");
+ return -BPF_LOADER_ERRNO__PROLOGUE;
+}
+
+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_debug("Internal error when hook preprocessor\n");
+ return -BPF_LOADER_ERRNO__INTERNAL;
+ }
+
+ 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 tevs don'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_debug("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(struct bpf_object *obj)
{
int err = 0;
@@ -399,6 +501,18 @@ int bpf__probe(struct bpf_object *obj)
pr_debug("bpf_probe: failed to apply perf probe events");
goto out;
}
+
+ /*
+ * After probing, let's consider prologue, which
+ * adds program fetcher to BPF programs.
+ *
+ * hook_load_preprocessorr() hooks pre-processor
+ * to bpf_program, let it generate prologue
+ * dynamically during loading.
+ */
+ err = hook_load_preprocessor(prog);
+ if (err)
+ goto out;
}
out:
return err < 0 ? err : 0;
@@ -482,7 +596,11 @@ int bpf__foreach_tev(struct bpf_object *obj,
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_debug("bpf: failed to get file descriptor\n");
return fd;
--
1.8.3.4

2015-11-16 12:49:03

by Wang Nan

[permalink] [raw]
Subject: [PATCH 12/13] perf test: Test BPF prologue

This patch introduces a new BPF script to test BPF prologue. The new
script probes at null_lseek, which is the function pointer when we try
to lseek on '/dev/null'.

null_lseek is chosen because it is a function pointer, so we don't need
to consider inlining and LTO.

By extracting file->f_mode, bpf-script-test-prologue.c should know whether
the file is writable or readonly. According to llseek_loop() and
bpf-script-test-prologue.c, one forth of total lseeks should be collected.

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/tests/Build | 9 +++++++-
tools/perf/tests/bpf-script-test-prologue.c | 35 +++++++++++++++++++++++++++++
tools/perf/tests/bpf.c | 34 ++++++++++++++++++++++++++++
tools/perf/tests/llvm.c | 4 ++++
tools/perf/tests/llvm.h | 2 ++
5 files changed, 83 insertions(+), 1 deletion(-)
create mode 100644 tools/perf/tests/bpf-script-test-prologue.c

diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index f41ebf8..0ff8a97 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -31,7 +31,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 llvm-src-base.o llvm-src-kbuild.o
+perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o
perf-y += bpf.o
perf-y += topology.o

@@ -49,6 +49,13 @@ $(OUTPUT)tests/llvm-src-kbuild.c: tests/bpf-script-test-kbuild.c
$(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
$(Q)echo ';' >> $@

+$(OUTPUT)tests/llvm-src-prologue.c: tests/bpf-script-test-prologue.c
+ $(call rule_mkdir)
+ $(Q)echo '#include <tests/llvm.h>' > $@
+ $(Q)echo 'const char test_llvm__bpf_test_prologue_prog[] =' >> $@
+ $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
+ $(Q)echo ';' >> $@
+
ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64))
perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
endif
diff --git a/tools/perf/tests/bpf-script-test-prologue.c b/tools/perf/tests/bpf-script-test-prologue.c
new file mode 100644
index 0000000..7230e62
--- /dev/null
+++ b/tools/perf/tests/bpf-script-test-prologue.c
@@ -0,0 +1,35 @@
+/*
+ * bpf-script-test-prologue.c
+ * Test BPF prologue
+ */
+#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 SEC(NAME) __attribute__((section(NAME), used))
+
+#include <uapi/linux/fs.h>
+
+#define FMODE_READ 0x1
+#define FMODE_WRITE 0x2
+
+static void (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
+ (void *) 6;
+
+SEC("func=null_lseek file->f_mode offset orig")
+int bpf_func__null_lseek(void *ctx, int err, unsigned long f_mode,
+ unsigned long offset, unsigned long orig)
+{
+ if (err)
+ return 0;
+ if (f_mode & FMODE_WRITE)
+ return 0;
+ if (offset & 1)
+ return 0;
+ if (orig == SEEK_CUR)
+ return 0;
+ return 1;
+}
+
+char _license[] SEC("license") = "GPL";
+int _version SEC("version") = LINUX_VERSION_CODE;
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
index ec16f78..c7131fa 100644
--- a/tools/perf/tests/bpf.c
+++ b/tools/perf/tests/bpf.c
@@ -19,6 +19,29 @@ static int epoll_pwait_loop(void)
return 0;
}

+#ifdef HAVE_BPF_PROLOGUE
+
+static int llseek_loop(void)
+{
+ int fds[2], i;
+
+ fds[0] = open("/dev/null", O_RDONLY);
+ fds[1] = open("/dev/null", O_RDWR);
+
+ if (fds[0] < 0 || fds[1] < 0)
+ return -1;
+
+ for (i = 0; i < NR_ITERS; i++) {
+ lseek(fds[i % 2], i, (i / 2) % 2 ? SEEK_CUR : SEEK_SET);
+ lseek(fds[(i + 1) % 2], i, (i / 2) % 2 ? SEEK_CUR : SEEK_SET);
+ }
+ close(fds[0]);
+ close(fds[1]);
+ return 0;
+}
+
+#endif
+
static struct {
enum test_llvm__testcase prog_id;
const char *desc;
@@ -37,6 +60,17 @@ static struct {
&epoll_pwait_loop,
(NR_ITERS + 1) / 2,
},
+#ifdef HAVE_BPF_PROLOGUE
+ {
+ LLVM_TESTCASE_BPF_PROLOGUE,
+ "Test BPF prologue generation",
+ "[bpf_prologue_test]",
+ "fix kbuild first",
+ "check your vmlinux setting?",
+ &llseek_loop,
+ (NR_ITERS + 1) / 4,
+ },
+#endif
};

static int do_test(struct bpf_object *obj, int (*func)(void),
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index bc4cf50..7f4f7f7 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -44,6 +44,10 @@ static struct {
.source = test_llvm__bpf_test_kbuild_prog,
.desc = "Test kbuild searching",
},
+ [LLVM_TESTCASE_BPF_PROLOGUE] = {
+ .source = test_llvm__bpf_test_prologue_prog,
+ .desc = "Test BPF prologue generation",
+ },
};


diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h
index d91d8f4..5150b4d 100644
--- a/tools/perf/tests/llvm.h
+++ b/tools/perf/tests/llvm.h
@@ -6,10 +6,12 @@

extern const char test_llvm__bpf_base_prog[];
extern const char test_llvm__bpf_test_kbuild_prog[];
+extern const char test_llvm__bpf_test_prologue_prog[];

enum test_llvm__testcase {
LLVM_TESTCASE_BASE,
LLVM_TESTCASE_KBUILD,
+ LLVM_TESTCASE_BPF_PROLOGUE,
__LLVM_TESTCASE_MAX,
};

--
1.8.3.4

2015-11-16 12:51:51

by Wang Nan

[permalink] [raw]
Subject: [PATCH 13/13] 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 prologues 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 be
grouped together.

Test result:

Sample BPF program:

SEC("inlines=no\n"
"func=SyS_dup? oldfd")
int func(void *ctx)
{
return 1;
}

It would probe at SyS_dup2 and SyS_dup3, extracts oldfd as its argument.

Following cmdline shows BPF program loaded into kernel by perf:

# ./perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog

Before this patch:

# ./perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog
[1] 24858
lrwx------ 1 root root 64 Nov 14 04:09 3 -> anon_inode:bpf-prog
lrwx------ 1 root root 64 Nov 14 04:09 4 -> anon_inode:bpf-prog
...

After this patch:

# ./perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog
[1] 25699
lrwx------ 1 root root 64 Nov 14 04:10 3 -> anon_inode:bpf-prog
...

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/bpf-loader.c | 136 ++++++++++++++++++++++++++++++++++++++++---
1 file changed, 129 insertions(+), 7 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index cad0d0f..86c50b9 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -38,6 +38,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 bool libbpf_initialized;
@@ -113,6 +115,7 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,

cleanup_perf_probe_events(&priv->pev, 1);
zfree(&priv->insns_buf);
+ zfree(&priv->type_mapping);
free(priv);
}

@@ -381,7 +384,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)
@@ -389,10 +392,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_debug("Internal error: prologue type %d not found\n", n);
+ return -BPF_LOADER_ERRNO__PROLOGUE;
+ }
+
+ tev = &pev->tevs[i];

buf = priv->insns_buf;
err = bpf__gen_prologue(tev->args, tev->nargs,
@@ -423,6 +436,101 @@ errout:
return -BPF_LOADER_ERRNO__PROLOGUE;
}

+/*
+ * compare_tev_args is reflexive, transitive and antisymmetric.
+ * I can proof it 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;
+}
+
+/*
+ * Assign a type number to each tevs in a pev.
+ * mapping is an array with same slots as tevs in that pev.
+ * nr_types will be set to number of types.
+ */
+static int map_prologue(struct perf_probe_event *pev, int *mapping,
+ int *nr_types)
+{
+ int i, type = 0;
+ struct probe_trace_event **ptevs;
+
+ size_t array_sz = sizeof(*ptevs) * pev->ntevs;
+
+ ptevs = malloc(array_sz);
+ if (!ptevs) {
+ pr_debug("No ehough memory: alloc ptevs failed\n");
+ return -ENOMEM;
+ }
+
+ pr_debug("In map_prologue, ntevs=%d\n", pev->ntevs);
+ for (i = 0; i < pev->ntevs; i++)
+ ptevs[i] = &pev->tevs[i];
+
+ qsort(ptevs, pev->ntevs, sizeof(*ptevs),
+ compare_tev_args);
+
+ for (i = 0; i < pev->ntevs; i++) {
+ int n;
+
+ n = ptevs[i] - pev->tevs;
+ if (i == 0) {
+ mapping[n] = type;
+ pr_debug("mapping[%d]=%d\n", n, type);
+ continue;
+ }
+
+ if (compare_tev_args(ptevs + i, ptevs + i - 1) == 0)
+ mapping[n] = type;
+ else
+ mapping[n] = ++type;
+
+ pr_debug("mapping[%d]=%d\n", n, mapping[n]);
+ }
+ free(ptevs);
+ *nr_types = type + 1;
+
+ return 0;
+}
+
static int hook_load_preprocessor(struct bpf_program *prog)
{
struct perf_probe_event *pev;
@@ -462,7 +570,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_debug("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;
}
@@ -596,9 +716,11 @@ int bpf__foreach_tev(struct bpf_object *obj,
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-11-16 14:13:50

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 00/13] perf tools: bpf: Improve BPF program ability

Em Mon, Nov 16, 2015 at 12:10:02PM +0000, Wang Nan escreveu:
> Resend patch set in [1] with sereval improvements based on Arnaldo's
> suggestions. This patch set is based on Arnaldo's perf/core.
>
> Patch 1/13 - 2/13 should have already been collected by Arnaldo but
> I can't find them in his repository, so I resend them.

Sorry, I have it in my perf/urgent branch and the latest signed tag
(perf-urgent-for-mingo), but I only pushed the later, just pushed the
former now.

Ingo should process those two soon, as they are purely bug fixes. I'll
go through this new patchkit now, thanks for considering my suggestions.

- Arnaldo

> Patch 3/13 - 6/13 are improved according to Arnaldo's suggestion: Some
> lines are made compat, return values of strdup() are checked, use
> named initializers. In addition, error messages are improved to reduce
> confusion.
>
> Patch 7/13 - 13/13 are identical to previous version, only cc-lists are
> shrinked.
>
> [1] http://lkml.kernel.org/g/[email protected]
>
> He Kuang (1):
> perf tools: Add prologue for BPF programs for fetching arguments
>
> Masami Hiramatsu (1):
> perf probe: Fix memory leaking on faiulre by clearing all
> probe_trace_events
>
> Wang Nan (11):
> perf probe: Clear probe_trace_event when add_probe_trace_event() fails
> perf tools: Allow BPF program attach to uprobe events
> perf tools: Allow BPF program attach to modules
> perf tools: Introduce strtobool() to string.c
> perf tools: Allow BPF program config probing options
> bpf tools: Load a program with different instances using preprocessor
> perf tools: Add BPF_PROLOGUE config options for further patches
> perf tools: Compile dwarf-regs.c if CONFIG_BPF_PROLOGUE is on
> perf tools: Generate prologue for BPF programs
> perf test: Test BPF prologue
> perf tools: Use same BPF program if arguments are identical
>
> tools/lib/bpf/libbpf.c | 145 ++++++++-
> tools/lib/bpf/libbpf.h | 64 ++++
> tools/perf/arch/x86/util/Build | 1 +
> tools/perf/config/Makefile | 12 +
> tools/perf/tests/Build | 9 +-
> tools/perf/tests/bpf-script-test-prologue.c | 35 +++
> tools/perf/tests/bpf.c | 34 +++
> tools/perf/tests/llvm.c | 4 +
> tools/perf/tests/llvm.h | 2 +
> tools/perf/util/Build | 1 +
> tools/perf/util/bpf-loader.c | 432 +++++++++++++++++++++++++-
> tools/perf/util/bpf-loader.h | 4 +
> tools/perf/util/bpf-prologue.c | 455 ++++++++++++++++++++++++++++
> tools/perf/util/bpf-prologue.h | 34 +++
> tools/perf/util/include/linux/string.h | 5 +
> tools/perf/util/probe-event.c | 7 +-
> tools/perf/util/probe-finder.c | 24 +-
> tools/perf/util/string.c | 28 ++
> 18 files changed, 1269 insertions(+), 27 deletions(-)
> create mode 100644 tools/perf/tests/bpf-script-test-prologue.c
> create mode 100644 tools/perf/util/bpf-prologue.c
> create mode 100644 tools/perf/util/bpf-prologue.h
>
> --
> 1.8.3.4

2015-11-16 14:14:24

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 03/13] perf tools: Allow BPF program attach to uprobe events

Em Mon, Nov 16, 2015 at 12:10:05PM +0000, Wang Nan escreveu:
> 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(
> "exec=/lib64/libc.so.6\n"

Do we really need this \n there? Is it really the separator? Why not ';'
or a comma? I'll check that in the code...

> "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 "exec"
> is for uprobe probing.
>
> Signed-off-by: Wang Nan <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Masami Hiramatsu <[email protected]>
> Cc: Zefan Li <[email protected]>
> Cc: [email protected]
> ---
> tools/perf/util/bpf-loader.c | 120 ++++++++++++++++++++++++++++++++++++++++---
> tools/perf/util/bpf-loader.h | 1 +
> 2 files changed, 115 insertions(+), 6 deletions(-)
>
> diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
> index 4c50411..54bfe62 100644
> --- a/tools/perf/util/bpf-loader.c
> +++ b/tools/perf/util/bpf-loader.c
> @@ -110,6 +110,113 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
> }
>
> static int
> +config__exec(const char *value, struct perf_probe_event *pev)
> +{
> + pev->uprobes = true;
> + pev->target = strdup(value);
> + if (!pev->target)
> + return -ENOMEM;
> + return 0;
> +}
> +
> +static struct {
> + const char *key;
> + const char *usage;
> + const char *desc;
> + int (*func)(const char *, struct perf_probe_event *);
> +} bpf_config_terms[] = {
> + {
> + .key = "exec",
> + .usage = "exec=<full path of file>",
> + .desc = "Set uprobe target",
> + .func = config__exec,
> + },
> +};
> +
> +static int
> +do_config(const char *key, const char *value,
> + struct perf_probe_event *pev)
> +{
> + unsigned int i;
> +
> + pr_debug("config bpf program: %s=%s\n", key, value);
> + for (i = 0; i < ARRAY_SIZE(bpf_config_terms); i++)
> + if (strcmp(key, bpf_config_terms[i].key) == 0)
> + return bpf_config_terms[i].func(value, pev);
> +
> + pr_debug("BPF: ERROR: invalid config option in object: %s=%s\n",
> + key, value);
> +
> + pr_debug("\nHint: Currently valid options are:\n");
> + for (i = 0; i < ARRAY_SIZE(bpf_config_terms); i++)
> + pr_debug("\t%s:\t%s\n", bpf_config_terms[i].usage,
> + bpf_config_terms[i].desc);
> + pr_debug("\n");
> +
> + return -BPF_LOADER_ERRNO__CONFIG_TERM;
> +}
> +
> +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_debug("No enough memory: dup config_str failed\n");
> + return ERR_PTR(-ENOMEM);
> + }
> +
> + 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 err ? ERR_PTR(err) : main_str;
> +}
> +
> +static int
> +parse_config(const char *config_str, struct perf_probe_event *pev)
> +{
> + int err;
> + const char *main_str = parse_config_kvpair(config_str, pev);
> +
> + if (IS_ERR(main_str))
> + return PTR_ERR(main_str);
> +
> + err = parse_perf_probe_command(main_str, pev);
> + if (err < 0) {
> + pr_debug("bpf: '%s' is not a valid config string\n",
> + config_str);
> + /* parse failed, don't need clear pev. */
> + return -BPF_LOADER_ERRNO__CONFIG;
> + }
> + return 0;
> +}
> +
> +static int
> config_bpf_program(struct bpf_program *prog)
> {
> struct perf_probe_event *pev = NULL;
> @@ -131,13 +238,9 @@ config_bpf_program(struct bpf_program *prog)
> pev = &priv->pev;
>
> pr_debug("bpf: config program '%s'\n", config_str);
> - err = parse_perf_probe_command(config_str, pev);
> - if (err < 0) {
> - pr_debug("bpf: '%s' is not a valid config string\n",
> - config_str);
> - err = -BPF_LOADER_ERRNO__CONFIG;
> + err = parse_config(config_str, pev);
> + if (err)
> goto errout;
> - }
>
> if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) {
> pr_debug("bpf: '%s': group for event is set and not '%s'.\n",
> @@ -340,6 +443,7 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = {
> [ERRCODE_OFFSET(EVENTNAME)] = "No event name found in config string",
> [ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error",
> [ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet",
> + [ERRCODE_OFFSET(CONFIG_TERM)] = "Invalid config term in config string",
> };
>
> static int
> @@ -420,6 +524,10 @@ int bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
> int err, char *buf, size_t size)
> {
> bpf__strerror_head(err, buf, size);
> + case BPF_LOADER_ERRNO__CONFIG_TERM: {
> + scnprintf(buf, size, "%s (add -v to see detail)", emsg);
> + break;
> + }
> bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'");
> bpf__strerror_entry(EACCES, "You need to be root");
> bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0");
> diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
> index 9caf3ae..d19f5c5 100644
> --- a/tools/perf/util/bpf-loader.h
> +++ b/tools/perf/util/bpf-loader.h
> @@ -20,6 +20,7 @@ enum bpf_loader_errno {
> BPF_LOADER_ERRNO__EVENTNAME, /* Event name is missing */
> BPF_LOADER_ERRNO__INTERNAL, /* BPF loader internal error */
> BPF_LOADER_ERRNO__COMPILE, /* Error when compiling BPF scriptlet */
> + BPF_LOADER_ERRNO__CONFIG_TERM, /* Invalid config term in config term */
> __BPF_LOADER_ERRNO__END,
> };
>
> --
> 1.8.3.4

2015-11-16 14:17:23

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 05/13] perf tools: Introduce strtobool() to string.c

Em Mon, Nov 16, 2015 at 12:10:07PM +0000, Wang Nan escreveu:
> This patch clones strtobool() from kernel's string.c to perf's string.c,
> then add the function entry to tools/perf/util/include/linux/string.h.
>
> string.h in perf utils doesn't have #ifdef guard. This patch fixes it.
>
> This is preparation for enforcing BPF program configuration, which
> would allow config string like 'inlines=yes'.

This is not perf specific at all, we're trying to move stuff out of
tools/perf/ so that it becomes generally usable by other tools/ living
code, not just perf.

I'll move it to where I think it should be and post as a reply to this
message.

- Arnaldo

> Signed-off-by: Wang Nan <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Masami Hiramatsu <[email protected]>
> Cc: Zefan Li <[email protected]>
> Cc: [email protected]
> ---
> tools/perf/util/include/linux/string.h | 5 +++++
> tools/perf/util/string.c | 28 ++++++++++++++++++++++++++++
> 2 files changed, 33 insertions(+)
>
> diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h
> index 6f19c54..2bb0057 100644
> --- a/tools/perf/util/include/linux/string.h
> +++ b/tools/perf/util/include/linux/string.h
> @@ -1,3 +1,8 @@
> +#ifndef PERF_LINUX_STRING_H_
> +#define PERF_LINUX_STRING_H_
> #include <string.h>
>
> void *memdup(const void *src, size_t len);
> +
> +int strtobool(const char *s, bool *res);
> +#endif
> diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
> index fc8781d..c7fd44d 100644
> --- a/tools/perf/util/string.c
> +++ b/tools/perf/util/string.c
> @@ -396,3 +396,31 @@ out_err_overflow:
> free(expr);
> return NULL;
> }
> +
> +/**
> + * strtobool - convert common user inputs into boolean values
> + * @s: input string
> + * @res: result
> + *
> + * This routine returns 0 iff the first character is one of 'Yy1Nn0'.
> + * Otherwise it will return -EINVAL. Value pointed to by res is
> + * updated upon finding a match.
> + */
> +int strtobool(const char *s, bool *res)
> +{
> + switch (s[0]) {
> + case 'y':
> + case 'Y':
> + case '1':
> + *res = true;
> + break;
> + case 'n':
> + case 'N':
> + case '0':
> + *res = false;
> + break;
> + default:
> + return -EINVAL;
> + }
> + return 0;
> +}
> --
> 1.8.3.4

2015-11-16 14:49:45

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 05/13] perf tools: Introduce strtobool() to string.c

Em Mon, Nov 16, 2015 at 11:17:18AM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Mon, Nov 16, 2015 at 12:10:07PM +0000, Wang Nan escreveu:
> > This patch clones strtobool() from kernel's string.c to perf's string.c,
> > then add the function entry to tools/perf/util/include/linux/string.h.
> >
> > string.h in perf utils doesn't have #ifdef guard. This patch fixes it.
> >
> > This is preparation for enforcing BPF program configuration, which
> > would allow config string like 'inlines=yes'.
>
> This is not perf specific at all, we're trying to move stuff out of
> tools/perf/ so that it becomes generally usable by other tools/ living
> code, not just perf.
>
> I'll move it to where I think it should be and post as a reply to this
> message.

Find it at the bottom, I had to do a prep patch to move memdup() first,
so that I could move tools/perf/util/include/linux/string.h to
tools/include/linux/string.h, there is still work to do to fully move
tools/perf/util/string.c to tools/lib/, will do eventually, for now what
you need is there.

- Arnaldo

commit 939d89c6acc78ee48e8ad83e9565a372b9f3987a
Author: Wang Nan <[email protected]>
Date: Mon Nov 16 11:42:05 2015 -0300

tools: Clone the kernel's strtobool function

Copying it to tools/lib/string.c, the counterpart to the kernel's
lib/string.c.

This is preparation for enhancing BPF program configuration, which will
allow config string like 'inlines=yes'.

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Jonathan Cameron <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
[ Copied it to tools/lib/string.c instead, to make it usable by other tools/ ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>

diff --git a/tools/include/linux/string.h b/tools/include/linux/string.h
index f3a6db6ad732..2e2f736c039c 100644
--- a/tools/include/linux/string.h
+++ b/tools/include/linux/string.h
@@ -6,4 +6,6 @@

void *memdup(const void *src, size_t len);

+int strtobool(const char *s, bool *res);
+
#endif /* _LINUX_STRING_H_ */
diff --git a/tools/lib/string.c b/tools/lib/string.c
index ecfd43a9b24e..065e54f42d8f 100644
--- a/tools/lib/string.c
+++ b/tools/lib/string.c
@@ -1,5 +1,20 @@
+/*
+ * linux/tools/lib/string.c
+ *
+ * Copied from linux/lib/string.c, where it is:
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * More specifically, the first copied function was strtobool, which
+ * was introduced by:
+ *
+ * d0f1fed29e6e ("Add a strtobool function matching semantics of existing in kernel equivalents")
+ * Author: Jonathan Cameron <[email protected]>
+ */
+
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
#include <linux/string.h>

/**
@@ -17,3 +32,31 @@ void *memdup(const void *src, size_t len)

return p;
}
+
+/**
+ * strtobool - convert common user inputs into boolean values
+ * @s: input string
+ * @res: result
+ *
+ * This routine returns 0 iff the first character is one of 'Yy1Nn0'.
+ * Otherwise it will return -EINVAL. Value pointed to by res is
+ * updated upon finding a match.
+ */
+int strtobool(const char *s, bool *res)
+{
+ switch (s[0]) {
+ case 'y':
+ case 'Y':
+ case '1':
+ *res = true;
+ break;
+ case 'n':
+ case 'N':
+ case '0':
+ *res = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}

2015-11-16 15:55:31

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 05/13] perf tools: Introduce strtobool() to string.c

Em Mon, Nov 16, 2015 at 11:49:36AM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Mon, Nov 16, 2015 at 11:17:18AM -0300, Arnaldo Carvalho de Melo escreveu:
> > Em Mon, Nov 16, 2015 at 12:10:07PM +0000, Wang Nan escreveu:
> > > This patch clones strtobool() from kernel's string.c to perf's string.c,
> > > then add the function entry to tools/perf/util/include/linux/string.h.
> > >
> > > string.h in perf utils doesn't have #ifdef guard. This patch fixes it.
> > >
> > > This is preparation for enforcing BPF program configuration, which
> > > would allow config string like 'inlines=yes'.
> >
> > This is not perf specific at all, we're trying to move stuff out of
> > tools/perf/ so that it becomes generally usable by other tools/ living
> > code, not just perf.
> >
> > I'll move it to where I think it should be and post as a reply to this
> > message.
>
> Find it at the bottom, I had to do a prep patch to move memdup() first,
> so that I could move tools/perf/util/include/linux/string.h to
> tools/include/linux/string.h, there is still work to do to fully move
> tools/perf/util/string.c to tools/lib/, will do eventually, for now what
> you need is there.

Also, I'm pushing what I have to the perf/ebpf branch in my tree.

- Arnaldo

> - Arnaldo
>
> commit 939d89c6acc78ee48e8ad83e9565a372b9f3987a
> Author: Wang Nan <[email protected]>
> Date: Mon Nov 16 11:42:05 2015 -0300
>
> tools: Clone the kernel's strtobool function
>
> Copying it to tools/lib/string.c, the counterpart to the kernel's
> lib/string.c.
>
> This is preparation for enhancing BPF program configuration, which will
> allow config string like 'inlines=yes'.
>
> Signed-off-by: Wang Nan <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Jonathan Cameron <[email protected]>
> Cc: Masami Hiramatsu <[email protected]>
> Cc: Zefan Li <[email protected]>
> Cc: [email protected]
> Link: http://lkml.kernel.org/r/[email protected]
> [ Copied it to tools/lib/string.c instead, to make it usable by other tools/ ]
> Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
>
> diff --git a/tools/include/linux/string.h b/tools/include/linux/string.h
> index f3a6db6ad732..2e2f736c039c 100644
> --- a/tools/include/linux/string.h
> +++ b/tools/include/linux/string.h
> @@ -6,4 +6,6 @@
>
> void *memdup(const void *src, size_t len);
>
> +int strtobool(const char *s, bool *res);
> +
> #endif /* _LINUX_STRING_H_ */
> diff --git a/tools/lib/string.c b/tools/lib/string.c
> index ecfd43a9b24e..065e54f42d8f 100644
> --- a/tools/lib/string.c
> +++ b/tools/lib/string.c
> @@ -1,5 +1,20 @@
> +/*
> + * linux/tools/lib/string.c
> + *
> + * Copied from linux/lib/string.c, where it is:
> + *
> + * Copyright (C) 1991, 1992 Linus Torvalds
> + *
> + * More specifically, the first copied function was strtobool, which
> + * was introduced by:
> + *
> + * d0f1fed29e6e ("Add a strtobool function matching semantics of existing in kernel equivalents")
> + * Author: Jonathan Cameron <[email protected]>
> + */
> +
> #include <stdlib.h>
> #include <string.h>
> +#include <errno.h>
> #include <linux/string.h>
>
> /**
> @@ -17,3 +32,31 @@ void *memdup(const void *src, size_t len)
>
> return p;
> }
> +
> +/**
> + * strtobool - convert common user inputs into boolean values
> + * @s: input string
> + * @res: result
> + *
> + * This routine returns 0 iff the first character is one of 'Yy1Nn0'.
> + * Otherwise it will return -EINVAL. Value pointed to by res is
> + * updated upon finding a match.
> + */
> +int strtobool(const char *s, bool *res)
> +{
> + switch (s[0]) {
> + case 'y':
> + case 'Y':
> + case '1':
> + *res = true;
> + break;
> + case 'n':
> + case 'N':
> + case '0':
> + *res = false;
> + break;
> + default:
> + return -EINVAL;
> + }
> + return 0;
> +}

2015-11-16 19:03:11

by Arnaldo Carvalho de Melo

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

Em Mon, Nov 16, 2015 at 12:10:09PM +0000, Wang Nan escreveu:
> This patch is a preparation for BPF prologue support which allows
> generating a series of BPF bytecode for fetching kernel data before
> calling program code. With the newly introduced multiple instances
> support, perf is able to create different prologues for different
> kprobe points.
>
> Before this patch, a bpf_program can be loaded into kernel only once,
> and get the only resuling fd. What this patch done is to allow creating
> and loading different variants of one bpf_program, then fetching their
> fds.
>
> Here describe the basic idea in this patch. The detail description of
> the newly introduced APIs can be found in comment in the patch body.
>
> The key of this patch is the new mechanism in bpf_program__load().
> Instead of loading BPF program into kernel directly, it calls a
> 'pre-processor' to generate program instances which would be final
> loaded into kernel based on the original code. To enable multiple
> instances generation, libbpf passes an index to the pre-processor
> so it know which instance is being loaded.
>
> Pre-processor should be passed from libbpf's user (perf) using
> bpf_program__set_prep(). The number of instances and the relationship
> between indics and the target instance should be clear when calling
> bpf_program__set_prep().
>
> To retrive fd for a specific instance of a program,
> bpf_program__nth_fd() is introduced. It return the resuling fd
> according to index.
>
> Signed-off-by: Wang Nan <[email protected]>
> Signed-off-by: He Kuang <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Masami Hiramatsu <[email protected]>
> Cc: Zefan Li <[email protected]>
> Cc: [email protected]
> ---
> tools/lib/bpf/libbpf.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++---
> tools/lib/bpf/libbpf.h | 64 ++++++++++++++++++++++
> 2 files changed, 200 insertions(+), 9 deletions(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index e176bad..fcfa39f 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -152,7 +152,11 @@ struct bpf_program {
> } *reloc_desc;
> int nr_reloc;
>
> - int fd;
> + struct {
> + int nr;
> + int *fds;
> + } instances;
> + bpf_program_prep_t preprocessor;
>
> struct bpf_object *obj;
> void *priv;
> @@ -206,10 +210,24 @@ struct bpf_object {
>
> static void bpf_program__unload(struct bpf_program *prog)
> {
> + int i;
> +
> if (!prog)
> return;
>
> - zclose(prog->fd);
> + /*
> + * If the object is opened but the program is never loaded,
> + * it is possible that prog->instances.nr == -1.
> + */
> + if (prog->instances.nr > 0) {
> + for (i = 0; i < prog->instances.nr; i++)
> + zclose(prog->instances.fds[i]);
> + } else if (prog->instances.nr != -1)
> + pr_warning("Internal error: instances.nr is %d\n",
> + prog->instances.nr);
> +


Multi line if/else blocks should be under {}, like you did for the if
part, but forgot to do for the else part.

I'm fixing this up, but please try to be consistent about these details
next time, more below.

> + prog->instances.nr = -1;
> + zfree(&prog->instances.fds);
> }
>
> static void bpf_program__exit(struct bpf_program *prog)
> @@ -260,7 +278,8 @@ bpf_program__init(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->instances.fds = NULL;
> + prog->instances.nr = -1;
>
> return 0;
> errout:
> @@ -860,13 +879,73 @@ static int
> bpf_program__load(struct bpf_program *prog,
> char *license, u32 kern_version)
> {
> - int err, fd;
> + int err = 0, fd, i;
> +
> + if (prog->instances.nr < 0 || !prog->instances.fds) {
> + if (prog->preprocessor) {
> + pr_warning("Internal error: can't load program '%s'\n",
> + prog->section_name);
> + return -LIBBPF_ERRNO__INTERNAL;
> + }

Those errors I think should come with some prefix to provide more
context about what kind of "internal error" is this, in some cases I am
adding "BPF", but not in all, more to think about how to make this
clearer.

[root@zoo ~]# ./funcptr
hello, world
ret=13
hello, world
ret=13
[root@zoo ~]# cat funcptr.c
#include <stdio.h>

typedef int (*bpf_program_prep_t)(char *msg);

static int hello(char *msg)
{
return printf(msg);
}

int main(void)
{
bpf_program_prep_t preprocessor = hello;
int ret = preprocessor("hello, world\n");
printf("ret=%d\n", ret);
ret = (*preprocessor)("hello, world\n");
printf("ret=%d\n", ret);
return 0;
}

Cleaned that.


> +
> + prog->instances.fds = malloc(sizeof(int));
> + if (!prog->instances.fds) {
> + pr_warning("No enough memory for fds\n");

In this case I'm changing to:

pr_warning("Not enough memory for BPF fds\n");

for instance.

> + return -ENOMEM;
> + }
> + prog->instances.nr = 1;
> + prog->instances.fds[0] = -1;
> + }
> +
> + if (!prog->preprocessor) {
> + if (prog->instances.nr != 1)
> + pr_warning("Program '%s' inconsistent: nr(%d) not 1\n",
> + prog->section_name, prog->instances.nr);
>
> - err = load_program(prog->insns, prog->insns_cnt,
> - license, kern_version, &fd);
> - if (!err)
> - prog->fd = fd;
> + err = load_program(prog->insns, prog->insns_cnt,
> + license, kern_version, &fd);
> + if (!err)
> + prog->instances.fds[0] = fd;
> + goto out;
> + }
> +
> + for (i = 0; i < prog->instances.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);

The simpler way here:
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->instances.fds[i] = -1;
> + if (result.pfd)
> + *result.pfd = -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->instances.fds[i] = fd;
> + }
> +out:
> if (err)
> pr_warning("failed to load program '%s'\n",
> prog->section_name);
> @@ -1121,5 +1200,53 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy)
>
> 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_instances,
> + bpf_program_prep_t prep)
> +{
> + int *instances_fds;
> +
> + if (nr_instances <= 0 || !prep)
> + return -EINVAL;
> +
> + if (prog->instances.nr > 0 || prog->instances.fds) {
> + pr_warning("Can't set pre-processor after loading\n");
> + return -EINVAL;
> + }
> +
> + instances_fds = malloc(sizeof(int) * nr_instances);
> + if (!instances_fds) {
> + pr_warning("alloc memory failed for fds\n");
> + return -ENOMEM;
> + }
> +
> + /* fill all fd with -1 */
> + memset(instances_fds, 0xff, sizeof(int) * nr_instances);

Why not use:

memset(instances_fds, -1, sizeof(int) * nr_instances);

[root@zoo ~]# cat memset-1.c
#include <stdio.h>
#include <string.h>

int main(void)
{
int a[10], i;

memset(a, -1, sizeof(a));

for (i = 0; i < 10; ++i)
printf("a[%d]=%d\n", i, a[i]);

return 0;
}
[root@zoo ~]# ./memset-1
a[0]=-1
a[1]=-1
a[2]=-1
a[3]=-1
a[4]=-1
a[5]=-1
a[6]=-1
a[7]=-1
a[8]=-1
a[9]=-1
[root@zoo ~]#

Fixed that as well.

> +
> + prog->instances.nr = nr_instances;
> + prog->instances.fds = instances_fds;
> + prog->preprocessor = prep;
> + return 0;
> +}
> +
> +int bpf_program__nth_fd(struct bpf_program *prog, int n)
> +{
> + int fd;
> +
> + if (n >= prog->instances.nr || n < 0) {
> + pr_warning("Can't get the %dth fd from program %s: only %d instances\n",
> + n, prog->section_name, prog->instances.nr);
> + return -EINVAL;
> + }
> +
> + fd = prog->instances.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 c9a9aef..949df4b 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -88,6 +88,70 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy);
>
> int bpf_program__fd(struct bpf_program *prog);
>
> +struct bpf_insn;
> +
> +/*
> + * Libbpf allows callers to adjust BPF programs before being loaded
> + * into kernel. One program in an object file can be transform into
> + * multiple variants to be attached to different code.
> + *
> + * bpf_program_prep_t, bpf_program__set_prep and bpf_program__nth_fd
> + * are APIs for this propose.
> + *
> + * - bpf_program_prep_t:
> + * It defines 'preprocessor', which is a caller defined function
> + * passed to libbpf through bpf_program__set_prep(), and will be
> + * called before program is loaded. The processor should adjust
> + * the program one time for each instances according to the number
> + * passed to it.
> + *
> + * - bpf_program__set_prep:
> + * Attachs a preprocessor to a BPF program. The number of instances
> + * whould be created is also passed through this function.
> + *
> + * - bpf_program__nth_fd:
> + * After the program is loaded, get resuling fds from bpf program for
> + * each instances.
> + *
> + * If bpf_program__set_prep() is not used, the program whould be loaded
> + * without adjustment during bpf_object__load(). The program has only
> + * one instance. In this case bpf_program__fd(prog) is equal to
> + * bpf_program__nth_fd(prog, 0).
> + */
> +
> +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;
> +};
> +
> +/*
> + * Parameters of bpf_program_prep_t:
> + * - prog: The bpf_program being loaded.
> + * - n: Index of instance being generated.
> + * - insns: BPF instructions array.
> + * - insns_cnt:Number of instructions in insns.
> + * - res: Output parameter, result of transformation.
> + *
> + * Return value:
> + * - Zero: pre-processing success.
> + * - Non-zero: pre-processing, stop loading.
> + */
> +typedef int (*bpf_program_prep_t)(struct bpf_program *prog, int n,
> + struct bpf_insn *insns, int insns_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-11-17 01:29:30

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 12/13] perf test: Test BPF prologue

Em Mon, Nov 16, 2015 at 12:10:14PM +0000, Wang Nan escreveu:
> This patch introduces a new BPF script to test BPF prologue. The new
> script probes at null_lseek, which is the function pointer when we try
> to lseek on '/dev/null'.
>
> null_lseek is chosen because it is a function pointer, so we don't need
> to consider inlining and LTO.
>
> By extracting file->f_mode, bpf-script-test-prologue.c should know whether
> the file is writable or readonly. According to llseek_loop() and
> bpf-script-test-prologue.c, one forth of total lseeks should be collected.

So I tentatively changed the section name key=val separator from '\n' to
';', applied all the patches up to this one (will review the last one
tomorrow), and tested it, reproducing your results, for some reason that
SEC() wasn't working, have to check again, using it expanded, as in my
previous tests, works, I updated the comments to reflect the tests I
did, please take a look.

I've pushed everything to my perf/ebpf branch, please let me know if
what is there is acceptable, then it will be up to Ingo to decide where
to put this, if in perf/urgent for this merge window, or in perf/core,
for the next one.

Ah, to extract the output for these BPF sub-tests I had to use -v, i.e.
just:

# perf test BPF
37: Test BPF filter : Ok
#

Ditto for the LLVM one.

Doesn't tell us too much about all those nice sub-tests...

How about:

# perf test -v BPF
37: Test BPF filter:
37.1: test a : Ok
37.2: test b : Ok
37.3: Test BPF prologue generation : Ok
37.4: Another... : Ok
37: Test BPF filter : Ok
#

Thanks!

- Arnaldo

> Signed-off-by: Wang Nan <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Masami Hiramatsu <[email protected]>
> Cc: Zefan Li <[email protected]>
> Cc: [email protected]
> ---
> tools/perf/tests/Build | 9 +++++++-
> tools/perf/tests/bpf-script-test-prologue.c | 35 +++++++++++++++++++++++++++++
> tools/perf/tests/bpf.c | 34 ++++++++++++++++++++++++++++
> tools/perf/tests/llvm.c | 4 ++++
> tools/perf/tests/llvm.h | 2 ++
> 5 files changed, 83 insertions(+), 1 deletion(-)
> create mode 100644 tools/perf/tests/bpf-script-test-prologue.c
>
> diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
> index f41ebf8..0ff8a97 100644
> --- a/tools/perf/tests/Build
> +++ b/tools/perf/tests/Build
> @@ -31,7 +31,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 llvm-src-base.o llvm-src-kbuild.o
> +perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o
> perf-y += bpf.o
> perf-y += topology.o
>
> @@ -49,6 +49,13 @@ $(OUTPUT)tests/llvm-src-kbuild.c: tests/bpf-script-test-kbuild.c
> $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
> $(Q)echo ';' >> $@
>
> +$(OUTPUT)tests/llvm-src-prologue.c: tests/bpf-script-test-prologue.c
> + $(call rule_mkdir)
> + $(Q)echo '#include <tests/llvm.h>' > $@
> + $(Q)echo 'const char test_llvm__bpf_test_prologue_prog[] =' >> $@
> + $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
> + $(Q)echo ';' >> $@
> +
> ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64))
> perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
> endif
> diff --git a/tools/perf/tests/bpf-script-test-prologue.c b/tools/perf/tests/bpf-script-test-prologue.c
> new file mode 100644
> index 0000000..7230e62
> --- /dev/null
> +++ b/tools/perf/tests/bpf-script-test-prologue.c
> @@ -0,0 +1,35 @@
> +/*
> + * bpf-script-test-prologue.c
> + * Test BPF prologue
> + */
> +#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 SEC(NAME) __attribute__((section(NAME), used))
> +
> +#include <uapi/linux/fs.h>
> +
> +#define FMODE_READ 0x1
> +#define FMODE_WRITE 0x2
> +
> +static void (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
> + (void *) 6;
> +
> +SEC("func=null_lseek file->f_mode offset orig")
> +int bpf_func__null_lseek(void *ctx, int err, unsigned long f_mode,
> + unsigned long offset, unsigned long orig)
> +{
> + if (err)
> + return 0;
> + if (f_mode & FMODE_WRITE)
> + return 0;
> + if (offset & 1)
> + return 0;
> + if (orig == SEEK_CUR)
> + return 0;
> + return 1;
> +}
> +
> +char _license[] SEC("license") = "GPL";
> +int _version SEC("version") = LINUX_VERSION_CODE;
> diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
> index ec16f78..c7131fa 100644
> --- a/tools/perf/tests/bpf.c
> +++ b/tools/perf/tests/bpf.c
> @@ -19,6 +19,29 @@ static int epoll_pwait_loop(void)
> return 0;
> }
>
> +#ifdef HAVE_BPF_PROLOGUE
> +
> +static int llseek_loop(void)
> +{
> + int fds[2], i;
> +
> + fds[0] = open("/dev/null", O_RDONLY);
> + fds[1] = open("/dev/null", O_RDWR);
> +
> + if (fds[0] < 0 || fds[1] < 0)
> + return -1;
> +
> + for (i = 0; i < NR_ITERS; i++) {
> + lseek(fds[i % 2], i, (i / 2) % 2 ? SEEK_CUR : SEEK_SET);
> + lseek(fds[(i + 1) % 2], i, (i / 2) % 2 ? SEEK_CUR : SEEK_SET);
> + }
> + close(fds[0]);
> + close(fds[1]);
> + return 0;
> +}
> +
> +#endif
> +
> static struct {
> enum test_llvm__testcase prog_id;
> const char *desc;
> @@ -37,6 +60,17 @@ static struct {
> &epoll_pwait_loop,
> (NR_ITERS + 1) / 2,
> },
> +#ifdef HAVE_BPF_PROLOGUE
> + {
> + LLVM_TESTCASE_BPF_PROLOGUE,
> + "Test BPF prologue generation",
> + "[bpf_prologue_test]",
> + "fix kbuild first",
> + "check your vmlinux setting?",
> + &llseek_loop,
> + (NR_ITERS + 1) / 4,
> + },
> +#endif
> };
>
> static int do_test(struct bpf_object *obj, int (*func)(void),
> diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
> index bc4cf50..7f4f7f7 100644
> --- a/tools/perf/tests/llvm.c
> +++ b/tools/perf/tests/llvm.c
> @@ -44,6 +44,10 @@ static struct {
> .source = test_llvm__bpf_test_kbuild_prog,
> .desc = "Test kbuild searching",
> },
> + [LLVM_TESTCASE_BPF_PROLOGUE] = {
> + .source = test_llvm__bpf_test_prologue_prog,
> + .desc = "Test BPF prologue generation",
> + },
> };
>
>
> diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h
> index d91d8f4..5150b4d 100644
> --- a/tools/perf/tests/llvm.h
> +++ b/tools/perf/tests/llvm.h
> @@ -6,10 +6,12 @@
>
> extern const char test_llvm__bpf_base_prog[];
> extern const char test_llvm__bpf_test_kbuild_prog[];
> +extern const char test_llvm__bpf_test_prologue_prog[];
>
> enum test_llvm__testcase {
> LLVM_TESTCASE_BASE,
> LLVM_TESTCASE_KBUILD,
> + LLVM_TESTCASE_BPF_PROLOGUE,
> __LLVM_TESTCASE_MAX,
> };
>
> --
> 1.8.3.4

2015-11-17 01:31:42

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 12/13] perf test: Test BPF prologue



On 2015/11/17 9:29, Arnaldo Carvalho de Melo wrote:
> Em Mon, Nov 16, 2015 at 12:10:14PM +0000, Wang Nan escreveu:
>> This patch introduces a new BPF script to test BPF prologue. The new
>> script probes at null_lseek, which is the function pointer when we try
>> to lseek on '/dev/null'.
>>
>> null_lseek is chosen because it is a function pointer, so we don't need
>> to consider inlining and LTO.
>>
>> By extracting file->f_mode, bpf-script-test-prologue.c should know whether
>> the file is writable or readonly. According to llseek_loop() and
>> bpf-script-test-prologue.c, one forth of total lseeks should be collected.
> So I tentatively changed the section name key=val separator from '\n' to
> ';', applied all the patches up to this one (will review the last one
> tomorrow), and tested it, reproducing your results, for some reason that
> SEC() wasn't working, have to check again, using it expanded, as in my
> previous tests, works, I updated the comments to reflect the tests I
> did, please take a look.
>
> I've pushed everything to my perf/ebpf branch, please let me know if
> what is there is acceptable, then it will be up to Ingo to decide where
> to put this, if in perf/urgent for this merge window, or in perf/core,
> for the next one.
>
> Ah, to extract the output for these BPF sub-tests I had to use -v, i.e.
> just:
>
> # perf test BPF
> 37: Test BPF filter : Ok
> #
>
> Ditto for the LLVM one.
>
> Doesn't tell us too much about all those nice sub-tests...
>
> How about:
>
> # perf test -v BPF
> 37: Test BPF filter:
> 37.1: test a : Ok
> 37.2: test b : Ok
> 37.3: Test BPF prologue generation : Ok
> 37.4: Another... : Ok
> 37: Test BPF filter : Ok
> #
>
> Thanks!

Thanks to your work. I'm checking them now.

> - Arnaldo
>

2015-11-17 03:06:31

by Wang Nan

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



On 2015/11/16 20:10, Wang Nan wrote:
> This patch allows creating only one BPF program for different
> 'probe_trace_event'(tev) generated by one 'perf_probe_event'(pev), if
> their prologues 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 be
> grouped together.
>
> Test result:
>
> Sample BPF program:
>
> SEC("inlines=no\n"
> "func=SyS_dup? oldfd")
> int func(void *ctx)
> {
> return 1;
> }
>
> It would probe at SyS_dup2 and SyS_dup3, extracts oldfd as its argument.
>
> Following cmdline shows BPF program loaded into kernel by perf:
>
> # ./perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog
>
> Before this patch:
>
> # ./perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog
> [1] 24858
> lrwx------ 1 root root 64 Nov 14 04:09 3 -> anon_inode:bpf-prog
> lrwx------ 1 root root 64 Nov 14 04:09 4 -> anon_inode:bpf-prog
> ...
>
> After this patch:
>
> # ./perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog
> [1] 25699
> lrwx------ 1 root root 64 Nov 14 04:10 3 -> anon_inode:bpf-prog
> ...
>
> Signed-off-by: Wang Nan <[email protected]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Masami Hiramatsu <[email protected]>
> Cc: Zefan Li <[email protected]>
> Cc: [email protected]
> ---

[SNIP]

> @@ -462,7 +570,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_debug("No enough memory: alloc type_mapping failed\n");
> + return -ENOMEM;
> + }
> + memset(priv->type_mapping, 0xff,
> + sizeof(int) * pev->ntevs);
> +

We can change 0xff to -1 like previous patches. Will do it by resending.

Thank you.

2015-11-17 03:54:17

by Wang Nan

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



On 2015/11/17 3:02, Arnaldo Carvalho de Melo wrote:
> Em Mon, Nov 16, 2015 at 12:10:09PM +0000, Wang Nan escreveu:
>> This patch is a preparation for BPF prologue support which allows
>> generating a series of BPF bytecode for fetching kernel data before
>> calling program code. With the newly introduced multiple instances
>> support, perf is able to create different prologues for different
>> kprobe points.
>>
>> Before this patch, a bpf_program can be loaded into kernel only once,
>> and get the only resuling fd. What this patch done is to allow creating
>> and loading different variants of one bpf_program, then fetching their
>> fds.
>>
>> Here describe the basic idea in this patch. The detail description of
>> the newly introduced APIs can be found in comment in the patch body.
>>
>> The key of this patch is the new mechanism in bpf_program__load().
>> Instead of loading BPF program into kernel directly, it calls a
>> 'pre-processor' to generate program instances which would be final
>> loaded into kernel based on the original code. To enable multiple
>> instances generation, libbpf passes an index to the pre-processor
>> so it know which instance is being loaded.
>>
>> Pre-processor should be passed from libbpf's user (perf) using
>> bpf_program__set_prep(). The number of instances and the relationship
>> between indics and the target instance should be clear when calling
>> bpf_program__set_prep().
>>
>> To retrive fd for a specific instance of a program,
>> bpf_program__nth_fd() is introduced. It return the resuling fd
>> according to index.
>>
>> Signed-off-by: Wang Nan <[email protected]>
>> Signed-off-by: He Kuang <[email protected]>
>> Cc: Alexei Starovoitov <[email protected]>
>> Cc: Arnaldo Carvalho de Melo <[email protected]>
>> Cc: Masami Hiramatsu <[email protected]>
>> Cc: Zefan Li <[email protected]>
>> Cc: [email protected]
>> ---
>> tools/lib/bpf/libbpf.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++---
>> tools/lib/bpf/libbpf.h | 64 ++++++++++++++++++++++
>> 2 files changed, 200 insertions(+), 9 deletions(-)
>>
>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>> index e176bad..fcfa39f 100644
>> --- a/tools/lib/bpf/libbpf.c
>> +++ b/tools/lib/bpf/libbpf.c
>> @@ -152,7 +152,11 @@ struct bpf_program {
>> } *reloc_desc;
>> int nr_reloc;
>>
>> - int fd;
>> + struct {
>> + int nr;
>> + int *fds;
>> + } instances;
>> + bpf_program_prep_t preprocessor;
>>
>> struct bpf_object *obj;
>> void *priv;
>> @@ -206,10 +210,24 @@ struct bpf_object {
>>
>> static void bpf_program__unload(struct bpf_program *prog)
>> {
>> + int i;
>> +
>> if (!prog)
>> return;
>>
>> - zclose(prog->fd);
>> + /*
>> + * If the object is opened but the program is never loaded,
>> + * it is possible that prog->instances.nr == -1.
>> + */
>> + if (prog->instances.nr > 0) {
>> + for (i = 0; i < prog->instances.nr; i++)
>> + zclose(prog->instances.fds[i]);
>> + } else if (prog->instances.nr != -1)
>> + pr_warning("Internal error: instances.nr is %d\n",
>> + prog->instances.nr);
>> +
>
> Multi line if/else blocks should be under {}, like you did for the if
> part, but forgot to do for the else part.
>
> I'm fixing this up, but please try to be consistent about these details
> next time, more below.
>
>> + prog->instances.nr = -1;
>> + zfree(&prog->instances.fds);
>> }
>>
>> static void bpf_program__exit(struct bpf_program *prog)
>> @@ -260,7 +278,8 @@ bpf_program__init(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->instances.fds = NULL;
>> + prog->instances.nr = -1;
>>
>> return 0;
>> errout:
>> @@ -860,13 +879,73 @@ static int
>> bpf_program__load(struct bpf_program *prog,
>> char *license, u32 kern_version)
>> {
>> - int err, fd;
>> + int err = 0, fd, i;
>> +
>> + if (prog->instances.nr < 0 || !prog->instances.fds) {
>> + if (prog->preprocessor) {
>> + pr_warning("Internal error: can't load program '%s'\n",
>> + prog->section_name);
>> + return -LIBBPF_ERRNO__INTERNAL;
>> + }
> Those errors I think should come with some prefix to provide more
> context about what kind of "internal error" is this, in some cases I am
> adding "BPF", but not in all, more to think about how to make this
> clearer.

In bpf_program__set_prep, the parameters are already checked, so
if prog->preprocessor is set (!= NULL), the conditions in
'if' statement should never be satisified. Therefore it must an
internal error (bug?) in libbpf.

All LIBBPF error code except LIBBPF_ERRNO__INTERNAL can be triggered
by user by some way (for example, providing an invalid object file).
Therefore, we need to tell them the reason of failure formally, let
error messages hint users how to fix their fault.
LIBBPF_ERRNO__INTERNAL is different. If it raise there must be a bug
in libbpf or perf (incorrectly use libbpf's API). In this case, we
have already provided pr_{debug,warning} to developers. In my opinion,
formally reporting is not require because perf's user don't need to
be told too much about the internal implementation (it causes confusion).
All they need to know is "there is a bug, let's report it with '-v' output".

Thank you.

2015-11-17 04:39:00

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 12/13] perf test: Test BPF prologue



On 2015/11/17 9:29, Arnaldo Carvalho de Melo wrote:
> Em Mon, Nov 16, 2015 at 12:10:14PM +0000, Wang Nan escreveu:
>> This patch introduces a new BPF script to test BPF prologue. The new
>> script probes at null_lseek, which is the function pointer when we try
>> to lseek on '/dev/null'.
>>
>> null_lseek is chosen because it is a function pointer, so we don't need
>> to consider inlining and LTO.
>>
>> By extracting file->f_mode, bpf-script-test-prologue.c should know whether
>> the file is writable or readonly. According to llseek_loop() and
>> bpf-script-test-prologue.c, one forth of total lseeks should be collected.
> So I tentatively changed the section name key=val separator from '\n' to
> ';', applied all the patches up to this one (will review the last one
> tomorrow), and tested it, reproducing your results, for some reason that
> SEC() wasn't working, have to check again, using it expanded, as in my
> previous tests, works, I updated the comments to reflect the tests I
> did, please take a look.
>
> I've pushed everything to my perf/ebpf branch, please let me know if
> what is there is acceptable, then it will be up to Ingo to decide where
> to put this, if in perf/urgent for this merge window, or in perf/core,
> for the next one.
>
> Ah, to extract the output for these BPF sub-tests I had to use -v, i.e.
> just:
>
> # perf test BPF
> 37: Test BPF filter : Ok
> #
>
> Ditto for the LLVM one.
>
> Doesn't tell us too much about all those nice sub-tests...
>
> How about:
>
> # perf test -v BPF
> 37: Test BPF filter:
> 37.1: test a : Ok
> 37.2: test b : Ok
> 37.3: Test BPF prologue generation : Ok
> 37.4: Another... : Ok
> 37: Test BPF filter : Ok
> #
>
> Thanks!

I think what you want is to report state of subtests *without* -v?

That would be nice but changing of perf testing infrastructure is required
because there's no "sub-test" before, and we both agree that testcase
should be silent without '-v', so we need a way to output them in
builtin-test.c.

Let me try it but I think it should not be a blocking problem.

Thank you.

2015-11-17 08:36:59

by Wang Nan

[permalink] [raw]
Subject: [PATCH 0/5] perf tools: Improve BPF support

This patchset is based on Arnaldo's perf/ebpf branch
(commit 14dcf0ba2a26f680776093a7a61e74526713f9b2).

In response to Arnaldo's comment in [1], in this patch set:

Patch 1/5 fixes a bug in BPF testcase which incorrectly returns
TEST_OK when failure;

Patch 2/5 is resent with some coding style improvements.

Patch 3/5 - 4/5 provide improvements for 'perf test' to support
sub-tests output which Arnaldo looks forward [2].

Patch 5/5 mutes all testcases during testing to ensure outupt
of 'perf test' is clean when verbose is not set.

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

Wang Nan (5):
perf test: Fix 2 bugs in 'perf test BPF'
perf tools: Use same BPF program if arguments are identical
perf test: Print result for each subtest for llvm
perf test: Print result for each subtest for BPF
perf test: Mute test cases if verbose == 0

tools/perf/tests/bpf.c | 45 +++++++++----
tools/perf/tests/builtin-test.c | 134 ++++++++++++++++++++++++++++++++------
tools/perf/tests/llvm.c | 65 +++++++++----------
tools/perf/tests/tests.h | 27 +++++++-
tools/perf/util/bpf-loader.c | 138 ++++++++++++++++++++++++++++++++++++++--
5 files changed, 330 insertions(+), 79 deletions(-)

--
1.8.3.4

2015-11-17 08:37:08

by Wang Nan

[permalink] [raw]
Subject: [PATCH 1/5] perf test: Fix 2 bugs in 'perf test BPF'

Two bugs in 'perf test BPF' are found when testing BPF prologue without
vmlinux:

# mv /lib/modules/4.3.0-rc4+/build/vmlinux{,.bak}
# ./perf test BPF
37: Test BPF filter :Failed to find the path for kernel: No such file or directory
Ok

Test BPF should fail in this case.

This patch fixes two bugs in 'perf test BPF'. After this patch:

# ./perf test BPF
37: Test BPF filter :Failed to find the path for kernel: No such file or directory
FAILED!
# mv /lib/modules/4.3.0-rc4+/build/vmlinux{.bak,}
# ./perf test BPF
37: Test BPF filter : Ok

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/tests/bpf.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
index c7131fa..dca3998 100644
--- a/tools/perf/tests/bpf.c
+++ b/tools/perf/tests/bpf.c
@@ -102,8 +102,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj);
if (err || list_empty(&parse_evlist.list)) {
pr_debug("Failed to add events selected by BPF\n");
- if (!err)
- return TEST_FAIL;
+ return TEST_FAIL;
}

snprintf(pid, sizeof(pid), "%d", getpid());
@@ -157,8 +156,10 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
}
}

- if (count != expect)
+ if (count != expect) {
pr_debug("BPF filter result incorrect\n");
+ goto out_delete_evlist;
+ }

ret = TEST_OK;

--
1.8.3.4

2015-11-17 08:37:10

by Wang Nan

[permalink] [raw]
Subject: [PATCH 2/5] 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 prologues 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 be
grouped together.

Test result:

Sample BPF program:

#define SEC(NAME) __attribute__((section(NAME), used))
SEC("inlines=no;"
"func=SyS_dup? oldfd")
int func(void *ctx)
{
return 1;
}

It would probe at SyS_dup2 and SyS_dup3, extracts oldfd as its argument.

Following cmdline shows BPF program loaded into kernel by perf:

# ./perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog

Before this patch:

# ./perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog
[1] 24858
lrwx------ 1 root root 64 Nov 14 04:09 3 -> anon_inode:bpf-prog
lrwx------ 1 root root 64 Nov 14 04:09 4 -> anon_inode:bpf-prog
...

After this patch:

# ./perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog
[1] 25699
lrwx------ 1 root root 64 Nov 14 04:10 3 -> anon_inode:bpf-prog
...

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/util/bpf-loader.c | 138 ++++++++++++++++++++++++++++++++++++++++---
1 file changed, 131 insertions(+), 7 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 190a1c7..36544e5 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -38,6 +38,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 bool libbpf_initialized;
@@ -113,6 +115,7 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,

cleanup_perf_probe_events(&priv->pev, 1);
zfree(&priv->insns_buf);
+ zfree(&priv->type_mapping);
free(priv);
}

@@ -381,7 +384,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)
@@ -389,10 +392,21 @@ 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_debug("Internal error: prologue type %d not found\n", n);
+ return -BPF_LOADER_ERRNO__PROLOGUE;
+ }
+
+ tev = &pev->tevs[i];

buf = priv->insns_buf;
err = bpf__gen_prologue(tev->args, tev->nargs,
@@ -423,6 +437,101 @@ errout:
return -BPF_LOADER_ERRNO__PROLOGUE;
}

+/*
+ * compare_tev_args is reflexive, transitive and antisymmetric.
+ * I can proof it 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;
+}
+
+/*
+ * Assign a type number to each tevs in a pev.
+ * mapping is an array with same slots as tevs in that pev.
+ * nr_types will be set to number of types.
+ */
+static int map_prologue(struct perf_probe_event *pev, int *mapping,
+ int *nr_types)
+{
+ int i, type = 0;
+ struct probe_trace_event **ptevs;
+
+ size_t array_sz = sizeof(*ptevs) * pev->ntevs;
+
+ ptevs = malloc(array_sz);
+ if (!ptevs) {
+ pr_debug("No ehough memory: alloc ptevs failed\n");
+ return -ENOMEM;
+ }
+
+ pr_debug("In map_prologue, ntevs=%d\n", pev->ntevs);
+ for (i = 0; i < pev->ntevs; i++)
+ ptevs[i] = &pev->tevs[i];
+
+ qsort(ptevs, pev->ntevs, sizeof(*ptevs),
+ compare_tev_args);
+
+ for (i = 0; i < pev->ntevs; i++) {
+ int n;
+
+ n = ptevs[i] - pev->tevs;
+ if (i == 0) {
+ mapping[n] = type;
+ pr_debug("mapping[%d]=%d\n", n, type);
+ continue;
+ }
+
+ if (compare_tev_args(ptevs + i, ptevs + i - 1) == 0)
+ mapping[n] = type;
+ else
+ mapping[n] = ++type;
+
+ pr_debug("mapping[%d]=%d\n", n, mapping[n]);
+ }
+ free(ptevs);
+ *nr_types = type + 1;
+
+ return 0;
+}
+
static int hook_load_preprocessor(struct bpf_program *prog)
{
struct perf_probe_event *pev;
@@ -462,7 +571,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_debug("No enough memory: alloc type_mapping failed\n");
+ return -ENOMEM;
+ }
+ memset(priv->type_mapping, -1,
+ 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;
}
@@ -596,10 +717,13 @@ int bpf__foreach_tev(struct bpf_object *obj,
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) {
pr_debug("bpf: failed to get file descriptor\n");
--
1.8.3.4

2015-11-17 08:37:49

by Wang Nan

[permalink] [raw]
Subject: [PATCH 3/5] perf test: Print result for each subtest for llvm

Currently 'perf test llvm' and 'perf test BPF' are combined by multiple
sub-tests, but the result is only one line:

# perf test LLVM
35: Test LLVM searching and compiling : Ok

This patch introduces sub-tests support, allows perf test to report
result for each sub-tests:

# ./perf test LLVM
35: Test LLVM searching and compiling :
35.1: Basic BPF llvm compiling test : Ok
35.2: Test kbuild searching : Ok
35.3: Compile source for BPF prologue generation test : Ok

When failure:

# cat ~/.perfconfig
[llvm]
clang-path = "/bin/false"
# ./perf test LLVM
35: Test LLVM searching and compiling :
35.1: Basic BPF llvm compiling test : FAILED!
35.2: Test kbuild searching : Skip
35.3: Compile source for BPF prologue generation test : Skip

And:

# rm ~/.perfconfig
# ./perf test LLVM
35: Test LLVM searching and compiling :
35.1: Basic BPF llvm compiling test : Skip
35.2: Test kbuild searching : Skip
35.3: Compile source for BPF prologue generation test : Skip

Skip by user:

# ./perf test -s 1,`seq -s , 3 42`
1: vmlinux symtab matches kallsyms : Skip (user override)
2: detect openat syscall event : Ok
...
35: Test LLVM searching and compiling : Skip (user override)
...

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/tests/builtin-test.c | 111 ++++++++++++++++++++++++++++++++--------
tools/perf/tests/llvm.c | 65 +++++++++++------------
tools/perf/tests/tests.h | 22 +++++++-
3 files changed, 141 insertions(+), 57 deletions(-)

diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 80c442e..29225142 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -159,7 +159,13 @@ static struct test generic_tests[] = {
},
{
.desc = "Test LLVM searching and compiling",
- .func = test__llvm,
+ .need_subtests = true,
+ .subtest = {
+ .skip_if_fail = true,
+ .get_nr = test__llvm_subtest_get_nr,
+ .get_desc = test__llvm_subtest_get_desc,
+ .func = test__llvm_subtest,
+ },
},
{
.desc = "Test topology in session",
@@ -203,7 +209,7 @@ static bool perf_test__matches(struct test *test, int curr, int argc, const char
return false;
}

-static int run_test(struct test *test)
+static int run_test(struct test *test, int subtest)
{
int status, err = -1, child = fork();
char sbuf[STRERR_BUFSIZE];
@@ -216,7 +222,10 @@ static int run_test(struct test *test)

if (!child) {
pr_debug("test child forked, pid %d\n", getpid());
- err = test->func();
+ if (!test->need_subtests)
+ err = test->func();
+ else
+ err = test->subtest.func(subtest);
exit(err);
}

@@ -234,8 +243,48 @@ static int run_test(struct test *test)
}

#define for_each_test(j, t) \
- for (j = 0; j < ARRAY_SIZE(tests); j++) \
- for (t = &tests[j][0]; t->func; t++)
+ for (j = 0; j < ARRAY_SIZE(tests); j++) \
+ for (t = &tests[j][0]; \
+ (!t->need_subtests && t->func) || \
+ (t->need_subtests && \
+ t->subtest.get_nr && \
+ t->subtest.get_desc && \
+ t->subtest.func); \
+ t++)
+
+static int test_and_print(struct test *t, bool force_skip, int subtest)
+{
+ int err;
+
+ if (!force_skip) {
+ pr_debug("\n--- start ---\n");
+ err = run_test(t, subtest);
+ pr_debug("---- end ----\n");
+ } else {
+ pr_debug("\n--- force skipped ---\n");
+ err = TEST_SKIP;
+ }
+
+ if (!t->need_subtests)
+ pr_debug("%s:", t->desc);
+ else
+ pr_debug("%s subtest %d:", t->desc, subtest);
+
+ switch (err) {
+ case TEST_OK:
+ pr_info(" Ok\n");
+ break;
+ case TEST_SKIP:
+ color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip\n");
+ break;
+ case TEST_FAIL:
+ default:
+ color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n");
+ break;
+ }
+
+ return err;
+}

static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
{
@@ -264,21 +313,43 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
continue;
}

- pr_debug("\n--- start ---\n");
- err = run_test(t);
- pr_debug("---- end ----\n%s:", t->desc);
-
- switch (err) {
- case TEST_OK:
- pr_info(" Ok\n");
- break;
- case TEST_SKIP:
- color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip\n");
- break;
- case TEST_FAIL:
- default:
- color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n");
- break;
+ if (!t->need_subtests) {
+ test_and_print(t, false, -1);
+ } else {
+ int subn = t->subtest.get_nr();
+ /*
+ * minus 2 to align with normal testcases.
+ * For subtest we print additional '.x' in number.
+ * for example:
+ *
+ * 35: Test LLVM searching and compiling :
+ * 35.1: Basic BPF llvm compiling test : Ok
+ */
+ int subw = width > 2 ? width - 2 : width;
+ bool skip = false;
+ int subi;
+
+ if (subn <= 0) {
+ color_fprintf(stderr, PERF_COLOR_YELLOW,
+ " Skip (not compiled in)\n");
+ continue;
+ }
+ pr_info("\n");
+
+ for (subi = 0; subi < subn; subi++) {
+ int len = strlen(t->subtest.get_desc(subi));
+
+ if (subw < len)
+ subw = len;
+ }
+
+ for (subi = 0; subi < subn; subi++) {
+ pr_info("%2d.%1d: %-*s:", i, subi + 1, subw,
+ t->subtest.get_desc(subi));
+ err = test_and_print(t, skip, subi);
+ if (err != TEST_OK && t->subtest.skip_if_fail)
+ skip = true;
+ }
}
}

diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index 7f4f7f7..cebb593 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -46,7 +46,7 @@ static struct {
},
[LLVM_TESTCASE_BPF_PROLOGUE] = {
.source = test_llvm__bpf_test_prologue_prog,
- .desc = "Test BPF prologue generation",
+ .desc = "Compile source for BPF prologue generation test",
},
};

@@ -131,44 +131,39 @@ out:
return ret;
}

-int test__llvm(void)
+int test__llvm_subtest(int i)
{
- enum test_llvm__testcase i;
+ int ret;
+ void *obj_buf = NULL;
+ size_t obj_buf_sz = 0;

- for (i = 0; i < __LLVM_TESTCASE_MAX; i++) {
- int ret;
- void *obj_buf = NULL;
- size_t obj_buf_sz = 0;
+ if ((i < 0) || (i >= __LLVM_TESTCASE_MAX))
+ return TEST_FAIL;

- ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz,
- i, false);
+ ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz,
+ i, false);

- if (ret == TEST_OK) {
- ret = test__bpf_parsing(obj_buf, obj_buf_sz);
- if (ret != TEST_OK)
- pr_debug("Failed to parse test case '%s'\n",
- bpf_source_table[i].desc);
- }
- free(obj_buf);
-
- switch (ret) {
- case TEST_SKIP:
- return TEST_SKIP;
- case TEST_OK:
- break;
- default:
- /*
- * Test 0 is the basic LLVM test. If test 0
- * fail, the basic LLVM support not functional
- * so the whole test should fail. If other test
- * case fail, it can be fixed by adjusting
- * config so don't report error.
- */
- if (i == 0)
- return TEST_FAIL;
- else
- return TEST_SKIP;
+ if (ret == TEST_OK) {
+ ret = test__bpf_parsing(obj_buf, obj_buf_sz);
+ if (ret != TEST_OK) {
+ pr_debug("Failed to parse test case '%s'\n",
+ bpf_source_table[i].desc);
}
}
- return TEST_OK;
+ free(obj_buf);
+
+ return ret;
+}
+
+int test__llvm_subtest_get_nr(void)
+{
+ return __LLVM_TESTCASE_MAX;
+}
+
+const char *test__llvm_subtest_get_desc(int i)
+{
+ if ((i < 0) || (i >= __LLVM_TESTCASE_MAX))
+ return NULL;
+
+ return bpf_source_table[i].desc;
}
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index 3c8734a..f8f9eb6 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -1,6 +1,8 @@
#ifndef TESTS_H
#define TESTS_H

+#include <stdbool.h>
+
#define TEST_ASSERT_VAL(text, cond) \
do { \
if (!(cond)) { \
@@ -26,7 +28,19 @@ enum {

struct test {
const char *desc;
- int (*func)(void);
+
+ bool need_subtests;
+
+ union {
+ int (*func)(void);
+
+ struct {
+ bool skip_if_fail;
+ int (*get_nr)(void);
+ const char *(*get_desc)(int i);
+ int (*func)(int i);
+ } subtest;
+ };
};

/* Tests */
@@ -65,7 +79,11 @@ 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);
+
+const char *test__llvm_subtest_get_desc(int i);
+int test__llvm_subtest_get_nr(void);
+int test__llvm_subtest(int i);
+
int test__bpf(void);
int test_session_topology(void);

--
1.8.3.4

2015-11-17 08:37:12

by Wang Nan

[permalink] [raw]
Subject: [PATCH 4/5] perf test: Print result for each subtest for BPF

This patch prints each sub-tests results for BPF testcases.

Before:

# ./perf test BPF
37: Test BPF filter : Ok

After:

# ./perf test BPF
37: Test BPF filter :
37.1: Test basic BPF filtering : Ok
37.2: Test BPF prologue generation : Ok

When failure:

# cat ~/.perfconfig
[llvm]
clang-path = "/bin/false"
# ./perf test BPF
37: Test BPF filter :
37.1: Test basic BPF filtering : Skip
37.2: Test BPF prologue generation : Skip

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/tests/bpf.c | 38 ++++++++++++++++++++++++++++----------
tools/perf/tests/builtin-test.c | 8 +++++++-
tools/perf/tests/tests.h | 5 ++++-
3 files changed, 39 insertions(+), 12 deletions(-)

diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
index dca3998..8c2262d 100644
--- a/tools/perf/tests/bpf.c
+++ b/tools/perf/tests/bpf.c
@@ -215,28 +215,46 @@ out:
return ret;
}

-int test__bpf(void)
+int test__bpf_subtest_get_nr(void)
+{
+ return (int)ARRAY_SIZE(bpf_testcase_table);
+}
+
+const char *test__bpf_subtest_get_desc(int i)
+{
+ if (i < 0 || i >= (int)ARRAY_SIZE(bpf_testcase_table))
+ return NULL;
+ return bpf_testcase_table[i].desc;
+}
+
+int test__bpf_subtest(int i)
{
- unsigned int i;
int err;

+ if (i < 0 || i >= (int)ARRAY_SIZE(bpf_testcase_table))
+ return TEST_FAIL;
+
if (geteuid() != 0) {
pr_debug("Only root can run BPF test\n");
return TEST_SKIP;
}

- for (i = 0; i < ARRAY_SIZE(bpf_testcase_table); i++) {
- err = __test__bpf(i);
+ err = __test__bpf(i);
+ return err;
+}

- if (err != TEST_OK)
- return err;
- }
+#else
+int test__bpf_subtest_get_nr(void)
+{
+ return 0;
+}

- return TEST_OK;
+const char *test__bpf_subtest_get_desc(int i __maybe_unused)
+{
+ return NULL;
}

-#else
-int test__bpf(void)
+int test__bpf_subtest(int i __maybe_unused)
{
pr_debug("Skip BPF test because BPF support is not compiled\n");
return TEST_SKIP;
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 29225142..9914412 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -173,7 +173,13 @@ static struct test generic_tests[] = {
},
{
.desc = "Test BPF filter",
- .func = test__bpf,
+ .need_subtests = true,
+ .subtest = {
+ .skip_if_fail = true,
+ .get_nr = test__bpf_subtest_get_nr,
+ .get_desc = test__bpf_subtest_get_desc,
+ .func = test__bpf_subtest,
+ },
},
{
.func = NULL,
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index f8f9eb6..2ce66b2 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -84,7 +84,10 @@ const char *test__llvm_subtest_get_desc(int i);
int test__llvm_subtest_get_nr(void);
int test__llvm_subtest(int i);

-int test__bpf(void);
+const char *test__bpf_subtest_get_desc(int i);
+int test__bpf_subtest_get_nr(void);
+int test__bpf_subtest(int i);
+
int test_session_topology(void);

#if defined(__arm__) || defined(__aarch64__)
--
1.8.3.4

2015-11-17 08:37:14

by Wang Nan

[permalink] [raw]
Subject: [PATCH 5/5] perf test: Mute test cases if verbose == 0

Sometimes error messages in breaks the pretty output of 'perf test'.
For example:

# mv /lib/modules/4.3.0-rc4+/build/vmlinux{,.bak}
# ./perf test LLVM BPF
35: Test LLVM searching and compiling :
35.1: Basic BPF llvm compiling test : Ok
35.2: Test kbuild searching : Ok
35.3: Compile source for BPF prologue generation test : Ok
37: Test BPF filter :
37.1: Test basic BPF filtering : Ok
37.2: Test BPF prologue generation :Failed to find the path for kernel: No such file or directory
FAILED!

This patch mute test cases thoroughly by redirect their stdout and
stderr to /dev/null when verbose == 0. After applying this patch:

# ./perf test LLVM BPF
35: Test LLVM searching and compiling :
35.1: Basic BPF llvm compiling test : Ok
35.2: Test kbuild searching : Ok
35.3: Compile source for BPF prologue generation test : Ok
37: Test BPF filter :
37.1: Test basic BPF filtering : Ok
37.2: Test BPF prologue generation : FAILED!

# ./perf test -v LLVM BPF
35: Test LLVM searching and compiling :
35.1: Basic BPF llvm compiling test :
--- start ---
test child forked, pid 13183
Kernel build dir is set to /lib/modules/4.3.0-rc4+/build
set env: KBUILD_DIR=/lib/modules/4.3.0-rc4+/build
...
bpf: config 'func=null_lseek file->f_mode offset orig' is ok
Looking at the vmlinux_path (7 entries long)
Failed to find the path for kernel: No such file or directory
bpf_probe: failed to convert perf probe eventsFailed to add events selected by BPF
test child finished with -1
---- end ----
Test BPF filter subtest 1: FAILED!

Signed-off-by: Wang Nan <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/tests/builtin-test.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 9914412..799edad 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -227,7 +227,22 @@ static int run_test(struct test *test, int subtest)
}

if (!child) {
+ int nullfd;
+
pr_debug("test child forked, pid %d\n", getpid());
+
+ if (!verbose) {
+ nullfd = open("/dev/null", O_WRONLY);
+ if (nullfd >= 0) {
+ close(STDERR_FILENO);
+ close(STDOUT_FILENO);
+
+ dup2(nullfd, STDOUT_FILENO);
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+ close(nullfd);
+ }
+ }
+
if (!test->need_subtests)
err = test->func();
else
--
1.8.3.4

2015-11-17 08:45:13

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 12/13] perf test: Test BPF prologue

Hi Arnaldo,

On 2015/11/17 9:29, Arnaldo Carvalho de Melo wrote:

[SNIP]
> I've pushed everything to my perf/ebpf branch, please let me know if
> what is there is acceptable, then it will be up to Ingo to decide where
> to put this, if in perf/urgent for this merge window, or in perf/core,
> for the next one.

I have checked and tested your changes in your perf/ebpf and they
are okay to me.

>
> Ah, to extract the output for these BPF sub-tests I had to use -v, i.e.
> just:
>
> # perf test BPF
> 37: Test BPF filter : Ok
> #
>
> Ditto for the LLVM one.
>
> Doesn't tell us too much about all those nice sub-tests...
>
> How about:
>
> # perf test -v BPF
> 37: Test BPF filter:
> 37.1: test a : Ok
> 37.2: test b : Ok
> 37.3: Test BPF prologue generation : Ok
> 37.4: Another... : Ok
> 37: Test BPF filter : Ok
> #

I have sent a cset based on perf/ebpf in [1]. With the last 3 patch
you will see 'perf test' output information in this way. Please have a try:

# ./perf test LLVM BPF
35: Test LLVM searching and compiling :
35.1: Basic BPF llvm compiling test : Ok
35.2: Test kbuild searching : Ok
35.3: Compile source for BPF prologue generation test : Ok
37: Test BPF filter :
37.1: Test basic BPF filtering : Ok
37.2: Test BPF prologue generation : Ok

Thank you.

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

2015-11-17 09:50:07

by Wang Nan

[permalink] [raw]
Subject: [PATCH] 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]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---

Hi Arnaldo,

This patch is a standalone improvement to support BPF argument fetching
better. Without this patch vmlinux must be reside in standard path
for BPF loader finding it. I think you can review this patch with other
patch I send today. Other patch on my local tree is not belong to this
topic.

Thank you.

---
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 199fc31..cfd0e14 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1118,6 +1118,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-11-17 12:20:28

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 12/13] perf test: Test BPF prologue

Em Tue, Nov 17, 2015 at 12:38:37PM +0800, Wangnan (F) escreveu:
> On 2015/11/17 9:29, Arnaldo Carvalho de Melo wrote:
> >How about:
> >
> > # perf test -v BPF
> > 37: Test BPF filter:
> > 37.1: test a : Ok
> > 37.2: test b : Ok
> > 37.3: Test BPF prologue generation : Ok
> > 37.4: Another... : Ok
> > 37: Test BPF filter : Ok
> > #

> I think what you want is to report state of subtests *without* -v?

> That would be nice but changing of perf testing infrastructure is required
> because there's no "sub-test" before, and we both agree that testcase
> should be silent without '-v', so we need a way to output them in
> builtin-test.c.

> Let me try it but I think it should not be a blocking problem.

Thanks for looking into this,

- Arnaldo

2015-11-17 12:33:46

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH] perf record: Support custom vmlinux path

Em Tue, Nov 17, 2015 at 09:44:44AM +0000, Wang Nan escreveu:
> 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]>
> Cc: Alexei Starovoitov <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> Cc: Masami Hiramatsu <[email protected]>
> Cc: Namhyung Kim <[email protected]>
> Cc: Zefan Li <[email protected]>
> Cc: [email protected]
> ---
>
> Hi Arnaldo,
>
> This patch is a standalone improvement to support BPF argument fetching
> better. Without this patch vmlinux must be reside in standard path
> for BPF loader finding it. I think you can review this patch with other
> patch I send today. Other patch on my local tree is not belong to this
> topic.

Thanks, applying.

> Thank you.
>
> ---
> 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 199fc31..cfd0e14 100644
> --- a/tools/perf/builtin-record.c
> +++ b/tools/perf/builtin-record.c
> @@ -1118,6 +1118,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-11-17 12:42:29

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH] perf record: Support custom vmlinux path

Em Tue, Nov 17, 2015 at 09:33:40AM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Tue, Nov 17, 2015 at 09:44:44AM +0000, Wang Nan escreveu:
>
> Thanks, applying.

I take that back, I think this is questionable, we should instead always
have the --vmlinux option, and when the feature it deals with is not
built in, warn the user about it, something like:

# perf record --vmlinux /a/b/c ...
WARNING: --vmlinux being ignored, as BPF prologue not built-in...
<output that can be produced while ignoring --vmlinux>

And please consider that for other options that are surrounded by ifdefs
related to the eBPF support, there are a few more, clang related.

Also this isn't updating the perf-record man page, please do so and
please put this in a separate section for BPF related options, something
like:

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

BPF:

These options can be used when BPF support is built-in.

...

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

This way the user gets to know that the tool can be used with BPF while
reading its documentation and if they decide to try it, then the
warnings will be presented if the feature is not built-in.

- Arnaldo

2015-11-17 12:45:45

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH] perf record: Support custom vmlinux path



?????ҵ? iPhone

> ?? 2015??11??17?գ?????8:42??Arnaldo Carvalho de Melo <[email protected]> д????
>
> Em Tue, Nov 17, 2015 at 09:33:40AM -0300, Arnaldo Carvalho de Melo escreveu:
>> Em Tue, Nov 17, 2015 at 09:44:44AM +0000, Wang Nan escreveu:
>>
>> Thanks, applying.
>
> I take that back, I think this is questionable, we should instead always
> have the --vmlinux option, and when the feature it deals with is not
> built in, warn the user about it, something like:
>
> # perf record --vmlinux /a/b/c ...
> WARNING: --vmlinux being ignored, as BPF prologue not built-in...
> <output that can be produced while ignoring --vmlinux>
>
> And please consider that for other options that are surrounded by ifdefs
> related to the eBPF support, there are a few more, clang related.
>
> Also this isn't updating the perf-record man page, please do so and
> please put this in a separate section for BPF related options, something
> like:
>
> --------------------
>
> BPF:
>
> These options can be used when BPF support is built-in.
>
> ...
>
> ---------------------
>
> This way the user gets to know that the tool can be used with BPF while
> reading its documentation and if they decide to try it, then the
> warnings will be presented if the feature is not built-in.
>

Ok. Thanks for your comment. I will do that tomorrow.

> - Arnaldo

2015-11-17 12:56:51

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 1/5] perf test: Fix 2 bugs in 'perf test BPF'

Em Tue, Nov 17, 2015 at 08:32:46AM +0000, Wang Nan escreveu:
> Two bugs in 'perf test BPF' are found when testing BPF prologue without
> vmlinux:
>
> # mv /lib/modules/4.3.0-rc4+/build/vmlinux{,.bak}
> # ./perf test BPF
> 37: Test BPF filter :Failed to find the path for kernel: No such file or directory
> Ok
>
> Test BPF should fail in this case.
>
> This patch fixes two bugs in 'perf test BPF'. After this patch:
>
> # ./perf test BPF
> 37: Test BPF filter :Failed to find the path for kernel: No such file or directory
> FAILED!
> # mv /lib/modules/4.3.0-rc4+/build/vmlinux{.bak,}
> # ./perf test BPF
> 37: Test BPF filter : Ok

Applied, it is a clear improvement, but:

[root@zoo ~]# perf test
1: vmlinux symtab matches kallsyms : Skip
2: detect openat syscall event : Ok
<SNIP>
5: parse events tests : Ok
6: Validate PERF_RECORD_* events & perf_sample fields : FAILED!
7: Test perf pmu format parsing : Ok
<SNIP>
37: Test BPF filter :Failed to find the path for kernel: Invalid ELF file
FAILED!
38: x86 rdpmc test : Ok
39: Test converting perf time to TSC : FAILED!
40: Test dwarf unwind : Ok
41: Test x86 instruction decoder - new instructions : Ok
42: Test intel cqm nmi context read : Skip
[root@zoo ~]#

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

It is now the only test to emit some message in non-verbose mode that is not
"Ok", "FAILED!" or "Skip" :-)

- Arnaldo

> Signed-off-by: Wang Nan <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> ---
> tools/perf/tests/bpf.c | 7 ++++---
> 1 file changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
> index c7131fa..dca3998 100644
> --- a/tools/perf/tests/bpf.c
> +++ b/tools/perf/tests/bpf.c
> @@ -102,8 +102,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
> err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj);
> if (err || list_empty(&parse_evlist.list)) {
> pr_debug("Failed to add events selected by BPF\n");
> - if (!err)
> - return TEST_FAIL;
> + return TEST_FAIL;
> }
>
> snprintf(pid, sizeof(pid), "%d", getpid());
> @@ -157,8 +156,10 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
> }
> }
>
> - if (count != expect)
> + if (count != expect) {
> pr_debug("BPF filter result incorrect\n");
> + goto out_delete_evlist;
> + }
>
> ret = TEST_OK;
>
> --
> 1.8.3.4

2015-11-17 13:02:01

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 1/5] perf test: Fix 2 bugs in 'perf test BPF'



?????ҵ? iPhone

> ?? 2015??11??17?գ?????8:56??Arnaldo Carvalho de Melo <[email protected]> д????
>
> Em Tue, Nov 17, 2015 at 08:32:46AM +0000, Wang Nan escreveu:
>> Two bugs in 'perf test BPF' are found when testing BPF prologue without
>> vmlinux:
>>
>> # mv /lib/modules/4.3.0-rc4+/build/vmlinux{,.bak}
>> # ./perf test BPF
>> 37: Test BPF filter :Failed to find the path for kernel: No such file or directory
>> Ok
>>
>> Test BPF should fail in this case.
>>
>> This patch fixes two bugs in 'perf test BPF'. After this patch:
>>
>> # ./perf test BPF
>> 37: Test BPF filter :Failed to find the path for kernel: No such file or directory
>> FAILED!
>> # mv /lib/modules/4.3.0-rc4+/build/vmlinux{.bak,}
>> # ./perf test BPF
>> 37: Test BPF filter : Ok
>
> Applied, it is a clear improvement, but:
>
> [root@zoo ~]# perf test
> 1: vmlinux symtab matches kallsyms : Skip
> 2: detect openat syscall event : Ok
> <SNIP>
> 5: parse events tests : Ok
> 6: Validate PERF_RECORD_* events & perf_sample fields : FAILED!
> 7: Test perf pmu format parsing : Ok
> <SNIP>
> 37: Test BPF filter :Failed to find the path for kernel: Invalid ELF file
> FAILED!
> 38: x86 rdpmc test : Ok
> 39: Test converting perf time to TSC : FAILED!
> 40: Test dwarf unwind : Ok
> 41: Test x86 instruction decoder - new instructions : Ok
> 42: Test intel cqm nmi context read : Skip
> [root@zoo ~]#
>
> ------------------------
>
> It is now the only test to emit some message in non-verbose mode that is not
> "Ok", "FAILED!" or "Skip" :-)
>

Please see 5/5. In that patch I try to fix it throughly by output redirection.

Thank you.


> - Arnaldo
>
>> Signed-off-by: Wang Nan <[email protected]>
>> Cc: Arnaldo Carvalho de Melo <[email protected]>
>> ---
>> tools/perf/tests/bpf.c | 7 ++++---
>> 1 file changed, 4 insertions(+), 3 deletions(-)
>>
>> diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
>> index c7131fa..dca3998 100644
>> --- a/tools/perf/tests/bpf.c
>> +++ b/tools/perf/tests/bpf.c
>> @@ -102,8 +102,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
>> err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj);
>> if (err || list_empty(&parse_evlist.list)) {
>> pr_debug("Failed to add events selected by BPF\n");
>> - if (!err)
>> - return TEST_FAIL;
>> + return TEST_FAIL;
>> }
>>
>> snprintf(pid, sizeof(pid), "%d", getpid());
>> @@ -157,8 +156,10 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
>> }
>> }
>>
>> - if (count != expect)
>> + if (count != expect) {
>> pr_debug("BPF filter result incorrect\n");
>> + goto out_delete_evlist;
>> + }
>>
>> ret = TEST_OK;
>>
>> --
>> 1.8.3.4

2015-11-17 13:03:13

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 3/5] perf test: Print result for each subtest for llvm

Em Tue, Nov 17, 2015 at 08:32:48AM +0000, Wang Nan escreveu:
> Currently 'perf test llvm' and 'perf test BPF' are combined by multiple
> sub-tests, but the result is only one line:

Thanks for working on this! I'm fixing this:

[acme@zoo linux]$ am /wb/1.patch
Applying: perf test: Print result for each subtest for llvm
/home/acme/git/linux/.git/rebase-apply/patch:56: space before tab in
indent.
(t->need_subtests && \
warning: 1 line adds whitespace errors.
tools/perf/tests/builtin-test.c:249: space before tab in indent.
+ (t->need_subtests && \
[acme@zoo linux]$


Please check your .git/hooks/ files so that you can catch this while
commiting your work.

- Arnaldo

2015-11-17 13:11:44

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 5/5] perf test: Mute test cases if verbose == 0

Em Tue, Nov 17, 2015 at 08:32:50AM +0000, Wang Nan escreveu:
> Sometimes error messages in breaks the pretty output of 'perf test'.
> For example:

Ok, great, nevermind my report about this, thanks for fixing it!

- Arnaldo

> # mv /lib/modules/4.3.0-rc4+/build/vmlinux{,.bak}
> # ./perf test LLVM BPF
> 35: Test LLVM searching and compiling :
> 35.1: Basic BPF llvm compiling test : Ok
> 35.2: Test kbuild searching : Ok
> 35.3: Compile source for BPF prologue generation test : Ok
> 37: Test BPF filter :
> 37.1: Test basic BPF filtering : Ok
> 37.2: Test BPF prologue generation :Failed to find the path for kernel: No such file or directory
> FAILED!
>
> This patch mute test cases thoroughly by redirect their stdout and
> stderr to /dev/null when verbose == 0. After applying this patch:
>
> # ./perf test LLVM BPF
> 35: Test LLVM searching and compiling :
> 35.1: Basic BPF llvm compiling test : Ok
> 35.2: Test kbuild searching : Ok
> 35.3: Compile source for BPF prologue generation test : Ok
> 37: Test BPF filter :
> 37.1: Test basic BPF filtering : Ok
> 37.2: Test BPF prologue generation : FAILED!
>
> # ./perf test -v LLVM BPF
> 35: Test LLVM searching and compiling :
> 35.1: Basic BPF llvm compiling test :
> --- start ---
> test child forked, pid 13183
> Kernel build dir is set to /lib/modules/4.3.0-rc4+/build
> set env: KBUILD_DIR=/lib/modules/4.3.0-rc4+/build
> ...
> bpf: config 'func=null_lseek file->f_mode offset orig' is ok
> Looking at the vmlinux_path (7 entries long)
> Failed to find the path for kernel: No such file or directory
> bpf_probe: failed to convert perf probe eventsFailed to add events selected by BPF
> test child finished with -1
> ---- end ----
> Test BPF filter subtest 1: FAILED!
>
> Signed-off-by: Wang Nan <[email protected]>
> Cc: Arnaldo Carvalho de Melo <[email protected]>
> ---
> tools/perf/tests/builtin-test.c | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
> index 9914412..799edad 100644
> --- a/tools/perf/tests/builtin-test.c
> +++ b/tools/perf/tests/builtin-test.c
> @@ -227,7 +227,22 @@ static int run_test(struct test *test, int subtest)
> }
>
> if (!child) {
> + int nullfd;
> +
> pr_debug("test child forked, pid %d\n", getpid());
> +
> + if (!verbose) {
> + nullfd = open("/dev/null", O_WRONLY);
> + if (nullfd >= 0) {
> + close(STDERR_FILENO);
> + close(STDOUT_FILENO);
> +
> + dup2(nullfd, STDOUT_FILENO);
> + dup2(STDOUT_FILENO, STDERR_FILENO);
> + close(nullfd);
> + }
> + }
> +
> if (!test->need_subtests)
> err = test->func();
> else
> --
> 1.8.3.4

2015-11-17 13:16:06

by Arnaldo Carvalho de Melo

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

Em Tue, Nov 17, 2015 at 11:05:41AM +0800, Wangnan (F) escreveu:
>
>
> On 2015/11/16 20:10, Wang Nan wrote:
> >This patch allows creating only one BPF program for different
> >'probe_trace_event'(tev) generated by one 'perf_probe_event'(pev), if
> >their prologues 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 be
> >grouped together.
> >
> >Test result:
> >
> >Sample BPF program:
> >
> > SEC("inlines=no\n"
> > "func=SyS_dup? oldfd")
> > int func(void *ctx)
> > {
> > return 1;
> > }
> >
> >It would probe at SyS_dup2 and SyS_dup3, extracts oldfd as its argument.
> >
> >Following cmdline shows BPF program loaded into kernel by perf:
> >
> > # ./perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog
> >
> >Before this patch:
> >
> > # ./perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog
> > [1] 24858
> > lrwx------ 1 root root 64 Nov 14 04:09 3 -> anon_inode:bpf-prog
> > lrwx------ 1 root root 64 Nov 14 04:09 4 -> anon_inode:bpf-prog
> > ...
> >
> >After this patch:
> >
> > # ./perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog
> > [1] 25699
> > lrwx------ 1 root root 64 Nov 14 04:10 3 -> anon_inode:bpf-prog
> > ...
> >
> >Signed-off-by: Wang Nan <[email protected]>
> >Cc: Alexei Starovoitov <[email protected]>
> >Cc: Arnaldo Carvalho de Melo <[email protected]>
> >Cc: Masami Hiramatsu <[email protected]>
> >Cc: Zefan Li <[email protected]>
> >Cc: [email protected]
> >---
>
> [SNIP]
>
> >@@ -462,7 +570,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_debug("No enough memory: alloc type_mapping failed\n");
> >+ return -ENOMEM;
> >+ }
> >+ memset(priv->type_mapping, 0xff,
> >+ sizeof(int) * pev->ntevs);
> >+
>
> We can change 0xff to -1 like previous patches. Will do it by resending.

Thanks for doing that,

Applied the new version of this patch,

- Arnaldo

2015-11-17 13:34:33

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH 1/5] perf test: Fix 2 bugs in 'perf test BPF'

Em Tue, Nov 17, 2015 at 09:01:41PM +0800, pi3orama escreveu:
> 发自我的 iPhone
> > 在 2015年11月17日,下午8:56,Arnaldo Carvalho de Melo <[email protected]> 写道:
> > It is now the only test to emit some message in non-verbose mode that is not
> > "Ok", "FAILED!" or "Skip" :-)

> Please see 5/5. In that patch I try to fix it throughly by output redirection.

Yeah, I saw it, perhaps next time you could have it in a different
order, i.e. as soon as you saw that this message was out of place,
needing a fix, you back off a bit from what you're doing, do the fix,
then resume work, so that when someone goes on testing your patches one
by one, the message doesn't appear.

Anyway, all patches applied, doing a build-test on a few distros now,
will go to Ingo today, thanks!

- Arnaldo

2015-11-19 14:03:39

by Wang Nan

[permalink] [raw]
Subject: [PATCH 0/2] perf tools: Builtin options related improvements

This patch set is based on Arnaldo's perf/ebpf.

According to his suggestion, patch 1/2 ensure options always there even
if they are not build.

Patch 2/2 add vmlinux option to perf record to support BPF argument
fetching better. Without this patch vmlinux must be reside in standard
path for BPF loader finding it.

He Kuang (1):
perf record: Support custom vmlinux path

Wang Nan (1):
perf tools: Always give options even it not compiled

tools/perf/Documentation/perf-record.txt | 10 ++++++++--
tools/perf/builtin-probe.c | 23 +++++++++++++++++++++++
tools/perf/builtin-record.c | 18 ++++++++++++++++++
tools/perf/util/parse-options.c | 30 +++++++++++++++++++++++++++++-
tools/perf/util/parse-options.h | 16 ++++++++++++++++
5 files changed, 94 insertions(+), 3 deletions(-)

--
1.8.3.4

2015-11-19 14:04:01

by Wang Nan

[permalink] [raw]
Subject: [PATCH 1/2] perf tools: Always give options even it not compiled

This patch keeps options of perf builtins same in all condition. If the
option is disabled because of compiling options, users should be
notified.

This patch does it by introducing a series of new option macros, flags
and field in struct options. For those options disabled by compiling,
OPT_NOTBUILT_NOARG, OPT_NOTBUILT_OPTARG and OPT_NOTBUILT can be used
to record the help messages and the reason why not built them.

Options in 'perf record' and 'perf probe' are fixed by those new macros.

Test result:

- Normal result:
# ./perf probe -L sys_write
<SyS_write@/path/to/kernel/fs/read_write.c:0>
0 SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
...

- Build with NO_DWARF=1:

# ./perf probe -L sys_write
ERROR: option '--line' is not built (disabled by NO_DWARF)

Usage: perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]
or: perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]
or: perf probe [<options>] --del '[GROUP:]EVENT' ...
or: perf probe --list [GROUP:]EVENT ...
or: perf probe [<options>] --funcs

-L, --line <FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]>
Show source code lines.
(disabled by NO_DWARF)

# ./perf probe -k /tmp/vmlinux sys_write
WARNING: option '--vmlinux' is not built (disabled by NO_DWARF)
Added new event:
probe:sys_write (on sys_write)

You can now use it in all perf tools, such as:

perf record -e probe:sys_write -aR sleep 1

# ./perf probe -k sys_write

Usage: perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]
or: perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]
...

- Help messages:

# ./perf probe -h

Usage: perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]
or: perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]
...
-k, --vmlinux <file> vmlinux pathname
(disabled by NO_DWARF)
-L, --line <FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]>
Show source code lines.
(disabled by NO_DWARF)
-l, --list <[GROUP:]EVENT>
list up probe events
...
-s, --source <directory>
path to kernel source
(disabled by NO_DWARF)
-V, --vars <FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT>
Show accessible variables on PROBEDEF
(disabled by NO_DWARF)
...
--no-inlines Don't search inlined functions
(disabled by NO_DWARF)
--range Show variables location range in scope (with --vars only)
(disabled by NO_DWARF)

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/builtin-probe.c | 23 +++++++++++++++++++++++
tools/perf/builtin-record.c | 7 +++++++
tools/perf/util/parse-options.c | 30 +++++++++++++++++++++++++++++-
tools/perf/util/parse-options.h | 16 ++++++++++++++++
4 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 132afc9..4d7cf57 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -490,6 +490,29 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
"directory", "path to kernel source"),
OPT_BOOLEAN('\0', "no-inlines", &probe_conf.no_inlines,
"Don't search inlined functions"),
+#else
+#define DISABLE_MSG "disabled by NO_DWARF"
+ OPT_NOTBUILT('L', "line",
+ "FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]",
+ "Show source code lines.",
+ DISABLE_MSG, false),
+ OPT_NOTBUILT('V', "vars",
+ "FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT",
+ "Show accessible variables on PROBEDEF",
+ DISABLE_MSG, false),
+ OPT_NOTBUILT_NOARG('\0', "externs",
+ "Show external variables too (with --vars only)",
+ DISABLE_MSG, false),
+ OPT_NOTBUILT_NOARG('\0', "range",
+ "Show variables location range in scope (with --vars only)",
+ DISABLE_MSG, false),
+ OPT_NOTBUILT('k', "vmlinux", "file", "vmlinux pathname",
+ DISABLE_MSG, true),
+ OPT_NOTBUILT('s', "source", "directory", "path to kernel source",
+ DISABLE_MSG, true),
+ OPT_NOTBUILT_NOARG('\0', "no-inlines", "Don't search inlined functions",
+ DISABLE_MSG, true),
+#undef DISABLE_MSG
#endif
OPT__DRY_RUN(&probe_event_dry_run),
OPT_INTEGER('\0', "max-probes", &probe_conf.max_probes,
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 199fc31..b02d1ee 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1118,6 +1118,13 @@ 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"),
+#else
+ OPT_NOTBUILT(0, "clang-path", "clang path",
+ "clang binary to use for compiling BPF scriptlets",
+ "disabled by NO_LIBBPF", true),
+ OPT_NOTBUILT(0, "clang-opt", "clang options",
+ "options passed to clang when compiling BPF scriptlets",
+ "disabled by NO_LIBBPF", true),
#endif
OPT_END()
};
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
index 9fca092..5c3eb7a 100644
--- a/tools/perf/util/parse-options.c
+++ b/tools/perf/util/parse-options.c
@@ -1,4 +1,5 @@
#include "util.h"
+#include "debug.h"
#include "parse-options.h"
#include "cache.h"
#include "header.h"
@@ -69,6 +70,7 @@ static int get_value(struct parse_opt_ctx_t *p,
if (!(flags & OPT_SHORT) && p->opt) {
switch (opt->type) {
case OPTION_CALLBACK:
+ case OPTION_NOTBUILT:
if (!(opt->flags & PARSE_OPT_NOARG))
break;
/* FALLTHROUGH */
@@ -152,7 +154,28 @@ static int get_value(struct parse_opt_ctx_t *p,
if (get_arg(p, opt, flags, &arg))
return -1;
return (*opt->callback)(opt, arg, 0) ? (-1) : 0;
-
+ case OPTION_NOTBUILT: {
+ char short_str[2] = {opt->short_name, '\0'};
+ int ret = -1;
+
+ pr_warning("%s: option '-%s%s' is not built (%s)\n",
+ opt->flags & PARSE_OPT_CANSKIP ?
+ "WARNING" : "ERROR",
+ opt->long_name ? "-" : short_str,
+ opt->long_name ? : "",
+ opt->notbuild_reason);
+ if (opt->flags & PARSE_OPT_CANSKIP)
+ ret = 0;
+ if (unset)
+ return ret;
+ if (opt->flags & PARSE_OPT_NOARG)
+ return ret;
+ if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
+ return ret;
+ if (get_arg(p, opt, flags, &arg))
+ return -1;
+ return ret;
+ }
case OPTION_INTEGER:
if (unset) {
*(int *)opt->value = 0;
@@ -607,6 +630,7 @@ static void print_option_help(const struct option *opts, int full)
pos += fprintf(stderr, " <n>");
break;
case OPTION_CALLBACK:
+ case OPTION_NOTBUILT:
if (opts->flags & PARSE_OPT_NOARG)
break;
/* FALLTHROUGH */
@@ -647,6 +671,10 @@ static void print_option_help(const struct option *opts, int full)
pad = USAGE_OPTS_WIDTH;
}
fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
+ if (opts->type == OPTION_NOTBUILT)
+ fprintf(stderr, "%*s(%s)\n",
+ USAGE_OPTS_WIDTH + USAGE_GAP, "",
+ opts->notbuild_reason);
}

static int option__cmp(const void *va, const void *vb)
diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h
index a8e407b..08cc9b5 100644
--- a/tools/perf/util/parse-options.h
+++ b/tools/perf/util/parse-options.h
@@ -22,6 +22,7 @@ enum parse_opt_type {
OPTION_CALLBACK,
OPTION_U64,
OPTION_UINTEGER,
+ OPTION_NOTBUILT,
};

enum parse_opt_flags {
@@ -41,6 +42,7 @@ enum parse_opt_option_flags {
PARSE_OPT_DISABLED = 32,
PARSE_OPT_EXCLUSIVE = 64,
PARSE_OPT_NOEMPTY = 128,
+ PARSE_OPT_CANSKIP = 256,
};

struct option;
@@ -96,6 +98,7 @@ struct option {
void *value;
const char *argh;
const char *help;
+ const char *notbuild_reason;

int flags;
parse_opt_cb *callback;
@@ -146,6 +149,19 @@ struct option {
.value = (v), (a), .help = (h), .callback = (f), \
.flags = PARSE_OPT_OPTARG, .data = (d) }

+#define OPT_NOTBUILT_NOARG(s, l, h, r, skip) \
+ { .type = OPTION_NOTBUILT, .short_name = (s), .long_name = (l), \
+ .help = (h), .notbuild_reason = (r), \
+ .flags = PARSE_OPT_NOARG | (skip ? PARSE_OPT_CANSKIP : 0)}
+#define OPT_NOTBUILT_OPTARG(s, l, a, h, r, skip) \
+ { .type = OPTION_NOTBUILT, .short_name = (s), .long_name = (l), \
+ .argh = (a), .help = (h), .notbuild_reason = (r), \
+ .flags = PARSE_OPT_OPTARG | (skip ? PARSE_OPT_CANSKIP : 0)}
+#define OPT_NOTBUILT(s, l, a, h, r, skip) \
+ { .type = OPTION_NOTBUILT, .short_name = (s), .long_name = (l), \
+ .argh = (a), .help = (h), .notbuild_reason = (r), \
+ .flags = skip ? PARSE_OPT_CANSKIP : 0}
+
/* parse_options() will filter out the processed options and leave the
* non-option argments in argv[].
* Returns the number of arguments left in argv[].
--
1.8.3.4

2015-11-19 14:04:07

by Wang Nan

[permalink] [raw]
Subject: [PATCH 2/2] 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.

Documentation is also updated.

Signed-off-by: He Kuang <[email protected]>
Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
---
tools/perf/Documentation/perf-record.txt | 10 ++++++++--
tools/perf/builtin-record.c | 11 +++++++++++
2 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt
index e630a7d..8d032f4 100644
--- a/tools/perf/Documentation/perf-record.txt
+++ b/tools/perf/Documentation/perf-record.txt
@@ -314,11 +314,17 @@ This option sets the time out limit. The default value is 500 ms.
Record context switch events i.e. events of type PERF_RECORD_SWITCH or
PERF_RECORD_SWITCH_CPU_WIDE.

---clang-path::
+--clang-path=PATH::
Path to clang binary to use for compiling BPF scriptlets.
+(enabled when BPF support is on)

---clang-opt::
+--clang-opt=OPTIONS::
Options passed to clang when compiling BPF scriptlets.
+(enabled when BPF support is on)
+
+--vmlinux=PATH::
+Specify vmlinux path which has debuginfo.
+(enabled when BPF prologue is on)

SEE ALSO
--------
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index b02d1ee..b046fc3 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -1118,6 +1118,14 @@ 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"),
+#else
+ OPT_NOTBUILT(0, "vmlinux", "file",
+ "vmlinux pathname",
+ "BPF prologue not built-in", true),
+#endif
#else
OPT_NOTBUILT(0, "clang-path", "clang path",
"clang binary to use for compiling BPF scriptlets",
@@ -1125,6 +1133,9 @@ struct option __record_options[] = {
OPT_NOTBUILT(0, "clang-opt", "clang options",
"options passed to clang when compiling BPF scriptlets",
"disabled by NO_LIBBPF", true),
+ OPT_NOTBUILT(0, "vmlinux", "file",
+ "vmlinux pathname",
+ "disabled by NO_LIBBPF", true),
#endif
OPT_END()
};
--
1.8.3.4

Subject: RE: [PATCH 1/2] perf tools: Always give options even it not compiled

>From: Wang Nan [mailto:[email protected]]
>
>This patch keeps options of perf builtins same in all condition. If the
>option is disabled because of compiling options, users should be
>notified.
>
>This patch does it by introducing a series of new option macros, flags
>and field in struct options. For those options disabled by compiling,
>OPT_NOTBUILT_NOARG, OPT_NOTBUILT_OPTARG and OPT_NOTBUILT can be used
>to record the help messages and the reason why not built them.
>
>Options in 'perf record' and 'perf probe' are fixed by those new macros.

Hmm, OK, I agree the reason why this is useful. Could you reconsider
the implementation, because just cloning the code is ugly and not
maintainable?

It will be better if we can replace OPT_BOOLEAN with;

OPT_DEPENDS(HAVE_DWARF_SUPPORT, BOOLEAN, '\0', "no-inlines", &probe_conf.no_inlines, ...

This may be done by following macros ;

----
#define OPTMSG_HAVE_DWARF_SUPPORT "NO_DWARF"
#ifdef HAVE_DWARF_SUPPORT
#define OPTVAL_HAVE_DWARF_SUPPORT 1
#else
#define OPTVAL_HAVE_DWARF_SUPPORT 0
#endif

#define __OPT_DEPENDS(val, msg, opt, ...) \
{.type = OPTION_NEXT_DEPENDS, .value = (void *)val, .data = msg, }, opt(__VA_ARGS__)

#define _OPT_DEPENDS(val, msg, opt, ...) \
__OPT_DEPENDS(val, msg, opt, __VA_ARGS__)

#define OPT_DEPENDS(flag, opt, ...) \
_OPT_DEPENDS(OPTVAL_ ## flag, OPTMSG_ ## flag, OPT_ ## opt, __VA_ARGS__)
----

And if the parser find OPTION_NEXT_DEPENDS and .value != NULL, it skips next entry.


Thank you,

>
>Test result:
>
>- Normal result:
> # ./perf probe -L sys_write
> <SyS_write@/path/to/kernel/fs/read_write.c:0>
> 0 SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
> ...
>
>- Build with NO_DWARF=1:
>
> # ./perf probe -L sys_write
> ERROR: option '--line' is not built (disabled by NO_DWARF)
>
> Usage: perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]
> or: perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]
> or: perf probe [<options>] --del '[GROUP:]EVENT' ...
> or: perf probe --list [GROUP:]EVENT ...
> or: perf probe [<options>] --funcs
>
> -L, --line <FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]>
> Show source code lines.
> (disabled by NO_DWARF)
>
> # ./perf probe -k /tmp/vmlinux sys_write
> WARNING: option '--vmlinux' is not built (disabled by NO_DWARF)
> Added new event:
> probe:sys_write (on sys_write)
>
> You can now use it in all perf tools, such as:
>
> perf record -e probe:sys_write -aR sleep 1
>
> # ./perf probe -k sys_write
>
> Usage: perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]
> or: perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]
> ...
>
>- Help messages:
>
> # ./perf probe -h
>
> Usage: perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]
> or: perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]
> ...
> -k, --vmlinux <file> vmlinux pathname
> (disabled by NO_DWARF)
> -L, --line <FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]>
> Show source code lines.
> (disabled by NO_DWARF)
> -l, --list <[GROUP:]EVENT>
> list up probe events
> ...
> -s, --source <directory>
> path to kernel source
> (disabled by NO_DWARF)
> -V, --vars <FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT>
> Show accessible variables on PROBEDEF
> (disabled by NO_DWARF)
> ...
> --no-inlines Don't search inlined functions
> (disabled by NO_DWARF)
> --range Show variables location range in scope (with --vars only)
> (disabled by NO_DWARF)
>
>Signed-off-by: Wang Nan <[email protected]>
>Cc: Alexei Starovoitov <[email protected]>
>Cc: Arnaldo Carvalho de Melo <[email protected]>
>Cc: Masami Hiramatsu <[email protected]>
>Cc: Namhyung Kim <[email protected]>
>Cc: Zefan Li <[email protected]>
>Cc: [email protected]
>---
> tools/perf/builtin-probe.c | 23 +++++++++++++++++++++++
> tools/perf/builtin-record.c | 7 +++++++
> tools/perf/util/parse-options.c | 30 +++++++++++++++++++++++++++++-
> tools/perf/util/parse-options.h | 16 ++++++++++++++++
> 4 files changed, 75 insertions(+), 1 deletion(-)
>
>diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
>index 132afc9..4d7cf57 100644
>--- a/tools/perf/builtin-probe.c
>+++ b/tools/perf/builtin-probe.c
>@@ -490,6 +490,29 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
> "directory", "path to kernel source"),
> OPT_BOOLEAN('\0', "no-inlines", &probe_conf.no_inlines,
> "Don't search inlined functions"),
>+#else
>+#define DISABLE_MSG "disabled by NO_DWARF"
>+ OPT_NOTBUILT('L', "line",
>+ "FUNC[:RLN[+NUM|-RLN2]]|SRC:ALN[+NUM|-ALN2]",
>+ "Show source code lines.",
>+ DISABLE_MSG, false),
>+ OPT_NOTBUILT('V', "vars",
>+ "FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT",
>+ "Show accessible variables on PROBEDEF",
>+ DISABLE_MSG, false),
>+ OPT_NOTBUILT_NOARG('\0', "externs",
>+ "Show external variables too (with --vars only)",
>+ DISABLE_MSG, false),
>+ OPT_NOTBUILT_NOARG('\0', "range",
>+ "Show variables location range in scope (with --vars only)",
>+ DISABLE_MSG, false),
>+ OPT_NOTBUILT('k', "vmlinux", "file", "vmlinux pathname",
>+ DISABLE_MSG, true),
>+ OPT_NOTBUILT('s', "source", "directory", "path to kernel source",
>+ DISABLE_MSG, true),
>+ OPT_NOTBUILT_NOARG('\0', "no-inlines", "Don't search inlined functions",
>+ DISABLE_MSG, true),
>+#undef DISABLE_MSG
> #endif
> OPT__DRY_RUN(&probe_event_dry_run),
> OPT_INTEGER('\0', "max-probes", &probe_conf.max_probes,
>diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
>index 199fc31..b02d1ee 100644
>--- a/tools/perf/builtin-record.c
>+++ b/tools/perf/builtin-record.c
>@@ -1118,6 +1118,13 @@ 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"),
>+#else
>+ OPT_NOTBUILT(0, "clang-path", "clang path",
>+ "clang binary to use for compiling BPF scriptlets",
>+ "disabled by NO_LIBBPF", true),
>+ OPT_NOTBUILT(0, "clang-opt", "clang options",
>+ "options passed to clang when compiling BPF scriptlets",
>+ "disabled by NO_LIBBPF", true),
> #endif
> OPT_END()
> };
>diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
>index 9fca092..5c3eb7a 100644
>--- a/tools/perf/util/parse-options.c
>+++ b/tools/perf/util/parse-options.c
>@@ -1,4 +1,5 @@
> #include "util.h"
>+#include "debug.h"
> #include "parse-options.h"
> #include "cache.h"
> #include "header.h"
>@@ -69,6 +70,7 @@ static int get_value(struct parse_opt_ctx_t *p,
> if (!(flags & OPT_SHORT) && p->opt) {
> switch (opt->type) {
> case OPTION_CALLBACK:
>+ case OPTION_NOTBUILT:
> if (!(opt->flags & PARSE_OPT_NOARG))
> break;
> /* FALLTHROUGH */
>@@ -152,7 +154,28 @@ static int get_value(struct parse_opt_ctx_t *p,
> if (get_arg(p, opt, flags, &arg))
> return -1;
> return (*opt->callback)(opt, arg, 0) ? (-1) : 0;
>-
>+ case OPTION_NOTBUILT: {
>+ char short_str[2] = {opt->short_name, '\0'};
>+ int ret = -1;
>+
>+ pr_warning("%s: option '-%s%s' is not built (%s)\n",
>+ opt->flags & PARSE_OPT_CANSKIP ?
>+ "WARNING" : "ERROR",
>+ opt->long_name ? "-" : short_str,
>+ opt->long_name ? : "",
>+ opt->notbuild_reason);
>+ if (opt->flags & PARSE_OPT_CANSKIP)
>+ ret = 0;
>+ if (unset)
>+ return ret;
>+ if (opt->flags & PARSE_OPT_NOARG)
>+ return ret;
>+ if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
>+ return ret;
>+ if (get_arg(p, opt, flags, &arg))
>+ return -1;
>+ return ret;
>+ }
> case OPTION_INTEGER:
> if (unset) {
> *(int *)opt->value = 0;
>@@ -607,6 +630,7 @@ static void print_option_help(const struct option *opts, int full)
> pos += fprintf(stderr, " <n>");
> break;
> case OPTION_CALLBACK:
>+ case OPTION_NOTBUILT:
> if (opts->flags & PARSE_OPT_NOARG)
> break;
> /* FALLTHROUGH */
>@@ -647,6 +671,10 @@ static void print_option_help(const struct option *opts, int full)
> pad = USAGE_OPTS_WIDTH;
> }
> fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help);
>+ if (opts->type == OPTION_NOTBUILT)
>+ fprintf(stderr, "%*s(%s)\n",
>+ USAGE_OPTS_WIDTH + USAGE_GAP, "",
>+ opts->notbuild_reason);
> }
>
> static int option__cmp(const void *va, const void *vb)
>diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h
>index a8e407b..08cc9b5 100644
>--- a/tools/perf/util/parse-options.h
>+++ b/tools/perf/util/parse-options.h
>@@ -22,6 +22,7 @@ enum parse_opt_type {
> OPTION_CALLBACK,
> OPTION_U64,
> OPTION_UINTEGER,
>+ OPTION_NOTBUILT,
> };
>
> enum parse_opt_flags {
>@@ -41,6 +42,7 @@ enum parse_opt_option_flags {
> PARSE_OPT_DISABLED = 32,
> PARSE_OPT_EXCLUSIVE = 64,
> PARSE_OPT_NOEMPTY = 128,
>+ PARSE_OPT_CANSKIP = 256,
> };
>
> struct option;
>@@ -96,6 +98,7 @@ struct option {
> void *value;
> const char *argh;
> const char *help;
>+ const char *notbuild_reason;
>
> int flags;
> parse_opt_cb *callback;
>@@ -146,6 +149,19 @@ struct option {
> .value = (v), (a), .help = (h), .callback = (f), \
> .flags = PARSE_OPT_OPTARG, .data = (d) }
>
>+#define OPT_NOTBUILT_NOARG(s, l, h, r, skip) \
>+ { .type = OPTION_NOTBUILT, .short_name = (s), .long_name = (l), \
>+ .help = (h), .notbuild_reason = (r), \
>+ .flags = PARSE_OPT_NOARG | (skip ? PARSE_OPT_CANSKIP : 0)}
>+#define OPT_NOTBUILT_OPTARG(s, l, a, h, r, skip) \
>+ { .type = OPTION_NOTBUILT, .short_name = (s), .long_name = (l), \
>+ .argh = (a), .help = (h), .notbuild_reason = (r), \
>+ .flags = PARSE_OPT_OPTARG | (skip ? PARSE_OPT_CANSKIP : 0)}
>+#define OPT_NOTBUILT(s, l, a, h, r, skip) \
>+ { .type = OPTION_NOTBUILT, .short_name = (s), .long_name = (l), \
>+ .argh = (a), .help = (h), .notbuild_reason = (r), \
>+ .flags = skip ? PARSE_OPT_CANSKIP : 0}
>+
> /* parse_options() will filter out the processed options and leave the
> * non-option argments in argv[].
> * Returns the number of arguments left in argv[].
>--
>1.8.3.4

????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m???? ????????I?

Subject: [tip:perf/core] tools: Clone the kernel's strtobool function

Commit-ID: 7d85c434214ea0b3416f7a62f76a0785b00d8797
Gitweb: http://git.kernel.org/tip/7d85c434214ea0b3416f7a62f76a0785b00d8797
Author: Wang Nan <[email protected]>
AuthorDate: Mon, 16 Nov 2015 11:42:05 -0300
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 18 Nov 2015 17:51:02 -0300

tools: Clone the kernel's strtobool function

Copying it to tools/lib/string.c, the counterpart to the kernel's
lib/string.c.

This is preparation for enhancing BPF program configuration, which will
allow config string like 'inlines=yes'.

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Jonathan Cameron <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
[ Copied it to tools/lib/string.c instead, to make it usable by other tools/ ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/include/linux/string.h | 2 ++
tools/lib/string.c | 43 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 45 insertions(+)

diff --git a/tools/include/linux/string.h b/tools/include/linux/string.h
index f3a6db6..2e2f736 100644
--- a/tools/include/linux/string.h
+++ b/tools/include/linux/string.h
@@ -6,4 +6,6 @@

void *memdup(const void *src, size_t len);

+int strtobool(const char *s, bool *res);
+
#endif /* _LINUX_STRING_H_ */
diff --git a/tools/lib/string.c b/tools/lib/string.c
index ecfd43a..065e54f 100644
--- a/tools/lib/string.c
+++ b/tools/lib/string.c
@@ -1,5 +1,20 @@
+/*
+ * linux/tools/lib/string.c
+ *
+ * Copied from linux/lib/string.c, where it is:
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * More specifically, the first copied function was strtobool, which
+ * was introduced by:
+ *
+ * d0f1fed29e6e ("Add a strtobool function matching semantics of existing in kernel equivalents")
+ * Author: Jonathan Cameron <[email protected]>
+ */
+
#include <stdlib.h>
#include <string.h>
+#include <errno.h>
#include <linux/string.h>

/**
@@ -17,3 +32,31 @@ void *memdup(const void *src, size_t len)

return p;
}
+
+/**
+ * strtobool - convert common user inputs into boolean values
+ * @s: input string
+ * @res: result
+ *
+ * This routine returns 0 iff the first character is one of 'Yy1Nn0'.
+ * Otherwise it will return -EINVAL. Value pointed to by res is
+ * updated upon finding a match.
+ */
+int strtobool(const char *s, bool *res)
+{
+ switch (s[0]) {
+ case 'y':
+ case 'Y':
+ case '1':
+ *res = true;
+ break;
+ case 'n':
+ case 'N':
+ case '0':
+ *res = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}

Subject: [tip:perf/core] bpf tools: Load a program with different instances using preprocessor

Commit-ID: b580563e38487d9db8e94080149644da71c533c1
Gitweb: http://git.kernel.org/tip/b580563e38487d9db8e94080149644da71c533c1
Author: Wang Nan <[email protected]>
AuthorDate: Mon, 16 Nov 2015 12:10:09 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 18 Nov 2015 17:51:03 -0300

bpf tools: Load a program with different instances using preprocessor

This patch is a preparation for BPF prologue support which allows
generating a series of BPF bytecode for fetching kernel data before
calling program code. With the newly introduced multiple instances
support, perf is able to create different prologues for different kprobe
points.

Before this patch, a bpf_program can be loaded into kernel only once,
and get the only resulting fd. What this patch does is to allow creating
and loading different variants of one bpf_program, then fetching their
fds.

Here we describe the basic idea in this patch. The detailed description
of the newly introduced APIs can be found in comments in the patch body.

The key of this patch is the new mechanism in bpf_program__load().
Instead of loading BPF program into kernel directly, it calls a
'pre-processor' to generate program instances which would be finally
loaded into the kernel based on the original code. To enable the
generation of multiple instances, libbpf passes an index to the
pre-processor so it know which instance is being loaded.

Pre-processor should be called from libbpf's user (perf) using
bpf_program__set_prep(). The number of instances and the relationship
between indices and the target instance should be clear when calling
bpf_program__set_prep().

To retrieve a fd for a specific instance of a program,
bpf_program__nth_fd() is introduced. It returns the resulting fd
according to index.

Signed-off-by: He Kuang <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Wang Nan <[email protected]>
[ Enclosed multi-line if/else blocks with {}, (*func_ptr)() -> func_ptr() ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/lib/bpf/libbpf.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++---
tools/lib/bpf/libbpf.h | 64 ++++++++++++++++++++++
2 files changed, 201 insertions(+), 9 deletions(-)

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

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

struct bpf_object *obj;
void *priv;
@@ -206,10 +210,25 @@ struct bpf_object {

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

- zclose(prog->fd);
+ /*
+ * If the object is opened but the program was never loaded,
+ * it is possible that prog->instances.nr == -1.
+ */
+ if (prog->instances.nr > 0) {
+ for (i = 0; i < prog->instances.nr; i++)
+ zclose(prog->instances.fds[i]);
+ } else if (prog->instances.nr != -1) {
+ pr_warning("Internal error: instances.nr is %d\n",
+ prog->instances.nr);
+ }
+
+ prog->instances.nr = -1;
+ zfree(&prog->instances.fds);
}

static void bpf_program__exit(struct bpf_program *prog)
@@ -260,7 +279,8 @@ bpf_program__init(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->instances.fds = NULL;
+ prog->instances.nr = -1;

return 0;
errout:
@@ -860,13 +880,73 @@ static int
bpf_program__load(struct bpf_program *prog,
char *license, u32 kern_version)
{
- int err, fd;
+ int err = 0, fd, i;

- err = load_program(prog->insns, prog->insns_cnt,
- license, kern_version, &fd);
- if (!err)
- prog->fd = fd;
+ if (prog->instances.nr < 0 || !prog->instances.fds) {
+ if (prog->preprocessor) {
+ pr_warning("Internal error: can't load program '%s'\n",
+ prog->section_name);
+ return -LIBBPF_ERRNO__INTERNAL;
+ }

+ prog->instances.fds = malloc(sizeof(int));
+ if (!prog->instances.fds) {
+ pr_warning("Not enough memory for BPF fds\n");
+ return -ENOMEM;
+ }
+ prog->instances.nr = 1;
+ prog->instances.fds[0] = -1;
+ }
+
+ if (!prog->preprocessor) {
+ if (prog->instances.nr != 1) {
+ pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n",
+ prog->section_name, prog->instances.nr);
+ }
+ err = load_program(prog->insns, prog->insns_cnt,
+ license, kern_version, &fd);
+ if (!err)
+ prog->instances.fds[0] = fd;
+ goto out;
+ }
+
+ for (i = 0; i < prog->instances.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 the %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 the %dth instance of program '%s'\n",
+ i, prog->section_name);
+ prog->instances.fds[i] = -1;
+ if (result.pfd)
+ *result.pfd = -1;
+ continue;
+ }
+
+ err = load_program(result.new_insn_ptr,
+ result.new_insn_cnt,
+ license, kern_version, &fd);
+
+ if (err) {
+ pr_warning("Loading the %dth instance of program '%s' failed\n",
+ i, prog->section_name);
+ goto out;
+ }
+
+ if (result.pfd)
+ *result.pfd = fd;
+ prog->instances.fds[i] = fd;
+ }
+out:
if (err)
pr_warning("failed to load program '%s'\n",
prog->section_name);
@@ -1121,5 +1201,53 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy)

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_instances,
+ bpf_program_prep_t prep)
+{
+ int *instances_fds;
+
+ if (nr_instances <= 0 || !prep)
+ return -EINVAL;
+
+ if (prog->instances.nr > 0 || prog->instances.fds) {
+ pr_warning("Can't set pre-processor after loading\n");
+ return -EINVAL;
+ }
+
+ instances_fds = malloc(sizeof(int) * nr_instances);
+ if (!instances_fds) {
+ pr_warning("alloc memory failed for fds\n");
+ return -ENOMEM;
+ }
+
+ /* fill all fd with -1 */
+ memset(instances_fds, -1, sizeof(int) * nr_instances);
+
+ prog->instances.nr = nr_instances;
+ prog->instances.fds = instances_fds;
+ prog->preprocessor = prep;
+ return 0;
+}
+
+int bpf_program__nth_fd(struct bpf_program *prog, int n)
+{
+ int fd;
+
+ if (n >= prog->instances.nr || n < 0) {
+ pr_warning("Can't get the %dth fd from program %s: only %d instances\n",
+ n, prog->section_name, prog->instances.nr);
+ return -EINVAL;
+ }
+
+ fd = prog->instances.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 c9a9aef..949df4b 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -88,6 +88,70 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy);

int bpf_program__fd(struct bpf_program *prog);

+struct bpf_insn;
+
+/*
+ * Libbpf allows callers to adjust BPF programs before being loaded
+ * into kernel. One program in an object file can be transform into
+ * multiple variants to be attached to different code.
+ *
+ * bpf_program_prep_t, bpf_program__set_prep and bpf_program__nth_fd
+ * are APIs for this propose.
+ *
+ * - bpf_program_prep_t:
+ * It defines 'preprocessor', which is a caller defined function
+ * passed to libbpf through bpf_program__set_prep(), and will be
+ * called before program is loaded. The processor should adjust
+ * the program one time for each instances according to the number
+ * passed to it.
+ *
+ * - bpf_program__set_prep:
+ * Attachs a preprocessor to a BPF program. The number of instances
+ * whould be created is also passed through this function.
+ *
+ * - bpf_program__nth_fd:
+ * After the program is loaded, get resuling fds from bpf program for
+ * each instances.
+ *
+ * If bpf_program__set_prep() is not used, the program whould be loaded
+ * without adjustment during bpf_object__load(). The program has only
+ * one instance. In this case bpf_program__fd(prog) is equal to
+ * bpf_program__nth_fd(prog, 0).
+ */
+
+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;
+};
+
+/*
+ * Parameters of bpf_program_prep_t:
+ * - prog: The bpf_program being loaded.
+ * - n: Index of instance being generated.
+ * - insns: BPF instructions array.
+ * - insns_cnt:Number of instructions in insns.
+ * - res: Output parameter, result of transformation.
+ *
+ * Return value:
+ * - Zero: pre-processing success.
+ * - Non-zero: pre-processing, stop loading.
+ */
+typedef int (*bpf_program_prep_t)(struct bpf_program *prog, int n,
+ struct bpf_insn *insns, int insns_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.

Subject: [tip:perf/core] perf bpf: Add BPF_PROLOGUE config options for further patches

Commit-ID: 1c0ed63239012aa881cc811f726b549dca7279e4
Gitweb: http://git.kernel.org/tip/1c0ed63239012aa881cc811f726b549dca7279e4
Author: Wang Nan <[email protected]>
AuthorDate: Mon, 16 Nov 2015 12:10:10 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 18 Nov 2015 17:51:03 -0300

perf bpf: Add BPF_PROLOGUE config options for further patches

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

PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET is introduced in commit
63ab024a5b6f295ca17a293ad81b7c728f49a89a ("perf tools:
regs_query_register_offset() infrastructure"), which indicates that 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]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/config/Makefile | 12 ++++++++++++
1 file changed, 12 insertions(+)

diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index de89ec5..6eb9a95 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -318,6 +318,18 @@ ifndef NO_LIBELF
CFLAGS += -DHAVE_LIBBPF_SUPPORT
$(call detected,CONFIG_LIBBPF)
endif
+
+ ifndef NO_DWARF
+ ifdef PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET
+ CFLAGS += -DHAVE_BPF_PROLOGUE
+ $(call detected,CONFIG_BPF_PROLOGUE)
+ else
+ msg := $(warning BPF prologue is not supported by architecture $(ARCH), missing regs_query_register_offset());
+ endif
+ else
+ msg := $(warning DWARF support is off, BPF prologue is disabled);
+ endif
+
endif # NO_LIBBPF
endif # NO_LIBELF

Subject: [tip:perf/core] perf bpf: Compile dwarf-regs.c if CONFIG_BPF_PROLOGUE is on

Commit-ID: 30433a3a52b951faab95944e0f8b9d33a1e322ce
Gitweb: http://git.kernel.org/tip/30433a3a52b951faab95944e0f8b9d33a1e322ce
Author: Wang Nan <[email protected]>
AuthorDate: Mon, 16 Nov 2015 12:10:11 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 18 Nov 2015 17:51:03 -0300

perf bpf: Compile dwarf-regs.c if CONFIG_BPF_PROLOGUE is on

regs_query_register_offset() in dwarf-regs.c is required by BPF
prologue. This patch compiles it if CONFIG_BPF_PROLOGUE is on to avoid
build failure when CONFIG_BPF_PROLOGUE is on but CONFIG_DWARF is not
set.

Signed-off-by: He Kuang <[email protected]>
Acked-by: Masami Hiramatsu <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: He Kuang <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Wang Nan <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/arch/x86/util/Build | 1 +
1 file changed, 1 insertion(+)

diff --git a/tools/perf/arch/x86/util/Build b/tools/perf/arch/x86/util/Build
index ff63649..4659703 100644
--- a/tools/perf/arch/x86/util/Build
+++ b/tools/perf/arch/x86/util/Build
@@ -5,6 +5,7 @@ libperf-y += kvm-stat.o
libperf-y += perf_regs.o

libperf-$(CONFIG_DWARF) += dwarf-regs.o
+libperf-$(CONFIG_BPF_PROLOGUE) += dwarf-regs.o

libperf-$(CONFIG_LIBUNWIND) += unwind-libunwind.o
libperf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o

Subject: [tip:perf/core] perf bpf: Allow BPF program attach to uprobe events

Commit-ID: 361f2b1d1d7231b8685d990b886f599378a4d5a5
Gitweb: http://git.kernel.org/tip/361f2b1d1d7231b8685d990b886f599378a4d5a5
Author: Wang Nan <[email protected]>
AuthorDate: Mon, 16 Nov 2015 12:10:05 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 18 Nov 2015 17:51:03 -0300

perf bpf: Allow BPF program attach to uprobe events

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

SEC(
"exec=/lib64/libc.so.6;"
"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 is "exec",
for uprobes.

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
[ Changed the separator from \n to ; ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/bpf-loader.c | 120 ++++++++++++++++++++++++++++++++++++++++---
tools/perf/util/bpf-loader.h | 1 +
2 files changed, 115 insertions(+), 6 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 4c50411..84169d6 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -110,6 +110,113 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
}

static int
+config__exec(const char *value, struct perf_probe_event *pev)
+{
+ pev->uprobes = true;
+ pev->target = strdup(value);
+ if (!pev->target)
+ return -ENOMEM;
+ return 0;
+}
+
+static struct {
+ const char *key;
+ const char *usage;
+ const char *desc;
+ int (*func)(const char *, struct perf_probe_event *);
+} bpf_config_terms[] = {
+ {
+ .key = "exec",
+ .usage = "exec=<full path of file>",
+ .desc = "Set uprobe target",
+ .func = config__exec,
+ },
+};
+
+static int
+do_config(const char *key, const char *value,
+ struct perf_probe_event *pev)
+{
+ unsigned int i;
+
+ pr_debug("config bpf program: %s=%s\n", key, value);
+ for (i = 0; i < ARRAY_SIZE(bpf_config_terms); i++)
+ if (strcmp(key, bpf_config_terms[i].key) == 0)
+ return bpf_config_terms[i].func(value, pev);
+
+ pr_debug("BPF: ERROR: invalid config option in object: %s=%s\n",
+ key, value);
+
+ pr_debug("\nHint: Currently valid options are:\n");
+ for (i = 0; i < ARRAY_SIZE(bpf_config_terms); i++)
+ pr_debug("\t%s:\t%s\n", bpf_config_terms[i].usage,
+ bpf_config_terms[i].desc);
+ pr_debug("\n");
+
+ return -BPF_LOADER_ERRNO__CONFIG_TERM;
+}
+
+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_debug("No enough memory: dup config_str failed\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ line = text;
+ while ((sep = strchr(line, ';'))) {
+ 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 err ? ERR_PTR(err) : main_str;
+}
+
+static int
+parse_config(const char *config_str, struct perf_probe_event *pev)
+{
+ int err;
+ const char *main_str = parse_config_kvpair(config_str, pev);
+
+ if (IS_ERR(main_str))
+ return PTR_ERR(main_str);
+
+ err = parse_perf_probe_command(main_str, pev);
+ if (err < 0) {
+ pr_debug("bpf: '%s' is not a valid config string\n",
+ config_str);
+ /* parse failed, don't need clear pev. */
+ return -BPF_LOADER_ERRNO__CONFIG;
+ }
+ return 0;
+}
+
+static int
config_bpf_program(struct bpf_program *prog)
{
struct perf_probe_event *pev = NULL;
@@ -131,13 +238,9 @@ config_bpf_program(struct bpf_program *prog)
pev = &priv->pev;

pr_debug("bpf: config program '%s'\n", config_str);
- err = parse_perf_probe_command(config_str, pev);
- if (err < 0) {
- pr_debug("bpf: '%s' is not a valid config string\n",
- config_str);
- err = -BPF_LOADER_ERRNO__CONFIG;
+ err = parse_config(config_str, pev);
+ if (err)
goto errout;
- }

if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) {
pr_debug("bpf: '%s': group for event is set and not '%s'.\n",
@@ -340,6 +443,7 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = {
[ERRCODE_OFFSET(EVENTNAME)] = "No event name found in config string",
[ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error",
[ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet",
+ [ERRCODE_OFFSET(CONFIG_TERM)] = "Invalid config term in config string",
};

static int
@@ -420,6 +524,10 @@ int bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
int err, char *buf, size_t size)
{
bpf__strerror_head(err, buf, size);
+ case BPF_LOADER_ERRNO__CONFIG_TERM: {
+ scnprintf(buf, size, "%s (add -v to see detail)", emsg);
+ break;
+ }
bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'");
bpf__strerror_entry(EACCES, "You need to be root");
bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0");
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 9caf3ae..d19f5c5 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -20,6 +20,7 @@ enum bpf_loader_errno {
BPF_LOADER_ERRNO__EVENTNAME, /* Event name is missing */
BPF_LOADER_ERRNO__INTERNAL, /* BPF loader internal error */
BPF_LOADER_ERRNO__COMPILE, /* Error when compiling BPF scriptlet */
+ BPF_LOADER_ERRNO__CONFIG_TERM, /* Invalid config term in config term */
__BPF_LOADER_ERRNO__END,
};

Subject: [tip:perf/core] perf bpf: Allow attaching BPF programs to modules symbols

Commit-ID: 5dbd16c0c9d17ab1ab2226a5926482c26c0287ed
Gitweb: http://git.kernel.org/tip/5dbd16c0c9d17ab1ab2226a5926482c26c0287ed
Author: Wang Nan <[email protected]>
AuthorDate: Mon, 16 Nov 2015 12:10:06 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 18 Nov 2015 17:51:03 -0300

perf bpf: Allow attaching BPF programs to modules symbols

By extending the syntax of BPF object section names, this patch allows
users to attach BPF programs to symbols in modules. For example:

SEC("module=i915;"
"parse_cmds=i915_parse_cmds")
int parse_cmds(void *ctx)
{
return 1;
}

The implementation is very simple: like what 'perf probe' does, for module,
fill 'uprobe' field in 'struct perf_probe_event'. Other parts will be done
automatically.

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: 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/bpf-loader.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 84169d6..d0f02ed 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -119,6 +119,16 @@ config__exec(const char *value, struct perf_probe_event *pev)
return 0;
}

+static int
+config__module(const char *value, struct perf_probe_event *pev)
+{
+ pev->uprobes = false;
+ pev->target = strdup(value);
+ if (!pev->target)
+ return -ENOMEM;
+ return 0;
+}
+
static struct {
const char *key;
const char *usage;
@@ -131,6 +141,12 @@ static struct {
.desc = "Set uprobe target",
.func = config__exec,
},
+ {
+ .key = "module",
+ .usage = "module=<module name> ",
+ .desc = "Set kprobe module",
+ .func = config__module,
+ }
};

static int

Subject: [tip:perf/core] perf bpf: Allow BPF program config probing options

Commit-ID: 03e01f568759ddbfdaff892e299758e7771a3478
Gitweb: http://git.kernel.org/tip/03e01f568759ddbfdaff892e299758e7771a3478
Author: Wang Nan <[email protected]>
AuthorDate: Mon, 16 Nov 2015 12:10:08 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 18 Nov 2015 17:51:04 -0300

perf bpf: Allow BPF program config probing options

By extending the syntax of BPF object section names, this patch allows users to
config probing options like what they can do in 'perf probe'.

The error message in 'perf probe' is also updated.

Test result:

For following BPF file test_probe_glob.c:

# cat test_probe_glob.c
__attribute__((section("inlines=no;func=SyS_dup?"), used))

int func(void *ctx)
{
return 1;
}

char _license[] __attribute__((section("license"), used)) = "GPL";
int _version __attribute__((section("version"), used)) = 0x40300;
#
# ./perf record -e ./test_probe_glob.c ls /
...
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.013 MB perf.data ]
# ./perf evlist
perf_bpf_probe:func_1
perf_bpf_probe:func

After changing "inlines=no" to "inlines=yes":

# ./perf record -e ./test_probe_glob.c ls /
...
[ perf record: Woken up 2 times to write data ]
[ perf record: Captured and wrote 0.013 MB perf.data ]
# ./perf evlist
perf_bpf_probe:func_3
perf_bpf_probe:func_2
perf_bpf_probe:func_1
perf_bpf_probe:func

Then test 'force':

Use following program:

# cat test_probe_force.c
__attribute__((section("func=sys_write"), used))

int funca(void *ctx)
{
return 1;
}

__attribute__((section("force=yes;func=sys_write"), used))

int funcb(void *ctx)
{
return 1;
}

char _license[] __attribute__((section("license"), used)) = "GPL";
int _version __attribute__((section("version"), used)) = 0x40300;
#

# perf record -e ./test_probe_force.c usleep 1
Error: event "func" already exists.
Hint: Remove existing event by 'perf probe -d'
or force duplicates by 'perf probe -f'
or set 'force=yes' in BPF source.
event syntax error: './test_probe_force.c'
\___ Probe point exist. Try 'perf probe -d "*"' and set 'force=yes'

(add -v to see detail)
...

Then replace 'force=no' to 'force=yes':

# vim test_probe_force.c
# perf record -e ./test_probe_force.c usleep 1
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.017 MB perf.data ]
# perf evlist
perf_bpf_probe:func_1
perf_bpf_probe:func
#

Signed-off-by: Wang Nan <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/bpf-loader.c | 53 +++++++++++++++++++++++++++++++++++++++++--
tools/perf/util/probe-event.c | 7 ++++--
2 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index d0f02ed..98f2e5d 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -7,6 +7,7 @@

#include <bpf/libbpf.h>
#include <linux/err.h>
+#include <linux/string.h>
#include "perf.h"
#include "debug.h"
#include "bpf-loader.h"
@@ -129,6 +130,38 @@ config__module(const char *value, struct perf_probe_event *pev)
return 0;
}

+static int
+config__bool(const char *value,
+ bool *pbool, bool invert)
+{
+ int err;
+ bool bool_value;
+
+ if (!pbool)
+ return -EINVAL;
+
+ err = strtobool(value, &bool_value);
+ if (err)
+ return err;
+
+ *pbool = invert ? !bool_value : bool_value;
+ return 0;
+}
+
+static int
+config__inlines(const char *value,
+ struct perf_probe_event *pev __maybe_unused)
+{
+ return config__bool(value, &probe_conf.no_inlines, true);
+}
+
+static int
+config__force(const char *value,
+ struct perf_probe_event *pev __maybe_unused)
+{
+ return config__bool(value, &probe_conf.force_add, false);
+}
+
static struct {
const char *key;
const char *usage;
@@ -146,7 +179,19 @@ static struct {
.usage = "module=<module name> ",
.desc = "Set kprobe module",
.func = config__module,
- }
+ },
+ {
+ .key = "inlines",
+ .usage = "inlines=[yes|no] ",
+ .desc = "Probe at inline symbol",
+ .func = config__inlines,
+ },
+ {
+ .key = "force",
+ .usage = "force=[yes|no] ",
+ .desc = "Forcibly add events with existing name",
+ .func = config__force,
+ },
};

static int
@@ -240,6 +285,10 @@ config_bpf_program(struct bpf_program *prog)
const char *config_str;
int err;

+ /* Initialize per-program probing setting */
+ probe_conf.no_inlines = false;
+ probe_conf.force_add = false;
+
config_str = bpf_program__title(prog, false);
if (IS_ERR(config_str)) {
pr_debug("bpf: unable to get title for program\n");
@@ -544,7 +593,7 @@ int bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
scnprintf(buf, size, "%s (add -v to see detail)", emsg);
break;
}
- bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'");
+ bpf__strerror_entry(EEXIST, "Probe point exist. Try 'perf probe -d \"*\"' and set 'force=yes'");
bpf__strerror_entry(EACCES, "You need to be root");
bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0");
bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file");
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 03875f9..93996ec 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -2326,8 +2326,11 @@ static int get_new_event_name(char *buf, size_t len, const char *base,
goto out;

if (!allow_suffix) {
- pr_warning("Error: event \"%s\" already exists. "
- "(Use -f to force duplicates.)\n", buf);
+ pr_warning("Error: event \"%s\" already exists.\n"
+ " Hint: Remove existing event by 'perf probe -d'\n"
+ " or force duplicates by 'perf probe -f'\n"
+ " or set 'force=yes' in BPF source.\n",
+ buf);
ret = -EEXIST;
goto out;
}

Subject: [tip:perf/core] perf bpf: Add prologue for BPF programs for fetching arguments

Commit-ID: bfc077b4cf106793b30bf942e426ee99f1f4ac44
Gitweb: http://git.kernel.org/tip/bfc077b4cf106793b30bf942e426ee99f1f4ac44
Author: He Kuang <[email protected]>
AuthorDate: Mon, 16 Nov 2015 12:10:12 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 18 Nov 2015 17:51:04 -0300

perf bpf: Add prologue for BPF programs for fetching arguments

This patch generates a prologue for a BPF program which fetches 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 if dereferencing was done successfully.

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

Committer note:

At least testing if it builds and loads:

# cat test_probe_arg.c
struct pt_regs;

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

char _license[] __attribute__((section("license"), used)) = "GPL";
int _version __attribute__((section("version"), used)) = 0x40300;
# perf record -e ./test_probe_arg.c usleep 1
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.016 MB perf.data ]
# perf evlist
perf_bpf_probe:lock_page
#

Signed-off-by: He Kuang <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Wang Nan <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Wang Nan <[email protected]>
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/Build | 1 +
tools/perf/util/bpf-loader.c | 3 +
tools/perf/util/bpf-loader.h | 3 +
tools/perf/util/bpf-prologue.c | 455 +++++++++++++++++++++++++++++++++++++++++
tools/perf/util/bpf-prologue.h | 34 +++
5 files changed, 496 insertions(+)

diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index e231690..0513dd5 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -89,6 +89,7 @@ libperf-y += parse-branch-options.o
libperf-y += parse-regs-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-file.o
libperf-$(CONFIG_LIBELF) += probe-event.o
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 98f2e5d..bd14be4 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -509,6 +509,9 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = {
[ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error",
[ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet",
[ERRCODE_OFFSET(CONFIG_TERM)] = "Invalid config term in config string",
+ [ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue",
+ [ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program",
+ [ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue",
};

static int
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index d19f5c5..a58740b 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -21,6 +21,9 @@ enum bpf_loader_errno {
BPF_LOADER_ERRNO__INTERNAL, /* BPF loader internal error */
BPF_LOADER_ERRNO__COMPILE, /* Error when compiling BPF scriptlet */
BPF_LOADER_ERRNO__CONFIG_TERM, /* Invalid config term in config term */
+ BPF_LOADER_ERRNO__PROLOGUE, /* Failed to generate prologue */
+ BPF_LOADER_ERRNO__PROLOGUE2BIG, /* Prologue too big for program */
+ BPF_LOADER_ERRNO__PROLOGUEOOB, /* Offset out of bound for prologue */
__BPF_LOADER_ERRNO__END,
};

diff --git a/tools/perf/util/bpf-prologue.c b/tools/perf/util/bpf-prologue.c
new file mode 100644
index 0000000..6cdbee1
--- /dev/null
+++ b/tools/perf/util/bpf-prologue.c
@@ -0,0 +1,455 @@
+/*
+ * bpf-prologue.c
+ *
+ * Copyright (C) 2015 He Kuang <[email protected]>
+ * 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"
+#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 -BPF_LOADER_ERRNO__PROLOGUE2BIG;
+
+ if (pos->pos + 1 >= pos->end) {
+ pr_err("bpf prologue: prologue too long\n");
+ pos->pos = NULL;
+ return -BPF_LOADER_ERRNO__PROLOGUE2BIG;
+ }
+
+ *(pos->pos)++ = new_insn;
+ return 0;
+}
+
+static int
+check_pos(struct bpf_insn_pos *pos)
+{
+ if (!pos->pos || pos->pos >= pos->end)
+ return -BPF_LOADER_ERRNO__PROLOGUE2BIG;
+ 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 = regs_query_register_offset(reg);
+
+ if (offset < 0) {
+ pr_err("bpf: prologue: failed to get register %s\n",
+ reg);
+ return offset;
+ }
+ ins(BPF_LDX_MEM(BPF_DW, target_reg, ctx_reg, offset), pos);
+
+ return check_pos(pos);
+}
+
+/*
+ * 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);
+
+ return check_pos(pos);
+}
+
+/*
+ * 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, err = 0;
+
+ for (i = 0; i < nargs; i++) {
+ err = gen_ldx_reg_from_ctx(pos, BPF_REG_1, args[i].value,
+ BPF_PROLOGUE_START_ARG_REG + i);
+ if (err)
+ goto errout;
+ }
+
+ return check_pos(pos);
+errout:
+ return err;
+}
+
+/*
+ * 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 err, 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 */
+ err = gen_ldx_reg_from_ctx(pos, BPF_REG_CTX, reg,
+ BPF_REG_ARG3);
+ if (err) {
+ 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);
+ err = gen_read_mem(pos, BPF_REG_3, BPF_REG_7,
+ ref->offset);
+ if (err) {
+ 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);
+
+ return check_pos(pos);
+errout:
+ return err;
+}
+
+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 -BPF_LOADER_ERRNO__PROLOGUE2BIG;
+
+ for (insn = pos->begin; insn < pos->pos; insn++) {
+ struct bpf_insn *target;
+ 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:
+ target = error_code;
+ break;
+ case JMP_TO_SUCCESS_CODE:
+ target = success_code;
+ break;
+ case JMP_TO_USER_CODE:
+ target = user_code;
+ break;
+ default:
+ pr_err("bpf prologue: internal error: relocation failed\n");
+ return -BPF_LOADER_ERRNO__PROLOGUE;
+ }
+
+ insn->off = target - (insn + 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 err = 0, i;
+
+ if (!new_prog || !new_cnt)
+ return -EINVAL;
+
+ if (cnt_space > BPF_MAXINSNS)
+ cnt_space = BPF_MAXINSNS;
+
+ 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) {
+ pr_warning("bpf: prologue: %d arguments are dropped\n",
+ nargs - BPF_PROLOGUE_MAX_ARGS);
+ nargs = BPF_PROLOGUE_MAX_ARGS;
+ }
+
+ /* 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 -BPF_LOADER_ERRNO__PROLOGUEOOB;
+ }
+#endif
+ ref = ref->next;
+ }
+ }
+ pr_debug("prologue: pass validation\n");
+
+ if (fastpath) {
+ /* If all variables are registers... */
+ pr_debug("prologue: fast path\n");
+ err = gen_prologue_fastpath(&pos, args, nargs);
+ if (err)
+ 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);
+
+ err = gen_prologue_slowpath(&pos, args, nargs);
+ if (err)
+ goto errout;
+ /*
+ * start of ERROR_CODE (only slow pass needs error code)
+ * mov r2 <- 1 // r2 is error number
+ * mov r3 <- 0 // r3, r4... should be touched or
+ * // verifier would complain
+ * mov r4 <- 0
+ * ...
+ * 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);
+ err = prologue_relocate(&pos, error_code, success_code,
+ user_code);
+ if (err)
+ goto errout;
+ }
+
+ err = check_pos(&pos);
+ if (err)
+ goto errout;
+
+ *new_cnt = pos_get_cnt(&pos);
+ return 0;
+errout:
+ return err;
+}
diff --git a/tools/perf/util/bpf-prologue.h b/tools/perf/util/bpf-prologue.h
new file mode 100644
index 0000000..d94cbea
--- /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 -ENOTSUP;
+}
+#endif
+#endif /* __BPF_PROLOGUE_H */

Subject: [tip:perf/core] perf bpf: Generate prologue for BPF programs

Commit-ID: a08357d8dc7d3025d1094f727ad1f7e837766f93
Gitweb: http://git.kernel.org/tip/a08357d8dc7d3025d1094f727ad1f7e837766f93
Author: Wang Nan <[email protected]>
AuthorDate: Mon, 16 Nov 2015 12:10:13 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 18 Nov 2015 17:51:04 -0300

perf bpf: Generate prologue for BPF programs

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

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

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/bpf-loader.c | 120 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 119 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index bd14be4..190a1c7 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -5,12 +5,15 @@
* Copyright (C) 2015 Huawei Inc.
*/

+#include <linux/bpf.h>
#include <bpf/libbpf.h>
#include <linux/err.h>
#include <linux/string.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" // for MAX_PROBES
#include "llvm-utils.h"
@@ -33,6 +36,8 @@ DEFINE_PRINT_FN(debug, 1)

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

static bool libbpf_initialized;
@@ -107,6 +112,7 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
struct bpf_prog_priv *priv = _priv;

cleanup_perf_probe_events(&priv->pev, 1);
+ zfree(&priv->insns_buf);
free(priv);
}

@@ -365,6 +371,102 @@ static int bpf__prepare_probe(void)
return err;
}

+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 = "[unknown]";
+
+ pr_debug("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_debug("Internal error in preproc_gen_prologue\n");
+ return -BPF_LOADER_ERRNO__PROLOGUE;
+}
+
+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_debug("Internal error when hook preprocessor\n");
+ return -BPF_LOADER_ERRNO__INTERNAL;
+ }
+
+ 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 tevs don'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_debug("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(struct bpf_object *obj)
{
int err = 0;
@@ -399,6 +501,18 @@ int bpf__probe(struct bpf_object *obj)
pr_debug("bpf_probe: failed to apply perf probe events");
goto out;
}
+
+ /*
+ * After probing, let's consider prologue, which
+ * adds program fetcher to BPF programs.
+ *
+ * hook_load_preprocessorr() hooks pre-processor
+ * to bpf_program, let it generate prologue
+ * dynamically during loading.
+ */
+ err = hook_load_preprocessor(prog);
+ if (err)
+ goto out;
}
out:
return err < 0 ? err : 0;
@@ -482,7 +596,11 @@ int bpf__foreach_tev(struct bpf_object *obj,
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_debug("bpf: failed to get file descriptor\n");
return fd;

Subject: [tip:perf/core] perf test: Test the BPF prologue adding infrastructure

Commit-ID: bbb7d4925a05ecd5bbfdbc1147d402b0db203a5a
Gitweb: http://git.kernel.org/tip/bbb7d4925a05ecd5bbfdbc1147d402b0db203a5a
Author: Wang Nan <[email protected]>
AuthorDate: Mon, 16 Nov 2015 12:10:14 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 18 Nov 2015 17:51:04 -0300

perf test: Test the BPF prologue adding infrastructure

This patch introduces a new BPF script to test the BPF prologue adding
routines. The new script probes at null_lseek, which is the function pointer
used when we try to lseek on '/dev/null'.

The null_lseek function is chosen because it is used by function pointers, so
we don't need to consider inlining and LTO.

By extracting file->f_mode, bpf-script-test-prologue.c should know whether the
file is writable or readonly. According to llseek_loop() and
bpf-script-test-prologue.c, one fourth of total lseeks should be collected.

Committer note:

Testing it:

# perf test -v BPF
<SNIP>
Kernel build dir is set to /lib/modules/4.3.0+/build
set env: KBUILD_DIR=/lib/modules/4.3.0+/build
unset env: KBUILD_OPTS
include option is set to -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.9.2/include -I/home/git/linux/arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated -I/home/git/linux/include -Iinclude -I/home/git/linux/arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I/home/git/linux/include/uapi -Iinclude/generated/uapi -include /home/git/linux/include/linux/kconfig.h
set env: NR_CPUS=4
set env: LINUX_VERSION_CODE=0x40300
set env: CLANG_EXEC=/usr/libexec/icecc/bin/clang
set env: CLANG_OPTIONS=-xc
set env: KERNEL_INC_OPTIONS= -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.9.2/include -I/home/git/linux/arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated -I/home/git/linux/include -Iinclude -I/home/git/linux/arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I/home/git/linux/include/uapi -Iinclude/generated/uapi -include /home/git/linux/include/linux/kconfig.h
set env: WORKING_DIR=/lib/modules/4.3.0+/build
set env: CLANG_SOURCE=-
llvm compiling command template: echo '/*
* bpf-script-test-prologue.c
* Test BPF prologue
*/
#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 SEC(NAME) __attribute__((section(NAME), used))

#include <uapi/linux/fs.h>

#define FMODE_READ 0x1
#define FMODE_WRITE 0x2

static void (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
(void *) 6;

SEC("func=null_lseek file->f_mode offset orig")
int bpf_func__null_lseek(void *ctx, int err, unsigned long f_mode,
unsigned long offset, unsigned long orig)
{
if (err)
return 0;
if (f_mode & FMODE_WRITE)
return 0;
if (offset & 1)
return 0;
if (orig == SEEK_CUR)
return 0;
return 1;
}

char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
' | $CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS -DLINUX_VERSION_CODE=$LINUX_VERSION_CODE $CLANG_OPTIONS $KERNEL_INC_OPTIONS -Wno-unused-value -Wno-pointer-sign -working-directory $WORKING_DIR -c "$CLANG_SOURCE" -target bpf -O2 -o -
libbpf: loading object '[bpf_prologue_test]' from buffer
libbpf: section .strtab, size 135, link 0, flags 0, type=3
libbpf: section .text, size 0, link 0, flags 6, type=1
libbpf: section .data, size 0, link 0, flags 3, type=1
libbpf: section .bss, size 0, link 0, flags 3, type=8
libbpf: section func=null_lseek file->f_mode offset orig, size 112, link 0, flags 6, type=1
libbpf: found program func=null_lseek file->f_mode offset orig
libbpf: section license, size 4, link 0, flags 3, type=1
libbpf: license of [bpf_prologue_test] is GPL
libbpf: section version, size 4, link 0, flags 3, type=1
libbpf: kernel version of [bpf_prologue_test] is 40300
libbpf: section .symtab, size 168, link 1, flags 0, type=2
bpf: config program 'func=null_lseek file->f_mode offset orig'
symbol:null_lseek file:(null) line:0 offset:0 return:0 lazy:(null)
parsing arg: file->f_mode into file, f_mode(1)
parsing arg: offset into offset
parsing arg: orig into orig
bpf: config 'func=null_lseek file->f_mode offset orig' is ok
Looking at the vmlinux_path (7 entries long)
Using /lib/modules/4.3.0+/build/vmlinux for symbols
Open Debuginfo file: /lib/modules/4.3.0+/build/vmlinux
Try to find probe point from debuginfo.
Matched function: null_lseek
Probe point found: null_lseek+0
Searching 'file' variable in context.
Converting variable file into trace event.
converting f_mode in file
f_mode type is unsigned int.
Searching 'offset' variable in context.
Converting variable offset into trace event.
offset type is long long int.
Searching 'orig' variable in context.
Converting variable orig into trace event.
orig type is int.
Found 1 probe_trace_events.
Opening /sys/kernel/debug/tracing//kprobe_events write=1
Writing event: p:perf_bpf_probe/func _text+4840528 f_mode=+68(%di):u32 offset=%si:s64 orig=%dx:s32
libbpf: don't need create maps for [bpf_prologue_test]
prologue: pass validation
prologue: slow path
prologue: fetch arg 0, base reg is %di
prologue: arg 0: offset 68
prologue: fetch arg 1, base reg is %si
prologue: fetch arg 2, base reg is %dx
add bpf event perf_bpf_probe:func and attach bpf program 3
adding perf_bpf_probe:func
adding perf_bpf_probe:func to 0x51672c0
mmap size 1052672B
Opening /sys/kernel/debug/tracing//kprobe_events write=1
Opening /sys/kernel/debug/tracing//uprobe_events write=1
Parsing probe_events: p:perf_bpf_probe/func _text+4840528 f_mode=+68(%di):u32 offset=%si:s64 orig=%dx:s32
Group:perf_bpf_probe Event:func probe:p
Writing event: -:perf_bpf_probe/func
test child finished with 0
---- end ----
Test BPF filter: Ok
#

Signed-off-by: Wang Nan <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
[ Added tools/perf/tests/llvm-src-prologue.c to .gitignore ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/tests/.gitignore | 1 +
tools/perf/tests/Build | 9 +++++++-
tools/perf/tests/bpf-script-test-prologue.c | 35 +++++++++++++++++++++++++++++
tools/perf/tests/bpf.c | 34 ++++++++++++++++++++++++++++
tools/perf/tests/llvm.c | 4 ++++
tools/perf/tests/llvm.h | 2 ++
6 files changed, 84 insertions(+), 1 deletion(-)

diff --git a/tools/perf/tests/.gitignore b/tools/perf/tests/.gitignore
index 489fc9f..bf016c4 100644
--- a/tools/perf/tests/.gitignore
+++ b/tools/perf/tests/.gitignore
@@ -1,2 +1,3 @@
llvm-src-base.c
llvm-src-kbuild.c
+llvm-src-prologue.c
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index f41ebf8..0ff8a97 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -31,7 +31,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 llvm-src-base.o llvm-src-kbuild.o
+perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o
perf-y += bpf.o
perf-y += topology.o

@@ -49,6 +49,13 @@ $(OUTPUT)tests/llvm-src-kbuild.c: tests/bpf-script-test-kbuild.c
$(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
$(Q)echo ';' >> $@

+$(OUTPUT)tests/llvm-src-prologue.c: tests/bpf-script-test-prologue.c
+ $(call rule_mkdir)
+ $(Q)echo '#include <tests/llvm.h>' > $@
+ $(Q)echo 'const char test_llvm__bpf_test_prologue_prog[] =' >> $@
+ $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
+ $(Q)echo ';' >> $@
+
ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64))
perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
endif
diff --git a/tools/perf/tests/bpf-script-test-prologue.c b/tools/perf/tests/bpf-script-test-prologue.c
new file mode 100644
index 0000000..7230e62
--- /dev/null
+++ b/tools/perf/tests/bpf-script-test-prologue.c
@@ -0,0 +1,35 @@
+/*
+ * bpf-script-test-prologue.c
+ * Test BPF prologue
+ */
+#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 SEC(NAME) __attribute__((section(NAME), used))
+
+#include <uapi/linux/fs.h>
+
+#define FMODE_READ 0x1
+#define FMODE_WRITE 0x2
+
+static void (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) =
+ (void *) 6;
+
+SEC("func=null_lseek file->f_mode offset orig")
+int bpf_func__null_lseek(void *ctx, int err, unsigned long f_mode,
+ unsigned long offset, unsigned long orig)
+{
+ if (err)
+ return 0;
+ if (f_mode & FMODE_WRITE)
+ return 0;
+ if (offset & 1)
+ return 0;
+ if (orig == SEEK_CUR)
+ return 0;
+ return 1;
+}
+
+char _license[] SEC("license") = "GPL";
+int _version SEC("version") = LINUX_VERSION_CODE;
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
index 6ebfdee..d584422 100644
--- a/tools/perf/tests/bpf.c
+++ b/tools/perf/tests/bpf.c
@@ -19,6 +19,29 @@ static int epoll_pwait_loop(void)
return 0;
}

+#ifdef HAVE_BPF_PROLOGUE
+
+static int llseek_loop(void)
+{
+ int fds[2], i;
+
+ fds[0] = open("/dev/null", O_RDONLY);
+ fds[1] = open("/dev/null", O_RDWR);
+
+ if (fds[0] < 0 || fds[1] < 0)
+ return -1;
+
+ for (i = 0; i < NR_ITERS; i++) {
+ lseek(fds[i % 2], i, (i / 2) % 2 ? SEEK_CUR : SEEK_SET);
+ lseek(fds[(i + 1) % 2], i, (i / 2) % 2 ? SEEK_CUR : SEEK_SET);
+ }
+ close(fds[0]);
+ close(fds[1]);
+ return 0;
+}
+
+#endif
+
static struct {
enum test_llvm__testcase prog_id;
const char *desc;
@@ -37,6 +60,17 @@ static struct {
&epoll_pwait_loop,
(NR_ITERS + 1) / 2,
},
+#ifdef HAVE_BPF_PROLOGUE
+ {
+ LLVM_TESTCASE_BPF_PROLOGUE,
+ "Test BPF prologue generation",
+ "[bpf_prologue_test]",
+ "fix kbuild first",
+ "check your vmlinux setting?",
+ &llseek_loop,
+ (NR_ITERS + 1) / 4,
+ },
+#endif
};

static int do_test(struct bpf_object *obj, int (*func)(void),
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index 366e38b..b414763 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -44,6 +44,10 @@ static struct {
.source = test_llvm__bpf_test_kbuild_prog,
.desc = "Test kbuild searching",
},
+ [LLVM_TESTCASE_BPF_PROLOGUE] = {
+ .source = test_llvm__bpf_test_prologue_prog,
+ .desc = "Test BPF prologue generation",
+ },
};


diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h
index d91d8f4..5150b4d 100644
--- a/tools/perf/tests/llvm.h
+++ b/tools/perf/tests/llvm.h
@@ -6,10 +6,12 @@

extern const char test_llvm__bpf_base_prog[];
extern const char test_llvm__bpf_test_kbuild_prog[];
+extern const char test_llvm__bpf_test_prologue_prog[];

enum test_llvm__testcase {
LLVM_TESTCASE_BASE,
LLVM_TESTCASE_KBUILD,
+ LLVM_TESTCASE_BPF_PROLOGUE,
__LLVM_TESTCASE_MAX,
};

Subject: [tip:perf/core] perf test: Fix 'perf test BPF' when it fails to find a suitable vmlinux

Commit-ID: ad0dd7aed5df8009b3ffa39bec73ad93283332c9
Gitweb: http://git.kernel.org/tip/ad0dd7aed5df8009b3ffa39bec73ad93283332c9
Author: Wang Nan <[email protected]>
AuthorDate: Tue, 17 Nov 2015 08:32:46 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 18 Nov 2015 17:51:04 -0300

perf test: Fix 'perf test BPF' when it fails to find a suitable vmlinux

Two bugs in 'perf test BPF' are found when testing BPF prologue without
vmlinux:

# mv /lib/modules/4.3.0-rc4+/build/vmlinux{,.bak}
# ./perf test BPF
37: Test BPF filter :Failed to find the path for kernel: No such file or directory
Ok

Test BPF should fail in this case.

After this patch:

# ./perf test BPF
37: Test BPF filter :Failed to find the path for kernel: No such file or directory
FAILED!
# mv /lib/modules/4.3.0-rc4+/build/vmlinux{.bak,}
# ./perf test BPF
37: Test BPF filter : Ok

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/tests/bpf.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
index d584422..232043c 100644
--- a/tools/perf/tests/bpf.c
+++ b/tools/perf/tests/bpf.c
@@ -102,8 +102,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj);
if (err || list_empty(&parse_evlist.list)) {
pr_debug("Failed to add events selected by BPF\n");
- if (!err)
- return TEST_FAIL;
+ return TEST_FAIL;
}

snprintf(pid, sizeof(pid), "%d", getpid());
@@ -157,8 +156,10 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
}
}

- if (count != expect)
+ if (count != expect) {
pr_debug("BPF filter result incorrect\n");
+ goto out_delete_evlist;
+ }

ret = TEST_OK;

Subject: [tip:perf/core] perf bpf: Use same BPF program if arguments are identical

Commit-ID: d35b32891a61f1d3909bdc5280badf309adc4693
Gitweb: http://git.kernel.org/tip/d35b32891a61f1d3909bdc5280badf309adc4693
Author: Wang Nan <[email protected]>
AuthorDate: Tue, 17 Nov 2015 08:32:47 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Wed, 18 Nov 2015 17:51:05 -0300

perf bpf: Use same BPF program if arguments are identical

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

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

Test result:

Sample BPF program:

#define SEC(NAME) __attribute__((section(NAME), used))
SEC("inlines=no;"
"func=SyS_dup? oldfd")
int func(void *ctx)
{
return 1;
}

It would probe at SyS_dup2 and SyS_dup3, obtaining oldfd as its
argument.

The following cmdline shows a BPF program being loaded into the kernel
by perf:

# perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog

Before this patch:

# perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog
[1] 24858
lrwx------ 1 root root 64 Nov 14 04:09 3 -> anon_inode:bpf-prog
lrwx------ 1 root root 64 Nov 14 04:09 4 -> anon_inode:bpf-prog
...

After this patch:

# perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog
[1] 25699
lrwx------ 1 root root 64 Nov 14 04:10 3 -> anon_inode:bpf-prog
...

Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/util/bpf-loader.c | 138 ++++++++++++++++++++++++++++++++++++++++---
1 file changed, 131 insertions(+), 7 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 190a1c7..36544e5 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -38,6 +38,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 bool libbpf_initialized;
@@ -113,6 +115,7 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,

cleanup_perf_probe_events(&priv->pev, 1);
zfree(&priv->insns_buf);
+ zfree(&priv->type_mapping);
free(priv);
}

@@ -381,7 +384,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)
@@ -389,10 +392,21 @@ 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_debug("Internal error: prologue type %d not found\n", n);
+ return -BPF_LOADER_ERRNO__PROLOGUE;
+ }
+
+ tev = &pev->tevs[i];

buf = priv->insns_buf;
err = bpf__gen_prologue(tev->args, tev->nargs,
@@ -423,6 +437,101 @@ errout:
return -BPF_LOADER_ERRNO__PROLOGUE;
}

+/*
+ * compare_tev_args is reflexive, transitive and antisymmetric.
+ * I can proof it 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;
+}
+
+/*
+ * Assign a type number to each tevs in a pev.
+ * mapping is an array with same slots as tevs in that pev.
+ * nr_types will be set to number of types.
+ */
+static int map_prologue(struct perf_probe_event *pev, int *mapping,
+ int *nr_types)
+{
+ int i, type = 0;
+ struct probe_trace_event **ptevs;
+
+ size_t array_sz = sizeof(*ptevs) * pev->ntevs;
+
+ ptevs = malloc(array_sz);
+ if (!ptevs) {
+ pr_debug("No ehough memory: alloc ptevs failed\n");
+ return -ENOMEM;
+ }
+
+ pr_debug("In map_prologue, ntevs=%d\n", pev->ntevs);
+ for (i = 0; i < pev->ntevs; i++)
+ ptevs[i] = &pev->tevs[i];
+
+ qsort(ptevs, pev->ntevs, sizeof(*ptevs),
+ compare_tev_args);
+
+ for (i = 0; i < pev->ntevs; i++) {
+ int n;
+
+ n = ptevs[i] - pev->tevs;
+ if (i == 0) {
+ mapping[n] = type;
+ pr_debug("mapping[%d]=%d\n", n, type);
+ continue;
+ }
+
+ if (compare_tev_args(ptevs + i, ptevs + i - 1) == 0)
+ mapping[n] = type;
+ else
+ mapping[n] = ++type;
+
+ pr_debug("mapping[%d]=%d\n", n, mapping[n]);
+ }
+ free(ptevs);
+ *nr_types = type + 1;
+
+ return 0;
+}
+
static int hook_load_preprocessor(struct bpf_program *prog)
{
struct perf_probe_event *pev;
@@ -462,7 +571,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_debug("No enough memory: alloc type_mapping failed\n");
+ return -ENOMEM;
+ }
+ memset(priv->type_mapping, -1,
+ 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;
}
@@ -596,10 +717,13 @@ int bpf__foreach_tev(struct bpf_object *obj,
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) {
pr_debug("bpf: failed to get file descriptor\n");

Subject: [tip:perf/core] perf test: Print result for each LLVM subtest

Commit-ID: e8c6d500447c577e669c24ec04cd4173fe9f9afb
Gitweb: http://git.kernel.org/tip/e8c6d500447c577e669c24ec04cd4173fe9f9afb
Author: Wang Nan <[email protected]>
AuthorDate: Tue, 17 Nov 2015 08:32:48 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Thu, 19 Nov 2015 13:19:16 -0300

perf test: Print result for each LLVM subtest

Currently 'perf test llvm' and 'perf test BPF' have multiple sub-tests,
but the result is provided in only one line:

# perf test LLVM
35: Test LLVM searching and compiling : Ok

This patch introduces sub-tests support, allowing 'perf test' to report
result for each sub-tests:

# perf test LLVM
35: Test LLVM searching and compiling :
35.1: Basic BPF llvm compiling test : Ok
35.2: Test kbuild searching : Ok
35.3: Compile source for BPF prologue generation test : Ok

When a failure happens:

# cat ~/.perfconfig
[llvm]
clang-path = "/bin/false"
# perf test LLVM
35: Test LLVM searching and compiling :
35.1: Basic BPF llvm compiling test : FAILED!
35.2: Test kbuild searching : Skip
35.3: Compile source for BPF prologue generation test : Skip

And:

# rm ~/.perfconfig
# ./perf test LLVM
35: Test LLVM searching and compiling :
35.1: Basic BPF llvm compiling test : Skip
35.2: Test kbuild searching : Skip
35.3: Compile source for BPF prologue generation test : Skip

Skip by user:

# ./perf test -s 1,`seq -s , 3 42`
1: vmlinux symtab matches kallsyms : Skip (user override)
2: detect openat syscall event : Ok
...
35: Test LLVM searching and compiling : Skip (user override)
...

Suggested-and-Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
[ Changed so that func is not on an anonymous union ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/tests/builtin-test.c | 91 ++++++++++++++++++++++++++++++++++-------
tools/perf/tests/llvm.c | 65 ++++++++++++++---------------
tools/perf/tests/tests.h | 9 ++++
3 files changed, 115 insertions(+), 50 deletions(-)

diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 9cf4892..8136609 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -160,6 +160,11 @@ static struct test generic_tests[] = {
{
.desc = "Test LLVM searching and compiling",
.func = test__llvm,
+ .subtest = {
+ .skip_if_fail = true,
+ .get_nr = test__llvm_subtest_get_nr,
+ .get_desc = test__llvm_subtest_get_desc,
+ },
},
{
.desc = "Test topology in session",
@@ -237,6 +242,40 @@ static int run_test(struct test *test, int subtest)
for (j = 0; j < ARRAY_SIZE(tests); j++) \
for (t = &tests[j][0]; t->func; t++)

+static int test_and_print(struct test *t, bool force_skip, int subtest)
+{
+ int err;
+
+ if (!force_skip) {
+ pr_debug("\n--- start ---\n");
+ err = run_test(t, subtest);
+ pr_debug("---- end ----\n");
+ } else {
+ pr_debug("\n--- force skipped ---\n");
+ err = TEST_SKIP;
+ }
+
+ if (!t->subtest.get_nr)
+ pr_debug("%s:", t->desc);
+ else
+ pr_debug("%s subtest %d:", t->desc, subtest);
+
+ switch (err) {
+ case TEST_OK:
+ pr_info(" Ok\n");
+ break;
+ case TEST_SKIP:
+ color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip\n");
+ break;
+ case TEST_FAIL:
+ default:
+ color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n");
+ break;
+ }
+
+ return err;
+}
+
static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
{
struct test *t;
@@ -264,21 +303,43 @@ static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist)
continue;
}

- pr_debug("\n--- start ---\n");
- err = run_test(t, i);
- pr_debug("---- end ----\n%s:", t->desc);
-
- switch (err) {
- case TEST_OK:
- pr_info(" Ok\n");
- break;
- case TEST_SKIP:
- color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip\n");
- break;
- case TEST_FAIL:
- default:
- color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n");
- break;
+ if (!t->subtest.get_nr) {
+ test_and_print(t, false, -1);
+ } else {
+ int subn = t->subtest.get_nr();
+ /*
+ * minus 2 to align with normal testcases.
+ * For subtest we print additional '.x' in number.
+ * for example:
+ *
+ * 35: Test LLVM searching and compiling :
+ * 35.1: Basic BPF llvm compiling test : Ok
+ */
+ int subw = width > 2 ? width - 2 : width;
+ bool skip = false;
+ int subi;
+
+ if (subn <= 0) {
+ color_fprintf(stderr, PERF_COLOR_YELLOW,
+ " Skip (not compiled in)\n");
+ continue;
+ }
+ pr_info("\n");
+
+ for (subi = 0; subi < subn; subi++) {
+ int len = strlen(t->subtest.get_desc(subi));
+
+ if (subw < len)
+ subw = len;
+ }
+
+ for (subi = 0; subi < subn; subi++) {
+ pr_info("%2d.%1d: %-*s:", i, subi + 1, subw,
+ t->subtest.get_desc(subi));
+ err = test_and_print(t, skip, subi);
+ if (err != TEST_OK && t->subtest.skip_if_fail)
+ skip = true;
+ }
}
}

diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index 4350c45..06f45c1 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -46,7 +46,7 @@ static struct {
},
[LLVM_TESTCASE_BPF_PROLOGUE] = {
.source = test_llvm__bpf_test_prologue_prog,
- .desc = "Test BPF prologue generation",
+ .desc = "Compile source for BPF prologue generation test",
},
};

@@ -131,44 +131,39 @@ out:
return ret;
}

-int test__llvm(int subtest __maybe_unused)
+int test__llvm(int subtest)
{
- enum test_llvm__testcase i;
+ int ret;
+ void *obj_buf = NULL;
+ size_t obj_buf_sz = 0;

- for (i = 0; i < __LLVM_TESTCASE_MAX; i++) {
- int ret;
- void *obj_buf = NULL;
- size_t obj_buf_sz = 0;
+ if ((subtest < 0) || (subtest >= __LLVM_TESTCASE_MAX))
+ return TEST_FAIL;

- ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz,
- i, false);
+ ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz,
+ subtest, false);

- if (ret == TEST_OK) {
- ret = test__bpf_parsing(obj_buf, obj_buf_sz);
- if (ret != TEST_OK)
- pr_debug("Failed to parse test case '%s'\n",
- bpf_source_table[i].desc);
- }
- free(obj_buf);
-
- switch (ret) {
- case TEST_SKIP:
- return TEST_SKIP;
- case TEST_OK:
- break;
- default:
- /*
- * Test 0 is the basic LLVM test. If test 0
- * fail, the basic LLVM support not functional
- * so the whole test should fail. If other test
- * case fail, it can be fixed by adjusting
- * config so don't report error.
- */
- if (i == 0)
- return TEST_FAIL;
- else
- return TEST_SKIP;
+ if (ret == TEST_OK) {
+ ret = test__bpf_parsing(obj_buf, obj_buf_sz);
+ if (ret != TEST_OK) {
+ pr_debug("Failed to parse test case '%s'\n",
+ bpf_source_table[subtest].desc);
}
}
- return TEST_OK;
+ free(obj_buf);
+
+ return ret;
+}
+
+int test__llvm_subtest_get_nr(void)
+{
+ return __LLVM_TESTCASE_MAX;
+}
+
+const char *test__llvm_subtest_get_desc(int subtest)
+{
+ if ((subtest < 0) || (subtest >= __LLVM_TESTCASE_MAX))
+ return NULL;
+
+ return bpf_source_table[subtest].desc;
}
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index 204e4ee..f92af52 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -1,6 +1,8 @@
#ifndef TESTS_H
#define TESTS_H

+#include <stdbool.h>
+
#define TEST_ASSERT_VAL(text, cond) \
do { \
if (!(cond)) { \
@@ -27,6 +29,11 @@ enum {
struct test {
const char *desc;
int (*func)(int subtest);
+ struct {
+ bool skip_if_fail;
+ int (*get_nr)(void);
+ const char *(*get_desc)(int subtest);
+ } subtest;
};

/* Tests */
@@ -66,6 +73,8 @@ int test__fdarray__add(int subtest);
int test__kmod_path__parse(int subtest);
int test__thread_map(int subtest);
int test__llvm(int subtest);
+const char *test__llvm_subtest_get_desc(int subtest);
+int test__llvm_subtest_get_nr(void);
int test__bpf(int subtest);
int test_session_topology(int subtest);

Subject: [tip:perf/core] perf test: Print result for each BPF subtest

Commit-ID: 77a0cf682f7979554e10a6c605a1fef4f4197654
Gitweb: http://git.kernel.org/tip/77a0cf682f7979554e10a6c605a1fef4f4197654
Author: Wang Nan <[email protected]>
AuthorDate: Tue, 17 Nov 2015 08:32:49 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Thu, 19 Nov 2015 13:19:16 -0300

perf test: Print result for each BPF subtest

This patch prints each sub-tests results for BPF testcases.

Before:

# ./perf test BPF
37: Test BPF filter : Ok

After:

# ./perf test BPF
37: Test BPF filter :
37.1: Test basic BPF filtering : Ok
37.2: Test BPF prologue generation : Ok

When a failure happens:

# cat ~/.perfconfig
[llvm]
clang-path = "/bin/false"
# ./perf test BPF
37: Test BPF filter :
37.1: Test basic BPF filtering : Skip
37.2: Test BPF prologue generation : Skip

Suggested-and-Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Signed-off-by: Wang Nan <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
[ Fixed up not to use .func in an anonymous union ]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/tests/bpf.c | 38 ++++++++++++++++++++++++++++----------
tools/perf/tests/builtin-test.c | 5 +++++
tools/perf/tests/tests.h | 2 ++
3 files changed, 35 insertions(+), 10 deletions(-)

diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
index 4efdc16..33689a0 100644
--- a/tools/perf/tests/bpf.c
+++ b/tools/perf/tests/bpf.c
@@ -215,28 +215,46 @@ out:
return ret;
}

-int test__bpf(int subtest __maybe_unused)
+int test__bpf_subtest_get_nr(void)
+{
+ return (int)ARRAY_SIZE(bpf_testcase_table);
+}
+
+const char *test__bpf_subtest_get_desc(int i)
+{
+ if (i < 0 || i >= (int)ARRAY_SIZE(bpf_testcase_table))
+ return NULL;
+ return bpf_testcase_table[i].desc;
+}
+
+int test__bpf(int i)
{
- unsigned int i;
int err;

+ if (i < 0 || i >= (int)ARRAY_SIZE(bpf_testcase_table))
+ return TEST_FAIL;
+
if (geteuid() != 0) {
pr_debug("Only root can run BPF test\n");
return TEST_SKIP;
}

- for (i = 0; i < ARRAY_SIZE(bpf_testcase_table); i++) {
- err = __test__bpf(i);
+ err = __test__bpf(i);
+ return err;
+}

- if (err != TEST_OK)
- return err;
- }
+#else
+int test__bpf_subtest_get_nr(void)
+{
+ return 0;
+}

- return TEST_OK;
+const char *test__bpf_subtest_get_desc(int i __maybe_unused)
+{
+ return NULL;
}

-#else
-int test__bpf(void)
+int test__bpf(int i __maybe_unused)
{
pr_debug("Skip BPF test because BPF support is not compiled\n");
return TEST_SKIP;
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 8136609..146ae98 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -173,6 +173,11 @@ static struct test generic_tests[] = {
{
.desc = "Test BPF filter",
.func = test__bpf,
+ .subtest = {
+ .skip_if_fail = true,
+ .get_nr = test__bpf_subtest_get_nr,
+ .get_desc = test__bpf_subtest_get_desc,
+ },
},
{
.func = NULL,
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index f92af52..a0733aa 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -76,6 +76,8 @@ int test__llvm(int subtest);
const char *test__llvm_subtest_get_desc(int subtest);
int test__llvm_subtest_get_nr(void);
int test__bpf(int subtest);
+const char *test__bpf_subtest_get_desc(int subtest);
+int test__bpf_subtest_get_nr(void);
int test_session_topology(int subtest);

#if defined(__arm__) || defined(__aarch64__)

Subject: [tip:perf/core] perf test: Mute test cases error messages if verbose == 0

Commit-ID: 5bcf2fe05318deb6fec209b4028d8a31f9f47221
Gitweb: http://git.kernel.org/tip/5bcf2fe05318deb6fec209b4028d8a31f9f47221
Author: Wang Nan <[email protected]>
AuthorDate: Tue, 17 Nov 2015 08:32:50 +0000
Committer: Arnaldo Carvalho de Melo <[email protected]>
CommitDate: Thu, 19 Nov 2015 13:19:17 -0300

perf test: Mute test cases error messages if verbose == 0

Sometimes error messages in breaks the pretty output of 'perf test'.
For example:

# mv /lib/modules/4.3.0-rc4+/build/vmlinux{,.bak}
# perf test LLVM BPF
35: Test LLVM searching and compiling :
35.1: Basic BPF llvm compiling test : Ok
35.2: Test kbuild searching : Ok
35.3: Compile source for BPF prologue generation test : Ok
37: Test BPF filter :
37.1: Test basic BPF filtering : Ok
37.2: Test BPF prologue generation :Failed to find the path for kernel: No such file or directory FAILED!

This patch mute test cases thoroughly by redirect their stdout and
stderr to /dev/null when verbose == 0. After applying this patch:

# ./perf test LLVM BPF
35: Test LLVM searching and compiling :
35.1: Basic BPF llvm compiling test : Ok
35.2: Test kbuild searching : Ok
35.3: Compile source for BPF prologue generation test : Ok
37: Test BPF filter :
37.1: Test basic BPF filtering : Ok
37.2: Test BPF prologue generation : FAILED!

# ./perf test -v LLVM BPF
35: Test LLVM searching and compiling :
35.1: Basic BPF llvm compiling test :
--- start ---
test child forked, pid 13183
Kernel build dir is set to /lib/modules/4.3.0-rc4+/build
set env: KBUILD_DIR=/lib/modules/4.3.0-rc4+/build
...
bpf: config 'func=null_lseek file->f_mode offset orig' is ok
Looking at the vmlinux_path (7 entries long)
Failed to find the path for kernel: No such file or directory
bpf_probe: failed to convert perf probe eventsFailed to add events selected by BPF
test child finished with -1
---- end ----
Test BPF filter subtest 1: FAILED!

Signed-off-by: Wang Nan <[email protected]>
Tested-by: Arnaldo Carvalho de Melo <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Zefan Li <[email protected]>
Cc: [email protected]
Link: http://lkml.kernel.org/r/[email protected]
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
tools/perf/tests/builtin-test.c | 12 ++++++++++++
1 file changed, 12 insertions(+)

diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 146ae98..2b1ade1 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -226,6 +226,18 @@ static int run_test(struct test *test, int subtest)

if (!child) {
pr_debug("test child forked, pid %d\n", getpid());
+ if (!verbose) {
+ int nullfd = open("/dev/null", O_WRONLY);
+ if (nullfd >= 0) {
+ close(STDERR_FILENO);
+ close(STDOUT_FILENO);
+
+ dup2(nullfd, STDOUT_FILENO);
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+ close(nullfd);
+ }
+ }
+
err = test->func(subtest);
exit(err);
}

2015-11-26 08:05:57

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 1/2] perf tools: Always give options even it not compiled



On 2015/11/20 18:54, 平松雅巳 / HIRAMATU,MASAMI wrote:
>> From: Wang Nan [mailto:[email protected]]
>>
>> This patch keeps options of perf builtins same in all condition. If the
>> option is disabled because of compiling options, users should be
>> notified.
>>
>> This patch does it by introducing a series of new option macros, flags
>> and field in struct options. For those options disabled by compiling,
>> OPT_NOTBUILT_NOARG, OPT_NOTBUILT_OPTARG and OPT_NOTBUILT can be used
>> to record the help messages and the reason why not built them.
>>
>> Options in 'perf record' and 'perf probe' are fixed by those new macros.
> Hmm, OK, I agree the reason why this is useful. Could you reconsider
> the implementation, because just cloning the code is ugly and not
> maintainable?
>
> It will be better if we can replace OPT_BOOLEAN with;
>
> OPT_DEPENDS(HAVE_DWARF_SUPPORT, BOOLEAN, '\0', "no-inlines", &probe_conf.no_inlines, ...
>
> This may be done by following macros ;
>
> ----
> #define OPTMSG_HAVE_DWARF_SUPPORT "NO_DWARF"
> #ifdef HAVE_DWARF_SUPPORT
> #define OPTVAL_HAVE_DWARF_SUPPORT 1
> #else
> #define OPTVAL_HAVE_DWARF_SUPPORT 0
> #endif
>
> #define __OPT_DEPENDS(val, msg, opt, ...) \
> {.type = OPTION_NEXT_DEPENDS, .value = (void *)val, .data = msg, }, opt(__VA_ARGS__)
>
> #define _OPT_DEPENDS(val, msg, opt, ...) \
> __OPT_DEPENDS(val, msg, opt, __VA_ARGS__)
>
> #define OPT_DEPENDS(flag, opt, ...) \
> _OPT_DEPENDS(OPTVAL_ ## flag, OPTMSG_ ## flag, OPT_ ## opt, __VA_ARGS__)
> ----
>
> And if the parser find OPTION_NEXT_DEPENDS and .value != NULL, it skips next entry.
>

That would be good, but don't forget we have a options__order() which
reorder the options array. To archive our goal I think we must preprocess
the options array to 'merge' information in the OPTION_NEXT_DEPENDS into
the real option it decorates.

Thank you.

> Thank you,
>

2015-11-26 09:07:49

by Wang Nan

[permalink] [raw]
Subject: Re: [PATCH 1/2] perf tools: Always give options even it not compiled



On 2015/11/26 16:05, Wangnan (F) wrote:
>
>
> On 2015/11/20 18:54, 平松雅巳 / HIRAMATU,MASAMI wrote:
>>> From: Wang Nan [mailto:[email protected]]
>>>
>>> This patch keeps options of perf builtins same in all condition. If the
>>> option is disabled because of compiling options, users should be
>>> notified.
>>>
>>> This patch does it by introducing a series of new option macros, flags
>>> and field in struct options. For those options disabled by compiling,
>>> OPT_NOTBUILT_NOARG, OPT_NOTBUILT_OPTARG and OPT_NOTBUILT can be used
>>> to record the help messages and the reason why not built them.
>>>
>>> Options in 'perf record' and 'perf probe' are fixed by those new
>>> macros.
>> Hmm, OK, I agree the reason why this is useful. Could you reconsider
>> the implementation, because just cloning the code is ugly and not
>> maintainable?
>>
>> It will be better if we can replace OPT_BOOLEAN with;
>>
>> OPT_DEPENDS(HAVE_DWARF_SUPPORT, BOOLEAN, '\0', "no-inlines",
>> &probe_conf.no_inlines, ...
>>
>> This may be done by following macros ;
>>
>> ----
>> #define OPTMSG_HAVE_DWARF_SUPPORT "NO_DWARF"
>> #ifdef HAVE_DWARF_SUPPORT
>> #define OPTVAL_HAVE_DWARF_SUPPORT 1
>> #else
>> #define OPTVAL_HAVE_DWARF_SUPPORT 0
>> #endif
>>
>> #define __OPT_DEPENDS(val, msg, opt, ...) \
>> {.type = OPTION_NEXT_DEPENDS, .value = (void *)val, .data = msg,
>> }, opt(__VA_ARGS__)
>>
>> #define _OPT_DEPENDS(val, msg, opt, ...) \
>> __OPT_DEPENDS(val, msg, opt, __VA_ARGS__)
>>
>> #define OPT_DEPENDS(flag, opt, ...) \
>> _OPT_DEPENDS(OPTVAL_ ## flag, OPTMSG_ ## flag, OPT_ ## opt,
>> __VA_ARGS__)
>> ----
>>
>> And if the parser find OPTION_NEXT_DEPENDS and .value != NULL, it
>> skips next entry.
>>
>
> That would be good, but don't forget we have a options__order() which
> reorder the options array. To archive our goal I think we must preprocess
> the options array to 'merge' information in the OPTION_NEXT_DEPENDS into
> the real option it decorates.
>

Doing such merging in parse_options_subcommand() is natually but require
removing all
'const' decorators from struct options. Too much code changing for such
a small
feature...

Will think another solution...

> Thank you.
>
>> Thank you,
>>
>