diff mbox series

[16/18] perf tools: Synthesize bpf_trampoline/dispatcher ksymbol event

Message ID 20200226130345.209469-17-jolsa@kernel.org
State Changes Requested
Delegated to: BPF Maintainers
Headers show
Series bpf: Add trampoline and dispatcher to /proc/kallsyms | expand

Commit Message

Jiri Olsa Feb. 26, 2020, 1:03 p.m. UTC
Synthesize bpf images (trampolines/dispatchers) on start,
as ksymbol events from /proc/kallsyms. Having this perf
can recognize samples from those images and perf report
and top shows them correctly.

The rest of the ksymbol handling is already in place from
for the bpf programs monitoring, so only the initial state
was needed.

perf report output:

  # Overhead  Command     Shared Object                  Symbol

    12.37%  test_progs  [kernel.vmlinux]                 [k] entry_SYSCALL_64
    11.80%  test_progs  [kernel.vmlinux]                 [k] syscall_return_via_sysret
     9.63%  test_progs  bpf_prog_bcf7977d3b93787c_prog2  [k] bpf_prog_bcf7977d3b93787c_prog2
     6.90%  test_progs  bpf_trampoline_24456             [k] bpf_trampoline_24456
     6.36%  test_progs  [kernel.vmlinux]                 [k] memcpy_erms

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
 tools/perf/util/bpf-event.c | 98 +++++++++++++++++++++++++++++++++++++
 1 file changed, 98 insertions(+)

Comments

Song Liu Feb. 27, 2020, 5:50 a.m. UTC | #1
On Wed, Feb 26, 2020 at 5:06 AM Jiri Olsa <jolsa@kernel.org> wrote:
>
> Synthesize bpf images (trampolines/dispatchers) on start,
> as ksymbol events from /proc/kallsyms. Having this perf
> can recognize samples from those images and perf report
> and top shows them correctly.
>
> The rest of the ksymbol handling is already in place from
> for the bpf programs monitoring, so only the initial state
> was needed.
>
> perf report output:
>
>   # Overhead  Command     Shared Object                  Symbol
>
>     12.37%  test_progs  [kernel.vmlinux]                 [k] entry_SYSCALL_64
>     11.80%  test_progs  [kernel.vmlinux]                 [k] syscall_return_via_sysret
>      9.63%  test_progs  bpf_prog_bcf7977d3b93787c_prog2  [k] bpf_prog_bcf7977d3b93787c_prog2
>      6.90%  test_progs  bpf_trampoline_24456             [k] bpf_trampoline_24456
>      6.36%  test_progs  [kernel.vmlinux]                 [k] memcpy_erms
>
> Signed-off-by: Jiri Olsa <jolsa@kernel.org>

Acked-by: Song Liu <songliubraving@fb.com>
Arnaldo Carvalho de Melo Feb. 28, 2020, 1:14 p.m. UTC | #2
Em Wed, Feb 26, 2020 at 02:03:43PM +0100, Jiri Olsa escreveu:
> Synthesize bpf images (trampolines/dispatchers) on start,
> as ksymbol events from /proc/kallsyms. Having this perf
> can recognize samples from those images and perf report
> and top shows them correctly.
> 
> The rest of the ksymbol handling is already in place from
> for the bpf programs monitoring, so only the initial state
> was needed.

Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com>

But at some point we should try and consolidate all those
kallsym__parse() calls we have in tools/perf/ not to do it that many
times, see below _before_ this patch:

[root@five ~]# perf probe -x ~/bin/perf kallsyms__parse
Added new event:
  probe_perf:kallsyms__parse (on kallsyms__parse in /home/acme/bin/perf)

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

	perf record -e probe_perf:kallsyms__parse -aR sleep 1

