Adding '--tp' option for report command to show
tracepoint related info. Use can specify following
switches:
   - fields: shows separated tracepoint fields
   - format: shows tracepoints 'print fmt' in single column
             (This is default if no switch is given.)

  $ perf report --tp --no-children
  Samples: 4K of event 'sched:sched_switch', Event count (approx.): 4788
   Overhead          Command      Shared Object          Symbol  Print fmt
  +  26.27%          swapper  [kernel.kallsyms]  [k] __schedule  swapper/2:0 
[120] R ==> offlineimap:22134 [120]
  +  26.27%      offlineimap  [kernel.kallsyms]  [k] __schedule  
offlineimap:22134 [120] S ==> swapper/2:0 [120]
  -   8.15%          swapper  [kernel.kallsyms]  [k] __schedule  swapper/3:0 
[120] R ==> offlineimap:22134 [120]
       __schedule
       schedule_preempt_disabled
       cpu_startup_entry
       start_secondary
  +   8.15%      offlineimap  [kernel.kallsyms]  [k] __schedule  
offlineimap:22134 [120] S ==> swapper/3:0 [120]

  $ perf report --tp=fields --no-children
  Samples: 4K of event 'sched:sched_switch', Event count (approx.): 4788
    Overhead          Command      Shared Object          Symbol                
       prev_comm    prev_pid   prev_prio          prev_state                    
   next_comm    next_pid   next_prio
  +   26.27%          swapper  [kernel.kallsyms]  [k] __schedule                
       swapper/2           0         120                   0                    
 offlineimap       22134         120
  +   26.27%      offlineimap  [kernel.kallsyms]  [k] __schedule                
     offlineimap       22134         120                   1                    
   swapper/2           0         120
  -    8.15%          swapper  [kernel.kallsyms]  [k] __schedule                
       swapper/3           0         120                   0                    
 offlineimap       22134         120
       __schedule
       schedule_preempt_disabled
       cpu_startup_entry
       start_secondary
  +    8.15%      offlineimap  [kernel.kallsyms]  [k] __schedule                
     offlineimap       22134         120                   1                    
   swapper/3           0         120

Signed-off-by: Jiri Olsa <[email protected]>
Cc: Corey Ashford <[email protected]>
Cc: Frederic Weisbecker <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: David Ahern <[email protected]>
---
 tools/perf/Documentation/perf-report.txt |   6 +
 tools/perf/Makefile.perf                 |   1 +
 tools/perf/builtin-report.c              |  15 ++
 tools/perf/util/report-tp.c              | 244 +++++++++++++++++++++++++++++++
 tools/perf/util/report-tp.h              |  18 +++
 5 files changed, 284 insertions(+)
 create mode 100644 tools/perf/util/report-tp.c
 create mode 100644 tools/perf/util/report-tp.h

diff --git a/tools/perf/Documentation/perf-report.txt 
b/tools/perf/Documentation/perf-report.txt
index 36eb187..ae1a0cf 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -263,6 +263,12 @@ OPTIONS
 --header-only::
        Show only perf.data header (forces --stdio).
 
+--tp=::
+       Show tracepoint related info, following switches are supported:
+       - fields: shows separated tracepoint fields
+       - format: shows tracepoints 'print fmt' in single column
+                 (This is default if no switch is given.)
+
 SEE ALSO
 --------
 linkperf:perf-stat[1], linkperf:perf-annotate[1]
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 7257e7e..baccf1d 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -373,6 +373,7 @@ LIB_OBJS += $(OUTPUT)util/stat.o
 LIB_OBJS += $(OUTPUT)util/record.o
 LIB_OBJS += $(OUTPUT)util/srcline.o
 LIB_OBJS += $(OUTPUT)util/data.o
+LIB_OBJS += $(OUTPUT)util/report-tp.o
 
 LIB_OBJS += $(OUTPUT)ui/setup.o
 LIB_OBJS += $(OUTPUT)ui/helpline.o
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 0938c4e..a46bdc9 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -35,6 +35,7 @@
 #include "util/hist.h"
 #include "util/data.h"
 #include "arch/common.h"
+#include "util/report-tp.h"
 
 #include <dlfcn.h>
 #include <linux/bitmap.h>
@@ -664,6 +665,7 @@ int cmd_report(int argc, const char **argv, const char 
*prefix __maybe_unused)
                "perf report [<options>]",
                NULL
        };
