summaryrefslogtreecommitdiff
path: root/tools/perf/builtin-c2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-c2c.c')
-rw-r--r--tools/perf/builtin-c2c.c221
1 files changed, 91 insertions, 130 deletions
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index c9584dbedf77..715b75d42f2a 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -12,11 +12,14 @@
*/
#include <errno.h>
#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
#include <asm/bug.h>
#include <linux/compiler.h>
#include <linux/err.h>
#include <linux/kernel.h>
+#include <linux/string.h>
#include <linux/stringify.h>
#include <linux/zalloc.h>
#include <sys/param.h>
@@ -49,46 +52,10 @@
#include "ui/progress.h"
#include "ui/ui.h"
#include "util/annotate.h"
+#include "util/c2c.h"
#include "util/symbol.h"
#include "util/util.h"
-struct c2c_hists {
- struct hists hists;
- struct perf_hpp_list list;
- struct c2c_stats stats;
-};
-
-struct compute_stats {
- struct stats lcl_hitm;
- struct stats rmt_hitm;
- struct stats lcl_peer;
- struct stats rmt_peer;
- struct stats load;
-};
-
-struct c2c_hist_entry {
- struct c2c_hists *hists;
- struct evsel *evsel;
- struct c2c_stats stats;
- unsigned long *cpuset;
- unsigned long *nodeset;
- struct c2c_stats *node_stats;
- unsigned int cacheline_idx;
-
- struct compute_stats cstats;
-
- unsigned long paddr;
- unsigned long paddr_cnt;
- bool paddr_zero;
- char *nodestr;
-
- /*
- * must be at the end,
- * because of its callchain dynamic entry
- */
- struct hist_entry he;
-};
-
static char const *coalesce_default = "iaddr";
struct perf_c2c {
@@ -226,7 +193,6 @@ he__get_c2c_hists(struct hist_entry *he,
ret = c2c_hists__init(hists, sort, nr_header_lines, env);
if (ret) {
- perf_hpp__reset_output_field(&hists->list);
c2c_he->hists = NULL;
free(hists);
return NULL;
@@ -458,36 +424,6 @@ 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;
- struct sort_entry *se;
-
- 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;
-};
-
-#define SYMBOL_WIDTH 30
-
static struct c2c_dimension dim_symbol;
static struct c2c_dimension dim_srcline;
@@ -1389,23 +1325,6 @@ cl_idx_empty_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
return scnprintf(hpp->buf, hpp->size, "%*s", width, "");
}
-#define HEADER_LOW(__h) \
- { \
- .line[1] = { \
- .text = __h, \
- }, \
- }
-
-#define HEADER_BOTH(__h0, __h1) \
- { \
- .line[0] = { \
- .text = __h0, \
- }, \
- .line[1] = { \
- .text = __h1, \
- }, \
- }
-
#define HEADER_SPAN(__h0, __h1, __s) \
{ \
.line[0] = { \
@@ -1928,22 +1847,6 @@ 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;
@@ -2021,8 +1924,8 @@ static struct c2c_fmt *get_format(const char *name)
fmt->header = c2c_header;
fmt->width = c2c_width;
fmt->collapse = dim->se ? c2c_se_collapse : dim->cmp;
- fmt->equal = fmt_equal;
- fmt->free = fmt_free;
+ fmt->equal = c2c_fmt_equal;
+ fmt->free = c2c_fmt_free;
return c2c_fmt;
}
@@ -2063,26 +1966,38 @@ static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name, stru
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, env); \
- if (ret == -EINVAL) { \
- pr_err("Invalid --fields key: `%s'", tok); \
- break; \
- } else if (ret == -ESRCH) { \
- pr_err("Unknown --fields key: `%s'", tok); \
- break; \
- } \
- } \
- } while (0)
+static int __hpp_list__parse(struct perf_hpp_list *hpp_list, char *_list, struct perf_env *env,
+ int (*_fn)(struct perf_hpp_list *hpp_list, char *name, struct perf_env *env))
+{
+ char *tmp, *tok;
+ int ret = 0;
+
+ if (!_list)
+ return 0;
+
+ for (tok = strtok_r(_list, ", ", &tmp); tok; tok = strtok_r(NULL, ", ", &tmp)) {
+ ret = _fn(hpp_list, tok, env);
+ switch (ret) {
+ case 0:
+ continue;
+ case -EINVAL:
+ pr_err("Invalid --fields key: `%s'", tok);
+ goto out;
+ case -ESRCH:
+ pr_err("Unknown --fields key: `%s'", tok);
+ goto out;
+ default: {
+ char buf[STRERR_BUFSIZE];
+
+ pr_err("%s for --fields key: `%s'",
+ str_error_r(-ret, buf, sizeof(buf)), tok);
+ goto out;
+ }
+ }
+ }
+out:
+ return ret;
+}
static int hpp_list__parse(struct perf_hpp_list *hpp_list,
const char *output_,
@@ -2093,8 +2008,18 @@ static int hpp_list__parse(struct perf_hpp_list *hpp_list,
char *sort = sort_ ? strdup(sort_) : NULL;
int ret;
- PARSE_LIST(output, c2c_hists__init_output);
- PARSE_LIST(sort, c2c_hists__init_sort);
+ /* strdup() returns NULL on OOM, don't silently treat as empty */
+ if ((output_ && !output) || (sort_ && !sort)) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = __hpp_list__parse(hpp_list, output, env, c2c_hists__init_output);
+ if (ret)
+ goto out;
+ ret = __hpp_list__parse(hpp_list, sort, env, c2c_hists__init_sort);
+ if (ret)
+ goto out;
/* copy sort keys to output fields */
perf_hpp__setup_output_field(hpp_list);
@@ -2111,6 +2036,7 @@ static int hpp_list__parse(struct perf_hpp_list *hpp_list,
perf_hpp__append_sort_keys(&hists->list);
#endif
+out:
free(output);
free(sort);
return ret;
@@ -2121,6 +2047,8 @@ static int c2c_hists__init(struct c2c_hists *hists,
int nr_header_lines,
struct perf_env *env)
{
+ int ret;
+
__hists__init(&hists->hists, &hists->list);
/*
@@ -2133,7 +2061,13 @@ static int c2c_hists__init(struct c2c_hists *hists,
/* Overload number of header lines.*/
hists->list.nr_header_lines = nr_header_lines;
- return hpp_list__parse(&hists->list, /*output=*/NULL, sort, env);
+ ret = hpp_list__parse(&hists->list, /*output=*/NULL, sort, env);
+
+ /* Unregister any formats added before the failure point */
+ if (ret)
+ perf_hpp__reset_output_field(&hists->list);
+
+ return ret;
}
static int c2c_hists__reinit(struct c2c_hists *c2c_hists,
@@ -2141,8 +2075,16 @@ static int c2c_hists__reinit(struct c2c_hists *c2c_hists,
const char *sort,
struct perf_env *env)
{
+ int ret;
+
perf_hpp__reset_output_field(&c2c_hists->list);
- return hpp_list__parse(&c2c_hists->list, output, sort, env);
+ ret = hpp_list__parse(&c2c_hists->list, output, sort, env);
+
+ /* Unregister any formats added before the failure point */
+ if (ret)
+ perf_hpp__reset_output_field(&c2c_hists->list);
+
+ return ret;
}
#define DISPLAY_LINE_LIMIT 0.001
@@ -2281,6 +2223,7 @@ static int resort_cl_cb(struct hist_entry *he, void *arg)
struct c2c_hist_entry *c2c_he;
struct c2c_hists *c2c_hists;
bool display = he__display(he, &c2c.shared_clines_stats);
+ int ret;
c2c_he = container_of(he, struct c2c_hist_entry, he);
c2c_hists = c2c_he->hists;
@@ -2291,7 +2234,9 @@ static int resort_cl_cb(struct hist_entry *he, void *arg)
c2c_he->cacheline_idx = idx++;
calc_width(c2c_he);
- c2c_hists__reinit(c2c_hists, c2c.cl_output, c2c.cl_resort, env);
+ ret = c2c_hists__reinit(c2c_hists, c2c.cl_output, c2c.cl_resort, env);
+ if (ret)
+ return ret;
hists__collapse_resort(&c2c_hists->hists, NULL);
hists__output_resort_cb(&c2c_hists->hists, NULL, filter_cb);
@@ -2800,11 +2745,18 @@ perf_c2c_browser__new(struct hists *hists)
static int perf_c2c__hists_browse(struct hists *hists)
{
+ struct c2c_function_view_args func_args = {
+ .cl_hists = &c2c.hists,
+ .cl_sort = c2c.cl_sort,
+ .symbol_full = c2c.symbol_full,
+ .browse_cacheline = perf_c2c__browse_cacheline,
+ };
struct hist_browser *browser;
int key = -1;
static const char help[] =
" d Display cacheline details \n"
" ENTER Toggle callchains (if present) \n"
+ " TAB Switch to function view\n"
" q Quit \n";
browser = perf_c2c_browser__new(hists);
@@ -2826,6 +2778,9 @@ static int perf_c2c__hists_browse(struct hists *hists)
case 'd':
perf_c2c__browse_cacheline(browser->he_selection);
break;
+ case '\t':
+ perf_c2c__browse_function_view(&func_args);
+ break;
case '?':
ui_browser__help_window(&browser->b, help);
break;
@@ -3356,13 +3311,19 @@ static int perf_c2c__report(int argc, const char **argv)
else if (c2c.display == DISPLAY_SNP_PEER)
sort_str = "tot_peer";
- c2c_hists__reinit(&c2c.hists, output_str, sort_str, perf_session__env(session));
+ err = c2c_hists__reinit(&c2c.hists, output_str, sort_str, perf_session__env(session));
+ if (err) {
+ pr_err("Failed to reinitialize hists\n");
+ goto out_mem2node;
+ }
ui_progress__init(&prog, c2c.hists.hists.nr_entries, "Sorting...");
hists__collapse_resort(&c2c.hists.hists, NULL);
hists__output_resort_cb(&c2c.hists.hists, &prog, resort_shared_cl_cb);
- hists__iterate_cb(&c2c.hists.hists, resort_cl_cb, perf_session__env(session));
+ err = hists__iterate_cb(&c2c.hists.hists, resort_cl_cb, perf_session__env(session));
+ if (err)
+ goto out_mem2node;
ui_progress__finish();