diff options
author | Song Liu <songliubraving@fb.com> | 2019-01-17 11:15:15 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2019-01-21 15:00:57 -0500 |
commit | 6ee52e2a3fe4ea35520720736e6791df1fb67106 (patch) | |
tree | be662847ba4bee4b26c755a19b1f257bf59e0370 /kernel/events | |
parent | d764ac6464915523e68e220b6aa4c3c2eb8e3f94 (diff) |
perf, bpf: Introduce PERF_RECORD_BPF_EVENT
For better performance analysis of BPF programs, this patch introduces
PERF_RECORD_BPF_EVENT, a new perf_event_type that exposes BPF program
load/unload information to user space.
Each BPF program may contain up to BPF_MAX_SUBPROGS (256) sub programs.
The following example shows kernel symbols for a BPF program with 7 sub
programs:
ffffffffa0257cf9 t bpf_prog_b07ccb89267cf242_F
ffffffffa02592e1 t bpf_prog_2dcecc18072623fc_F
ffffffffa025b0e9 t bpf_prog_bb7a405ebaec5d5c_F
ffffffffa025dd2c t bpf_prog_a7540d4a39ec1fc7_F
ffffffffa025fcca t bpf_prog_05762d4ade0e3737_F
ffffffffa026108f t bpf_prog_db4bd11e35df90d4_F
ffffffffa0263f00 t bpf_prog_89d64e4abf0f0126_F
ffffffffa0257cf9 t bpf_prog_ae31629322c4b018__dummy_tracepoi
When a bpf program is loaded, PERF_RECORD_KSYMBOL is generated for each
of these sub programs. Therefore, PERF_RECORD_BPF_EVENT is not needed
for simple profiling.
For annotation, user space need to listen to PERF_RECORD_BPF_EVENT and
gather more information about these (sub) programs via sys_bpf.
Signed-off-by: Song Liu <songliubraving@fb.com>
Reviewed-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradeaed.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: kernel-team@fb.com
Cc: netdev@vger.kernel.org
Link: http://lkml.kernel.org/r/20190117161521.1341602-4-songliubraving@fb.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'kernel/events')
-rw-r--r-- | kernel/events/core.c | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/kernel/events/core.c b/kernel/events/core.c index e04ab5f325cf..236bb8ddb7bc 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
@@ -386,6 +386,7 @@ static atomic_t nr_task_events __read_mostly; | |||
386 | static atomic_t nr_freq_events __read_mostly; | 386 | static atomic_t nr_freq_events __read_mostly; |
387 | static atomic_t nr_switch_events __read_mostly; | 387 | static atomic_t nr_switch_events __read_mostly; |
388 | static atomic_t nr_ksymbol_events __read_mostly; | 388 | static atomic_t nr_ksymbol_events __read_mostly; |
389 | static atomic_t nr_bpf_events __read_mostly; | ||
389 | 390 | ||
390 | static LIST_HEAD(pmus); | 391 | static LIST_HEAD(pmus); |
391 | static DEFINE_MUTEX(pmus_lock); | 392 | static DEFINE_MUTEX(pmus_lock); |
@@ -4308,6 +4309,8 @@ static void unaccount_event(struct perf_event *event) | |||
4308 | dec = true; | 4309 | dec = true; |
4309 | if (event->attr.ksymbol) | 4310 | if (event->attr.ksymbol) |
4310 | atomic_dec(&nr_ksymbol_events); | 4311 | atomic_dec(&nr_ksymbol_events); |
4312 | if (event->attr.bpf_event) | ||
4313 | atomic_dec(&nr_bpf_events); | ||
4311 | 4314 | ||
4312 | if (dec) { | 4315 | if (dec) { |
4313 | if (!atomic_add_unless(&perf_sched_count, -1, 1)) | 4316 | if (!atomic_add_unless(&perf_sched_count, -1, 1)) |
@@ -7747,6 +7750,116 @@ err: | |||
7747 | WARN_ONCE(1, "%s: Invalid KSYMBOL type 0x%x\n", __func__, ksym_type); | 7750 | WARN_ONCE(1, "%s: Invalid KSYMBOL type 0x%x\n", __func__, ksym_type); |
7748 | } | 7751 | } |
7749 | 7752 | ||
7753 | /* | ||
7754 | * bpf program load/unload tracking | ||
7755 | */ | ||
7756 | |||
7757 | struct perf_bpf_event { | ||
7758 | struct bpf_prog *prog; | ||
7759 | struct { | ||
7760 | struct perf_event_header header; | ||
7761 | u16 type; | ||
7762 | u16 flags; | ||
7763 | u32 id; | ||
7764 | u8 tag[BPF_TAG_SIZE]; | ||
7765 | } event_id; | ||
7766 | }; | ||
7767 | |||
7768 | static int perf_event_bpf_match(struct perf_event *event) | ||
7769 | { | ||
7770 | return event->attr.bpf_event; | ||
7771 | } | ||
7772 | |||
7773 | static void perf_event_bpf_output(struct perf_event *event, void *data) | ||
7774 | { | ||
7775 | struct perf_bpf_event *bpf_event = data; | ||
7776 | struct perf_output_handle handle; | ||
7777 | struct perf_sample_data sample; | ||
7778 | int ret; | ||
7779 | |||
7780 | if (!perf_event_bpf_match(event)) | ||
7781 | return; | ||
7782 | |||
7783 | perf_event_header__init_id(&bpf_event->event_id.header, | ||
7784 | &sample, event); | ||
7785 | ret = perf_output_begin(&handle, event, | ||
7786 | bpf_event->event_id.header.size); | ||
7787 | if (ret) | ||
7788 | return; | ||
7789 | |||
7790 | perf_output_put(&handle, bpf_event->event_id); | ||
7791 | perf_event__output_id_sample(event, &handle, &sample); | ||
7792 | |||
7793 | perf_output_end(&handle); | ||
7794 | } | ||
7795 | |||
7796 | static void perf_event_bpf_emit_ksymbols(struct bpf_prog *prog, | ||
7797 | enum perf_bpf_event_type type) | ||
7798 | { | ||
7799 | bool unregister = type == PERF_BPF_EVENT_PROG_UNLOAD; | ||
7800 | char sym[KSYM_NAME_LEN]; | ||
7801 | int i; | ||
7802 | |||
7803 | if (prog->aux->func_cnt == 0) { | ||
7804 | bpf_get_prog_name(prog, sym); | ||
7805 | perf_event_ksymbol(PERF_RECORD_KSYMBOL_TYPE_BPF, | ||
7806 | (u64)(unsigned long)prog->bpf_func, | ||
7807 | prog->jited_len, unregister, sym); | ||
7808 | } else { | ||
7809 | for (i = 0; i < prog->aux->func_cnt; i++) { | ||
7810 | struct bpf_prog *subprog = prog->aux->func[i]; | ||
7811 | |||
7812 | bpf_get_prog_name(subprog, sym); | ||
7813 | perf_event_ksymbol( | ||
7814 | PERF_RECORD_KSYMBOL_TYPE_BPF, | ||
7815 | (u64)(unsigned long)subprog->bpf_func, | ||
7816 | subprog->jited_len, unregister, sym); | ||
7817 | } | ||
7818 | } | ||
7819 | } | ||
7820 | |||
7821 | void perf_event_bpf_event(struct bpf_prog *prog, | ||
7822 | enum perf_bpf_event_type type, | ||
7823 | u16 flags) | ||
7824 | { | ||
7825 | struct perf_bpf_event bpf_event; | ||
7826 | |||
7827 | if (type <= PERF_BPF_EVENT_UNKNOWN || | ||
7828 | type >= PERF_BPF_EVENT_MAX) | ||
7829 | return; | ||
7830 | |||
7831 | switch (type) { | ||
7832 | case PERF_BPF_EVENT_PROG_LOAD: | ||
7833 | case PERF_BPF_EVENT_PROG_UNLOAD: | ||
7834 | if (atomic_read(&nr_ksymbol_events)) | ||
7835 | perf_event_bpf_emit_ksymbols(prog, type); | ||
7836 | break; | ||
7837 | default: | ||
7838 | break; | ||
7839 | } | ||
7840 | |||
7841 | if (!atomic_read(&nr_bpf_events)) | ||
7842 | return; | ||
7843 | |||
7844 | bpf_event = (struct perf_bpf_event){ | ||
7845 | .prog = prog, | ||
7846 | .event_id = { | ||
7847 | .header = { | ||
7848 | .type = PERF_RECORD_BPF_EVENT, | ||
7849 | .size = sizeof(bpf_event.event_id), | ||
7850 | }, | ||
7851 | .type = type, | ||
7852 | .flags = flags, | ||
7853 | .id = prog->aux->id, | ||
7854 | }, | ||
7855 | }; | ||
7856 | |||
7857 | BUILD_BUG_ON(BPF_TAG_SIZE % sizeof(u64)); | ||
7858 | |||
7859 | memcpy(bpf_event.event_id.tag, prog->tag, BPF_TAG_SIZE); | ||
7860 | perf_iterate_sb(perf_event_bpf_output, &bpf_event, NULL); | ||
7861 | } | ||
7862 | |||
7750 | void perf_event_itrace_started(struct perf_event *event) | 7863 | void perf_event_itrace_started(struct perf_event *event) |
7751 | { | 7864 | { |
7752 | event->attach_state |= PERF_ATTACH_ITRACE; | 7865 | event->attach_state |= PERF_ATTACH_ITRACE; |
@@ -10008,6 +10121,8 @@ static void account_event(struct perf_event *event) | |||
10008 | inc = true; | 10121 | inc = true; |
10009 | if (event->attr.ksymbol) | 10122 | if (event->attr.ksymbol) |
10010 | atomic_inc(&nr_ksymbol_events); | 10123 | atomic_inc(&nr_ksymbol_events); |
10124 | if (event->attr.bpf_event) | ||
10125 | atomic_inc(&nr_bpf_events); | ||
10011 | 10126 | ||
10012 | if (inc) { | 10127 | if (inc) { |
10013 | /* | 10128 | /* |