perf c2c report: Add dimension support
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>
This commit is contained in:
parent
903a6f15b9
commit
c75540e316
@ -10,8 +10,14 @@
|
||||
#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");
|
||||
|
Loading…
Reference in New Issue
Block a user