[root@five ~]# perf trace -e probe_perf:kallsyms__parse/max-stack=8/ -- perf record sleep 1
     0.000 perf/6444 probe_perf:kallsyms__parse(__probe_ip: 4904384)
                                       kallsyms__parse (/home/acme/bin/perf)
                                       machine__get_running_kernel_start (/home/acme/bin/perf)
                                       machine__create_kernel_maps (/home/acme/bin/perf)
                                       perf_session__new (/home/acme/bin/perf)
                                       cmd_record (/home/acme/bin/perf)
                                       run_builtin (/home/acme/bin/perf)
                                       main (/home/acme/bin/perf)
                                       __libc_start_main (/usr/lib64/libc-2.30.so)
     0.124 perf/6444 probe_perf:kallsyms__parse(__probe_ip: 4904384)
                                       kallsyms__parse (/home/acme/bin/perf)
                                       machine__get_running_kernel_start (/home/acme/bin/perf)
                                       machine__create_kernel_maps (/home/acme/bin/perf)
                                       perf_session__new (/home/acme/bin/perf)
                                       cmd_record (/home/acme/bin/perf)
                                       run_builtin (/home/acme/bin/perf)
                                       main (/home/acme/bin/perf)
                                       __libc_start_main (/usr/lib64/libc-2.30.so)
    15.489 perf/6444 probe_perf:kallsyms__parse(__probe_ip: 4904384)
                                       kallsyms__parse (/home/acme/bin/perf)
                                       machine__create_kernel_maps (/home/acme/bin/perf)
                                       perf_session__new (/home/acme/bin/perf)
                                       cmd_record (/home/acme/bin/perf)
                                       run_builtin (/home/acme/bin/perf)
                                       main (/home/acme/bin/perf)
                                       __libc_start_main (/usr/lib64/libc-2.30.so)
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.019 MB perf.data (7 samples) ]
[root@five ~]#

