aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorStephane Eranian <eranian@google.com>2013-01-24 10:10:35 -0500
committerArnaldo Carvalho de Melo <acme@redhat.com>2013-04-01 11:20:13 -0400
commit98a3b32c99ada4bca8aaf4f91efd96fc906dd5c4 (patch)
treef10d745caaecb65307a4aa71205a0c5836b186db /tools
parent05484298cbfebbf8c8c55b000541a245bc286bec (diff)
perf tools: Add mem access sampling core support
This patch adds the sorting and histogram support functions to enable profiling of memory accesses. The following sorting orders are added: - symbol_daddr: data address symbol (or raw address) - dso_daddr: data address shared object - locked: access uses locked transaction - tlb : TLB access - mem : memory level of the access (L1, L2, L3, RAM, ...) - snoop: access snoop mode Signed-off-by: Stephane Eranian <eranian@google.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Namhyung Kim <namhyung.kim@lge.com> Cc: Peter Zijlstra <peterz@infradead.org> Link: http://lkml.kernel.org/r/1359040242-8269-12-git-send-email-eranian@google.com [ committer note: changed to cope with fc5871ed, the move of methods to machine.[ch], and the rename of dsrc to data_src, to match the change made in the PERF_SAMPLE_DSRC in a previous patch. ] Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/util/event.h8
-rw-r--r--tools/perf/util/evsel.c6
-rw-r--r--tools/perf/util/hist.c86
-rw-r--r--tools/perf/util/hist.h13
-rw-r--r--tools/perf/util/machine.c32
-rw-r--r--tools/perf/util/machine.h3
-rw-r--r--tools/perf/util/session.c3
-rw-r--r--tools/perf/util/sort.c369
-rw-r--r--tools/perf/util/sort.h9
-rw-r--r--tools/perf/util/symbol.h6
10 files changed, 525 insertions, 10 deletions
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index a97fbbe6b3b3..181389535c0c 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -91,6 +91,7 @@ struct perf_sample {
91 u64 weight; 91 u64 weight;
92 u32 cpu; 92 u32 cpu;
93 u32 raw_size; 93 u32 raw_size;
94 u64 data_src;
94 void *raw_data; 95 void *raw_data;
95 struct ip_callchain *callchain; 96 struct ip_callchain *callchain;
96 struct branch_stack *branch_stack; 97 struct branch_stack *branch_stack;
@@ -98,6 +99,13 @@ struct perf_sample {
98 struct stack_dump user_stack; 99 struct stack_dump user_stack;
99}; 100};
100 101
102#define PERF_MEM_DATA_SRC_NONE \
103 (PERF_MEM_S(OP, NA) |\
104 PERF_MEM_S(LVL, NA) |\
105 PERF_MEM_S(SNOOP, NA) |\
106 PERF_MEM_S(LOCK, NA) |\
107 PERF_MEM_S(TLB, NA))
108
101struct build_id_event { 109struct build_id_event {
102 struct perf_event_header header; 110 struct perf_event_header header;
103 pid_t pid; 111 pid_t pid;
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 23061a6ccd77..5c4ca51c8f7b 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -1177,6 +1177,12 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
1177 array++; 1177 array++;
1178 } 1178 }
1179 1179
1180 data->data_src = PERF_MEM_DATA_SRC_NONE;
1181 if (type & PERF_SAMPLE_DATA_SRC) {
1182 data->data_src = *array;
1183 array++;
1184 }
1185
1180 return 0; 1186 return 0;
1181} 1187}
1182 1188
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 97ddd18acd7c..99cc719ce736 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -67,12 +67,16 @@ static void hists__set_unres_dso_col_len(struct hists *hists, int dso)
67void hists__calc_col_len(struct hists *hists, struct hist_entry *h) 67void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
68{ 68{
69 const unsigned int unresolved_col_width = BITS_PER_LONG / 4; 69 const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
70 int symlen;
70 u16 len; 71 u16 len;
71 72
72 if (h->ms.sym) 73 if (h->ms.sym)
73 hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4); 74 hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4);
74 else 75 else {
76 symlen = unresolved_col_width + 4 + 2;
77 hists__new_col_len(hists, HISTC_SYMBOL, symlen);
75 hists__set_unres_dso_col_len(hists, HISTC_DSO); 78 hists__set_unres_dso_col_len(hists, HISTC_DSO);
79 }
76 80
77 len = thread__comm_len(h->thread); 81 len = thread__comm_len(h->thread);
78 if (hists__new_col_len(hists, HISTC_COMM, len)) 82 if (hists__new_col_len(hists, HISTC_COMM, len))
@@ -87,7 +91,6 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
87 hists__new_col_len(hists, HISTC_PARENT, h->parent->namelen); 91 hists__new_col_len(hists, HISTC_PARENT, h->parent->namelen);
88 92
89 if (h->branch_info) { 93 if (h->branch_info) {
90 int symlen;
91 /* 94 /*
92 * +4 accounts for '[x] ' priv level info 95 * +4 accounts for '[x] ' priv level info
93 * +2 account of 0x prefix on raw addresses 96 * +2 account of 0x prefix on raw addresses
@@ -116,6 +119,42 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
116 hists__set_unres_dso_col_len(hists, HISTC_DSO_TO); 119 hists__set_unres_dso_col_len(hists, HISTC_DSO_TO);
117 } 120 }
118 } 121 }
122
123 if (h->mem_info) {
124 /*
125 * +4 accounts for '[x] ' priv level info
126 * +2 account of 0x prefix on raw addresses
127 */
128 if (h->mem_info->daddr.sym) {
129 symlen = (int)h->mem_info->daddr.sym->namelen + 4
130 + unresolved_col_width + 2;
131 hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,
132 symlen);
133 } else {
134 symlen = unresolved_col_width + 4 + 2;
135 hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,
136 symlen);
137 }
138 if (h->mem_info->daddr.map) {
139 symlen = dso__name_len(h->mem_info->daddr.map->dso);
140 hists__new_col_len(hists, HISTC_MEM_DADDR_DSO,
141 symlen);
142 } else {
143 symlen = unresolved_col_width + 4 + 2;
144 hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO);
145 }
146 } else {
147 symlen = unresolved_col_width + 4 + 2;
148 hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, symlen);
149 hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO);
150 }
151
152 hists__new_col_len(hists, HISTC_MEM_LOCKED, 6);
153 hists__new_col_len(hists, HISTC_MEM_TLB, 22);
154 hists__new_col_len(hists, HISTC_MEM_SNOOP, 12);
155 hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3);
156 hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12);
157 hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12);
119} 158}
120 159
121void hists__output_recalc_col_len(struct hists *hists, int max_rows) 160void hists__output_recalc_col_len(struct hists *hists, int max_rows)
@@ -158,6 +197,7 @@ static void hist_entry__add_cpumode_period(struct hist_entry *he,
158static void he_stat__add_period(struct he_stat *he_stat, u64 period, 197static void he_stat__add_period(struct he_stat *he_stat, u64 period,
159 u64 weight) 198 u64 weight)
160{ 199{
200
161 he_stat->period += period; 201 he_stat->period += period;
162 he_stat->weight += weight; 202 he_stat->weight += weight;
163 he_stat->nr_events += 1; 203 he_stat->nr_events += 1;
@@ -243,7 +283,7 @@ void hists__decay_entries_threaded(struct hists *hists,
243static struct hist_entry *hist_entry__new(struct hist_entry *template) 283static struct hist_entry *hist_entry__new(struct hist_entry *template)
244{ 284{
245 size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0; 285 size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0;
246 struct hist_entry *he = malloc(sizeof(*he) + callchain_size); 286 struct hist_entry *he = zalloc(sizeof(*he) + callchain_size);
247 287
248 if (he != NULL) { 288 if (he != NULL) {
249 *he = *template; 289 *he = *template;
@@ -258,6 +298,13 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
258 he->branch_info->to.map->referenced = true; 298 he->branch_info->to.map->referenced = true;
259 } 299 }
260 300
301 if (he->mem_info) {
302 if (he->mem_info->iaddr.map)
303 he->mem_info->iaddr.map->referenced = true;
304 if (he->mem_info->daddr.map)
305 he->mem_info->daddr.map->referenced = true;
306 }
307
261 if (symbol_conf.use_callchain) 308 if (symbol_conf.use_callchain)
262 callchain_init(he->callchain); 309 callchain_init(he->callchain);
263 310
@@ -346,6 +393,36 @@ out_unlock:
346 return he; 393 return he;
347} 394}
348 395
396struct hist_entry *__hists__add_mem_entry(struct hists *self,
397 struct addr_location *al,
398 struct symbol *sym_parent,
399 struct mem_info *mi,
400 u64 period,
401 u64 weight)
402{
403 struct hist_entry entry = {
404 .thread = al->thread,
405 .ms = {
406 .map = al->map,
407 .sym = al->sym,
408 },
409 .stat = {
410 .period = period,
411 .weight = weight,
412 .nr_events = 1,
413 },
414 .cpu = al->cpu,
415 .ip = al->addr,
416 .level = al->level,
417 .parent = sym_parent,
418 .filtered = symbol__parent_filter(sym_parent),
419 .hists = self,
420 .mem_info = mi,
421 .branch_info = NULL,
422 };
423 return add_hist_entry(self, &entry, al, period, weight);
424}
425
349struct hist_entry *__hists__add_branch_entry(struct hists *self, 426struct hist_entry *__hists__add_branch_entry(struct hists *self,
350 struct addr_location *al, 427 struct addr_location *al,
351 struct symbol *sym_parent, 428 struct symbol *sym_parent,
@@ -371,6 +448,7 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self,
371 .filtered = symbol__parent_filter(sym_parent), 448 .filtered = symbol__parent_filter(sym_parent),
372 .branch_info = bi, 449 .branch_info = bi,
373 .hists = self, 450 .hists = self,
451 .mem_info = NULL,
374 }; 452 };
375 453
376 return add_hist_entry(self, &entry, al, period, weight); 454 return add_hist_entry(self, &entry, al, period, weight);
@@ -398,6 +476,8 @@ struct hist_entry *__hists__add_entry(struct hists *self,
398 .parent = sym_parent, 476 .parent = sym_parent,
399 .filtered = symbol__parent_filter(sym_parent), 477 .filtered = symbol__parent_filter(sym_parent),
400 .hists = self, 478 .hists = self,
479 .branch_info = NULL,
480 .mem_info = NULL,
401 }; 481 };
402 482
403 return add_hist_entry(self, &entry, al, period, weight); 483 return add_hist_entry(self, &entry, al, period, weight);
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 121cc14b6041..fd6313416476 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -51,6 +51,12 @@ enum hist_column {
51 HISTC_SRCLINE, 51 HISTC_SRCLINE,
52 HISTC_LOCAL_WEIGHT, 52 HISTC_LOCAL_WEIGHT,
53 HISTC_GLOBAL_WEIGHT, 53 HISTC_GLOBAL_WEIGHT,
54 HISTC_MEM_DADDR_SYMBOL,
55 HISTC_MEM_DADDR_DSO,
56 HISTC_MEM_LOCKED,
57 HISTC_MEM_TLB,
58 HISTC_MEM_LVL,
59 HISTC_MEM_SNOOP,
54 HISTC_NR_COLS, /* Last entry */ 60 HISTC_NR_COLS, /* Last entry */
55}; 61};
56 62
@@ -90,6 +96,13 @@ struct hist_entry *__hists__add_branch_entry(struct hists *self,
90 u64 period, 96 u64 period,
91 u64 weight); 97 u64 weight);
92 98
99struct hist_entry *__hists__add_mem_entry(struct hists *self,
100 struct addr_location *al,
101 struct symbol *sym_parent,
102 struct mem_info *mi,
103 u64 period,
104 u64 weight);
105
93void hists__output_resort(struct hists *self); 106void hists__output_resort(struct hists *self);
94void hists__output_resort_threaded(struct hists *hists); 107void hists__output_resort_threaded(struct hists *hists);
95void hists__collapse_resort(struct hists *self); 108void hists__collapse_resort(struct hists *self);
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index c5e3b123782b..d77ba869d7ed 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -1097,6 +1097,38 @@ found:
1097 ams->map = al.map; 1097 ams->map = al.map;
1098} 1098}
1099 1099
1100static void ip__resolve_data(struct machine *machine, struct thread *thread,
1101 u8 m, struct addr_map_symbol *ams, u64 addr)
1102{
1103 struct addr_location al;
1104
1105 memset(&al, 0, sizeof(al));
1106
1107 thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, &al,
1108 NULL);
1109 ams->addr = addr;
1110 ams->al_addr = al.addr;
1111 ams->sym = al.sym;
1112 ams->map = al.map;
1113}
1114
1115struct mem_info *machine__resolve_mem(struct machine *machine,
1116 struct thread *thr,
1117 struct perf_sample *sample,
1118 u8 cpumode)
1119{
1120 struct mem_info *mi = zalloc(sizeof(*mi));
1121
1122 if (!mi)
1123 return NULL;
1124
1125 ip__resolve_ams(machine, thr, &mi->iaddr, sample->ip);
1126 ip__resolve_data(machine, thr, cpumode, &mi->daddr, sample->addr);
1127 mi->data_src.val = sample->data_src;
1128
1129 return mi;
1130}
1131
1100struct branch_info *machine__resolve_bstack(struct machine *machine, 1132struct branch_info *machine__resolve_bstack(struct machine *machine,
1101 struct thread *thr, 1133 struct thread *thr,
1102 struct branch_stack *bs) 1134 struct branch_stack *bs)
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index e0b2c00b2e75..77940680f1fc 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -76,6 +76,9 @@ void machine__delete(struct machine *machine);
76struct branch_info *machine__resolve_bstack(struct machine *machine, 76struct branch_info *machine__resolve_bstack(struct machine *machine,
77 struct thread *thread, 77 struct thread *thread,
78 struct branch_stack *bs); 78 struct branch_stack *bs);
79struct mem_info *machine__resolve_mem(struct machine *machine,
80 struct thread *thread,
81 struct perf_sample *sample, u8 cpumode);
79int machine__resolve_callchain(struct machine *machine, 82int machine__resolve_callchain(struct machine *machine,
80 struct perf_evsel *evsel, 83 struct perf_evsel *evsel,
81 struct thread *thread, 84 struct thread *thread,
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 627be09b479e..cf1fe01b7e89 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -801,6 +801,9 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
801 801
802 if (sample_type & PERF_SAMPLE_WEIGHT) 802 if (sample_type & PERF_SAMPLE_WEIGHT)
803 printf("... weight: %" PRIu64 "\n", sample->weight); 803 printf("... weight: %" PRIu64 "\n", sample->weight);
804
805 if (sample_type & PERF_SAMPLE_DATA_SRC)
806 printf(" . data_src: 0x%"PRIx64"\n", sample->data_src);
804} 807}
805 808
806static struct machine * 809static struct machine *
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index d66bcd33248c..32a1ef15912c 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -198,11 +198,19 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
198 } 198 }
199 199
200 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); 200 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
201 if (sym) 201 if (sym && map) {
202 ret += repsep_snprintf(bf + ret, size - ret, "%-*s", 202 if (map->type == MAP__VARIABLE) {
203 width - ret, 203 ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
204 sym->name); 204 ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
205 else { 205 ip - sym->start);
206 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
207 width - ret, "");
208 } else {
209 ret += repsep_snprintf(bf + ret, size - ret, "%-*s",
210 width - ret,
211 sym->name);
212 }
213 } else {
206 size_t len = BITS_PER_LONG / 4; 214 size_t len = BITS_PER_LONG / 4;
207 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", 215 ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
208 len, ip); 216 len, ip);
@@ -457,6 +465,304 @@ static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf,
457 return repsep_snprintf(bf, size, "%-*s", width, out); 465 return repsep_snprintf(bf, size, "%-*s", width, out);
458} 466}
459 467
468/* --sort daddr_sym */
469static int64_t
470sort__daddr_cmp(struct hist_entry *left, struct hist_entry *right)
471{
472 uint64_t l = 0, r = 0;
473
474 if (left->mem_info)
475 l = left->mem_info->daddr.addr;
476 if (right->mem_info)
477 r = right->mem_info->daddr.addr;
478
479 return (int64_t)(r - l);
480}
481
482static int hist_entry__daddr_snprintf(struct hist_entry *self, char *bf,
483 size_t size, unsigned int width)
484{
485 uint64_t addr = 0;
486 struct map *map = NULL;
487 struct symbol *sym = NULL;
488
489 if (self->mem_info) {
490 addr = self->mem_info->daddr.addr;
491 map = self->mem_info->daddr.map;
492 sym = self->mem_info->daddr.sym;
493 }
494 return _hist_entry__sym_snprintf(map, sym, addr, self->level, bf, size,
495 width);
496}
497
498static int64_t
499sort__dso_daddr_cmp(struct hist_entry *left, struct hist_entry *right)
500{
501 struct map *map_l = NULL;
502 struct map *map_r = NULL;
503
504 if (left->mem_info)
505 map_l = left->mem_info->daddr.map;
506 if (right->mem_info)
507 map_r = right->mem_info->daddr.map;
508
509 return _sort__dso_cmp(map_l, map_r);
510}
511
512static int hist_entry__dso_daddr_snprintf(struct hist_entry *self, char *bf,
513 size_t size, unsigned int width)
514{
515 struct map *map = NULL;
516
517 if (self->mem_info)
518 map = self->mem_info->daddr.map;
519
520 return _hist_entry__dso_snprintf(map, bf, size, width);
521}
522
523static int64_t
524sort__locked_cmp(struct hist_entry *left, struct hist_entry *right)
525{
526 union perf_mem_data_src data_src_l;
527 union perf_mem_data_src data_src_r;
528
529 if (left->mem_info)
530 data_src_l = left->mem_info->data_src;
531 else
532 data_src_l.mem_lock = PERF_MEM_LOCK_NA;
533
534 if (right->mem_info)
535 data_src_r = right->mem_info->data_src;
536 else
537 data_src_r.mem_lock = PERF_MEM_LOCK_NA;
538
539 return (int64_t)(data_src_r.mem_lock - data_src_l.mem_lock);
540}
541
542static int hist_entry__locked_snprintf(struct hist_entry *self, char *bf,
543 size_t size, unsigned int width)
544{
545 const char *out;
546 u64 mask = PERF_MEM_LOCK_NA;
547
548 if (self->mem_info)
549 mask = self->mem_info->data_src.mem_lock;
550
551 if (mask & PERF_MEM_LOCK_NA)
552 out = "N/A";
553 else if (mask & PERF_MEM_LOCK_LOCKED)
554 out = "Yes";
555 else
556 out = "No";
557
558 return repsep_snprintf(bf, size, "%-*s", width, out);
559}
560
561static int64_t
562sort__tlb_cmp(struct hist_entry *left, struct hist_entry *right)
563{
564 union perf_mem_data_src data_src_l;
565 union perf_mem_data_src data_src_r;
566
567 if (left->mem_info)
568 data_src_l = left->mem_info->data_src;
569 else
570 data_src_l.mem_dtlb = PERF_MEM_TLB_NA;
571
572 if (right->mem_info)
573 data_src_r = right->mem_info->data_src;
574 else
575 data_src_r.mem_dtlb = PERF_MEM_TLB_NA;
576
577 return (int64_t)(data_src_r.mem_dtlb - data_src_l.mem_dtlb);
578}
579
580static const char * const tlb_access[] = {
581 "N/A",
582 "HIT",
583 "MISS",
584 "L1",
585 "L2",
586 "Walker",
587 "Fault",
588};
589#define NUM_TLB_ACCESS (sizeof(tlb_access)/sizeof(const char *))
590
591static int hist_entry__tlb_snprintf(struct hist_entry *self, char *bf,
592 size_t size, unsigned int width)
593{
594 char out[64];
595 size_t sz = sizeof(out) - 1; /* -1 for null termination */
596 size_t l = 0, i;
597 u64 m = PERF_MEM_TLB_NA;
598 u64 hit, miss;
599
600 out[0] = '\0';
601
602 if (self->mem_info)
603 m = self->mem_info->data_src.mem_dtlb;
604
605 hit = m & PERF_MEM_TLB_HIT;
606 miss = m & PERF_MEM_TLB_MISS;
607
608 /* already taken care of */
609 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
610
611 for (i = 0; m && i < NUM_TLB_ACCESS; i++, m >>= 1) {
612 if (!(m & 0x1))
613 continue;
614 if (l) {
615 strcat(out, " or ");
616 l += 4;
617 }
618 strncat(out, tlb_access[i], sz - l);
619 l += strlen(tlb_access[i]);
620 }
621 if (*out == '\0')
622 strcpy(out, "N/A");
623 if (hit)
624 strncat(out, " hit", sz - l);
625 if (miss)
626 strncat(out, " miss", sz - l);
627
628 return repsep_snprintf(bf, size, "%-*s", width, out);
629}
630
631static int64_t
632sort__lvl_cmp(struct hist_entry *left, struct hist_entry *right)
633{
634 union perf_mem_data_src data_src_l;
635 union perf_mem_data_src data_src_r;
636
637 if (left->mem_info)
638 data_src_l = left->mem_info->data_src;
639 else
640 data_src_l.mem_lvl = PERF_MEM_LVL_NA;
641
642 if (right->mem_info)
643 data_src_r = right->mem_info->data_src;
644 else
645 data_src_r.mem_lvl = PERF_MEM_LVL_NA;
646
647 return (int64_t)(data_src_r.mem_lvl - data_src_l.mem_lvl);
648}
649
650static const char * const mem_lvl[] = {
651 "N/A",
652 "HIT",
653 "MISS",
654 "L1",
655 "LFB",
656 "L2",
657 "L3",
658 "Local RAM",
659 "Remote RAM (1 hop)",
660 "Remote RAM (2 hops)",
661 "Remote Cache (1 hop)",
662 "Remote Cache (2 hops)",
663 "I/O",
664 "Uncached",
665};
666#define NUM_MEM_LVL (sizeof(mem_lvl)/sizeof(const char *))
667
668static int hist_entry__lvl_snprintf(struct hist_entry *self, char *bf,
669 size_t size, unsigned int width)
670{
671 char out[64];
672 size_t sz = sizeof(out) - 1; /* -1 for null termination */
673 size_t i, l = 0;
674 u64 m = PERF_MEM_LVL_NA;
675 u64 hit, miss;
676
677 if (self->mem_info)
678 m = self->mem_info->data_src.mem_lvl;
679
680 out[0] = '\0';
681
682 hit = m & PERF_MEM_LVL_HIT;
683 miss = m & PERF_MEM_LVL_MISS;
684
685 /* already taken care of */
686 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
687
688 for (i = 0; m && i < NUM_MEM_LVL; i++, m >>= 1) {
689 if (!(m & 0x1))
690 continue;
691 if (l) {
692 strcat(out, " or ");
693 l += 4;
694 }
695 strncat(out, mem_lvl[i], sz - l);
696 l += strlen(mem_lvl[i]);
697 }
698 if (*out == '\0')
699 strcpy(out, "N/A");
700 if (hit)
701 strncat(out, " hit", sz - l);
702 if (miss)
703 strncat(out, " miss", sz - l);
704
705 return repsep_snprintf(bf, size, "%-*s", width, out);
706}
707
708static int64_t
709sort__snoop_cmp(struct hist_entry *left, struct hist_entry *right)
710{
711 union perf_mem_data_src data_src_l;
712 union perf_mem_data_src data_src_r;
713
714 if (left->mem_info)
715 data_src_l = left->mem_info->data_src;
716 else
717 data_src_l.mem_snoop = PERF_MEM_SNOOP_NA;
718
719 if (right->mem_info)
720 data_src_r = right->mem_info->data_src;
721 else
722 data_src_r.mem_snoop = PERF_MEM_SNOOP_NA;
723
724 return (int64_t)(data_src_r.mem_snoop - data_src_l.mem_snoop);
725}
726
727static const char * const snoop_access[] = {
728 "N/A",
729 "None",
730 "Miss",
731 "Hit",
732 "HitM",
733};
734#define NUM_SNOOP_ACCESS (sizeof(snoop_access)/sizeof(const char *))
735
736static int hist_entry__snoop_snprintf(struct hist_entry *self, char *bf,
737 size_t size, unsigned int width)
738{
739 char out[64];
740 size_t sz = sizeof(out) - 1; /* -1 for null termination */
741 size_t i, l = 0;
742 u64 m = PERF_MEM_SNOOP_NA;
743
744 out[0] = '\0';
745
746 if (self->mem_info)
747 m = self->mem_info->data_src.mem_snoop;
748
749 for (i = 0; m && i < NUM_SNOOP_ACCESS; i++, m >>= 1) {
750 if (!(m & 0x1))
751 continue;
752 if (l) {
753 strcat(out, " or ");
754 l += 4;
755 }
756 strncat(out, snoop_access[i], sz - l);
757 l += strlen(snoop_access[i]);
758 }
759
760 if (*out == '\0')
761 strcpy(out, "N/A");
762
763 return repsep_snprintf(bf, size, "%-*s", width, out);
764}
765
460struct sort_entry sort_mispredict = { 766struct sort_entry sort_mispredict = {
461 .se_header = "Branch Mispredicted", 767 .se_header = "Branch Mispredicted",
462 .se_cmp = sort__mispredict_cmp, 768 .se_cmp = sort__mispredict_cmp,
@@ -507,6 +813,48 @@ struct sort_entry sort_global_weight = {
507 .se_width_idx = HISTC_GLOBAL_WEIGHT, 813 .se_width_idx = HISTC_GLOBAL_WEIGHT,
508}; 814};
509 815
816struct sort_entry sort_mem_daddr_sym = {
817 .se_header = "Data Symbol",
818 .se_cmp = sort__daddr_cmp,
819 .se_snprintf = hist_entry__daddr_snprintf,
820 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
821};
822
823struct sort_entry sort_mem_daddr_dso = {
824 .se_header = "Data Object",
825 .se_cmp = sort__dso_daddr_cmp,
826 .se_snprintf = hist_entry__dso_daddr_snprintf,
827 .se_width_idx = HISTC_MEM_DADDR_SYMBOL,
828};
829
830struct sort_entry sort_mem_locked = {
831 .se_header = "Locked",
832 .se_cmp = sort__locked_cmp,
833 .se_snprintf = hist_entry__locked_snprintf,
834 .se_width_idx = HISTC_MEM_LOCKED,
835};
836
837struct sort_entry sort_mem_tlb = {
838 .se_header = "TLB access",
839 .se_cmp = sort__tlb_cmp,
840 .se_snprintf = hist_entry__tlb_snprintf,
841 .se_width_idx = HISTC_MEM_TLB,
842};
843
844struct sort_entry sort_mem_lvl = {
845 .se_header = "Memory access",
846 .se_cmp = sort__lvl_cmp,
847 .se_snprintf = hist_entry__lvl_snprintf,
848 .se_width_idx = HISTC_MEM_LVL,
849};
850
851struct sort_entry sort_mem_snoop = {
852 .se_header = "Snoop",
853 .se_cmp = sort__snoop_cmp,
854 .se_snprintf = hist_entry__snoop_snprintf,
855 .se_width_idx = HISTC_MEM_SNOOP,
856};
857
510struct sort_dimension { 858struct sort_dimension {
511 const char *name; 859 const char *name;
512 struct sort_entry *entry; 860 struct sort_entry *entry;
@@ -525,6 +873,12 @@ static struct sort_dimension common_sort_dimensions[] = {
525 DIM(SORT_SRCLINE, "srcline", sort_srcline), 873 DIM(SORT_SRCLINE, "srcline", sort_srcline),
526 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), 874 DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
527 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), 875 DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
876 DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
877 DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
878 DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
879 DIM(SORT_MEM_TLB, "tlb", sort_mem_tlb),
880 DIM(SORT_MEM_LVL, "mem", sort_mem_lvl),
881 DIM(SORT_MEM_SNOOP, "snoop", sort_mem_snoop),
528}; 882};
529 883
530#undef DIM 884#undef DIM
@@ -561,7 +915,10 @@ int sort_dimension__add(const char *tok)
561 return -EINVAL; 915 return -EINVAL;
562 } 916 }
563 sort__has_parent = 1; 917 sort__has_parent = 1;
564 } else if (sd->entry == &sort_sym) { 918 } else if (sd->entry == &sort_sym ||
919 sd->entry == &sort_sym_from ||
920 sd->entry == &sort_sym_to ||
921 sd->entry == &sort_mem_daddr_sym) {
565 sort__has_sym = 1; 922 sort__has_sym = 1;
566 } 923 }
567 924
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 393925012796..f24bdf64238c 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -101,7 +101,8 @@ struct hist_entry {
101 struct rb_root sorted_chain; 101 struct rb_root sorted_chain;
102 struct branch_info *branch_info; 102 struct branch_info *branch_info;
103 struct hists *hists; 103 struct hists *hists;
104 struct callchain_root callchain[0]; 104 struct mem_info *mem_info;
105 struct callchain_root callchain[0]; /* must be last member */
105}; 106};
106 107
107static inline bool hist_entry__has_pairs(struct hist_entry *he) 108static inline bool hist_entry__has_pairs(struct hist_entry *he)
@@ -133,6 +134,12 @@ enum sort_type {
133 SORT_SRCLINE, 134 SORT_SRCLINE,
134 SORT_LOCAL_WEIGHT, 135 SORT_LOCAL_WEIGHT,
135 SORT_GLOBAL_WEIGHT, 136 SORT_GLOBAL_WEIGHT,
137 SORT_MEM_DADDR_SYMBOL,
138 SORT_MEM_DADDR_DSO,
139 SORT_MEM_LOCKED,
140 SORT_MEM_TLB,
141 SORT_MEM_LVL,
142 SORT_MEM_SNOOP,
136 143
137 /* branch stack specific sort keys */ 144 /* branch stack specific sort keys */
138 __SORT_BRANCH_STACK, 145 __SORT_BRANCH_STACK,
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index d7654c23861a..5f720dc076da 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -156,6 +156,12 @@ struct branch_info {
156 struct branch_flags flags; 156 struct branch_flags flags;
157}; 157};
158 158
159struct mem_info {
160 struct addr_map_symbol iaddr;
161 struct addr_map_symbol daddr;
162 union perf_mem_data_src data_src;
163};
164
159struct addr_location { 165struct addr_location {
160 struct thread *thread; 166 struct thread *thread;
161 struct map *map; 167 struct map *map;