perf c2c report: Add dimension support
authorJiri Olsa <jolsa@kernel.org>
Thu, 22 Sep 2016 15:36:41 +0000 (17:36 +0200)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 19 Oct 2016 16:18:31 +0000 (13:18 -0300)
Adding bare bones of dimension support for c2c report.

Main interface functions are:

  c2c_hists__init
  c2c_hists__reinit

which re/initialize 'struct c2c_hists' object with sort/display entries
string, in a similar way that setup_sorting function does.

We overload the dimension to provide multi line header support for
sort/display entries.

Also we overload base 'struct perf_hpp_fmt' object with 'struct c2c_fmt'
to define c2c specific functions to deal with multi line headers and
spans.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: David Ahern <dsahern@gmail.com>
Cc: Don Zickus <dzickus@redhat.com>
Cc: Joe Mario <jmario@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1474558645-19956-14-git-send-email-jolsa@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/builtin-c2c.c

index 3fac3a294bddb845426ea6c840719c23a896416d..63c0e2d8d2d8a6838c7cade3c688696e3366847b 100644 (file)
 #include "tool.h"
 #include "data.h"
 
+struct c2c_hists {
+       struct hists            hists;
+       struct perf_hpp_list    list;
+};
+
 struct perf_c2c {
-       struct perf_tool tool;
+       struct perf_tool        tool;
+       struct c2c_hists        hists;
 };
 
 static struct perf_c2c c2c;
@@ -28,6 +34,231 @@ static const char * const __usage_report[] = {
 
 static const char * const *report_c2c_usage = __usage_report;
 
+#define C2C_HEADER_MAX 2
+
+struct c2c_header {
+       struct {
+               const char *text;
+               int         span;
+       } line[C2C_HEADER_MAX];
+};
+
+struct c2c_dimension {
+       struct c2c_header        header;
+       const char              *name;
+       int                      width;
+
+       int64_t (*cmp)(struct perf_hpp_fmt *fmt,
+                      struct hist_entry *, struct hist_entry *);
+       int   (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
+                      struct hist_entry *he);
+       int   (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
+                      struct hist_entry *he);
+};
+
+struct c2c_fmt {
+       struct perf_hpp_fmt      fmt;
+       struct c2c_dimension    *dim;
+};
+
+static int c2c_width(struct perf_hpp_fmt *fmt,
+                    struct perf_hpp *hpp __maybe_unused,
+                    struct hists *hists __maybe_unused)
+{
+       struct c2c_fmt *c2c_fmt;
+
+       c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
+       return c2c_fmt->dim->width;
+}
+
+static int c2c_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
+                     struct hists *hists __maybe_unused, int line, int *span)
+{
+       struct c2c_fmt *c2c_fmt;
+       struct c2c_dimension *dim;
+       int len = c2c_width(fmt, hpp, hists);
+       const char *text;
+
+       c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
+       dim = c2c_fmt->dim;
+
+       text = dim->header.line[line].text;
+       if (text == NULL)
+               text = "";
+
+       if (*span) {
+               (*span)--;
+               return 0;
+       } else {
+               *span = dim->header.line[line].span;
+       }
+
+       return scnprintf(hpp->buf, hpp->size, "%*s", len, text);
+}
+
+static struct c2c_dimension *dimensions[] = {
+       NULL,
+};
+
+static void fmt_free(struct perf_hpp_fmt *fmt)
+{
+       struct c2c_fmt *c2c_fmt;
+
+       c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
+       free(c2c_fmt);
+}
+
+static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
+{
+       struct c2c_fmt *c2c_a = container_of(a, struct c2c_fmt, fmt);
+       struct c2c_fmt *c2c_b = container_of(b, struct c2c_fmt, fmt);
+
+       return c2c_a->dim == c2c_b->dim;
+}
+
+static struct c2c_dimension *get_dimension(const char *name)
+{
+       unsigned int i;
+
+       for (i = 0; dimensions[i]; i++) {
+               struct c2c_dimension *dim = dimensions[i];
+
+               if (!strcmp(dim->name, name))
+                       return dim;
+       };
+
+       return NULL;
+}
+
+static struct c2c_fmt *get_format(const char *name)
+{
+       struct c2c_dimension *dim = get_dimension(name);
+       struct c2c_fmt *c2c_fmt;
+       struct perf_hpp_fmt *fmt;
+
+       if (!dim)
+               return NULL;
+
+       c2c_fmt = zalloc(sizeof(*c2c_fmt));
+       if (!c2c_fmt)
+               return NULL;
+
+       c2c_fmt->dim = dim;
+
+       fmt = &c2c_fmt->fmt;
+       INIT_LIST_HEAD(&fmt->list);
+       INIT_LIST_HEAD(&fmt->sort_list);
+
+       fmt->cmp        = dim->cmp;
+       fmt->sort       = dim->cmp;
+       fmt->entry      = dim->entry;
+       fmt->header     = c2c_header;
+       fmt->width      = c2c_width;
+       fmt->collapse   = dim->cmp;
+       fmt->equal      = fmt_equal;
+       fmt->free       = fmt_free;
+
+       return c2c_fmt;
+}
+
+static int c2c_hists__init_output(struct perf_hpp_list *hpp_list, char *name)
+{
+       struct c2c_fmt *c2c_fmt = get_format(name);
+
+       if (!c2c_fmt)
+               return -1;
+
+       perf_hpp_list__column_register(hpp_list, &c2c_fmt->fmt);
+       return 0;
+}
+
+static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name)
+{
+       struct c2c_fmt *c2c_fmt = get_format(name);
+
+       if (!c2c_fmt)
+               return -1;
+
+       perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
+       return 0;
+}
+
+#define PARSE_LIST(_list, _fn)                                                 \
+       do {                                                                    \
+               char *tmp, *tok;                                                \
+               ret = 0;                                                        \
+                                                                               \
+               if (!_list)                                                     \
+                       break;                                                  \
+                                                                               \
+               for (tok = strtok_r((char *)_list, ", ", &tmp);                 \
+                               tok; tok = strtok_r(NULL, ", ", &tmp)) {        \
+                       ret = _fn(hpp_list, tok);                               \
+                       if (ret == -EINVAL) {                                   \
+                               error("Invalid --fields key: `%s'", tok);       \
+                               break;                                          \
+                       } else if (ret == -ESRCH) {                             \
+                               error("Unknown --fields key: `%s'", tok);       \
+                               break;                                          \
+                       }                                                       \
+               }                                                               \
+       } while (0)
+
+static int hpp_list__parse(struct perf_hpp_list *hpp_list,
+                          const char *output_,
+                          const char *sort_)
+{
+       char *output = output_ ? strdup(output_) : NULL;
+       char *sort   = sort_   ? strdup(sort_) : NULL;
+       int ret;
+
+       PARSE_LIST(output, c2c_hists__init_output);
+       PARSE_LIST(sort,   c2c_hists__init_sort);
+
+       /* copy sort keys to output fields */
+       perf_hpp__setup_output_field(hpp_list);
+
+       /*
+        * We dont need other sorting keys other than those
+        * we already specified. It also really slows down
+        * the processing a lot with big number of output
+        * fields, so switching this off for c2c.
+        */
+
+#if 0
+       /* and then copy output fields to sort keys */
+       perf_hpp__append_sort_keys(&hists->list);
+#endif
+
+       free(output);
+       free(sort);
+       return ret;
+}
+
+static int c2c_hists__init(struct c2c_hists *hists,
+                          const char *sort)
+{
+       __hists__init(&hists->hists, &hists->list);
+
+       /*
+        * Initialize only with sort fields, we need to resort
+        * later anyway, and that's where we add output fields
+        * as well.
+        */
+       perf_hpp_list__init(&hists->list);
+
+       return hpp_list__parse(&hists->list, NULL, sort);
+}
+
+__maybe_unused
+static int c2c_hists__reinit(struct c2c_hists *c2c_hists,
+                            const char *output,
+                            const char *sort)
+{
+       perf_hpp__reset_output_field(&c2c_hists->list);
+       return hpp_list__parse(&c2c_hists->list, output, sort);
+}
+
 static int perf_c2c__report(int argc, const char **argv)
 {
        struct perf_session *session;
@@ -52,6 +283,12 @@ static int perf_c2c__report(int argc, const char **argv)
 
        file.path = input_name;
 
+       err = c2c_hists__init(&c2c.hists, "dcacheline");
+       if (err) {
+               pr_debug("Failed to initialize hists\n");
+               goto out;
+       }
+
        session = perf_session__new(&file, 0, &c2c.tool);
        if (session == NULL) {
                pr_debug("No memory for session\n");