+       enum report_tp_mode tp_mode;
        struct report report = {
                .tool = {
                        .sample          = process_sample_event,
@@ -779,6 +781,9 @@ int cmd_report(int argc, const char **argv, const char 
*prefix __maybe_unused)
        OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
                     "how to display percentage of filtered entries", 
parse_percentage),
        OPT_BOOLEAN(0, "list", &symbol_conf.show_list, "Show events list"),
+       OPT_CALLBACK_DEFAULT(0, "tp", &tp_mode, "fields,[format]", NULL,
+                            &report_tp_parse_mode, "format"),
+
        OPT_END()
        };
        struct perf_data_file file = {
@@ -924,6 +929,16 @@ repeat:
        if (symbol_conf.show_list)
                sort__setup_list();
 
+       if (tp_mode != REPORT_TO_MODE__NONE) {
+               ret = perf_evlist__add_tp_sort_entries(session->evlist,
+                                                      tp_mode);
+               if (ret) {
+                       pr_err("failed to add tracepoints sort entries\n");
+                       goto error;
+               }
+               report.raw_info = true;
+       }
+
        ret = __cmd_report(&report);
        if (ret == K_SWITCH_INPUT_DATA) {
                perf_session__delete(session);
diff --git a/tools/perf/util/report-tp.c b/tools/perf/util/report-tp.c
new file mode 100644
index 0000000..9608fd2
--- /dev/null
+++ b/tools/perf/util/report-tp.c
@@ -0,0 +1,244 @@
+#include <traceevent/event-parse.h>
+#include "evlist.h"
+#include "evsel.h"
+#include "sort.h"
+#include "report-tp.h"
+
+struct tp_sort_entry {
+       union {
+               struct format_field     *field;
+               struct event_format     *format;
+       };
+       struct sort_entry se;
+};
+
+#define tp_sort_entry(se) \
+       container_of(se, struct tp_sort_entry, se)
+
+int report_tp_parse_mode(const struct option *opt,
+                        const char *str, int unset)
+{
+       int *tp_mode = opt->value;
+
+       if (unset) {
+               *tp_mode = REPORT_TO_MODE__NONE;
+               return 0;
+       }
+
+       if (!str || !strcmp(str, "fields"))
+               *tp_mode = REPORT_TO_MODE__FIELDS;
+       else if (!strcmp(str, "format"))
+               *tp_mode = REPORT_TO_MODE__FORMAT;
+
+       return 0;
+}
+
+static int64_t field_sort_entry__cmp(struct sort_entry *se,
+                                    struct hist_entry *left,
+                                    struct hist_entry *right)
+{
+       struct tp_sort_entry *field_se = tp_sort_entry(se);
+       struct raw_info *raw_left = left->raw_info;
+       struct raw_info *raw_right = right->raw_info;
+
+       return pevent_field_cmp(field_se->field, field_se->field,
+                               raw_left->data, raw_left->size,
+                               raw_right->data, raw_right->size);
+}
+
+static int field_sort_entry__snprintf(struct sort_entry *se,
+                                  bool selected __maybe_unused,
+                                  struct hist_entry *he,
+                                  char *bf, size_t size,
+                                  unsigned int width)
+{
+       struct tp_sort_entry *field_se = tp_sort_entry(se);
+       struct raw_info *raw = he->raw_info;
+       struct trace_seq s;
+       int n;
+
+       trace_seq_init(&s);
+
+       pevent_field_info(&s, field_se->field, raw->data, raw->size, false);
+       n = scnprintf(bf, size, "%*s", width, s.buffer);
+       trace_seq_destroy(&s);
+       return n;
+}
+
+static struct tp_sort_entry*
+field_sort_entry__new(struct format_field *field, int width_idx)
+{
+       struct tp_sort_entry *field_se = zalloc(sizeof(*field_se));
+
+       if (field_se) {
+               INIT_LIST_HEAD(&field_se->se.list);
+               field_se->se.se_header          = strdup(field->name);
+               field_se->se.se_cmp             = field_sort_entry__cmp;
+               field_se->se.se_snprintf        = field_sort_entry__snprintf;
+               field_se->se.se_width_idx       = width_idx;
+               field_se->field                 = field;
+       }
+
+       return field_se;
+}
+
+static int perf_format_field__width(struct format_field *field)
+{
+       int len = field->size * 2 + 2 /* '0x' */;
+
+       if (field->flags & FIELD_IS_ARRAY)
+               len = 30;
+
+       return len;
+}
+
+static int add_tp_field_entries(struct perf_evsel *evsel)
+{
+       struct format_field **fields, **iter;
+       struct hists *hists = &evsel->hists;
+       struct tp_sort_entry *field_se;
+       int width, width_idx = HISTC_NR_COLS;
+       int ret = -1;
+
+       fields = iter = pevent_event_fields(evsel->tp_format);
+       if (!fields)
+               return 0;
+
+       while (*iter) {
+               field_se = field_sort_entry__new(*iter, width_idx);
+               if (!field_se)
+                       goto out;
+
+               if (hists__alloc_col_len(hists, width_idx + 1))
+                       goto out;
+
+               width = perf_format_field__width(*iter);
+               hists__set_col_len(hists, width_idx, width);
+
+               hists__sort_entry_add(hists, &field_se->se);
+
+               width_idx++;
+               iter++;
+       }
+
+       ret = 0;
+
+ out:
+       free(fields);
+       return ret;
+}
+
+static int64_t format_sort_entry__cmp(struct sort_entry *se,
+                                     struct hist_entry *left,
+                                     struct hist_entry *right)
+{
+       struct tp_sort_entry *format_se = tp_sort_entry(se);
+       struct raw_info *raw_left = left->raw_info;
+       struct raw_info *raw_right = right->raw_info;
+       struct format_field **fields, **iter;
+       int ret = 0;
+
+       fields = iter = pevent_event_fields(format_se->format);
+       if (!fields)
+               return 0;
+
+       while (*iter) {
+               struct format_field *field = *iter;
+
+               ret = pevent_field_cmp(field, field,
+                                      raw_left->data, raw_left->size,
+                                      raw_right->data, raw_right->size);
+               if (ret)
+                       break;
+
+               iter++;
+       }
+
+       free(fields);
+       return ret;
+}
+
+static int format_sort_entry__snprintf(struct sort_entry *se,
+                                      bool selected __maybe_unused,
+                                      struct hist_entry *he,
+                                      char *bf, size_t size,
+                                      unsigned int width)
+{
+       struct tp_sort_entry *format_se = tp_sort_entry(se);
+       struct raw_info *raw = he->raw_info;
+       struct pevent_record record;
+       struct trace_seq s;
+       int n;
+
+       trace_seq_init(&s);
+
+       memset(&record, 0, sizeof(record));
+       record.cpu  = he->cpu;
+       record.size = raw->size;
+       record.data = raw->data;
+
+       pevent_event_info(&s, format_se->format, &record);
+       n = scnprintf(bf, size, "%*s", width, s.buffer);
+       trace_seq_destroy(&s);
+       return n;
+}
+
+static int add_tp_format_entry(struct perf_evsel *evsel)
+{
+       struct tp_sort_entry *format_se = zalloc(sizeof(*format_se));
+       struct hists *hists = &evsel->hists;
+       int width_idx = HISTC_NR_COLS;
+
+       if (!format_se)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&format_se->se.list);
+       format_se->se.se_header         = "Print fmt";
+       format_se->se.se_cmp            = format_sort_entry__cmp;
+       format_se->se.se_snprintf       = format_sort_entry__snprintf;
+       format_se->se.se_width_idx      = width_idx;
+       format_se->format               = evsel->tp_format;
+
+       if (hists__alloc_col_len(hists, width_idx + 1)) {
+               free(format_se);
+               return -ENOMEM;
+       }
+
+       hists__set_col_len(hists, width_idx, 9);
+       hists__sort_entry_add(hists, &format_se->se);
+       return 0;
+}
+
+static bool perf_evsel__is_tracepoint(struct perf_evsel *evsel)
+{
+       return evsel->attr.type == PERF_TYPE_TRACEPOINT;
+}
+
+int perf_evlist__add_tp_sort_entries(struct perf_evlist *evlist,
+                                    enum report_tp_mode mode)
+{
+       struct perf_evsel *evsel;
+       int ret = 0;
+
+       list_for_each_entry(evsel, &evlist->entries, node) {
+               if (!perf_evsel__is_tracepoint(evsel))
+                       continue;
+
+               switch (mode) {
+               case REPORT_TO_MODE__FIELDS:
+                       ret = add_tp_field_entries(evsel);
+                       break;
+               case REPORT_TO_MODE__FORMAT:
+                       ret = add_tp_format_entry(evsel);
+                       break;
+               case REPORT_TO_MODE__NONE:
+               default:
+                       BUG_ON(1);
+               }
+
+               if (ret)
+                       break;
+       }
+
+       return ret;
+}
diff --git a/tools/perf/util/report-tp.h b/tools/perf/util/report-tp.h
new file mode 100644
index 0000000..e131982
--- /dev/null
+++ b/tools/perf/util/report-tp.h
@@ -0,0 +1,18 @@
+#ifndef PERF_REPORT_TP_H
+#define PERF_REPORT_TP_H
+
+#include "parse-options.h"
+
+enum report_tp_mode {
+       REPORT_TO_MODE__NONE,
+       REPORT_TO_MODE__FIELDS,
+       REPORT_TO_MODE__FORMAT,
+};
+
+int perf_evlist__add_tp_sort_entries(struct perf_evlist *evlist,
+                                    enum report_tp_mode mode);
+
+int report_tp_parse_mode(const struct option *opt,
+                        const char *str, int unset);
+
+#endif /* PERF_REPORT_TP_H */
-- 
1.8.3.1

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

Reply via email to