|
@@ -46,6 +46,8 @@ struct c2c_hist_entry {
|
|
struct hist_entry he;
|
|
struct hist_entry he;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static char const *coalesce_default = "pid,tid,iaddr";
|
|
|
|
+
|
|
struct perf_c2c {
|
|
struct perf_c2c {
|
|
struct perf_tool tool;
|
|
struct perf_tool tool;
|
|
struct c2c_hists hists;
|
|
struct c2c_hists hists;
|
|
@@ -65,6 +67,11 @@ struct perf_c2c {
|
|
int shared_clines;
|
|
int shared_clines;
|
|
|
|
|
|
int display;
|
|
int display;
|
|
|
|
+
|
|
|
|
+ const char *coalesce;
|
|
|
|
+ char *cl_sort;
|
|
|
|
+ char *cl_resort;
|
|
|
|
+ char *cl_output;
|
|
};
|
|
};
|
|
|
|
|
|
enum {
|
|
enum {
|
|
@@ -239,7 +246,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
|
if (!mi_dup)
|
|
if (!mi_dup)
|
|
goto free_mi;
|
|
goto free_mi;
|
|
|
|
|
|
- c2c_hists = he__get_c2c_hists(he, "offset", 2);
|
|
|
|
|
|
+ c2c_hists = he__get_c2c_hists(he, c2c.cl_sort, 2);
|
|
if (!c2c_hists)
|
|
if (!c2c_hists)
|
|
goto free_mi_dup;
|
|
goto free_mi_dup;
|
|
|
|
|
|
@@ -1742,22 +1749,7 @@ static int resort_cl_cb(struct hist_entry *he)
|
|
c2c_hists = c2c_he->hists;
|
|
c2c_hists = c2c_he->hists;
|
|
|
|
|
|
if (display && c2c_hists) {
|
|
if (display && c2c_hists) {
|
|
- c2c_hists__reinit(c2c_hists,
|
|
|
|
- "percent_rmt_hitm,"
|
|
|
|
- "percent_lcl_hitm,"
|
|
|
|
- "percent_stores_l1hit,"
|
|
|
|
- "percent_stores_l1miss,"
|
|
|
|
- "offset,"
|
|
|
|
- "pid,"
|
|
|
|
- "tid,"
|
|
|
|
- "mean_rmt,"
|
|
|
|
- "mean_lcl,"
|
|
|
|
- "mean_load,"
|
|
|
|
- "cpucnt,"
|
|
|
|
- "symbol,"
|
|
|
|
- "dso,"
|
|
|
|
- "node",
|
|
|
|
- "offset,rmt_hitm,lcl_hitm");
|
|
|
|
|
|
+ c2c_hists__reinit(c2c_hists, c2c.cl_output, c2c.cl_resort);
|
|
|
|
|
|
hists__collapse_resort(&c2c_hists->hists, NULL);
|
|
hists__collapse_resort(&c2c_hists->hists, NULL);
|
|
hists__output_resort_cb(&c2c_hists->hists, NULL, filter_cb);
|
|
hists__output_resort_cb(&c2c_hists->hists, NULL, filter_cb);
|
|
@@ -2001,6 +1993,7 @@ static void print_c2c_info(FILE *out, struct perf_session *session)
|
|
}
|
|
}
|
|
fprintf(out, " Cachelines sort on : %s HITMs\n",
|
|
fprintf(out, " Cachelines sort on : %s HITMs\n",
|
|
c2c.display == DISPLAY_LCL ? "Local" : "Remote");
|
|
c2c.display == DISPLAY_LCL ? "Local" : "Remote");
|
|
|
|
+ fprintf(out, " Cacheline data grouping : %s\n", c2c.cl_sort);
|
|
}
|
|
}
|
|
|
|
|
|
static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
|
|
static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
|
|
@@ -2280,6 +2273,89 @@ static int setup_display(const char *str)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#define for_each_token(__tok, __buf, __sep, __tmp) \
|
|
|
|
+ for (__tok = strtok_r(__buf, __sep, &__tmp); __tok; \
|
|
|
|
+ __tok = strtok_r(NULL, __sep, &__tmp))
|
|
|
|
+
|
|
|
|
+static int build_cl_output(char *cl_sort)
|
|
|
|
+{
|
|
|
|
+ char *tok, *tmp, *buf = strdup(cl_sort);
|
|
|
|
+ bool add_pid = false;
|
|
|
|
+ bool add_tid = false;
|
|
|
|
+ bool add_iaddr = false;
|
|
|
|
+ bool add_sym = false;
|
|
|
|
+ bool add_dso = false;
|
|
|
|
+ bool add_src = false;
|
|
|
|
+
|
|
|
|
+ if (!buf)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ for_each_token(tok, buf, ",", tmp) {
|
|
|
|
+ if (!strcmp(tok, "tid")) {
|
|
|
|
+ add_tid = true;
|
|
|
|
+ } else if (!strcmp(tok, "pid")) {
|
|
|
|
+ add_pid = true;
|
|
|
|
+ } else if (!strcmp(tok, "iaddr")) {
|
|
|
|
+ add_iaddr = true;
|
|
|
|
+ add_sym = true;
|
|
|
|
+ add_dso = true;
|
|
|
|
+ add_src = true;
|
|
|
|
+ } else if (!strcmp(tok, "dso")) {
|
|
|
|
+ add_dso = true;
|
|
|
|
+ } else if (strcmp(tok, "offset")) {
|
|
|
|
+ pr_err("unrecognized sort token: %s\n", tok);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (asprintf(&c2c.cl_output,
|
|
|
|
+ "%s%s%s%s%s%s%s%s%s",
|
|
|
|
+ "percent_rmt_hitm,"
|
|
|
|
+ "percent_lcl_hitm,"
|
|
|
|
+ "percent_stores_l1hit,"
|
|
|
|
+ "percent_stores_l1miss,"
|
|
|
|
+ "offset,",
|
|
|
|
+ add_pid ? "pid," : "",
|
|
|
|
+ add_tid ? "tid," : "",
|
|
|
|
+ add_iaddr ? "iaddr," : "",
|
|
|
|
+ "mean_rmt,"
|
|
|
|
+ "mean_lcl,"
|
|
|
|
+ "mean_load,"
|
|
|
|
+ "cpucnt,",
|
|
|
|
+ add_sym ? "symbol," : "",
|
|
|
|
+ add_dso ? "dso," : "",
|
|
|
|
+ add_src ? "cl_srcline," : "",
|
|
|
|
+ "node") < 0)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ c2c.show_src = add_src;
|
|
|
|
+
|
|
|
|
+ free(buf);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int setup_coalesce(const char *coalesce)
|
|
|
|
+{
|
|
|
|
+ const char *c = coalesce ?: coalesce_default;
|
|
|
|
+
|
|
|
|
+ if (asprintf(&c2c.cl_sort, "offset,%s", c) < 0)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ if (build_cl_output(c2c.cl_sort))
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ if (asprintf(&c2c.cl_resort, "offset,%s",
|
|
|
|
+ c2c.display == DISPLAY_RMT ?
|
|
|
|
+ "rmt_hitm,lcl_hitm" :
|
|
|
|
+ "lcl_hitm,rmt_hitm") < 0)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ pr_debug("coalesce sort fields: %s\n", c2c.cl_sort);
|
|
|
|
+ pr_debug("coalesce resort fields: %s\n", c2c.cl_resort);
|
|
|
|
+ pr_debug("coalesce output fields: %s\n", c2c.cl_output);
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static int perf_c2c__report(int argc, const char **argv)
|
|
static int perf_c2c__report(int argc, const char **argv)
|
|
{
|
|
{
|
|
struct perf_session *session;
|
|
struct perf_session *session;
|
|
@@ -2289,6 +2365,7 @@ static int perf_c2c__report(int argc, const char **argv)
|
|
};
|
|
};
|
|
char callchain_default_opt[] = CALLCHAIN_DEFAULT_OPT;
|
|
char callchain_default_opt[] = CALLCHAIN_DEFAULT_OPT;
|
|
const char *display = NULL;
|
|
const char *display = NULL;
|
|
|
|
+ const char *coalesce = NULL;
|
|
const struct option c2c_options[] = {
|
|
const struct option c2c_options[] = {
|
|
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
|
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
|
"file", "vmlinux pathname"),
|
|
"file", "vmlinux pathname"),
|
|
@@ -2308,6 +2385,8 @@ static int perf_c2c__report(int argc, const char **argv)
|
|
callchain_help, &parse_callchain_opt,
|
|
callchain_help, &parse_callchain_opt,
|
|
callchain_default_opt),
|
|
callchain_default_opt),
|
|
OPT_STRING('d', "display", &display, NULL, "lcl,rmt"),
|
|
OPT_STRING('d', "display", &display, NULL, "lcl,rmt"),
|
|
|
|
+ OPT_STRING('c', "coalesce", &coalesce, "coalesce fields",
|
|
|
|
+ "coalesce fields: pid,tid,iaddr,dso"),
|
|
OPT_END()
|
|
OPT_END()
|
|
};
|
|
};
|
|
int err = 0;
|
|
int err = 0;
|
|
@@ -2336,6 +2415,12 @@ static int perf_c2c__report(int argc, const char **argv)
|
|
if (err)
|
|
if (err)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
|
|
+ err = setup_coalesce(coalesce);
|
|
|
|
+ if (err) {
|
|
|
|
+ pr_debug("Failed to initialize hists\n");
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
err = c2c_hists__init(&c2c.hists, "dcacheline", 2);
|
|
err = c2c_hists__init(&c2c.hists, "dcacheline", 2);
|
|
if (err) {
|
|
if (err) {
|
|
pr_debug("Failed to initialize hists\n");
|
|
pr_debug("Failed to initialize hists\n");
|