#include "evsel.h"
#include "ui/browsers/hists.h"
#include "thread.h"
+#include "mem2node.h"
struct c2c_hists {
struct hists hists;
struct c2c_hists *hists;
struct c2c_stats stats;
unsigned long *cpuset;
+ unsigned long *nodeset;
struct c2c_stats *node_stats;
unsigned int cacheline_idx;
* because of its callchain dynamic entry
*/
struct hist_entry he;
+
+ unsigned long paddr;
+ unsigned long paddr_cnt;
+ bool paddr_zero;
+ char *nodestr;
};
static char const *coalesce_default = "pid,iaddr";
struct perf_c2c {
struct perf_tool tool;
struct c2c_hists hists;
+ struct mem2node mem2node;
unsigned long **nodes;
int nodes_cnt;
if (!c2c_he->cpuset)
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));
if (!c2c_he->node_stats)
return NULL;
}
free(c2c_he->cpuset);
+ free(c2c_he->nodeset);
+ free(c2c_he->nodestr);
free(c2c_he->node_stats);
free(c2c_he);
}
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,
struct c2c_stats *stats,
u64 weight)
c2c_add_stats(&c2c_hists->stats, &stats);
c2c_he__set_cpu(c2c_he, sample);
+ c2c_he__set_node(c2c_he, sample);
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
ret = hist_entry__append_callchain(he, sample);
compute_stats(c2c_he, &stats, sample->weight);
c2c_he__set_cpu(c2c_he, sample);
+ c2c_he__set_node(c2c_he, sample);
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
ret = hist_entry__append_callchain(he, sample);
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,
struct hist_entry *he)
{
.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_dimension dim_offset = {
.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 = {
.header = HEADER_LOW("Code address"),
.name = "iaddr",
static struct c2c_dimension *dimensions[] = {
&dim_dcacheline,
+ &dim_dcacheline_node,
&dim_offset,
+ &dim_offset_node,
&dim_iaddr,
&dim_tot_hitm,
&dim_lcl_hitm,
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)
{
struct c2c_hists *c2c_hists;
c2c_hists = container_of(c2c_he->he.hists, struct c2c_hists, hists);
hists__calc_col_len(&c2c_hists->hists, &c2c_he->he);
+ set_nodestr(c2c_he);
}
static int filter_cb(struct hist_entry *he)
"percent_lcl_hitm,"
"percent_stores_l1hit,"
"percent_stores_l1miss,"
- "offset,",
+ "offset,offset_node,",
add_pid ? "pid," : "",
add_tid ? "tid," : "",
add_iaddr ? "iaddr," : "",
goto out;
}
- err = setup_callchain(session->evlist);
+ err = mem2node__init(&c2c.mem2node, &session->header.env);
if (err)
goto out_session;
+ err = setup_callchain(session->evlist);
+ if (err)
+ goto out_mem2node;
+
if (symbol__init(&session->header.env) < 0)
- goto out_session;
+ goto out_mem2node;
/* No pipe support at the moment. */
if (perf_data__is_pipe(session->data)) {
pr_debug("No pipe support at the moment.\n");
- goto out_session;
+ goto out_mem2node;
}
if (c2c.use_stdio)
err = perf_session__process_events(session);
if (err) {
pr_err("failed to process sample\n");
- goto out_session;
+ goto out_mem2node;
}
c2c_hists__reinit(&c2c.hists,
"cl_idx,"
"dcacheline,"
+ "dcacheline_node,"
"tot_recs,"
"percent_hitm,"
"tot_hitm,lcl_hitm,rmt_hitm,"
perf_c2c_display(session);
+out_mem2node:
+ mem2node__exit(&c2c.mem2node);
out_session:
perf_session__delete(session);
out: