perf script: Print bpf-output events in 'perf script'
authorWang Nan <wangnan0@huawei.com>
Wed, 24 Feb 2016 11:20:45 +0000 (11:20 +0000)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 24 Feb 2016 15:09:24 +0000 (12:09 -0300)
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>
tools/perf/builtin-script.c

index 8ff5ff0fe38c104a70f7e59bb1091a105f146c0a..ec4fbd410a4b04ccfced83dc210cfafbcb3f6948 100644 (file)
@@ -61,6 +61,7 @@ enum perf_output_field {
        PERF_OUTPUT_BRSTACKSYM      = 1U << 16,
        PERF_OUTPUT_DATA_SRC        = 1U << 17,
        PERF_OUTPUT_WEIGHT          = 1U << 18,
+       PERF_OUTPUT_BPF_OUTPUT      = 1U << 19,
 };
 
 struct output_option {
@@ -86,6 +87,7 @@ struct output_option {
        {.str = "brstacksym", .field = PERF_OUTPUT_BRSTACKSYM},
        {.str = "data_src", .field = PERF_OUTPUT_DATA_SRC},
        {.str = "weight",   .field = PERF_OUTPUT_WEIGHT},
+       {.str = "bpf-output",   .field = PERF_OUTPUT_BPF_OUTPUT},
 };
 
 /* default set to maintain compatibility with current format */
@@ -106,7 +108,7 @@ static struct {
                              PERF_OUTPUT_SYM | PERF_OUTPUT_DSO |
                              PERF_OUTPUT_PERIOD,
 
-               .invalid_fields = PERF_OUTPUT_TRACE,
+               .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT,
        },
 
        [PERF_TYPE_SOFTWARE] = {
@@ -116,7 +118,7 @@ static struct {
                              PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
                              PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |
                              PERF_OUTPUT_SYM | PERF_OUTPUT_DSO |
-                             PERF_OUTPUT_PERIOD,
+                             PERF_OUTPUT_PERIOD | PERF_OUTPUT_BPF_OUTPUT,
 
                .invalid_fields = PERF_OUTPUT_TRACE,
        },
@@ -126,7 +128,7 @@ static struct {
 
                .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
                                  PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
-                                 PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE,
+                                 PERF_OUTPUT_EVNAME | PERF_OUTPUT_TRACE
        },
 
        [PERF_TYPE_RAW] = {
@@ -139,7 +141,7 @@ static struct {
                              PERF_OUTPUT_PERIOD |  PERF_OUTPUT_ADDR |
                              PERF_OUTPUT_DATA_SRC | PERF_OUTPUT_WEIGHT,
 
-               .invalid_fields = PERF_OUTPUT_TRACE,
+               .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT,
        },
 
        [PERF_TYPE_BREAKPOINT] = {
@@ -151,7 +153,7 @@ static struct {
                              PERF_OUTPUT_SYM | PERF_OUTPUT_DSO |
                              PERF_OUTPUT_PERIOD,
 
-               .invalid_fields = PERF_OUTPUT_TRACE,
+               .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT,
        },
 };
 
@@ -624,6 +626,84 @@ static void print_sample_flags(u32 flags)
        printf("  %-4s ", str);
 }
 
+struct printer_data {
+       int line_no;
+       bool hit_nul;
+       bool is_printable;
+};
+
+static void
+print_sample_bpf_output_printer(enum binary_printer_ops op,
+                               unsigned int val,
+                               void *extra)
+{
+       unsigned char ch = (unsigned char)val;
+       struct printer_data *printer_data = extra;
+
+       switch (op) {
+       case BINARY_PRINT_DATA_BEGIN:
+               printf("\n");
+               break;
+       case BINARY_PRINT_LINE_BEGIN:
+               printf("%17s", !printer_data->line_no ? "BPF output:" :
+                                                       "           ");
+               break;
+       case BINARY_PRINT_ADDR:
+               printf(" %04x:", val);
+               break;
+       case BINARY_PRINT_NUM_DATA:
+               printf(" %02x", val);
+               break;
+       case BINARY_PRINT_NUM_PAD:
+               printf("   ");
+               break;
+       case BINARY_PRINT_SEP:
+               printf("  ");
+               break;
+       case BINARY_PRINT_CHAR_DATA:
+               if (printer_data->hit_nul && ch)
+                       printer_data->is_printable = false;
+
+               if (!isprint(ch)) {
+                       printf("%c", '.');
+
+                       if (!printer_data->is_printable)
+                               break;
+
+                       if (ch == '\0')
+                               printer_data->hit_nul = true;
+                       else
+                               printer_data->is_printable = false;
+               } else {
+                       printf("%c", ch);
+               }
+               break;
+       case BINARY_PRINT_CHAR_PAD:
+               printf(" ");
+               break;
+       case BINARY_PRINT_LINE_END:
+               printf("\n");
+               printer_data->line_no++;
+               break;
+       case BINARY_PRINT_DATA_END:
+       default:
+               break;
+       }
+}
+
+static void print_sample_bpf_output(struct perf_sample *sample)
+{
+       unsigned int nr_bytes = sample->raw_size;
+       struct printer_data printer_data = {0, false, true};
+
+       print_binary(sample->raw_data, nr_bytes, 8,
+                    print_sample_bpf_output_printer, &printer_data);
+
+       if (printer_data.is_printable && printer_data.hit_nul)
+               printf("%17s \"%s\"\n", "BPF string:",
+                      (char *)(sample->raw_data));
+}
+
 struct perf_script {
        struct perf_tool        tool;
        struct perf_session     *session;
@@ -731,6 +811,9 @@ static void process_event(struct perf_script *script, union perf_event *event,
        else if (PRINT_FIELD(BRSTACKSYM))
                print_sample_brstacksym(event, sample, thread, attr);
 
+       if (perf_evsel__is_bpf_output(evsel) && PRINT_FIELD(BPF_OUTPUT))
+               print_sample_bpf_output(sample);
+
        printf("\n");
 }