|
@@ -32,6 +32,7 @@
|
|
#include "evsel.h"
|
|
#include "evsel.h"
|
|
#include "ui/browsers/hists.h"
|
|
#include "ui/browsers/hists.h"
|
|
#include "thread.h"
|
|
#include "thread.h"
|
|
|
|
+#include "mem2node.h"
|
|
|
|
|
|
struct c2c_hists {
|
|
struct c2c_hists {
|
|
struct hists hists;
|
|
struct hists hists;
|
|
@@ -49,6 +50,7 @@ struct c2c_hist_entry {
|
|
struct c2c_hists *hists;
|
|
struct c2c_hists *hists;
|
|
struct c2c_stats stats;
|
|
struct c2c_stats stats;
|
|
unsigned long *cpuset;
|
|
unsigned long *cpuset;
|
|
|
|
+ unsigned long *nodeset;
|
|
struct c2c_stats *node_stats;
|
|
struct c2c_stats *node_stats;
|
|
unsigned int cacheline_idx;
|
|
unsigned int cacheline_idx;
|
|
|
|
|
|
@@ -59,6 +61,11 @@ struct c2c_hist_entry {
|
|
* because of its callchain dynamic entry
|
|
* because of its callchain dynamic entry
|
|
*/
|
|
*/
|
|
struct hist_entry he;
|
|
struct hist_entry he;
|
|
|
|
+
|
|
|
|
+ unsigned long paddr;
|
|
|
|
+ unsigned long paddr_cnt;
|
|
|
|
+ bool paddr_zero;
|
|
|
|
+ char *nodestr;
|
|
};
|
|
};
|
|
|
|
|
|
static char const *coalesce_default = "pid,iaddr";
|
|
static char const *coalesce_default = "pid,iaddr";
|
|
@@ -66,6 +73,7 @@ static char const *coalesce_default = "pid,iaddr";
|
|
struct perf_c2c {
|
|
struct perf_c2c {
|
|
struct perf_tool tool;
|
|
struct perf_tool tool;
|
|
struct c2c_hists hists;
|
|
struct c2c_hists hists;
|
|
|
|
+ struct mem2node mem2node;
|
|
|
|
|
|
unsigned long **nodes;
|
|
unsigned long **nodes;
|
|
int nodes_cnt;
|
|
int nodes_cnt;
|
|
@@ -123,6 +131,10 @@ static void *c2c_he_zalloc(size_t size)
|
|
if (!c2c_he->cpuset)
|
|
if (!c2c_he->cpuset)
|
|
return NULL;
|
|
return NULL;
|
|
|
|
|
|
|
|
+ c2c_he->nodeset = bitmap_alloc(c2c.nodes_cnt);
|
|
|
|
+ if (!c2c_he->nodeset)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
c2c_he->node_stats = zalloc(c2c.nodes_cnt * sizeof(*c2c_he->node_stats));
|
|
c2c_he->node_stats = zalloc(c2c.nodes_cnt * sizeof(*c2c_he->node_stats));
|
|
if (!c2c_he->node_stats)
|
|
if (!c2c_he->node_stats)
|
|
return NULL;
|
|
return NULL;
|
|
@@ -145,6 +157,8 @@ static void c2c_he_free(void *he)
|
|
}
|
|
}
|
|
|
|
|
|
free(c2c_he->cpuset);
|
|
free(c2c_he->cpuset);
|
|
|
|
+ free(c2c_he->nodeset);
|
|
|
|
+ free(c2c_he->nodestr);
|
|
free(c2c_he->node_stats);
|
|
free(c2c_he->node_stats);
|
|
free(c2c_he);
|
|
free(c2c_he);
|
|
}
|
|
}
|
|
@@ -194,6 +208,28 @@ static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he,
|
|
set_bit(sample->cpu, c2c_he->cpuset);
|
|
set_bit(sample->cpu, c2c_he->cpuset);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void c2c_he__set_node(struct c2c_hist_entry *c2c_he,
|
|
|
|
+ struct perf_sample *sample)
|
|
|
|
+{
|
|
|
|
+ int node;
|
|
|
|
+
|
|
|
|
+ if (!sample->phys_addr) {
|
|
|
|
+ c2c_he->paddr_zero = true;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ node = mem2node__node(&c2c.mem2node, sample->phys_addr);
|
|
|
|
+ if (WARN_ONCE(node < 0, "WARNING: failed to find node\n"))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ set_bit(node, c2c_he->nodeset);
|
|
|
|
+
|
|
|
|
+ if (c2c_he->paddr != sample->phys_addr) {
|
|
|
|
+ c2c_he->paddr_cnt++;
|
|
|
|
+ c2c_he->paddr = sample->phys_addr;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
static void compute_stats(struct c2c_hist_entry *c2c_he,
|
|
static void compute_stats(struct c2c_hist_entry *c2c_he,
|
|
struct c2c_stats *stats,
|
|
struct c2c_stats *stats,
|
|
u64 weight)
|
|
u64 weight)
|
|
@@ -257,6 +293,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
|
c2c_add_stats(&c2c_hists->stats, &stats);
|
|
c2c_add_stats(&c2c_hists->stats, &stats);
|
|
|
|
|
|
c2c_he__set_cpu(c2c_he, sample);
|
|
c2c_he__set_cpu(c2c_he, sample);
|
|
|
|
+ c2c_he__set_node(c2c_he, sample);
|
|
|
|
|
|
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
|
|
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
|
|
ret = hist_entry__append_callchain(he, sample);
|
|
ret = hist_entry__append_callchain(he, sample);
|
|
@@ -293,6 +330,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
|
compute_stats(c2c_he, &stats, sample->weight);
|
|
compute_stats(c2c_he, &stats, sample->weight);
|
|
|
|
|
|
c2c_he__set_cpu(c2c_he, sample);
|
|
c2c_he__set_cpu(c2c_he, sample);
|
|
|
|
+ c2c_he__set_node(c2c_he, sample);
|
|
|
|
|
|
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
|
|
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
|
|
ret = hist_entry__append_callchain(he, sample);
|
|
ret = hist_entry__append_callchain(he, sample);
|
|
@@ -455,6 +493,20 @@ static int dcacheline_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
|
return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
|
|
return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int
|
|
|
|
+dcacheline_node_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
|
|
|
+ struct hist_entry *he)
|
|
|
|
+{
|
|
|
|
+ struct c2c_hist_entry *c2c_he;
|
|
|
|
+ int width = c2c_width(fmt, hpp, he->hists);
|
|
|
|
+
|
|
|
|
+ c2c_he = container_of(he, struct c2c_hist_entry, he);
|
|
|
|
+ if (WARN_ON_ONCE(!c2c_he->nodestr))
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ return scnprintf(hpp->buf, hpp->size, "%*s", width, c2c_he->nodestr);
|
|
|
|
+}
|
|
|
|
+
|
|
static int offset_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
|
static int offset_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
|
struct hist_entry *he)
|
|
struct hist_entry *he)
|
|
{
|
|
{
|
|
@@ -1207,6 +1259,14 @@ static struct c2c_dimension dim_dcacheline = {
|
|
.width = 18,
|
|
.width = 18,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static struct c2c_dimension dim_dcacheline_node = {
|
|
|
|
+ .header = HEADER_LOW("Node"),
|
|
|
|
+ .name = "dcacheline_node",
|
|
|
|
+ .cmp = empty_cmp,
|
|
|
|
+ .entry = dcacheline_node_entry,
|
|
|
|
+ .width = 4,
|
|
|
|
+};
|
|
|
|
+
|
|
static struct c2c_header header_offset_tui = HEADER_LOW("Off");
|
|
static struct c2c_header header_offset_tui = HEADER_LOW("Off");
|
|
|
|
|
|
static struct c2c_dimension dim_offset = {
|
|
static struct c2c_dimension dim_offset = {
|
|
@@ -1217,6 +1277,14 @@ static struct c2c_dimension dim_offset = {
|
|
.width = 18,
|
|
.width = 18,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static struct c2c_dimension dim_offset_node = {
|
|
|
|
+ .header = HEADER_LOW("Node"),
|
|
|
|
+ .name = "offset_node",
|
|
|
|
+ .cmp = empty_cmp,
|
|
|
|
+ .entry = dcacheline_node_entry,
|
|
|
|
+ .width = 4,
|
|
|
|
+};
|
|
|
|
+
|
|
static struct c2c_dimension dim_iaddr = {
|
|
static struct c2c_dimension dim_iaddr = {
|
|
.header = HEADER_LOW("Code address"),
|
|
.header = HEADER_LOW("Code address"),
|
|
.name = "iaddr",
|
|
.name = "iaddr",
|
|
@@ -1536,7 +1604,9 @@ static struct c2c_dimension dim_dcacheline_num_empty = {
|
|
|
|
|
|
static struct c2c_dimension *dimensions[] = {
|
|
static struct c2c_dimension *dimensions[] = {
|
|
&dim_dcacheline,
|
|
&dim_dcacheline,
|
|
|
|
+ &dim_dcacheline_node,
|
|
&dim_offset,
|
|
&dim_offset,
|
|
|
|
+ &dim_offset_node,
|
|
&dim_iaddr,
|
|
&dim_iaddr,
|
|
&dim_tot_hitm,
|
|
&dim_tot_hitm,
|
|
&dim_lcl_hitm,
|
|
&dim_lcl_hitm,
|
|
@@ -1839,12 +1909,44 @@ static inline int valid_hitm_or_store(struct hist_entry *he)
|
|
return has_hitm || c2c_he->stats.store;
|
|
return has_hitm || c2c_he->stats.store;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void set_node_width(struct c2c_hist_entry *c2c_he, int len)
|
|
|
|
+{
|
|
|
|
+ struct c2c_dimension *dim;
|
|
|
|
+
|
|
|
|
+ dim = &c2c.hists == c2c_he->hists ?
|
|
|
|
+ &dim_dcacheline_node : &dim_offset_node;
|
|
|
|
+
|
|
|
|
+ if (len > dim->width)
|
|
|
|
+ dim->width = len;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int set_nodestr(struct c2c_hist_entry *c2c_he)
|
|
|
|
+{
|
|
|
|
+ char buf[30];
|
|
|
|
+ int len;
|
|
|
|
+
|
|
|
|
+ if (c2c_he->nodestr)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ if (bitmap_weight(c2c_he->nodeset, c2c.nodes_cnt)) {
|
|
|
|
+ len = bitmap_scnprintf(c2c_he->nodeset, c2c.nodes_cnt,
|
|
|
|
+ buf, sizeof(buf));
|
|
|
|
+ } else {
|
|
|
|
+ len = scnprintf(buf, sizeof(buf), "N/A");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ set_node_width(c2c_he, len);
|
|
|
|
+ c2c_he->nodestr = strdup(buf);
|
|
|
|
+ return c2c_he->nodestr ? 0 : -ENOMEM;
|
|
|
|
+}
|
|
|
|
+
|
|
static void calc_width(struct c2c_hist_entry *c2c_he)
|
|
static void calc_width(struct c2c_hist_entry *c2c_he)
|
|
{
|
|
{
|
|
struct c2c_hists *c2c_hists;
|
|
struct c2c_hists *c2c_hists;
|
|
|
|
|
|
c2c_hists = container_of(c2c_he->he.hists, struct c2c_hists, hists);
|
|
c2c_hists = container_of(c2c_he->he.hists, struct c2c_hists, hists);
|
|
hists__calc_col_len(&c2c_hists->hists, &c2c_he->he);
|
|
hists__calc_col_len(&c2c_hists->hists, &c2c_he->he);
|
|
|
|
+ set_nodestr(c2c_he);
|
|
}
|
|
}
|
|
|
|
|
|
static int filter_cb(struct hist_entry *he)
|
|
static int filter_cb(struct hist_entry *he)
|
|
@@ -2474,7 +2576,7 @@ static int build_cl_output(char *cl_sort, bool no_source)
|
|
"percent_lcl_hitm,"
|
|
"percent_lcl_hitm,"
|
|
"percent_stores_l1hit,"
|
|
"percent_stores_l1hit,"
|
|
"percent_stores_l1miss,"
|
|
"percent_stores_l1miss,"
|
|
- "offset,",
|
|
|
|
|
|
+ "offset,offset_node,",
|
|
add_pid ? "pid," : "",
|
|
add_pid ? "pid," : "",
|
|
add_tid ? "tid," : "",
|
|
add_tid ? "tid," : "",
|
|
add_iaddr ? "iaddr," : "",
|
|
add_iaddr ? "iaddr," : "",
|
|
@@ -2603,17 +2705,21 @@ static int perf_c2c__report(int argc, const char **argv)
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
- err = setup_callchain(session->evlist);
|
|
|
|
|
|
+ err = mem2node__init(&c2c.mem2node, &session->header.env);
|
|
if (err)
|
|
if (err)
|
|
goto out_session;
|
|
goto out_session;
|
|
|
|
|
|
|
|
+ err = setup_callchain(session->evlist);
|
|
|
|
+ if (err)
|
|
|
|
+ goto out_mem2node;
|
|
|
|
+
|
|
if (symbol__init(&session->header.env) < 0)
|
|
if (symbol__init(&session->header.env) < 0)
|
|
- goto out_session;
|
|
|
|
|
|
+ goto out_mem2node;
|
|
|
|
|
|
/* No pipe support at the moment. */
|
|
/* No pipe support at the moment. */
|
|
if (perf_data__is_pipe(session->data)) {
|
|
if (perf_data__is_pipe(session->data)) {
|
|
pr_debug("No pipe support at the moment.\n");
|
|
pr_debug("No pipe support at the moment.\n");
|
|
- goto out_session;
|
|
|
|
|
|
+ goto out_mem2node;
|
|
}
|
|
}
|
|
|
|
|
|
if (c2c.use_stdio)
|
|
if (c2c.use_stdio)
|
|
@@ -2626,12 +2732,13 @@ static int perf_c2c__report(int argc, const char **argv)
|
|
err = perf_session__process_events(session);
|
|
err = perf_session__process_events(session);
|
|
if (err) {
|
|
if (err) {
|
|
pr_err("failed to process sample\n");
|
|
pr_err("failed to process sample\n");
|
|
- goto out_session;
|
|
|
|
|
|
+ goto out_mem2node;
|
|
}
|
|
}
|
|
|
|
|
|
c2c_hists__reinit(&c2c.hists,
|
|
c2c_hists__reinit(&c2c.hists,
|
|
"cl_idx,"
|
|
"cl_idx,"
|
|
"dcacheline,"
|
|
"dcacheline,"
|
|
|
|
+ "dcacheline_node,"
|
|
"tot_recs,"
|
|
"tot_recs,"
|
|
"percent_hitm,"
|
|
"percent_hitm,"
|
|
"tot_hitm,lcl_hitm,rmt_hitm,"
|
|
"tot_hitm,lcl_hitm,rmt_hitm,"
|
|
@@ -2657,6 +2764,8 @@ static int perf_c2c__report(int argc, const char **argv)
|
|
|
|
|
|
perf_c2c_display(session);
|
|
perf_c2c_display(session);
|
|
|
|
|
|
|
|
+out_mem2node:
|
|
|
|
+ mem2node__exit(&c2c.mem2node);
|
|
out_session:
|
|
out_session:
|
|
perf_session__delete(session);
|
|
perf_session__delete(session);
|
|
out:
|
|
out:
|