diff options
author | Wang Nan <wangnan0@huawei.com> | 2016-02-24 06:20:45 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2016-02-24 10:09:24 -0500 |
commit | 30372f04c9dc159f99f1f09c61e5e0dbe4c91251 (patch) | |
tree | f36de1d6d7d664537fa85cfe47b48fac3e106fa6 /tools/perf/builtin-script.c | |
parent | c339b1a90e6cd638a1d99cbbf49d870ce233198e (diff) |
perf script: Print bpf-output events in 'perf script'
This patch allows 'perf script' output messages from BPF program. For
example, use test_bpf_output_3.c at the end of this commit message,
# ./perf record -e bpf-output/no-inherit,name=evt/ \
-e ./test_bpf_output_3.c/map:channel.event=evt/ \
usleep 100000
# ./perf script
usleep 4882 21384.532523: evt: ffffffff810e97d1 sys_nanosleep ([kernel.kallsyms])
BPF output: 0000: 52 61 69 73 65 20 61 20 Raise a
0008: 42 50 46 20 65 76 65 6e BPF even
0010: 74 21 00 00 t!..
BPF string: "Raise a BPF event!"
usleep 4882 21384.632606: evt: ffffffff8105c609 kretprobe_trampoline_holder ([kernel.kallsyms
BPF output: 0000: 52 61 69 73 65 20 61 20 Raise a
0008: 42 50 46 20 65 76 65 6e BPF even
0010: 74 21 00 00 t!..
BPF string: "Raise a BPF event!"
Two samples from BPF output are printed by both binary and string
format.
If BPF program output something unprintable, string format is
suppressed.
/************************ BEGIN **************************/
#include <uapi/linux/bpf.h>
struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
};
#define SEC(NAME) __attribute__((section(NAME), used))
static u64 (*ktime_get_ns)(void) =
(void *)BPF_FUNC_ktime_get_ns;
static int (*trace_printk)(const char *fmt, int fmt_size, ...) =
(void *)BPF_FUNC_trace_printk;
static int (*get_smp_processor_id)(void) =
(void *)BPF_FUNC_get_smp_processor_id;
static int (*perf_event_output)(void *, struct bpf_map_def *, int, void *, unsigned long) =
(void *)BPF_FUNC_perf_event_output;
struct bpf_map_def SEC("maps") channel = {
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
.key_size = sizeof(int),
.value_size = sizeof(u32),
.max_entries = __NR_CPUS__,
};
static inline int __attribute__((always_inline))
func(void *ctx, int type)
{
char output_str[] = "Raise a BPF event!";
perf_event_output(ctx, &channel, get_smp_processor_id(),
&output_str, sizeof(output_str));
return 0;
}
SEC("func_begin=sys_nanosleep")
int func_begin(void *ctx) {return func(ctx, 1);}
SEC("func_end=sys_nanosleep%return")
int func_end(void *ctx) { return func(ctx, 2);}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
/************************* END ***************************/
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Li Zefan <lizefan@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1456312845-111583-3-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/builtin-script.c')
-rw-r--r-- | tools/perf/builtin-script.c | 93 |
1 files changed, 88 insertions, 5 deletions
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 8ff5ff0fe38c..ec4fbd410a4b 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -61,6 +61,7 @@ enum perf_output_field { | |||
61 | PERF_OUTPUT_BRSTACKSYM = 1U << 16, | 61 | PERF_OUTPUT_BRSTACKSYM = 1U << 16, |
62 | PERF_OUTPUT_DATA_SRC = 1U << 17, | 62 | PERF_OUTPUT_DATA_SRC = 1U << 17, |
63 | PERF_OUTPUT_WEIGHT = 1U << 18, | 63 | PERF_OUTPUT_WEIGHT = 1U << 18, |
64 | PERF_OUTPUT_BPF_OUTPUT = 1U << 19, | ||
64 | }; | 65 | }; |
65 | 66 | ||
66 | struct output_option { | 67 | struct output_option { |
@@ -86,6 +87,7 @@ struct output_option { | |||
86 | {.str = "brstacksym", .field = PERF_OUTPUT_BRSTACKSYM}, | 87 | {.str = "brstacksym", .field = PERF_OUTPUT_BRSTACKSYM}, |
87 | {.str = "data_src", .field = PERF_OUTPUT_DATA_SRC}, | 88 | {.str = "data_src", .field = PERF_OUTPUT_DATA_SRC}, |
88 | {.str = "weight", .field = PERF_OUTPUT_WEIGHT}, | 89 | {.str = "weight", .field = PERF_OUTPUT_WEIGHT}, |
90 | {.str = "bpf-output", .field = PERF_OUTPUT_BPF_OUTPUT}, | ||
89 | }; | 91 | }; |
90 | 92 | ||
91 | /* default set to maintain compatibility with current format */ | 93 | /* default set to maintain compatibility with current format */ |
@@ -106,7 +108,7 @@ static struct { | |||
106 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | | 108 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | |
107 | PERF_OUTPUT_PERIOD, | 109 | PERF_OUTPUT_PERIOD, |
108 | 110 | ||
109 | .invalid_fields = PERF_OUTPUT_TRACE, | 111 | .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, |
110 | }, | 112 | }, |
111 | 113 | ||
112 | [PERF_TYPE_SOFTWARE] = { | 114 | [PERF_TYPE_SOFTWARE] = { |
@@ -116,7 +118,7 @@ static struct { | |||
116 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 118 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
117 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | | 119 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP | |
118 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | | 120 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | |
119 | PERF_OUTPUT_PERIOD, | 121 | PERF_OUTPUT_PERIOD | PERF_OUTPUT_BPF_OUTPUT, |
120 | 122 | ||
121 | .invalid_fields = PERF_OUTPUT_TRACE, | 123 | .invalid_fields = PERF_OUTPUT_TRACE, |
122 | }, | 124 | }, |
@@ -126,7 +128,7 @@ static struct { | |||
126 | 128 | ||
127 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | | 129 | .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID | |
128 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | | 130 | PERF_OUTPUT_CPU | PERF_OUTPUT_TIME | |
129 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE, | 131 | PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE |
130 | }, | 132 | }, |
131 | 133 | ||
132 | [PERF_TYPE_RAW] = { | 134 | [PERF_TYPE_RAW] = { |
@@ -139,7 +141,7 @@ static struct { | |||
139 | PERF_OUTPUT_PERIOD | PERF_OUTPUT_ADDR | | 141 | PERF_OUTPUT_PERIOD | PERF_OUTPUT_ADDR | |
140 | PERF_OUTPUT_DATA_SRC | PERF_OUTPUT_WEIGHT, | 142 | PERF_OUTPUT_DATA_SRC | PERF_OUTPUT_WEIGHT, |
141 | 143 | ||
142 | .invalid_fields = PERF_OUTPUT_TRACE, | 144 | .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, |
143 | }, | 145 | }, |
144 | 146 | ||
145 | [PERF_TYPE_BREAKPOINT] = { | 147 | [PERF_TYPE_BREAKPOINT] = { |
@@ -151,7 +153,7 @@ static struct { | |||
151 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | | 153 | PERF_OUTPUT_SYM | PERF_OUTPUT_DSO | |
152 | PERF_OUTPUT_PERIOD, | 154 | PERF_OUTPUT_PERIOD, |
153 | 155 | ||
154 | .invalid_fields = PERF_OUTPUT_TRACE, | 156 | .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT, |
155 | }, | 157 | }, |
156 | }; | 158 | }; |
157 | 159 | ||
@@ -624,6 +626,84 @@ static void print_sample_flags(u32 flags) | |||
624 | printf(" %-4s ", str); | 626 | printf(" %-4s ", str); |
625 | } | 627 | } |
626 | 628 | ||
629 | struct printer_data { | ||
630 | int line_no; | ||
631 | bool hit_nul; | ||
632 | bool is_printable; | ||
633 | }; | ||
634 | |||
635 | static void | ||
636 | print_sample_bpf_output_printer(enum binary_printer_ops op, | ||
637 | unsigned int val, | ||
638 | void *extra) | ||
639 | { | ||
640 | unsigned char ch = (unsigned char)val; | ||
641 | struct printer_data *printer_data = extra; | ||
642 | |||
643 | switch (op) { | ||
644 | case BINARY_PRINT_DATA_BEGIN: | ||
645 | printf("\n"); | ||
646 | break; | ||
647 | case BINARY_PRINT_LINE_BEGIN: | ||
648 | printf("%17s", !printer_data->line_no ? "BPF output:" : | ||
649 | " "); | ||
650 | break; | ||
651 | case BINARY_PRINT_ADDR: | ||
652 | printf(" %04x:", val); | ||
653 | break; | ||
654 | case BINARY_PRINT_NUM_DATA: | ||
655 | printf(" %02x", val); | ||
656 | break; | ||
657 | case BINARY_PRINT_NUM_PAD: | ||
658 | printf(" "); | ||
659 | break; | ||
660 | case BINARY_PRINT_SEP: | ||
661 | printf(" "); | ||
662 | break; | ||
663 | case BINARY_PRINT_CHAR_DATA: | ||
664 | if (printer_data->hit_nul && ch) | ||
665 | printer_data->is_printable = false; | ||
666 | |||
667 | if (!isprint(ch)) { | ||
668 | printf("%c", '.'); | ||
669 | |||
670 | if (!printer_data->is_printable) | ||
671 | break; | ||
672 | |||
673 | if (ch == '\0') | ||
674 | printer_data->hit_nul = true; | ||
675 | else | ||
676 | printer_data->is_printable = false; | ||
677 | } else { | ||
678 | printf("%c", ch); | ||
679 | } | ||
680 | break; | ||
681 | case BINARY_PRINT_CHAR_PAD: | ||
682 | printf(" "); | ||
683 | break; | ||
684 | case BINARY_PRINT_LINE_END: | ||
685 | printf("\n"); | ||
686 | printer_data->line_no++; | ||
687 | break; | ||
688 | case BINARY_PRINT_DATA_END: | ||
689 | default: | ||
690 | break; | ||
691 | } | ||
692 | } | ||
693 | |||
694 | static void print_sample_bpf_output(struct perf_sample *sample) | ||
695 | { | ||
696 | unsigned int nr_bytes = sample->raw_size; | ||
697 | struct printer_data printer_data = {0, false, true}; | ||
698 | |||
699 | print_binary(sample->raw_data, nr_bytes, 8, | ||
700 | print_sample_bpf_output_printer, &printer_data); | ||
701 | |||
702 | if (printer_data.is_printable && printer_data.hit_nul) | ||
703 | printf("%17s \"%s\"\n", "BPF string:", | ||
704 | (char *)(sample->raw_data)); | ||
705 | } | ||
706 | |||
627 | struct perf_script { | 707 | struct perf_script { |
628 | struct perf_tool tool; | 708 | struct perf_tool tool; |
629 | struct perf_session *session; | 709 | struct perf_session *session; |
@@ -731,6 +811,9 @@ static void process_event(struct perf_script *script, union perf_event *event, | |||
731 | else if (PRINT_FIELD(BRSTACKSYM)) | 811 | else if (PRINT_FIELD(BRSTACKSYM)) |
732 | print_sample_brstacksym(event, sample, thread, attr); | 812 | print_sample_brstacksym(event, sample, thread, attr); |
733 | 813 | ||
814 | if (perf_evsel__is_bpf_output(evsel) && PRINT_FIELD(BPF_OUTPUT)) | ||
815 | print_sample_bpf_output(sample); | ||
816 | |||
734 | printf("\n"); | 817 | printf("\n"); |
735 | } | 818 | } |
736 | 819 | ||