Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1270078 > unrolled thread

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

Started byWang Nan <wangnan0@huawei.com>
First post2015-11-16 13:50 +0100
Last post2015-11-16 15:20 +0100
Articles 20 on this page of 56 — 7 participants

Back to article view | Back to linux.kernel


Contents

  [PATCH 00/13] perf tools: bpf: Improve BPF program ability Wang Nan <wangnan0@huawei.com> - 2015-11-16 13:50 +0100
    [PATCH 03/13] perf tools: Allow BPF program attach to uprobe events Wang Nan <wangnan0@huawei.com> - 2015-11-16 13:50 +0100
      Re: [PATCH 03/13] perf tools: Allow BPF program attach to uprobe  events Arnaldo Carvalho de Melo <acme@kernel.org> - 2015-11-16 15:20 +0100
      [tip:perf/core] perf bpf:   Allow BPF program attach to uprobe events tip-bot for Wang Nan <tipbot@zytor.com> - 2015-11-23 17:10 +0100
    [PATCH 10/13] perf tools: Add prologue for BPF programs for fetching arguments Wang Nan <wangnan0@huawei.com> - 2015-11-16 13:50 +0100
      [tip:perf/core] perf bpf:   Add prologue for BPF programs for fetching arguments tip-bot for He Kuang <tipbot@zytor.com> - 2015-11-23 17:10 +0100
    [PATCH 04/13] perf tools: Allow BPF program attach to modules Wang Nan <wangnan0@huawei.com> - 2015-11-16 13:50 +0100
      [tip:perf/core] perf bpf:   Allow attaching BPF programs to modules symbols tip-bot for Wang Nan <tipbot@zytor.com> - 2015-11-23 17:10 +0100
    [PATCH 07/13] bpf tools: Load a program with different instances using preprocessor Wang Nan <wangnan0@huawei.com> - 2015-11-16 13:50 +0100
      Re: [PATCH 07/13] bpf tools: Load a program with different instances  using preprocessor Arnaldo Carvalho de Melo <acme@kernel.org> - 2015-11-16 20:10 +0100
        Re: [PATCH 07/13] bpf tools: Load a program with different instances  using preprocessor "Wangnan (F)" <wangnan0@huawei.com> - 2015-11-17 05:00 +0100
      [tip:perf/core] bpf tools:   Load a program with different instances using preprocessor tip-bot for Wang Nan <tipbot@zytor.com> - 2015-11-23 17:10 +0100
    [PATCH 13/13] perf tools: Use same BPF program if arguments are identical Wang Nan <wangnan0@huawei.com> - 2015-11-16 14:00 +0100
      Re: [PATCH 13/13] perf tools: Use same BPF program if arguments are  identical "Wangnan (F)" <wangnan0@huawei.com> - 2015-11-17 04:10 +0100
        Re: [PATCH 13/13] perf tools: Use same BPF program if arguments are  identical Arnaldo Carvalho de Melo <acme@kernel.org> - 2015-11-17 14:20 +0100
    [PATCH 12/13] perf test: Test BPF prologue Wang Nan <wangnan0@huawei.com> - 2015-11-16 14:00 +0100
      Re: [PATCH 12/13] perf test: Test BPF prologue Arnaldo Carvalho de Melo <acme@kernel.org> - 2015-11-17 02:30 +0100
        Re: [PATCH 12/13] perf test: Test BPF prologue "Wangnan (F)" <wangnan0@huawei.com> - 2015-11-17 02:40 +0100
        Re: [PATCH 12/13] perf test: Test BPF prologue "Wangnan (F)" <wangnan0@huawei.com> - 2015-11-17 05:40 +0100
          Re: [PATCH 12/13] perf test: Test BPF prologue Arnaldo Carvalho de Melo <acme@kernel.org> - 2015-11-17 13:30 +0100
        [PATCH 0/5] perf tools: Improve BPF support Wang Nan <wangnan0@huawei.com> - 2015-11-17 09:40 +0100
          [PATCH 5/5] perf test: Mute test cases if verbose == 0 Wang Nan <wangnan0@huawei.com> - 2015-11-17 09:40 +0100
            Re: [PATCH 5/5] perf test: Mute test cases if verbose == 0 Arnaldo Carvalho de Melo <acme@kernel.org> - 2015-11-17 14:20 +0100
            [tip:perf/core] perf test:   Mute test cases error messages if verbose == 0 tip-bot for Wang Nan <tipbot@zytor.com> - 2015-11-23 17:20 +0100
          [PATCH 2/5] perf tools: Use same BPF program if arguments are identical Wang Nan <wangnan0@huawei.com> - 2015-11-17 09:40 +0100
            [tip:perf/core] perf bpf:   Use same BPF program if arguments are identical tip-bot for Wang Nan <tipbot@zytor.com> - 2015-11-23 17:10 +0100
          [PATCH 4/5] perf test: Print result for each subtest for BPF Wang Nan <wangnan0@huawei.com> - 2015-11-17 09:40 +0100
            [tip:perf/core] perf test: Print result for each BPF subtest tip-bot for Wang Nan <tipbot@zytor.com> - 2015-11-23 17:20 +0100
          [PATCH 1/5] perf test: Fix 2 bugs in 'perf test BPF' Wang Nan <wangnan0@huawei.com> - 2015-11-17 09:40 +0100
            Re: [PATCH 1/5] perf test: Fix 2 bugs in 'perf test BPF' Arnaldo Carvalho de Melo <acme@kernel.org> - 2015-11-17 14:00 +0100
              Re: [PATCH 1/5] perf test: Fix 2 bugs in 'perf test BPF' pi3orama <pi3orama@163.com> - 2015-11-17 14:10 +0100
                Re: [PATCH 1/5] perf test: Fix 2 bugs in 'perf test BPF' Arnaldo Carvalho de Melo <acme@kernel.org> - 2015-11-17 14:40 +0100
            [tip:perf/core] perf test: Fix 'perf test BPF'   when it fails to find a suitable vmlinux tip-bot for Wang Nan <tipbot@zytor.com> - 2015-11-23 17:10 +0100
          [PATCH 3/5] perf test: Print result for each subtest for llvm Wang Nan <wangnan0@huawei.com> - 2015-11-17 09:40 +0100
            Re: [PATCH 3/5] perf test: Print result for each subtest for llvm Arnaldo Carvalho de Melo <acme@kernel.org> - 2015-11-17 14:10 +0100
            [tip:perf/core] perf test: Print result for each LLVM subtest tip-bot for Wang Nan <tipbot@zytor.com> - 2015-11-23 17:20 +0100
        Re: [PATCH 12/13] perf test: Test BPF prologue "Wangnan (F)" <wangnan0@huawei.com> - 2015-11-17 09:50 +0100
        [PATCH] perf record: Support custom vmlinux path Wang Nan <wangnan0@huawei.com> - 2015-11-17 11:00 +0100
          Re: [PATCH] perf record: Support custom vmlinux path Arnaldo Carvalho de Melo <acme@kernel.org> - 2015-11-17 13:40 +0100
            Re: [PATCH] perf record: Support custom vmlinux path Arnaldo Carvalho de Melo <acme@kernel.org> - 2015-11-17 13:50 +0100
              Re: [PATCH] perf record: Support custom vmlinux path pi3orama <pi3orama@163.com> - 2015-11-17 13:50 +0100
              [PATCH 0/2] perf tools: Builtin options related improvements Wang Nan <wangnan0@huawei.com> - 2015-11-19 15:10 +0100
                [PATCH 2/2] perf record: Support custom vmlinux path Wang Nan <wangnan0@huawei.com> - 2015-11-19 15:10 +0100
                [PATCH 1/2] perf tools: Always give options even it not compiled Wang Nan <wangnan0@huawei.com> - 2015-11-19 15:10 +0100
                  RE: [PATCH 1/2] perf tools: Always give options even it not compiled 平松雅巳 / HIRAMATU,MASAMI   <masami.hiramatsu.pt@hitachi.com> - 2015-11-20 12:00 +0100
                    Re: [PATCH 1/2] perf tools: Always give options even it not compiled "Wangnan (F)" <wangnan0@huawei.com> - 2015-11-26 09:10 +0100
                      Re: [PATCH 1/2] perf tools: Always give options even it not compiled "Wangnan (F)" <wangnan0@huawei.com> - 2015-11-26 10:10 +0100
      [tip:perf/core] perf test:   Test the BPF prologue adding infrastructure tip-bot for Wang Nan <tipbot@zytor.com> - 2015-11-23 17:10 +0100
    [PATCH 06/13] perf tools: Allow BPF program config probing options Wang Nan <wangnan0@huawei.com> - 2015-11-16 14:00 +0100
      [tip:perf/core] perf bpf:   Allow BPF program config probing options tip-bot for Wang Nan <tipbot@zytor.com> - 2015-11-23 17:10 +0100
    [PATCH 08/13] perf tools: Add BPF_PROLOGUE config options for further patches Wang Nan <wangnan0@huawei.com> - 2015-11-16 14:00 +0100
      [tip:perf/core] perf bpf:   Add BPF_PROLOGUE config options for further patches tip-bot for Wang Nan <tipbot@zytor.com> - 2015-11-23 17:10 +0100
    [PATCH 11/13] perf tools: Generate prologue for BPF programs Wang Nan <wangnan0@huawei.com> - 2015-11-16 14:00 +0100
      [tip:perf/core] perf bpf: Generate prologue for BPF programs tip-bot for Wang Nan <tipbot@zytor.com> - 2015-11-23 17:10 +0100
    [PATCH 02/13] perf probe: Clear probe_trace_event when add_probe_trace_event() fails Wang Nan <wangnan0@huawei.com> - 2015-11-16 14:00 +0100
    Re: [PATCH 00/13] perf tools: bpf: Improve BPF program ability Arnaldo Carvalho de Melo <acme@kernel.org> - 2015-11-16 15:20 +0100