- Arnaldo

 
> perf report output:
> 
>   # Overhead  Command     Shared Object                  Symbol
> 
>     12.37%  test_progs  [kernel.vmlinux]                 [k] entry_SYSCALL_64
>     11.80%  test_progs  [kernel.vmlinux]                 [k] syscall_return_via_sysret
>      9.63%  test_progs  bpf_prog_bcf7977d3b93787c_prog2  [k] bpf_prog_bcf7977d3b93787c_prog2
>      6.90%  test_progs  bpf_trampoline_24456             [k] bpf_trampoline_24456
>      6.36%  test_progs  [kernel.vmlinux]                 [k] memcpy_erms
> 
> Signed-off-by: Jiri Olsa <jolsa@kernel.org>
> ---
>  tools/perf/util/bpf-event.c | 98 +++++++++++++++++++++++++++++++++++++
>  1 file changed, 98 insertions(+)
> 
> diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c
> index a3207d900339..120ec547ae75 100644
> --- a/tools/perf/util/bpf-event.c
> +++ b/tools/perf/util/bpf-event.c
> @@ -6,6 +6,9 @@
>  #include <bpf/libbpf.h>
>  #include <linux/btf.h>
>  #include <linux/err.h>
> +#include <linux/string.h>
> +#include <internal/lib.h>
> +#include <symbol/kallsyms.h>
>  #include "bpf-event.h"
>  #include "debug.h"
>  #include "dso.h"
> @@ -290,11 +293,87 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
>  	return err ? -1 : 0;
>  }
>  
> +struct kallsyms_parse {
> +	union perf_event	*event;
> +	perf_event__handler_t	 process;
> +	struct machine		*machine;
> +	struct perf_tool	*tool;
> +};
> +
> +static int
> +process_bpf_image(char *name, u64 addr, struct kallsyms_parse *data)
> +{
> +	struct machine *machine = data->machine;
> +	union perf_event *event = data->event;
> +	struct perf_record_ksymbol *ksymbol;
> +	u32 size;
> +
> +	ksymbol = &event->ksymbol;
> +
> +	/*
> +	 * The bpf image (trampoline/dispatcher) size is aligned to
> +	 * page, while it starts little bit after the page boundary.
> +	 */
> +	size = page_size - (addr - PERF_ALIGN(addr, page_size));
> +
> +	*ksymbol = (struct perf_record_ksymbol) {
> +		.header = {
> +			.type = PERF_RECORD_KSYMBOL,
> +			.size = offsetof(struct perf_record_ksymbol, name),
> +		},
> +		.addr      = addr,
> +		.len       = size,
> +		.ksym_type = PERF_RECORD_KSYMBOL_TYPE_BPF,
> +		.flags     = 0,
> +	};
> +
> +	strncpy(ksymbol->name, name, KSYM_NAME_LEN);
> +	ksymbol->header.size += PERF_ALIGN(strlen(name) + 1, sizeof(u64));
> +	memset((void *) event + event->header.size, 0, machine->id_hdr_size);
> +	event->header.size += machine->id_hdr_size;
> +
> +	return perf_tool__process_synth_event(data->tool, event, machine,
> +					      data->process);
> +}
> +
> +static int
> +kallsyms_process_symbol(void *data, const char *_name,
> +			char type __maybe_unused, u64 start)
> +{
> +	char *module, *name;
> +	unsigned long id;
> +	int err = 0;
> +
> +	module = strchr(_name, '\t');
> +	if (!module)
> +		return 0;
> +
> +	/* We are going after [bpf] module ... */
> +	if (strcmp(module + 1, "[bpf]"))
> +		return 0;
> +
> +	name = memdup(_name, (module - _name) + 1);
> +	if (!name)
> +		return -ENOMEM;
> +
> +	name[module - _name] = 0;
> +
> +	/* .. and only for trampolines and dispatchers */
> +	if ((sscanf(name, "bpf_trampoline_%lu", &id) == 1) ||
> +	    (sscanf(name, "bpf_dispatcher_%lu", &id) == 1))
> +		err = process_bpf_image(name, start, data);
> +
> +	free(name);
> +	return err;
> +}
> +
>  int perf_event__synthesize_bpf_events(struct perf_session *session,
>  				      perf_event__handler_t process,
>  				      struct machine *machine,
>  				      struct record_opts *opts)
>  {
> +	const char *kallsyms_filename = "/proc/kallsyms";
> +	struct kallsyms_parse arg;
>  	union perf_event *event;
>  	__u32 id = 0;
>  	int err;
> @@ -303,6 +382,8 @@ int perf_event__synthesize_bpf_events(struct perf_session *session,
>  	event = malloc(sizeof(event->bpf) + KSYM_NAME_LEN + machine->id_hdr_size);
>  	if (!event)
>  		return -1;
> +
> +	/* Synthesize all the bpf programs in system. */
>  	while (true) {
>  		err = bpf_prog_get_next_id(id, &id);
>  		if (err) {
> @@ -335,6 +416,23 @@ int perf_event__synthesize_bpf_events(struct perf_session *session,
>  			break;
>  		}
>  	}
> +
> +	/* Synthesize all the bpf images - trampolines/dispatchers. */
> +	if (symbol_conf.kallsyms_name != NULL)
> +		kallsyms_filename = symbol_conf.kallsyms_name;
> +
> +	arg = (struct kallsyms_parse) {
> +		.event   = event,
> +		.process = process,
> +		.machine = machine,
> +		.tool    = session->tool,
> +	};
> +
> +	if (kallsyms__parse(kallsyms_filename, &arg, kallsyms_process_symbol)) {
> +		pr_err("%s: failed to synthesize bpf images: %s\n",
> +		       __func__, strerror(errno));
> +	}
> +
>  	free(event);
>  	return err;
>  }
> -- 
> 2.24.1
diff mbox series

Patch

diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c
index a3207d900339..120ec547ae75 100644
--- a/tools/perf/util/bpf-event.c
+++ b/tools/perf/util/bpf-event.c
@@ -6,6 +6,9 @@ 
 #include <bpf/libbpf.h>
 #include <linux/btf.h>
 #include <linux/err.h>
+#include <linux/string.h>
+#include <internal/lib.h>
+#include <symbol/kallsyms.h>
 #include "bpf-event.h"
 #include "debug.h"
 #include "dso.h"
@@ -290,11 +293,87 @@  static int perf_event__synthesize_one_bpf_prog(struct perf_session *session,
 	return err ? -1 : 0;
 }
 
+struct kallsyms_parse {
+	union perf_event	*event;
+	perf_event__handler_t	 process;
+	struct machine		*machine;
+	struct perf_tool	*tool;
+};
+
+static int
+process_bpf_image(char *name, u64 addr, struct kallsyms_parse *data)
+{
+	struct machine *machine = data->machine;
+	union perf_event *event = data->event;
+	struct perf_record_ksymbol *ksymbol;
+	u32 size;
+
+	ksymbol = &event->ksymbol;
+
+	/*
+	 * The bpf image (trampoline/dispatcher) size is aligned to
+	 * page, while it starts little bit after the page boundary.
+	 */
+	size = page_size - (addr - PERF_ALIGN(addr, page_size));
+
+	*ksymbol = (struct perf_record_ksymbol) {
+		.header = {
+			.type = PERF_RECORD_KSYMBOL,
+			.size = offsetof(struct perf_record_ksymbol, name),
+		},
+		.addr      = addr,
+		.len       = size,
+		.ksym_type = PERF_RECORD_KSYMBOL_TYPE_BPF,
+		.flags     = 0,
+	};
+
+	strncpy(ksymbol->name, name, KSYM_NAME_LEN);
+	ksymbol->header.size += PERF_ALIGN(strlen(name) + 1, sizeof(u64));
+	memset((void *) event + event->header.size, 0, machine->id_hdr_size);
+	event->header.size += machine->id_hdr_size;
+
+	return perf_tool__process_synth_event(data->tool, event, machine,
+					      data->process);
+}
+
+static int
+kallsyms_process_symbol(void *data, const char *_name,
+			char type __maybe_unused, u64 start)
+{
+	char *module, *name;
+	unsigned long id;
+	int err = 0;
+
+	module = strchr(_name, '\t');
+	if (!module)
+		return 0;
+
+	/* We are going after [bpf] module ... */
+	if (strcmp(module + 1, "[bpf]"))
+		return 0;
+
+	name = memdup(_name, (module - _name) + 1);
+	if (!name)
+		return -ENOMEM;
+
+	name[module - _name] = 0;
+
+	/* .. and only for trampolines and dispatchers */
+	if ((sscanf(name, "bpf_trampoline_%lu", &id) == 1) ||
+	    (sscanf(name, "bpf_dispatcher_%lu", &id) == 1))
+		err = process_bpf_image(name, start, data);
+
+	free(name);
+	return err;
+}
+
 int perf_event__synthesize_bpf_events(struct perf_session *session,
 				      perf_event__handler_t process,
 				      struct machine *machine,
 				      struct record_opts *opts)
 {
+	const char *kallsyms_filename = "/proc/kallsyms";
+	struct kallsyms_parse arg;
 	union perf_event *event;
 	__u32 id = 0;
 	int err;
@@ -303,6 +382,8 @@  int perf_event__synthesize_bpf_events(struct perf_session *session,
 	event = malloc(sizeof(event->bpf) + KSYM_NAME_LEN + machine->id_hdr_size);
 	if (!event)
 		return -1;
+
+	/* Synthesize all the bpf programs in system. */
 	while (true) {
 		err = bpf_prog_get_next_id(id, &id);
 		if (err) {
@@ -335,6 +416,23 @@  int perf_event__synthesize_bpf_events(struct perf_session *session,
 			break;
 		}
 	}
+
+	/* Synthesize all the bpf images - trampolines/dispatchers. */
+	if (symbol_conf.kallsyms_name != NULL)
+		kallsyms_filename = symbol_conf.kallsyms_name;
+
+	arg = (struct kallsyms_parse) {
+		.event   = event,
+		.process = process,
+		.machine = machine,
+		.tool    = session->tool,
+	};
+
+	if (kallsyms__parse(kallsyms_filename, &arg, kallsyms_process_symbol)) {
+		pr_err("%s: failed to synthesize bpf images: %s\n",
+		       __func__, strerror(errno));
+	}
+
 	free(event);
 	return err;
 }