Page 1 of 3  [1] 2 3  Next page →


#1270078 — [PATCH 00/13] perf tools: bpf: Improve BPF program ability

FromWang Nan <wangnan0@huawei.com>
Date2015-11-16 13:50 +0100
Subject[PATCH 00/13] perf tools: bpf: Improve BPF program ability
Message-ID<qvrom-7n2-5@gated-at.bofh.it>
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/1447417761-156094-1-git-send-email-wangnan0@huawei.com

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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [next] | [standalone]


#1270082 — [PATCH 03/13] perf tools: Allow BPF program attach to uprobe events

FromWang Nan <wangnan0@huawei.com>
Date2015-11-16 13:50 +0100
Subject[PATCH 03/13] perf tools: Allow BPF program attach to uprobe events
Message-ID<qvrom-7n2-25@gated-at.bofh.it>
In reply to#1270078
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 <wangnan0@huawei.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
---
 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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1270213 — Re: [PATCH 03/13] perf tools: Allow BPF program attach to uprobe events

FromArnaldo Carvalho de Melo <acme@kernel.org>
Date2015-11-16 15:20 +0100
SubjectRe: [PATCH 03/13] perf tools: Allow BPF program attach to uprobe events
Message-ID<qvsNs-8o3-29@gated-at.bofh.it>
In reply to#1270082
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 <wangnan0@huawei.com>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
> Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
> Cc: Zefan Li <lizefan@huawei.com>
> Cc: pi3orama@163.com
> ---
>  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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1275513 — [tip:perf/core] perf bpf: Allow BPF program attach to uprobe events

Fromtip-bot for Wang Nan <tipbot@zytor.com>
Date2015-11-23 17:10 +0100
Subject[tip:perf/core] perf bpf: Allow BPF program attach to uprobe events
Message-ID<qy1QK-48K-5@gated-at.bofh.it>
In reply to#1270082
Commit-ID:  361f2b1d1d7231b8685d990b886f599378a4d5a5
Gitweb:     http://git.kernel.org/tip/361f2b1d1d7231b8685d990b886f599378a4d5a5
Author:     Wang Nan <wangnan0@huawei.com>
AuthorDate: Mon, 16 Nov 2015 12:10:05 +0000
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
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 <wangnan0@huawei.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1447675815-166222-4-git-send-email-wangnan0@huawei.com
[ Changed the separator from \n to ; ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 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,
 };
 
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1270083 — [PATCH 10/13] perf tools: Add prologue for BPF programs for fetching arguments

FromWang Nan <wangnan0@huawei.com>
Date2015-11-16 13:50 +0100
Subject[PATCH 10/13] perf tools: Add prologue for BPF programs for fetching arguments
Message-ID<qvron-7n2-31@gated-at.bofh.it>
In reply to#1270078
From: He Kuang <hekuang@huawei.com>

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 <hekuang@huawei.com>
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
---
 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 <hekuang@huawei.com>
+ * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
+ * 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 <hekuang@huawei.com>
+ * 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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1275514 — [tip:perf/core] perf bpf: Add prologue for BPF programs for fetching arguments

Fromtip-bot for He Kuang <tipbot@zytor.com>
Date2015-11-23 17:10 +0100
Subject[tip:perf/core] perf bpf: Add prologue for BPF programs for fetching arguments
Message-ID<qy1QK-48K-7@gated-at.bofh.it>
In reply to#1270083
Commit-ID:  bfc077b4cf106793b30bf942e426ee99f1f4ac44
Gitweb:     http://git.kernel.org/tip/bfc077b4cf106793b30bf942e426ee99f1f4ac44
Author:     He Kuang <hekuang@huawei.com>
AuthorDate: Mon, 16 Nov 2015 12:10:12 +0000
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
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 <hekuang@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1447675815-166222-11-git-send-email-wangnan0@huawei.com
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 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 <hekuang@huawei.com>
+ * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
+ * 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 <hekuang@huawei.com>
+ * 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 */
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1270084 — [PATCH 04/13] perf tools: Allow BPF program attach to modules

FromWang Nan <wangnan0@huawei.com>
Date2015-11-16 13:50 +0100
Subject[PATCH 04/13] perf tools: Allow BPF program attach to modules
Message-ID<qvron-7n2-33@gated-at.bofh.it>
In reply to#1270078
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 <wangnan0@huawei.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David Ahern <dsahern@gmail.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kaixu Xia <xiakaixu@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Link: http://lkml.kernel.org/n/1445915248-175553-1-git-send-email-wangnan0@huawei.com
---
 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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1275524 — [tip:perf/core] perf bpf: Allow attaching BPF programs to modules symbols

Fromtip-bot for Wang Nan <tipbot@zytor.com>
Date2015-11-23 17:10 +0100
Subject[tip:perf/core] perf bpf: Allow attaching BPF programs to modules symbols
Message-ID<qy1QL-48K-51@gated-at.bofh.it>
In reply to#1270084
Commit-ID:  5dbd16c0c9d17ab1ab2226a5926482c26c0287ed
Gitweb:     http://git.kernel.org/tip/5dbd16c0c9d17ab1ab2226a5926482c26c0287ed
Author:     Wang Nan <wangnan0@huawei.com>
AuthorDate: Mon, 16 Nov 2015 12:10:06 +0000
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
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 <wangnan0@huawei.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David Ahern <dsahern@gmail.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kaixu Xia <xiakaixu@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1447675815-166222-5-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1270085 — [PATCH 07/13] bpf tools: Load a program with different instances using preprocessor

FromWang Nan <wangnan0@huawei.com>
Date2015-11-16 13:50 +0100
Subject[PATCH 07/13] bpf tools: Load a program with different instances using preprocessor
Message-ID<qvron-7n2-35@gated-at.bofh.it>
In reply to#1270078
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 <wangnan0@huawei.com>
Signed-off-by: He Kuang <hekuang@huawei.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
---
 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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1270481 — Re: [PATCH 07/13] bpf tools: Load a program with different instances using preprocessor

FromArnaldo Carvalho de Melo <acme@kernel.org>
Date2015-11-16 20:10 +0100
SubjectRe: [PATCH 07/13] bpf tools: Load a program with different instances using preprocessor
Message-ID<qvxk6-2PK-19@gated-at.bofh.it>
In reply to#1270085
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 <wangnan0@huawei.com>
> Signed-off-by: He Kuang <hekuang@huawei.com>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
> Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
> Cc: Zefan Li <lizefan@huawei.com>
> Cc: pi3orama@163.com
> ---
>  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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1270838 — Re: [PATCH 07/13] bpf tools: Load a program with different instances using preprocessor

From"Wangnan (F)" <wangnan0@huawei.com>
Date2015-11-17 05:00 +0100
SubjectRe: [PATCH 07/13] bpf tools: Load a program with different instances using preprocessor
Message-ID<qvFB0-86h-3@gated-at.bofh.it>
In reply to#1270481

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 <wangnan0@huawei.com>
>> Signed-off-by: He Kuang <hekuang@huawei.com>
>> Cc: Alexei Starovoitov <ast@kernel.org>
>> Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
>> Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
>> Cc: Zefan Li <lizefan@huawei.com>
>> Cc: pi3orama@163.com
>> ---
>>   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.


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


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

Fromtip-bot for Wang Nan <tipbot@zytor.com>
Date2015-11-23 17:10 +0100
Subject[tip:perf/core] bpf tools: Load a program with different instances using preprocessor
Message-ID<qy1QL-48K-27@gated-at.bofh.it>
In reply to#1270085
Commit-ID:  b580563e38487d9db8e94080149644da71c533c1
Gitweb:     http://git.kernel.org/tip/b580563e38487d9db8e94080149644da71c533c1
Author:     Wang Nan <wangnan0@huawei.com>
AuthorDate: Mon, 16 Nov 2015 12:10:09 +0000
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
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 <hekuang@huawei.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: He Kuang <hekuang@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1447675815-166222-8-git-send-email-wangnan0@huawei.com
Signed-off-by: Wang Nan <wangnan0@huawei.com>
[ Enclosed multi-line if/else blocks with {}, (*func_ptr)() -> func_ptr() ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 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.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1270086 — [PATCH 13/13] perf tools: Use same BPF program if arguments are identical

FromWang Nan <wangnan0@huawei.com>
Date2015-11-16 14:00 +0100
Subject[PATCH 13/13] perf tools: Use same BPF program if arguments are identical
Message-ID<qvry2-7qr-9@gated-at.bofh.it>
In reply to#1270078
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 <wangnan0@huawei.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
---
 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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1270782 — Re: [PATCH 13/13] perf tools: Use same BPF program if arguments are identical

From"Wangnan (F)" <wangnan0@huawei.com>
Date2015-11-17 04:10 +0100
SubjectRe: [PATCH 13/13] perf tools: Use same BPF program if arguments are identical
Message-ID<qvEOC-7G2-9@gated-at.bofh.it>
In reply to#1270086

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 <wangnan0@huawei.com>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
> Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
> Cc: Zefan Li <lizefan@huawei.com>
> Cc: pi3orama@163.com
> ---

[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.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1271177 — Re: [PATCH 13/13] perf tools: Use same BPF program if arguments are identical

FromArnaldo Carvalho de Melo <acme@kernel.org>
Date2015-11-17 14:20 +0100
SubjectRe: [PATCH 13/13] perf tools: Use same BPF program if arguments are identical
Message-ID<qvOkW-5vY-23@gated-at.bofh.it>
In reply to#1270782
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 <wangnan0@huawei.com>
> >Cc: Alexei Starovoitov <ast@kernel.org>
> >Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
> >Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
> >Cc: Zefan Li <lizefan@huawei.com>
> >Cc: pi3orama@163.com
> >---
> 
> [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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1270087 — [PATCH 12/13] perf test: Test BPF prologue

FromWang Nan <wangnan0@huawei.com>
Date2015-11-16 14:00 +0100
Subject[PATCH 12/13] perf test: Test BPF prologue
Message-ID<qvry2-7qr-13@gated-at.bofh.it>
In reply to#1270078
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 <wangnan0@huawei.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
---
 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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1270748 — Re: [PATCH 12/13] perf test: Test BPF prologue

FromArnaldo Carvalho de Melo <acme@kernel.org>
Date2015-11-17 02:30 +0100
SubjectRe: [PATCH 12/13] perf test: Test BPF prologue
Message-ID<qvDfQ-6Ck-17@gated-at.bofh.it>
In reply to#1270087
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 <wangnan0@huawei.com>
> Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
> Cc: Alexei Starovoitov <ast@kernel.org>
> Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
> Cc: Zefan Li <lizefan@huawei.com>
> Cc: pi3orama@163.com
> ---
>  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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1270753 — Re: [PATCH 12/13] perf test: Test BPF prologue

From"Wangnan (F)" <wangnan0@huawei.com>
Date2015-11-17 02:40 +0100
SubjectRe: [PATCH 12/13] perf test: Test BPF prologue
Message-ID<qvDpv-6FC-15@gated-at.bofh.it>
In reply to#1270748

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
>   

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1270865 — Re: [PATCH 12/13] perf test: Test BPF prologue

From"Wangnan (F)" <wangnan0@huawei.com>
Date2015-11-17 05:40 +0100
SubjectRe: [PATCH 12/13] perf test: Test BPF prologue
Message-ID<qvGdI-7W-15@gated-at.bofh.it>
In reply to#1270748

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.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1271135 — Re: [PATCH 12/13] perf test: Test BPF prologue

FromArnaldo Carvalho de Melo <acme@kernel.org>
Date2015-11-17 13:30 +0100
SubjectRe: [PATCH 12/13] perf test: Test BPF prologue
Message-ID<qvNyy-4XK-23@gated-at.bofh.it>
In reply to#1270865
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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


Page 1 of 3  [1] 2 3  Next page →

Back to top | Article view | linux.kernel


csiph-web