]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - tools/perf/builtin-c2c.c
perf c2c report: Allow to report callchains
[mirror_ubuntu-jammy-kernel.git] / tools / perf / builtin-c2c.c
CommitLineData
7aef3bf3
JO
1#include <linux/compiler.h>
2#include <linux/kernel.h>
cbb88500 3#include <linux/stringify.h>
1e181b92 4#include <asm/bug.h>
7aef3bf3
JO
5#include "util.h"
6#include "debug.h"
7#include "builtin.h"
8#include <subcmd/parse-options.h>
39bcd4a4 9#include "mem-events.h"
903a6f15
JO
10#include "session.h"
11#include "hist.h"
cbb88500 12#include "sort.h"
903a6f15
JO
13#include "tool.h"
14#include "data.h"
8d3f938d 15#include "sort.h"
2709b97d
JO
16#include "evlist.h"
17#include "evsel.h"
2d388bd0 18#include <asm/bug.h>
5a1a99cd 19#include "ui/browsers/hists.h"
dd805768 20#include "evlist.h"
903a6f15 21
c75540e3
JO
22struct c2c_hists {
23 struct hists hists;
24 struct perf_hpp_list list;
b2252ae6 25 struct c2c_stats stats;
c75540e3
JO
26};
27
92062d54
JO
28struct compute_stats {
29 struct stats lcl_hitm;
30 struct stats rmt_hitm;
31 struct stats load;
32};
33
78b27543
JO
34struct c2c_hist_entry {
35 struct c2c_hists *hists;
b2252ae6 36 struct c2c_stats stats;
1e181b92
JO
37 unsigned long *cpuset;
38 struct c2c_stats *node_stats;
92062d54
JO
39
40 struct compute_stats cstats;
41
78b27543
JO
42 /*
43 * must be at the end,
44 * because of its callchain dynamic entry
45 */
46 struct hist_entry he;
47};
48
903a6f15 49struct perf_c2c {
c75540e3
JO
50 struct perf_tool tool;
51 struct c2c_hists hists;
1e181b92
JO
52
53 unsigned long **nodes;
54 int nodes_cnt;
55 int cpus_cnt;
56 int *cpu2node;
57 int node_info;
89d9ba8f
JO
58
59 bool show_src;
5a1a99cd 60 bool use_stdio;
74c63a25 61 bool stats_only;
7ef2efaa
JO
62
63 /* HITM shared clines stats */
64 struct c2c_stats hitm_stats;
65 int shared_clines;
903a6f15
JO
66};
67
68static struct perf_c2c c2c;
7aef3bf3 69
78b27543
JO
70static void *c2c_he_zalloc(size_t size)
71{
72 struct c2c_hist_entry *c2c_he;
73
74 c2c_he = zalloc(size + sizeof(*c2c_he));
75 if (!c2c_he)
76 return NULL;
77
1e181b92
JO
78 c2c_he->cpuset = bitmap_alloc(c2c.cpus_cnt);
79 if (!c2c_he->cpuset)
80 return NULL;
81
82 c2c_he->node_stats = zalloc(c2c.nodes_cnt * sizeof(*c2c_he->node_stats));
83 if (!c2c_he->node_stats)
84 return NULL;
85
92062d54
JO
86 init_stats(&c2c_he->cstats.lcl_hitm);
87 init_stats(&c2c_he->cstats.rmt_hitm);
88 init_stats(&c2c_he->cstats.load);
89
78b27543
JO
90 return &c2c_he->he;
91}
92
93static void c2c_he_free(void *he)
94{
95 struct c2c_hist_entry *c2c_he;
96
97 c2c_he = container_of(he, struct c2c_hist_entry, he);
98 if (c2c_he->hists) {
99 hists__delete_entries(&c2c_he->hists->hists);
100 free(c2c_he->hists);
101 }
102
1e181b92
JO
103 free(c2c_he->cpuset);
104 free(c2c_he->node_stats);
78b27543
JO
105 free(c2c_he);
106}
107
108static struct hist_entry_ops c2c_entry_ops = {
109 .new = c2c_he_zalloc,
110 .free = c2c_he_free,
111};
112
ec06f9b9 113static int c2c_hists__init(struct c2c_hists *hists,
1d62fcd6
JO
114 const char *sort,
115 int nr_header_lines);
ec06f9b9 116
b2252ae6
JO
117static struct c2c_hists*
118he__get_c2c_hists(struct hist_entry *he,
1d62fcd6
JO
119 const char *sort,
120 int nr_header_lines)
ec06f9b9
JO
121{
122 struct c2c_hist_entry *c2c_he;
123 struct c2c_hists *hists;
124 int ret;
125
126 c2c_he = container_of(he, struct c2c_hist_entry, he);
127 if (c2c_he->hists)
b2252ae6 128 return c2c_he->hists;
ec06f9b9
JO
129
130 hists = c2c_he->hists = zalloc(sizeof(*hists));
131 if (!hists)
132 return NULL;
133
1d62fcd6 134 ret = c2c_hists__init(hists, sort, nr_header_lines);
ec06f9b9
JO
135 if (ret) {
136 free(hists);
137 return NULL;
138 }
139
b2252ae6 140 return hists;
ec06f9b9
JO
141}
142
1e181b92
JO
143static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he,
144 struct perf_sample *sample)
145{
146 if (WARN_ONCE(sample->cpu == (unsigned int) -1,
147 "WARNING: no sample cpu value"))
148 return;
149
150 set_bit(sample->cpu, c2c_he->cpuset);
151}
152
92062d54
JO
153static void compute_stats(struct c2c_hist_entry *c2c_he,
154 struct c2c_stats *stats,
155 u64 weight)
156{
157 struct compute_stats *cstats = &c2c_he->cstats;
158
159 if (stats->rmt_hitm)
160 update_stats(&cstats->rmt_hitm, weight);
161 else if (stats->lcl_hitm)
162 update_stats(&cstats->lcl_hitm, weight);
163 else if (stats->load)
164 update_stats(&cstats->load, weight);
165}
166
78b27543
JO
167static int process_sample_event(struct perf_tool *tool __maybe_unused,
168 union perf_event *event,
169 struct perf_sample *sample,
170 struct perf_evsel *evsel __maybe_unused,
171 struct machine *machine)
172{
b2252ae6
JO
173 struct c2c_hists *c2c_hists = &c2c.hists;
174 struct c2c_hist_entry *c2c_he;
175 struct c2c_stats stats = { .nr_entries = 0, };
78b27543
JO
176 struct hist_entry *he;
177 struct addr_location al;
ec06f9b9 178 struct mem_info *mi, *mi_dup;
78b27543
JO
179 int ret;
180
181 if (machine__resolve(machine, &al, sample) < 0) {
182 pr_debug("problem processing %d event, skipping it.\n",
183 event->header.type);
184 return -1;
185 }
186
dd805768
JO
187 ret = sample__resolve_callchain(sample, &callchain_cursor, NULL,
188 evsel, &al, sysctl_perf_event_max_stack);
189 if (ret)
190 goto out;
191
78b27543
JO
192 mi = sample__resolve_mem(sample, &al);
193 if (mi == NULL)
194 return -ENOMEM;
195
ec06f9b9
JO
196 mi_dup = memdup(mi, sizeof(*mi));
197 if (!mi_dup)
198 goto free_mi;
199
b2252ae6
JO
200 c2c_decode_stats(&stats, mi);
201
202 he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
78b27543
JO
203 &al, NULL, NULL, mi,
204 sample, true);
ec06f9b9
JO
205 if (he == NULL)
206 goto free_mi_dup;
78b27543 207
b2252ae6
JO
208 c2c_he = container_of(he, struct c2c_hist_entry, he);
209 c2c_add_stats(&c2c_he->stats, &stats);
210 c2c_add_stats(&c2c_hists->stats, &stats);
211
1e181b92
JO
212 c2c_he__set_cpu(c2c_he, sample);
213
b2252ae6 214 hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
78b27543
JO
215 ret = hist_entry__append_callchain(he, sample);
216
ec06f9b9 217 if (!ret) {
1e181b92
JO
218 /*
219 * There's already been warning about missing
220 * sample's cpu value. Let's account all to
221 * node 0 in this case, without any further
222 * warning.
223 *
224 * Doing node stats only for single callchain data.
225 */
226 int cpu = sample->cpu == (unsigned int) -1 ? 0 : sample->cpu;
227 int node = c2c.cpu2node[cpu];
228
ec06f9b9
JO
229 mi = mi_dup;
230
231 mi_dup = memdup(mi, sizeof(*mi));
232 if (!mi_dup)
233 goto free_mi;
234
1d62fcd6 235 c2c_hists = he__get_c2c_hists(he, "offset", 2);
b2252ae6 236 if (!c2c_hists)
ec06f9b9
JO
237 goto free_mi_dup;
238
b2252ae6 239 he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
ec06f9b9
JO
240 &al, NULL, NULL, mi,
241 sample, true);
242 if (he == NULL)
243 goto free_mi_dup;
244
b2252ae6
JO
245 c2c_he = container_of(he, struct c2c_hist_entry, he);
246 c2c_add_stats(&c2c_he->stats, &stats);
247 c2c_add_stats(&c2c_hists->stats, &stats);
1e181b92
JO
248 c2c_add_stats(&c2c_he->node_stats[node], &stats);
249
92062d54
JO
250 compute_stats(c2c_he, &stats, sample->weight);
251
1e181b92 252 c2c_he__set_cpu(c2c_he, sample);
b2252ae6
JO
253
254 hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
ec06f9b9
JO
255 ret = hist_entry__append_callchain(he, sample);
256 }
257
258out:
78b27543
JO
259 addr_location__put(&al);
260 return ret;
ec06f9b9
JO
261
262free_mi_dup:
263 free(mi_dup);
264free_mi:
265 free(mi);
266 ret = -ENOMEM;
267 goto out;
78b27543
JO
268}
269
270static struct perf_c2c c2c = {
271 .tool = {
272 .sample = process_sample_event,
273 .mmap = perf_event__process_mmap,
274 .mmap2 = perf_event__process_mmap2,
275 .comm = perf_event__process_comm,
276 .exit = perf_event__process_exit,
277 .fork = perf_event__process_fork,
278 .lost = perf_event__process_lost,
279 .ordered_events = true,
280 .ordering_requires_timestamps = true,
281 },
282};
283
7aef3bf3 284static const char * const c2c_usage[] = {
903a6f15 285 "perf c2c {record|report}",
7aef3bf3
JO
286 NULL
287};
288
903a6f15
JO
289static const char * const __usage_report[] = {
290 "perf c2c report",
291 NULL
292};
293
294static const char * const *report_c2c_usage = __usage_report;
295
c75540e3
JO
296#define C2C_HEADER_MAX 2
297
298struct c2c_header {
299 struct {
300 const char *text;
301 int span;
302 } line[C2C_HEADER_MAX];
303};
304
305struct c2c_dimension {
306 struct c2c_header header;
307 const char *name;
308 int width;
8d3f938d 309 struct sort_entry *se;
c75540e3
JO
310
311 int64_t (*cmp)(struct perf_hpp_fmt *fmt,
312 struct hist_entry *, struct hist_entry *);
313 int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
314 struct hist_entry *he);
315 int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
316 struct hist_entry *he);
317};
318
319struct c2c_fmt {
320 struct perf_hpp_fmt fmt;
321 struct c2c_dimension *dim;
322};
323
324static int c2c_width(struct perf_hpp_fmt *fmt,
325 struct perf_hpp *hpp __maybe_unused,
326 struct hists *hists __maybe_unused)
327{
328 struct c2c_fmt *c2c_fmt;
8d3f938d 329 struct c2c_dimension *dim;
c75540e3
JO
330
331 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
8d3f938d
JO
332 dim = c2c_fmt->dim;
333
334 return dim->se ? hists__col_len(hists, dim->se->se_width_idx) :
335 c2c_fmt->dim->width;
c75540e3
JO
336}
337
338static int c2c_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
8d3f938d 339 struct hists *hists, int line, int *span)
c75540e3 340{
8d3f938d 341 struct perf_hpp_list *hpp_list = hists->hpp_list;
c75540e3
JO
342 struct c2c_fmt *c2c_fmt;
343 struct c2c_dimension *dim;
8d3f938d
JO
344 const char *text = NULL;
345 int width = c2c_width(fmt, hpp, hists);
c75540e3
JO
346
347 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
348 dim = c2c_fmt->dim;
349
8d3f938d
JO
350 if (dim->se) {
351 text = dim->header.line[line].text;
352 /* Use the last line from sort_entry if not defined. */
353 if (!text && (line == hpp_list->nr_header_lines - 1))
354 text = dim->se->se_header;
c75540e3 355 } else {
8d3f938d
JO
356 text = dim->header.line[line].text;
357
358 if (*span) {
359 (*span)--;
360 return 0;
361 } else {
362 *span = dim->header.line[line].span;
363 }
c75540e3
JO
364 }
365
8d3f938d
JO
366 if (text == NULL)
367 text = "";
368
369 return scnprintf(hpp->buf, hpp->size, "%*s", width, text);
c75540e3
JO
370}
371
cbb88500
JO
372#define HEX_STR(__s, __v) \
373({ \
374 scnprintf(__s, sizeof(__s), "0x%" PRIx64, __v); \
375 __s; \
376})
377
378static int64_t
379dcacheline_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
380 struct hist_entry *left, struct hist_entry *right)
381{
382 return sort__dcacheline_cmp(left, right);
383}
384
385static int dcacheline_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
386 struct hist_entry *he)
387{
388 uint64_t addr = 0;
389 int width = c2c_width(fmt, hpp, he->hists);
390 char buf[20];
391
392 if (he->mem_info)
393 addr = cl_address(he->mem_info->daddr.addr);
394
395 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
396}
397
48acdebd
JO
398static int offset_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
399 struct hist_entry *he)
400{
401 uint64_t addr = 0;
402 int width = c2c_width(fmt, hpp, he->hists);
403 char buf[20];
404
405 if (he->mem_info)
406 addr = cl_offset(he->mem_info->daddr.al_addr);
407
408 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
409}
410
411static int64_t
412offset_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
413 struct hist_entry *left, struct hist_entry *right)
414{
415 uint64_t l = 0, r = 0;
416
417 if (left->mem_info)
418 l = cl_offset(left->mem_info->daddr.addr);
419 if (right->mem_info)
420 r = cl_offset(right->mem_info->daddr.addr);
421
422 return (int64_t)(r - l);
423}
424
43575a95
JO
425static int
426iaddr_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
427 struct hist_entry *he)
428{
429 uint64_t addr = 0;
430 int width = c2c_width(fmt, hpp, he->hists);
431 char buf[20];
432
433 if (he->mem_info)
434 addr = he->mem_info->iaddr.addr;
435
436 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
437}
438
439static int64_t
440iaddr_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
441 struct hist_entry *left, struct hist_entry *right)
442{
443 return sort__iaddr_cmp(left, right);
444}
445
97cb486e
JO
446static int
447tot_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
448 struct hist_entry *he)
449{
450 struct c2c_hist_entry *c2c_he;
451 int width = c2c_width(fmt, hpp, he->hists);
452 unsigned int tot_hitm;
453
454 c2c_he = container_of(he, struct c2c_hist_entry, he);
455 tot_hitm = c2c_he->stats.lcl_hitm + c2c_he->stats.rmt_hitm;
456
457 return scnprintf(hpp->buf, hpp->size, "%*u", width, tot_hitm);
458}
459
460static int64_t
461tot_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
462 struct hist_entry *left, struct hist_entry *right)
463{
464 struct c2c_hist_entry *c2c_left;
465 struct c2c_hist_entry *c2c_right;
466 unsigned int tot_hitm_left;
467 unsigned int tot_hitm_right;
468
469 c2c_left = container_of(left, struct c2c_hist_entry, he);
470 c2c_right = container_of(right, struct c2c_hist_entry, he);
471
472 tot_hitm_left = c2c_left->stats.lcl_hitm + c2c_left->stats.rmt_hitm;
473 tot_hitm_right = c2c_right->stats.lcl_hitm + c2c_right->stats.rmt_hitm;
474
475 return tot_hitm_left - tot_hitm_right;
476}
477
478#define STAT_FN_ENTRY(__f) \
479static int \
480__f ## _entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, \
481 struct hist_entry *he) \
482{ \
483 struct c2c_hist_entry *c2c_he; \
484 int width = c2c_width(fmt, hpp, he->hists); \
485 \
486 c2c_he = container_of(he, struct c2c_hist_entry, he); \
487 return scnprintf(hpp->buf, hpp->size, "%*u", width, \
488 c2c_he->stats.__f); \
489}
490
491#define STAT_FN_CMP(__f) \
492static int64_t \
493__f ## _cmp(struct perf_hpp_fmt *fmt __maybe_unused, \
494 struct hist_entry *left, struct hist_entry *right) \
495{ \
496 struct c2c_hist_entry *c2c_left, *c2c_right; \
497 \
498 c2c_left = container_of(left, struct c2c_hist_entry, he); \
499 c2c_right = container_of(right, struct c2c_hist_entry, he); \
500 return c2c_left->stats.__f - c2c_right->stats.__f; \
501}
502
503#define STAT_FN(__f) \
504 STAT_FN_ENTRY(__f) \
505 STAT_FN_CMP(__f)
506
507STAT_FN(rmt_hitm)
508STAT_FN(lcl_hitm)
0f18896d
JO
509STAT_FN(store)
510STAT_FN(st_l1hit)
511STAT_FN(st_l1miss)
1295f685
JO
512STAT_FN(ld_fbhit)
513STAT_FN(ld_l1hit)
514STAT_FN(ld_l2hit)
4d08910c
JO
515STAT_FN(ld_llchit)
516STAT_FN(rmt_hit)
97cb486e 517
04402d20
JO
518static uint64_t llc_miss(struct c2c_stats *stats)
519{
520 uint64_t llcmiss;
521
522 llcmiss = stats->lcl_dram +
523 stats->rmt_dram +
524 stats->rmt_hitm +
525 stats->rmt_hit;
526
527 return llcmiss;
528}
529
530static int
531ld_llcmiss_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
532 struct hist_entry *he)
533{
534 struct c2c_hist_entry *c2c_he;
535 int width = c2c_width(fmt, hpp, he->hists);
536
537 c2c_he = container_of(he, struct c2c_hist_entry, he);
538
539 return scnprintf(hpp->buf, hpp->size, "%*lu", width,
540 llc_miss(&c2c_he->stats));
541}
542
543static int64_t
544ld_llcmiss_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
545 struct hist_entry *left, struct hist_entry *right)
546{
547 struct c2c_hist_entry *c2c_left;
548 struct c2c_hist_entry *c2c_right;
549
550 c2c_left = container_of(left, struct c2c_hist_entry, he);
551 c2c_right = container_of(right, struct c2c_hist_entry, he);
552
553 return llc_miss(&c2c_left->stats) - llc_miss(&c2c_right->stats);
554}
555
01b84d76
JO
556static uint64_t total_records(struct c2c_stats *stats)
557{
558 uint64_t lclmiss, ldcnt, total;
559
560 lclmiss = stats->lcl_dram +
561 stats->rmt_dram +
562 stats->rmt_hitm +
563 stats->rmt_hit;
564
565 ldcnt = lclmiss +
566 stats->ld_fbhit +
567 stats->ld_l1hit +
568 stats->ld_l2hit +
569 stats->ld_llchit +
570 stats->lcl_hitm;
571
572 total = ldcnt +
573 stats->st_l1hit +
574 stats->st_l1miss;
575
576 return total;
577}
578
579static int
580tot_recs_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
581 struct hist_entry *he)
582{
583 struct c2c_hist_entry *c2c_he;
584 int width = c2c_width(fmt, hpp, he->hists);
585 uint64_t tot_recs;
586
587 c2c_he = container_of(he, struct c2c_hist_entry, he);
588 tot_recs = total_records(&c2c_he->stats);
589
590 return scnprintf(hpp->buf, hpp->size, "%*" PRIu64, width, tot_recs);
591}
592
593static int64_t
594tot_recs_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
595 struct hist_entry *left, struct hist_entry *right)
596{
597 struct c2c_hist_entry *c2c_left;
598 struct c2c_hist_entry *c2c_right;
599 uint64_t tot_recs_left;
600 uint64_t tot_recs_right;
601
602 c2c_left = container_of(left, struct c2c_hist_entry, he);
603 c2c_right = container_of(right, struct c2c_hist_entry, he);
604
605 tot_recs_left = total_records(&c2c_left->stats);
606 tot_recs_right = total_records(&c2c_right->stats);
607
608 return tot_recs_left - tot_recs_right;
609}
610
55177c4e
JO
611static uint64_t total_loads(struct c2c_stats *stats)
612{
613 uint64_t lclmiss, ldcnt;
614
615 lclmiss = stats->lcl_dram +
616 stats->rmt_dram +
617 stats->rmt_hitm +
618 stats->rmt_hit;
619
620 ldcnt = lclmiss +
621 stats->ld_fbhit +
622 stats->ld_l1hit +
623 stats->ld_l2hit +
624 stats->ld_llchit +
625 stats->lcl_hitm;
626
627 return ldcnt;
628}
629
630static int
631tot_loads_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
632 struct hist_entry *he)
633{
634 struct c2c_hist_entry *c2c_he;
635 int width = c2c_width(fmt, hpp, he->hists);
636 uint64_t tot_recs;
637
638 c2c_he = container_of(he, struct c2c_hist_entry, he);
639 tot_recs = total_loads(&c2c_he->stats);
640
641 return scnprintf(hpp->buf, hpp->size, "%*" PRIu64, width, tot_recs);
642}
643
644static int64_t
645tot_loads_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
646 struct hist_entry *left, struct hist_entry *right)
647{
648 struct c2c_hist_entry *c2c_left;
649 struct c2c_hist_entry *c2c_right;
650 uint64_t tot_recs_left;
651 uint64_t tot_recs_right;
652
653 c2c_left = container_of(left, struct c2c_hist_entry, he);
654 c2c_right = container_of(right, struct c2c_hist_entry, he);
655
656 tot_recs_left = total_loads(&c2c_left->stats);
657 tot_recs_right = total_loads(&c2c_right->stats);
658
659 return tot_recs_left - tot_recs_right;
660}
661
f0c50c15
JO
662typedef double (get_percent_cb)(struct c2c_hist_entry *);
663
664static int
665percent_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
666 struct hist_entry *he, get_percent_cb get_percent)
667{
668 struct c2c_hist_entry *c2c_he;
669 int width = c2c_width(fmt, hpp, he->hists);
670 double per;
671
672 c2c_he = container_of(he, struct c2c_hist_entry, he);
673 per = get_percent(c2c_he);
674
5a1a99cd
JO
675#ifdef HAVE_SLANG_SUPPORT
676 if (use_browser)
677 return __hpp__slsmg_color_printf(hpp, "%*.2f%%", width - 1, per);
678#endif
f0c50c15
JO
679 return hpp_color_scnprintf(hpp, "%*.2f%%", width - 1, per);
680}
681
682static double percent_hitm(struct c2c_hist_entry *c2c_he)
683{
684 struct c2c_hists *hists;
685 struct c2c_stats *stats;
686 struct c2c_stats *total;
687 int tot, st;
688 double p;
689
690 hists = container_of(c2c_he->he.hists, struct c2c_hists, hists);
691 stats = &c2c_he->stats;
692 total = &hists->stats;
693
694 st = stats->rmt_hitm;
695 tot = total->rmt_hitm;
696
697 p = tot ? (double) st / tot : 0;
698
699 return 100 * p;
700}
701
702#define PERC_STR(__s, __v) \
703({ \
704 scnprintf(__s, sizeof(__s), "%.2F%%", __v); \
705 __s; \
706})
707
708static int
709percent_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
710 struct hist_entry *he)
711{
712 struct c2c_hist_entry *c2c_he;
713 int width = c2c_width(fmt, hpp, he->hists);
714 char buf[10];
715 double per;
716
717 c2c_he = container_of(he, struct c2c_hist_entry, he);
718 per = percent_hitm(c2c_he);
719 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
720}
721
722static int
723percent_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
724 struct hist_entry *he)
725{
726 return percent_color(fmt, hpp, he, percent_hitm);
727}
728
729static int64_t
730percent_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
731 struct hist_entry *left, struct hist_entry *right)
732{
733 struct c2c_hist_entry *c2c_left;
734 struct c2c_hist_entry *c2c_right;
735 double per_left;
736 double per_right;
737
738 c2c_left = container_of(left, struct c2c_hist_entry, he);
739 c2c_right = container_of(right, struct c2c_hist_entry, he);
740
741 per_left = percent_hitm(c2c_left);
742 per_right = percent_hitm(c2c_right);
743
744 return per_left - per_right;
745}
746
9cb3500a
JO
747static struct c2c_stats *he_stats(struct hist_entry *he)
748{
749 struct c2c_hist_entry *c2c_he;
750
751 c2c_he = container_of(he, struct c2c_hist_entry, he);
752 return &c2c_he->stats;
753}
754
755static struct c2c_stats *total_stats(struct hist_entry *he)
756{
757 struct c2c_hists *hists;
758
759 hists = container_of(he->hists, struct c2c_hists, hists);
760 return &hists->stats;
761}
762
763static double percent(int st, int tot)
764{
765 return tot ? 100. * (double) st / (double) tot : 0;
766}
767
768#define PERCENT(__h, __f) percent(he_stats(__h)->__f, total_stats(__h)->__f)
769
770#define PERCENT_FN(__f) \
771static double percent_ ## __f(struct c2c_hist_entry *c2c_he) \
772{ \
773 struct c2c_hists *hists; \
774 \
775 hists = container_of(c2c_he->he.hists, struct c2c_hists, hists); \
776 return percent(c2c_he->stats.__f, hists->stats.__f); \
777}
778
779PERCENT_FN(rmt_hitm)
780PERCENT_FN(lcl_hitm)
781PERCENT_FN(st_l1hit)
782PERCENT_FN(st_l1miss)
783
784static int
785percent_rmt_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
786 struct hist_entry *he)
787{
788 int width = c2c_width(fmt, hpp, he->hists);
789 double per = PERCENT(he, rmt_hitm);
790 char buf[10];
791
792 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
793}
794
795static int
796percent_rmt_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
797 struct hist_entry *he)
798{
799 return percent_color(fmt, hpp, he, percent_rmt_hitm);
800}
801
802static int64_t
803percent_rmt_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
804 struct hist_entry *left, struct hist_entry *right)
805{
806 double per_left;
807 double per_right;
808
809 per_left = PERCENT(left, lcl_hitm);
810 per_right = PERCENT(right, lcl_hitm);
811
812 return per_left - per_right;
813}
814
815static int
816percent_lcl_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
817 struct hist_entry *he)
818{
819 int width = c2c_width(fmt, hpp, he->hists);
820 double per = PERCENT(he, lcl_hitm);
821 char buf[10];
822
823 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
824}
825
826static int
827percent_lcl_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
828 struct hist_entry *he)
829{
830 return percent_color(fmt, hpp, he, percent_lcl_hitm);
831}
832
833static int64_t
834percent_lcl_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
835 struct hist_entry *left, struct hist_entry *right)
836{
837 double per_left;
838 double per_right;
839
840 per_left = PERCENT(left, lcl_hitm);
841 per_right = PERCENT(right, lcl_hitm);
842
843 return per_left - per_right;
844}
845
846static int
847percent_stores_l1hit_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
848 struct hist_entry *he)
849{
850 int width = c2c_width(fmt, hpp, he->hists);
851 double per = PERCENT(he, st_l1hit);
852 char buf[10];
853
854 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
855}
856
857static int
858percent_stores_l1hit_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
859 struct hist_entry *he)
860{
861 return percent_color(fmt, hpp, he, percent_st_l1hit);
862}
863
864static int64_t
865percent_stores_l1hit_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
866 struct hist_entry *left, struct hist_entry *right)
867{
868 double per_left;
869 double per_right;
870
871 per_left = PERCENT(left, st_l1hit);
872 per_right = PERCENT(right, st_l1hit);
873
874 return per_left - per_right;
875}
876
877static int
878percent_stores_l1miss_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
879 struct hist_entry *he)
880{
881 int width = c2c_width(fmt, hpp, he->hists);
882 double per = PERCENT(he, st_l1miss);
883 char buf[10];
884
885 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
886}
887
888static int
889percent_stores_l1miss_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
890 struct hist_entry *he)
891{
892 return percent_color(fmt, hpp, he, percent_st_l1miss);
893}
894
895static int64_t
896percent_stores_l1miss_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
897 struct hist_entry *left, struct hist_entry *right)
898{
899 double per_left;
900 double per_right;
901
902 per_left = PERCENT(left, st_l1miss);
903 per_right = PERCENT(right, st_l1miss);
904
905 return per_left - per_right;
906}
907
6c70f54c
JO
908STAT_FN(lcl_dram)
909STAT_FN(rmt_dram)
910
36d3deb9
JO
911static int
912pid_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
913 struct hist_entry *he)
914{
915 int width = c2c_width(fmt, hpp, he->hists);
916
917 return scnprintf(hpp->buf, hpp->size, "%*d", width, he->thread->pid_);
918}
919
920static int64_t
921pid_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
922 struct hist_entry *left, struct hist_entry *right)
923{
924 return left->thread->pid_ - right->thread->pid_;
925}
926
1e181b92
JO
927static int64_t
928empty_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
929 struct hist_entry *left __maybe_unused,
930 struct hist_entry *right __maybe_unused)
931{
932 return 0;
933}
934
935static int
936node_entry(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
937 struct hist_entry *he)
938{
939 struct c2c_hist_entry *c2c_he;
940 bool first = true;
941 int node;
942 int ret = 0;
943
944 c2c_he = container_of(he, struct c2c_hist_entry, he);
945
946 for (node = 0; node < c2c.nodes_cnt; node++) {
947 DECLARE_BITMAP(set, c2c.cpus_cnt);
948
949 bitmap_zero(set, c2c.cpus_cnt);
950 bitmap_and(set, c2c_he->cpuset, c2c.nodes[node], c2c.cpus_cnt);
951
952 if (!bitmap_weight(set, c2c.cpus_cnt)) {
953 if (c2c.node_info == 1) {
954 ret = scnprintf(hpp->buf, hpp->size, "%21s", " ");
955 advance_hpp(hpp, ret);
956 }
957 continue;
958 }
959
960 if (!first) {
961 ret = scnprintf(hpp->buf, hpp->size, " ");
962 advance_hpp(hpp, ret);
963 }
964
965 switch (c2c.node_info) {
966 case 0:
967 ret = scnprintf(hpp->buf, hpp->size, "%2d", node);
968 advance_hpp(hpp, ret);
969 break;
970 case 1:
971 {
972 int num = bitmap_weight(c2c_he->cpuset, c2c.cpus_cnt);
973 struct c2c_stats *stats = &c2c_he->node_stats[node];
974
975 ret = scnprintf(hpp->buf, hpp->size, "%2d{%2d ", node, num);
976 advance_hpp(hpp, ret);
977
978
979 if (c2c_he->stats.rmt_hitm > 0) {
980 ret = scnprintf(hpp->buf, hpp->size, "%5.1f%% ",
981 percent(stats->rmt_hitm, c2c_he->stats.rmt_hitm));
982 } else {
983 ret = scnprintf(hpp->buf, hpp->size, "%6s ", "n/a");
984 }
985
986 advance_hpp(hpp, ret);
987
988 if (c2c_he->stats.store > 0) {
989 ret = scnprintf(hpp->buf, hpp->size, "%5.1f%%}",
990 percent(stats->store, c2c_he->stats.store));
991 } else {
992 ret = scnprintf(hpp->buf, hpp->size, "%6s}", "n/a");
993 }
994
995 advance_hpp(hpp, ret);
996 break;
997 }
998 case 2:
999 ret = scnprintf(hpp->buf, hpp->size, "%2d{", node);
1000 advance_hpp(hpp, ret);
1001
1002 ret = bitmap_scnprintf(set, c2c.cpus_cnt, hpp->buf, hpp->size);
1003 advance_hpp(hpp, ret);
1004
1005 ret = scnprintf(hpp->buf, hpp->size, "}");
1006 advance_hpp(hpp, ret);
1007 break;
1008 default:
1009 break;
1010 }
1011
1012 first = false;
1013 }
1014
1015 return 0;
1016}
1017
92062d54
JO
1018static int
1019mean_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1020 struct hist_entry *he, double mean)
1021{
1022 int width = c2c_width(fmt, hpp, he->hists);
1023 char buf[10];
1024
1025 scnprintf(buf, 10, "%6.0f", mean);
1026 return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1027}
1028
1029#define MEAN_ENTRY(__func, __val) \
1030static int \
1031__func(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, struct hist_entry *he) \
1032{ \
1033 struct c2c_hist_entry *c2c_he; \
1034 c2c_he = container_of(he, struct c2c_hist_entry, he); \
1035 return mean_entry(fmt, hpp, he, avg_stats(&c2c_he->cstats.__val)); \
1036}
1037
1038MEAN_ENTRY(mean_rmt_entry, rmt_hitm);
1039MEAN_ENTRY(mean_lcl_entry, lcl_hitm);
1040MEAN_ENTRY(mean_load_entry, load);
1041
b6fe2bbc
JO
1042static int
1043cpucnt_entry(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
1044 struct hist_entry *he)
1045{
1046 struct c2c_hist_entry *c2c_he;
1047 int width = c2c_width(fmt, hpp, he->hists);
1048 char buf[10];
1049
1050 c2c_he = container_of(he, struct c2c_hist_entry, he);
1051
1052 scnprintf(buf, 10, "%d", bitmap_weight(c2c_he->cpuset, c2c.cpus_cnt));
1053 return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1054}
1055
600a8cf4
JO
1056#define HEADER_LOW(__h) \
1057 { \
1058 .line[1] = { \
1059 .text = __h, \
1060 }, \
1061 }
1062
1063#define HEADER_BOTH(__h0, __h1) \
1064 { \
1065 .line[0] = { \
1066 .text = __h0, \
1067 }, \
1068 .line[1] = { \
1069 .text = __h1, \
1070 }, \
1071 }
1072
1073#define HEADER_SPAN(__h0, __h1, __s) \
1074 { \
1075 .line[0] = { \
1076 .text = __h0, \
1077 .span = __s, \
1078 }, \
1079 .line[1] = { \
1080 .text = __h1, \
1081 }, \
1082 }
1083
1084#define HEADER_SPAN_LOW(__h) \
1085 { \
1086 .line[1] = { \
1087 .text = __h, \
1088 }, \
1089 }
1090
cbb88500
JO
1091static struct c2c_dimension dim_dcacheline = {
1092 .header = HEADER_LOW("Cacheline"),
1093 .name = "dcacheline",
1094 .cmp = dcacheline_cmp,
1095 .entry = dcacheline_entry,
1096 .width = 18,
1097};
1098
5a1a99cd
JO
1099static struct c2c_header header_offset_tui = HEADER_LOW("Off");
1100
48acdebd
JO
1101static struct c2c_dimension dim_offset = {
1102 .header = HEADER_BOTH("Data address", "Offset"),
1103 .name = "offset",
1104 .cmp = offset_cmp,
1105 .entry = offset_entry,
1106 .width = 18,
1107};
1108
43575a95
JO
1109static struct c2c_dimension dim_iaddr = {
1110 .header = HEADER_LOW("Code address"),
1111 .name = "iaddr",
1112 .cmp = iaddr_cmp,
1113 .entry = iaddr_entry,
1114 .width = 18,
1115};
1116
97cb486e
JO
1117static struct c2c_dimension dim_tot_hitm = {
1118 .header = HEADER_SPAN("----- LLC Load Hitm -----", "Total", 2),
1119 .name = "tot_hitm",
1120 .cmp = tot_hitm_cmp,
1121 .entry = tot_hitm_entry,
1122 .width = 7,
1123};
1124
1125static struct c2c_dimension dim_lcl_hitm = {
1126 .header = HEADER_SPAN_LOW("Lcl"),
1127 .name = "lcl_hitm",
1128 .cmp = lcl_hitm_cmp,
1129 .entry = lcl_hitm_entry,
1130 .width = 7,
1131};
1132
1133static struct c2c_dimension dim_rmt_hitm = {
1134 .header = HEADER_SPAN_LOW("Rmt"),
1135 .name = "rmt_hitm",
1136 .cmp = rmt_hitm_cmp,
1137 .entry = rmt_hitm_entry,
1138 .width = 7,
1139};
1140
1141static struct c2c_dimension dim_cl_rmt_hitm = {
1142 .header = HEADER_SPAN("----- HITM -----", "Rmt", 1),
1143 .name = "cl_rmt_hitm",
1144 .cmp = rmt_hitm_cmp,
1145 .entry = rmt_hitm_entry,
1146 .width = 7,
1147};
1148
1149static struct c2c_dimension dim_cl_lcl_hitm = {
1150 .header = HEADER_SPAN_LOW("Lcl"),
1151 .name = "cl_lcl_hitm",
1152 .cmp = lcl_hitm_cmp,
1153 .entry = lcl_hitm_entry,
1154 .width = 7,
1155};
1156
0f18896d
JO
1157static struct c2c_dimension dim_stores = {
1158 .header = HEADER_SPAN("---- Store Reference ----", "Total", 2),
1159 .name = "stores",
1160 .cmp = store_cmp,
1161 .entry = store_entry,
1162 .width = 7,
1163};
1164
1165static struct c2c_dimension dim_stores_l1hit = {
1166 .header = HEADER_SPAN_LOW("L1Hit"),
1167 .name = "stores_l1hit",
1168 .cmp = st_l1hit_cmp,
1169 .entry = st_l1hit_entry,
1170 .width = 7,
1171};
1172
1173static struct c2c_dimension dim_stores_l1miss = {
1174 .header = HEADER_SPAN_LOW("L1Miss"),
1175 .name = "stores_l1miss",
1176 .cmp = st_l1miss_cmp,
1177 .entry = st_l1miss_entry,
1178 .width = 7,
1179};
1180
1181static struct c2c_dimension dim_cl_stores_l1hit = {
1182 .header = HEADER_SPAN("-- Store Refs --", "L1 Hit", 1),
1183 .name = "cl_stores_l1hit",
1184 .cmp = st_l1hit_cmp,
1185 .entry = st_l1hit_entry,
1186 .width = 7,
1187};
1188
1189static struct c2c_dimension dim_cl_stores_l1miss = {
1190 .header = HEADER_SPAN_LOW("L1 Miss"),
1191 .name = "cl_stores_l1miss",
1192 .cmp = st_l1miss_cmp,
1193 .entry = st_l1miss_entry,
1194 .width = 7,
1195};
1196
1295f685
JO
1197static struct c2c_dimension dim_ld_fbhit = {
1198 .header = HEADER_SPAN("----- Core Load Hit -----", "FB", 2),
1199 .name = "ld_fbhit",
1200 .cmp = ld_fbhit_cmp,
1201 .entry = ld_fbhit_entry,
1202 .width = 7,
1203};
1204
1205static struct c2c_dimension dim_ld_l1hit = {
1206 .header = HEADER_SPAN_LOW("L1"),
1207 .name = "ld_l1hit",
1208 .cmp = ld_l1hit_cmp,
1209 .entry = ld_l1hit_entry,
1210 .width = 7,
1211};
1212
1213static struct c2c_dimension dim_ld_l2hit = {
1214 .header = HEADER_SPAN_LOW("L2"),
1215 .name = "ld_l2hit",
1216 .cmp = ld_l2hit_cmp,
1217 .entry = ld_l2hit_entry,
1218 .width = 7,
1219};
1220
4d08910c
JO
1221static struct c2c_dimension dim_ld_llchit = {
1222 .header = HEADER_SPAN("-- LLC Load Hit --", "Llc", 1),
1223 .name = "ld_lclhit",
1224 .cmp = ld_llchit_cmp,
1225 .entry = ld_llchit_entry,
1226 .width = 8,
1227};
1228
1229static struct c2c_dimension dim_ld_rmthit = {
1230 .header = HEADER_SPAN_LOW("Rmt"),
1231 .name = "ld_rmthit",
1232 .cmp = rmt_hit_cmp,
1233 .entry = rmt_hit_entry,
1234 .width = 8,
1235};
1236
04402d20
JO
1237static struct c2c_dimension dim_ld_llcmiss = {
1238 .header = HEADER_BOTH("LLC", "Ld Miss"),
1239 .name = "ld_llcmiss",
1240 .cmp = ld_llcmiss_cmp,
1241 .entry = ld_llcmiss_entry,
1242 .width = 7,
1243};
1244
01b84d76
JO
1245static struct c2c_dimension dim_tot_recs = {
1246 .header = HEADER_BOTH("Total", "records"),
1247 .name = "tot_recs",
1248 .cmp = tot_recs_cmp,
1249 .entry = tot_recs_entry,
1250 .width = 7,
1251};
1252
55177c4e
JO
1253static struct c2c_dimension dim_tot_loads = {
1254 .header = HEADER_BOTH("Total", "Loads"),
1255 .name = "tot_loads",
1256 .cmp = tot_loads_cmp,
1257 .entry = tot_loads_entry,
1258 .width = 7,
1259};
1260
f0c50c15
JO
1261static struct c2c_dimension dim_percent_hitm = {
1262 .header = HEADER_LOW("%hitm"),
1263 .name = "percent_hitm",
1264 .cmp = percent_hitm_cmp,
1265 .entry = percent_hitm_entry,
1266 .color = percent_hitm_color,
1267 .width = 7,
1268};
1269
9cb3500a
JO
1270static struct c2c_dimension dim_percent_rmt_hitm = {
1271 .header = HEADER_SPAN("----- HITM -----", "Rmt", 1),
1272 .name = "percent_rmt_hitm",
1273 .cmp = percent_rmt_hitm_cmp,
1274 .entry = percent_rmt_hitm_entry,
1275 .color = percent_rmt_hitm_color,
1276 .width = 7,
1277};
1278
1279static struct c2c_dimension dim_percent_lcl_hitm = {
1280 .header = HEADER_SPAN_LOW("Lcl"),
1281 .name = "percent_lcl_hitm",
1282 .cmp = percent_lcl_hitm_cmp,
1283 .entry = percent_lcl_hitm_entry,
1284 .color = percent_lcl_hitm_color,
1285 .width = 7,
1286};
1287
1288static struct c2c_dimension dim_percent_stores_l1hit = {
1289 .header = HEADER_SPAN("-- Store Refs --", "L1 Hit", 1),
1290 .name = "percent_stores_l1hit",
1291 .cmp = percent_stores_l1hit_cmp,
1292 .entry = percent_stores_l1hit_entry,
1293 .color = percent_stores_l1hit_color,
1294 .width = 7,
1295};
1296
1297static struct c2c_dimension dim_percent_stores_l1miss = {
1298 .header = HEADER_SPAN_LOW("L1 Miss"),
1299 .name = "percent_stores_l1miss",
1300 .cmp = percent_stores_l1miss_cmp,
1301 .entry = percent_stores_l1miss_entry,
1302 .color = percent_stores_l1miss_color,
1303 .width = 7,
1304};
1305
6c70f54c
JO
1306static struct c2c_dimension dim_dram_lcl = {
1307 .header = HEADER_SPAN("--- Load Dram ----", "Lcl", 1),
1308 .name = "dram_lcl",
1309 .cmp = lcl_dram_cmp,
1310 .entry = lcl_dram_entry,
1311 .width = 8,
1312};
1313
1314static struct c2c_dimension dim_dram_rmt = {
1315 .header = HEADER_SPAN_LOW("Rmt"),
1316 .name = "dram_rmt",
1317 .cmp = rmt_dram_cmp,
1318 .entry = rmt_dram_entry,
1319 .width = 8,
1320};
1321
36d3deb9
JO
1322static struct c2c_dimension dim_pid = {
1323 .header = HEADER_LOW("Pid"),
1324 .name = "pid",
1325 .cmp = pid_cmp,
1326 .entry = pid_entry,
1327 .width = 7,
1328};
1329
e87019c5
JO
1330static struct c2c_dimension dim_tid = {
1331 .header = HEADER_LOW("Tid"),
1332 .name = "tid",
1333 .se = &sort_thread,
1334};
1335
51dedaa4
JO
1336static struct c2c_dimension dim_symbol = {
1337 .name = "symbol",
1338 .se = &sort_sym,
1339};
1340
1341static struct c2c_dimension dim_dso = {
1342 .header = HEADER_BOTH("Shared", "Object"),
1343 .name = "dso",
1344 .se = &sort_dso,
1345};
1346
1e181b92
JO
1347static struct c2c_header header_node[3] = {
1348 HEADER_LOW("Node"),
1349 HEADER_LOW("Node{cpus %hitms %stores}"),
1350 HEADER_LOW("Node{cpu list}"),
1351};
1352
1353static struct c2c_dimension dim_node = {
1354 .name = "node",
1355 .cmp = empty_cmp,
1356 .entry = node_entry,
1357 .width = 4,
1358};
1359
92062d54
JO
1360static struct c2c_dimension dim_mean_rmt = {
1361 .header = HEADER_SPAN("---------- cycles ----------", "rmt hitm", 2),
1362 .name = "mean_rmt",
1363 .cmp = empty_cmp,
1364 .entry = mean_rmt_entry,
1365 .width = 8,
1366};
1367
1368static struct c2c_dimension dim_mean_lcl = {
1369 .header = HEADER_SPAN_LOW("lcl hitm"),
1370 .name = "mean_lcl",
1371 .cmp = empty_cmp,
1372 .entry = mean_lcl_entry,
1373 .width = 8,
1374};
1375
1376static struct c2c_dimension dim_mean_load = {
1377 .header = HEADER_SPAN_LOW("load"),
1378 .name = "mean_load",
1379 .cmp = empty_cmp,
1380 .entry = mean_load_entry,
1381 .width = 8,
1382};
1383
b6fe2bbc
JO
1384static struct c2c_dimension dim_cpucnt = {
1385 .header = HEADER_BOTH("cpu", "cnt"),
1386 .name = "cpucnt",
1387 .cmp = empty_cmp,
1388 .entry = cpucnt_entry,
1389 .width = 8,
1390};
1391
89d9ba8f
JO
1392static struct c2c_dimension dim_srcline = {
1393 .name = "cl_srcline",
1394 .se = &sort_srcline,
1395};
1396
c75540e3 1397static struct c2c_dimension *dimensions[] = {
cbb88500 1398 &dim_dcacheline,
48acdebd 1399 &dim_offset,
43575a95 1400 &dim_iaddr,
97cb486e
JO
1401 &dim_tot_hitm,
1402 &dim_lcl_hitm,
1403 &dim_rmt_hitm,
1404 &dim_cl_lcl_hitm,
1405 &dim_cl_rmt_hitm,
0f18896d
JO
1406 &dim_stores,
1407 &dim_stores_l1hit,
1408 &dim_stores_l1miss,
1409 &dim_cl_stores_l1hit,
1410 &dim_cl_stores_l1miss,
1295f685
JO
1411 &dim_ld_fbhit,
1412 &dim_ld_l1hit,
1413 &dim_ld_l2hit,
4d08910c
JO
1414 &dim_ld_llchit,
1415 &dim_ld_rmthit,
04402d20 1416 &dim_ld_llcmiss,
01b84d76 1417 &dim_tot_recs,
55177c4e 1418 &dim_tot_loads,
f0c50c15 1419 &dim_percent_hitm,
9cb3500a
JO
1420 &dim_percent_rmt_hitm,
1421 &dim_percent_lcl_hitm,
1422 &dim_percent_stores_l1hit,
1423 &dim_percent_stores_l1miss,
6c70f54c
JO
1424 &dim_dram_lcl,
1425 &dim_dram_rmt,
36d3deb9 1426 &dim_pid,
e87019c5 1427 &dim_tid,
51dedaa4
JO
1428 &dim_symbol,
1429 &dim_dso,
1e181b92 1430 &dim_node,
92062d54
JO
1431 &dim_mean_rmt,
1432 &dim_mean_lcl,
1433 &dim_mean_load,
b6fe2bbc 1434 &dim_cpucnt,
89d9ba8f 1435 &dim_srcline,
c75540e3
JO
1436 NULL,
1437};
1438
1439static void fmt_free(struct perf_hpp_fmt *fmt)
1440{
1441 struct c2c_fmt *c2c_fmt;
1442
1443 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1444 free(c2c_fmt);
1445}
1446
1447static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1448{
1449 struct c2c_fmt *c2c_a = container_of(a, struct c2c_fmt, fmt);
1450 struct c2c_fmt *c2c_b = container_of(b, struct c2c_fmt, fmt);
1451
1452 return c2c_a->dim == c2c_b->dim;
1453}
1454
1455static struct c2c_dimension *get_dimension(const char *name)
1456{
1457 unsigned int i;
1458
1459 for (i = 0; dimensions[i]; i++) {
1460 struct c2c_dimension *dim = dimensions[i];
1461
1462 if (!strcmp(dim->name, name))
1463 return dim;
1464 };
1465
1466 return NULL;
1467}
1468
8d3f938d
JO
1469static int c2c_se_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1470 struct hist_entry *he)
1471{
1472 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1473 struct c2c_dimension *dim = c2c_fmt->dim;
1474 size_t len = fmt->user_len;
1475
1476 if (!len)
1477 len = hists__col_len(he->hists, dim->se->se_width_idx);
1478
1479 return dim->se->se_snprintf(he, hpp->buf, hpp->size, len);
1480}
1481
1482static int64_t c2c_se_cmp(struct perf_hpp_fmt *fmt,
1483 struct hist_entry *a, struct hist_entry *b)
1484{
1485 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1486 struct c2c_dimension *dim = c2c_fmt->dim;
1487
1488 return dim->se->se_cmp(a, b);
1489}
1490
1491static int64_t c2c_se_collapse(struct perf_hpp_fmt *fmt,
1492 struct hist_entry *a, struct hist_entry *b)
1493{
1494 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1495 struct c2c_dimension *dim = c2c_fmt->dim;
1496 int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
1497
1498 collapse_fn = dim->se->se_collapse ?: dim->se->se_cmp;
1499 return collapse_fn(a, b);
1500}
1501
c75540e3
JO
1502static struct c2c_fmt *get_format(const char *name)
1503{
1504 struct c2c_dimension *dim = get_dimension(name);
1505 struct c2c_fmt *c2c_fmt;
1506 struct perf_hpp_fmt *fmt;
1507
1508 if (!dim)
1509 return NULL;
1510
1511 c2c_fmt = zalloc(sizeof(*c2c_fmt));
1512 if (!c2c_fmt)
1513 return NULL;
1514
1515 c2c_fmt->dim = dim;
1516
1517 fmt = &c2c_fmt->fmt;
1518 INIT_LIST_HEAD(&fmt->list);
1519 INIT_LIST_HEAD(&fmt->sort_list);
1520
8d3f938d
JO
1521 fmt->cmp = dim->se ? c2c_se_cmp : dim->cmp;
1522 fmt->sort = dim->se ? c2c_se_cmp : dim->cmp;
9cb3500a 1523 fmt->color = dim->se ? NULL : dim->color;
8d3f938d 1524 fmt->entry = dim->se ? c2c_se_entry : dim->entry;
c75540e3
JO
1525 fmt->header = c2c_header;
1526 fmt->width = c2c_width;
8d3f938d 1527 fmt->collapse = dim->se ? c2c_se_collapse : dim->cmp;
c75540e3
JO
1528 fmt->equal = fmt_equal;
1529 fmt->free = fmt_free;
1530
1531 return c2c_fmt;
1532}
1533
1534static int c2c_hists__init_output(struct perf_hpp_list *hpp_list, char *name)
1535{
1536 struct c2c_fmt *c2c_fmt = get_format(name);
1537
5f2eca83
JO
1538 if (!c2c_fmt) {
1539 reset_dimensions();
1540 return output_field_add(hpp_list, name);
1541 }
c75540e3
JO
1542
1543 perf_hpp_list__column_register(hpp_list, &c2c_fmt->fmt);
1544 return 0;
1545}
1546
1547static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name)
1548{
1549 struct c2c_fmt *c2c_fmt = get_format(name);
51dedaa4 1550 struct c2c_dimension *dim;
c75540e3 1551
5f2eca83
JO
1552 if (!c2c_fmt) {
1553 reset_dimensions();
1554 return sort_dimension__add(hpp_list, name, NULL, 0);
1555 }
c75540e3 1556
51dedaa4
JO
1557 dim = c2c_fmt->dim;
1558 if (dim == &dim_dso)
1559 hpp_list->dso = 1;
1560
c75540e3
JO
1561 perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
1562 return 0;
1563}
1564
1565#define PARSE_LIST(_list, _fn) \
1566 do { \
1567 char *tmp, *tok; \
1568 ret = 0; \
1569 \
1570 if (!_list) \
1571 break; \
1572 \
1573 for (tok = strtok_r((char *)_list, ", ", &tmp); \
1574 tok; tok = strtok_r(NULL, ", ", &tmp)) { \
1575 ret = _fn(hpp_list, tok); \
1576 if (ret == -EINVAL) { \
1577 error("Invalid --fields key: `%s'", tok); \
1578 break; \
1579 } else if (ret == -ESRCH) { \
1580 error("Unknown --fields key: `%s'", tok); \
1581 break; \
1582 } \
1583 } \
1584 } while (0)
1585
1586static int hpp_list__parse(struct perf_hpp_list *hpp_list,
1587 const char *output_,
1588 const char *sort_)
1589{
1590 char *output = output_ ? strdup(output_) : NULL;
1591 char *sort = sort_ ? strdup(sort_) : NULL;
1592 int ret;
1593
1594 PARSE_LIST(output, c2c_hists__init_output);
1595 PARSE_LIST(sort, c2c_hists__init_sort);
1596
1597 /* copy sort keys to output fields */
1598 perf_hpp__setup_output_field(hpp_list);
1599
1600 /*
1601 * We dont need other sorting keys other than those
1602 * we already specified. It also really slows down
1603 * the processing a lot with big number of output
1604 * fields, so switching this off for c2c.
1605 */
1606
1607#if 0
1608 /* and then copy output fields to sort keys */
1609 perf_hpp__append_sort_keys(&hists->list);
1610#endif
1611
1612 free(output);
1613 free(sort);
1614 return ret;
1615}
1616
1617static int c2c_hists__init(struct c2c_hists *hists,
1d62fcd6
JO
1618 const char *sort,
1619 int nr_header_lines)
c75540e3
JO
1620{
1621 __hists__init(&hists->hists, &hists->list);
1622
1623 /*
1624 * Initialize only with sort fields, we need to resort
1625 * later anyway, and that's where we add output fields
1626 * as well.
1627 */
1628 perf_hpp_list__init(&hists->list);
1629
1d62fcd6
JO
1630 /* Overload number of header lines.*/
1631 hists->list.nr_header_lines = nr_header_lines;
1632
c75540e3
JO
1633 return hpp_list__parse(&hists->list, NULL, sort);
1634}
1635
1636__maybe_unused
1637static int c2c_hists__reinit(struct c2c_hists *c2c_hists,
1638 const char *output,
1639 const char *sort)
1640{
1641 perf_hpp__reset_output_field(&c2c_hists->list);
1642 return hpp_list__parse(&c2c_hists->list, output, sort);
1643}
1644
89d9ba8f 1645static int filter_cb(struct hist_entry *he)
ec06f9b9 1646{
89d9ba8f
JO
1647 if (c2c.show_src && !he->srcline)
1648 he->srcline = hist_entry__get_srcline(he);
1649
ec06f9b9
JO
1650 return 0;
1651}
1652
1653static int resort_cl_cb(struct hist_entry *he)
1654{
1655 struct c2c_hist_entry *c2c_he;
1656 struct c2c_hists *c2c_hists;
1657
1658 c2c_he = container_of(he, struct c2c_hist_entry, he);
1659 c2c_hists = c2c_he->hists;
1660
1661 if (c2c_hists) {
22dd59d1
JO
1662 c2c_hists__reinit(c2c_hists,
1663 "percent_rmt_hitm,"
1664 "percent_lcl_hitm,"
1665 "percent_stores_l1hit,"
1666 "percent_stores_l1miss,"
1667 "offset,"
1668 "pid,"
1669 "tid,"
1670 "mean_rmt,"
1671 "mean_lcl,"
1672 "mean_load,"
1673 "cpucnt,"
1674 "symbol,"
1675 "dso,"
1676 "node",
1677 "offset,rmt_hitm,lcl_hitm");
1678
ec06f9b9
JO
1679 hists__collapse_resort(&c2c_hists->hists, NULL);
1680 hists__output_resort_cb(&c2c_hists->hists, NULL, filter_cb);
1681 }
1682
1683 return 0;
1684}
1685
1e181b92
JO
1686static void setup_nodes_header(void)
1687{
1688 dim_node.header = header_node[c2c.node_info];
1689}
1690
1691static int setup_nodes(struct perf_session *session)
1692{
1693 struct numa_node *n;
1694 unsigned long **nodes;
1695 int node, cpu;
1696 int *cpu2node;
1697
1698 if (c2c.node_info > 2)
1699 c2c.node_info = 2;
1700
1701 c2c.nodes_cnt = session->header.env.nr_numa_nodes;
1702 c2c.cpus_cnt = session->header.env.nr_cpus_online;
1703
1704 n = session->header.env.numa_nodes;
1705 if (!n)
1706 return -EINVAL;
1707
1708 nodes = zalloc(sizeof(unsigned long *) * c2c.nodes_cnt);
1709 if (!nodes)
1710 return -ENOMEM;
1711
1712 c2c.nodes = nodes;
1713
1714 cpu2node = zalloc(sizeof(int) * c2c.cpus_cnt);
1715 if (!cpu2node)
1716 return -ENOMEM;
1717
1718 for (cpu = 0; cpu < c2c.cpus_cnt; cpu++)
1719 cpu2node[cpu] = -1;
1720
1721 c2c.cpu2node = cpu2node;
1722
1723 for (node = 0; node < c2c.nodes_cnt; node++) {
1724 struct cpu_map *map = n[node].map;
1725 unsigned long *set;
1726
1727 set = bitmap_alloc(c2c.cpus_cnt);
1728 if (!set)
1729 return -ENOMEM;
1730
1731 for (cpu = 0; cpu < map->nr; cpu++) {
1732 set_bit(map->map[cpu], set);
1733
1734 if (WARN_ONCE(cpu2node[map->map[cpu]] != -1, "node/cpu topology bug"))
1735 return -EINVAL;
1736
1737 cpu2node[map->map[cpu]] = node;
1738 }
1739
1740 nodes[node] = set;
1741 }
1742
1743 setup_nodes_header();
1744 return 0;
1745}
1746
7ef2efaa
JO
1747#define HAS_HITMS(__h) ((__h)->stats.lcl_hitm || (__h)->stats.rmt_hitm)
1748
1749static int resort_hitm_cb(struct hist_entry *he)
1750{
1751 struct c2c_hist_entry *c2c_he;
1752 c2c_he = container_of(he, struct c2c_hist_entry, he);
1753
1754 if (HAS_HITMS(c2c_he)) {
1755 c2c.shared_clines++;
1756 c2c_add_stats(&c2c.hitm_stats, &c2c_he->stats);
1757 }
1758
1759 return 0;
1760}
1761
1762static int hists__iterate_cb(struct hists *hists, hists__resort_cb_t cb)
1763{
1764 struct rb_node *next = rb_first(&hists->entries);
1765 int ret = 0;
1766
1767 while (next) {
1768 struct hist_entry *he;
1769
1770 he = rb_entry(next, struct hist_entry, rb_node);
1771 ret = cb(he);
1772 if (ret)
1773 break;
1774 next = rb_next(&he->rb_node);
1775 }
1776
1777 return ret;
1778}
1779
74c63a25
JO
1780static void print_c2c__display_stats(FILE *out)
1781{
1782 int llc_misses;
1783 struct c2c_stats *stats = &c2c.hists.stats;
1784
1785 llc_misses = stats->lcl_dram +
1786 stats->rmt_dram +
1787 stats->rmt_hit +
1788 stats->rmt_hitm;
1789
1790 fprintf(out, "=================================================\n");
1791 fprintf(out, " Trace Event Information \n");
1792 fprintf(out, "=================================================\n");
1793 fprintf(out, " Total records : %10d\n", stats->nr_entries);
1794 fprintf(out, " Locked Load/Store Operations : %10d\n", stats->locks);
1795 fprintf(out, " Load Operations : %10d\n", stats->load);
1796 fprintf(out, " Loads - uncacheable : %10d\n", stats->ld_uncache);
1797 fprintf(out, " Loads - IO : %10d\n", stats->ld_io);
1798 fprintf(out, " Loads - Miss : %10d\n", stats->ld_miss);
1799 fprintf(out, " Loads - no mapping : %10d\n", stats->ld_noadrs);
1800 fprintf(out, " Load Fill Buffer Hit : %10d\n", stats->ld_fbhit);
1801 fprintf(out, " Load L1D hit : %10d\n", stats->ld_l1hit);
1802 fprintf(out, " Load L2D hit : %10d\n", stats->ld_l2hit);
1803 fprintf(out, " Load LLC hit : %10d\n", stats->ld_llchit + stats->lcl_hitm);
1804 fprintf(out, " Load Local HITM : %10d\n", stats->lcl_hitm);
1805 fprintf(out, " Load Remote HITM : %10d\n", stats->rmt_hitm);
1806 fprintf(out, " Load Remote HIT : %10d\n", stats->rmt_hit);
1807 fprintf(out, " Load Local DRAM : %10d\n", stats->lcl_dram);
1808 fprintf(out, " Load Remote DRAM : %10d\n", stats->rmt_dram);
1809 fprintf(out, " Load MESI State Exclusive : %10d\n", stats->ld_excl);
1810 fprintf(out, " Load MESI State Shared : %10d\n", stats->ld_shared);
1811 fprintf(out, " Load LLC Misses : %10d\n", llc_misses);
1812 fprintf(out, " LLC Misses to Local DRAM : %10.1f%%\n", ((double)stats->lcl_dram/(double)llc_misses) * 100.);
1813 fprintf(out, " LLC Misses to Remote DRAM : %10.1f%%\n", ((double)stats->rmt_dram/(double)llc_misses) * 100.);
1814 fprintf(out, " LLC Misses to Remote cache (HIT) : %10.1f%%\n", ((double)stats->rmt_hit /(double)llc_misses) * 100.);
1815 fprintf(out, " LLC Misses to Remote cache (HITM) : %10.1f%%\n", ((double)stats->rmt_hitm/(double)llc_misses) * 100.);
1816 fprintf(out, " Store Operations : %10d\n", stats->store);
1817 fprintf(out, " Store - uncacheable : %10d\n", stats->st_uncache);
1818 fprintf(out, " Store - no mapping : %10d\n", stats->st_noadrs);
1819 fprintf(out, " Store L1D Hit : %10d\n", stats->st_l1hit);
1820 fprintf(out, " Store L1D Miss : %10d\n", stats->st_l1miss);
1821 fprintf(out, " No Page Map Rejects : %10d\n", stats->nomap);
1822 fprintf(out, " Unable to parse data source : %10d\n", stats->noparse);
1823}
1824
7ef2efaa
JO
1825static void print_shared_cacheline_info(FILE *out)
1826{
1827 struct c2c_stats *stats = &c2c.hitm_stats;
1828 int hitm_cnt = stats->lcl_hitm + stats->rmt_hitm;
1829
1830 fprintf(out, "=================================================\n");
1831 fprintf(out, " Global Shared Cache Line Event Information \n");
1832 fprintf(out, "=================================================\n");
1833 fprintf(out, " Total Shared Cache Lines : %10d\n", c2c.shared_clines);
1834 fprintf(out, " Load HITs on shared lines : %10d\n", stats->load);
1835 fprintf(out, " Fill Buffer Hits on shared lines : %10d\n", stats->ld_fbhit);
1836 fprintf(out, " L1D hits on shared lines : %10d\n", stats->ld_l1hit);
1837 fprintf(out, " L2D hits on shared lines : %10d\n", stats->ld_l2hit);
1838 fprintf(out, " LLC hits on shared lines : %10d\n", stats->ld_llchit + stats->lcl_hitm);
1839 fprintf(out, " Locked Access on shared lines : %10d\n", stats->locks);
1840 fprintf(out, " Store HITs on shared lines : %10d\n", stats->store);
1841 fprintf(out, " Store L1D hits on shared lines : %10d\n", stats->st_l1hit);
1842 fprintf(out, " Total Merged records : %10d\n", hitm_cnt + stats->store);
1843}
1844
2d388bd0
JO
1845static void print_cacheline(struct c2c_hists *c2c_hists,
1846 struct hist_entry *he_cl,
1847 struct perf_hpp_list *hpp_list,
1848 FILE *out)
1849{
1850 char bf[1000];
1851 struct perf_hpp hpp = {
1852 .buf = bf,
1853 .size = 1000,
1854 };
1855 static bool once;
1856
1857 if (!once) {
1858 hists__fprintf_headers(&c2c_hists->hists, out);
1859 once = true;
1860 } else {
1861 fprintf(out, "\n");
1862 }
1863
1864 fprintf(out, " ------------------------------------------------------\n");
1865 __hist_entry__snprintf(he_cl, &hpp, hpp_list);
1866 fprintf(out, "%s\n", bf);
1867 fprintf(out, " ------------------------------------------------------\n");
1868
1869 hists__fprintf(&c2c_hists->hists, false, 0, 0, 0, out, true);
1870}
1871
1872static void print_pareto(FILE *out)
1873{
1874 struct perf_hpp_list hpp_list;
1875 struct rb_node *nd;
1876 int ret;
1877
1878 perf_hpp_list__init(&hpp_list);
1879 ret = hpp_list__parse(&hpp_list,
1880 "cl_rmt_hitm,"
1881 "cl_lcl_hitm,"
1882 "cl_stores_l1hit,"
1883 "cl_stores_l1miss,"
1884 "dcacheline",
1885 NULL);
1886
1887 if (WARN_ONCE(ret, "failed to setup sort entries\n"))
1888 return;
1889
1890 nd = rb_first(&c2c.hists.hists.entries);
1891
1892 for (; nd; nd = rb_next(nd)) {
1893 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
1894 struct c2c_hist_entry *c2c_he;
1895
1896 if (he->filtered)
1897 continue;
1898
1899 c2c_he = container_of(he, struct c2c_hist_entry, he);
1900 print_cacheline(c2c_he->hists, he, &hpp_list, out);
1901 }
1902}
1903
2709b97d
JO
1904static void print_c2c_info(FILE *out, struct perf_session *session)
1905{
1906 struct perf_evlist *evlist = session->evlist;
1907 struct perf_evsel *evsel;
1908 bool first = true;
1909
1910 fprintf(out, "=================================================\n");
1911 fprintf(out, " c2c details \n");
1912 fprintf(out, "=================================================\n");
1913
1914 evlist__for_each_entry(evlist, evsel) {
1915 fprintf(out, "%-36s: %s\n", first ? " Events" : "",
1916 perf_evsel__name(evsel));
1917 first = false;
1918 }
1919}
1920
1921static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
2d388bd0
JO
1922{
1923 setup_pager();
1924
74c63a25 1925 print_c2c__display_stats(out);
7ef2efaa
JO
1926 fprintf(out, "\n");
1927 print_shared_cacheline_info(out);
2709b97d
JO
1928 fprintf(out, "\n");
1929 print_c2c_info(out, session);
74c63a25
JO
1930
1931 if (c2c.stats_only)
1932 return;
1933
2d388bd0
JO
1934 fprintf(out, "\n");
1935 fprintf(out, "=================================================\n");
1936 fprintf(out, " Shared Data Cache Line Table \n");
1937 fprintf(out, "=================================================\n");
1938 fprintf(out, "#\n");
1939
1940 hists__fprintf(&c2c.hists.hists, true, 0, 0, 0, stdout, false);
1941
1942 fprintf(out, "\n");
1943 fprintf(out, "=================================================\n");
1944 fprintf(out, " Shared Cache Line Distribution Pareto \n");
1945 fprintf(out, "=================================================\n");
1946 fprintf(out, "#\n");
1947
1948 print_pareto(out);
1949}
1e181b92 1950
5a1a99cd
JO
1951#ifdef HAVE_SLANG_SUPPORT
1952static void c2c_browser__update_nr_entries(struct hist_browser *hb)
1953{
1954 u64 nr_entries = 0;
1955 struct rb_node *nd = rb_first(&hb->hists->entries);
1956
1957 while (nd) {
1958 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
1959
1960 if (!he->filtered)
1961 nr_entries++;
1962
1963 nd = rb_next(nd);
1964 }
1965
1966 hb->nr_non_filtered_entries = nr_entries;
1967}
1968
f1c5fd4d
JO
1969struct c2c_cacheline_browser {
1970 struct hist_browser hb;
1971 struct hist_entry *he;
1972};
1973
1974static int
1975perf_c2c_cacheline_browser__title(struct hist_browser *browser,
1976 char *bf, size_t size)
1977{
1978 struct c2c_cacheline_browser *cl_browser;
1979 struct hist_entry *he;
1980 uint64_t addr = 0;
1981
1982 cl_browser = container_of(browser, struct c2c_cacheline_browser, hb);
1983 he = cl_browser->he;
1984
1985 if (he->mem_info)
1986 addr = cl_address(he->mem_info->daddr.addr);
1987
1988 scnprintf(bf, size, "Cacheline 0x%lx", addr);
1989 return 0;
1990}
1991
1992static struct c2c_cacheline_browser*
1993c2c_cacheline_browser__new(struct hists *hists, struct hist_entry *he)
1994{
1995 struct c2c_cacheline_browser *browser;
1996
1997 browser = zalloc(sizeof(*browser));
1998 if (browser) {
1999 hist_browser__init(&browser->hb, hists);
2000 browser->hb.c2c_filter = true;
2001 browser->hb.title = perf_c2c_cacheline_browser__title;
2002 browser->he = he;
2003 }
2004
2005 return browser;
2006}
2007
2008static int perf_c2c__browse_cacheline(struct hist_entry *he)
2009{
2010 struct c2c_hist_entry *c2c_he;
2011 struct c2c_hists *c2c_hists;
2012 struct c2c_cacheline_browser *cl_browser;
2013 struct hist_browser *browser;
2014 int key = -1;
2015
2016 c2c_he = container_of(he, struct c2c_hist_entry, he);
2017 c2c_hists = c2c_he->hists;
2018
2019 cl_browser = c2c_cacheline_browser__new(&c2c_hists->hists, he);
2020 if (cl_browser == NULL)
2021 return -1;
2022
2023 browser = &cl_browser->hb;
2024
2025 /* reset abort key so that it can get Ctrl-C as a key */
2026 SLang_reset_tty();
2027 SLang_init_tty(0, 0, 0);
2028
2029 c2c_browser__update_nr_entries(browser);
2030
2031 while (1) {
2032 key = hist_browser__run(browser, "help");
2033
2034 switch (key) {
2035 case 'q':
2036 goto out;
2037 default:
2038 break;
2039 }
2040 }
2041
2042out:
2043 free(cl_browser);
2044 return 0;
2045}
2046
5a1a99cd
JO
2047static int perf_c2c_browser__title(struct hist_browser *browser,
2048 char *bf, size_t size)
2049{
2050 scnprintf(bf, size,
2051 "Shared Data Cache Line Table "
2052 "(%lu entries)", browser->nr_non_filtered_entries);
2053 return 0;
2054}
2055
2056static struct hist_browser*
2057perf_c2c_browser__new(struct hists *hists)
2058{
2059 struct hist_browser *browser = hist_browser__new(hists);
2060
2061 if (browser) {
2062 browser->title = perf_c2c_browser__title;
2063 browser->c2c_filter = true;
2064 }
2065
2066 return browser;
2067}
2068
2069static int perf_c2c__hists_browse(struct hists *hists)
2070{
2071 struct hist_browser *browser;
2072 int key = -1;
2073
2074 browser = perf_c2c_browser__new(hists);
2075 if (browser == NULL)
2076 return -1;
2077
2078 /* reset abort key so that it can get Ctrl-C as a key */
2079 SLang_reset_tty();
2080 SLang_init_tty(0, 0, 0);
2081
2082 c2c_browser__update_nr_entries(browser);
2083
2084 while (1) {
2085 key = hist_browser__run(browser, "help");
2086
2087 switch (key) {
2088 case 'q':
2089 goto out;
f1c5fd4d
JO
2090 case 'd':
2091 perf_c2c__browse_cacheline(browser->he_selection);
2092 break;
5a1a99cd
JO
2093 default:
2094 break;
2095 }
2096 }
2097
2098out:
2099 hist_browser__delete(browser);
2100 return 0;
2101}
2102
2709b97d 2103static void perf_c2c_display(struct perf_session *session)
5a1a99cd
JO
2104{
2105 if (c2c.use_stdio)
2709b97d 2106 perf_c2c__hists_fprintf(stdout, session);
5a1a99cd
JO
2107 else
2108 perf_c2c__hists_browse(&c2c.hists.hists);
2109}
2110#else
2709b97d 2111static void perf_c2c_display(struct perf_session *session)
5a1a99cd
JO
2112{
2113 use_browser = 0;
2709b97d 2114 perf_c2c__hists_fprintf(stdout, session);
5a1a99cd
JO
2115}
2116#endif /* HAVE_SLANG_SUPPORT */
2117
2118static void ui_quirks(void)
2119{
2120 if (!c2c.use_stdio) {
2121 dim_offset.width = 5;
2122 dim_offset.header = header_offset_tui;
2123 }
2124}
2125
dd805768
JO
2126#define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function,percent"
2127
2128const char callchain_help[] = "Display call graph (stack chain/backtrace):\n\n"
2129 CALLCHAIN_REPORT_HELP
2130 "\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT;
2131
2132static int
2133parse_callchain_opt(const struct option *opt, const char *arg, int unset)
2134{
2135 struct callchain_param *callchain = opt->value;
2136
2137 callchain->enabled = !unset;
2138 /*
2139 * --no-call-graph
2140 */
2141 if (unset) {
2142 symbol_conf.use_callchain = false;
2143 callchain->mode = CHAIN_NONE;
2144 return 0;
2145 }
2146
2147 return parse_callchain_report_opt(arg);
2148}
2149
2150static int setup_callchain(struct perf_evlist *evlist)
2151{
2152 u64 sample_type = perf_evlist__combined_sample_type(evlist);
2153 enum perf_call_graph_mode mode = CALLCHAIN_NONE;
2154
2155 if ((sample_type & PERF_SAMPLE_REGS_USER) &&
2156 (sample_type & PERF_SAMPLE_STACK_USER))
2157 mode = CALLCHAIN_DWARF;
2158 else if (sample_type & PERF_SAMPLE_BRANCH_STACK)
2159 mode = CALLCHAIN_LBR;
2160 else if (sample_type & PERF_SAMPLE_CALLCHAIN)
2161 mode = CALLCHAIN_FP;
2162
2163 if (!callchain_param.enabled &&
2164 callchain_param.mode != CHAIN_NONE &&
2165 mode != CALLCHAIN_NONE) {
2166 symbol_conf.use_callchain = true;
2167 if (callchain_register_param(&callchain_param) < 0) {
2168 ui__error("Can't register callchain params.\n");
2169 return -EINVAL;
2170 }
2171 }
2172
2173 callchain_param.record_mode = mode;
2174 callchain_param.min_percent = 0;
2175 return 0;
2176}
2177
903a6f15
JO
2178static int perf_c2c__report(int argc, const char **argv)
2179{
2180 struct perf_session *session;
78b27543 2181 struct ui_progress prog;
903a6f15
JO
2182 struct perf_data_file file = {
2183 .mode = PERF_DATA_MODE_READ,
2184 };
dd805768 2185 char callchain_default_opt[] = CALLCHAIN_DEFAULT_OPT;
903a6f15
JO
2186 const struct option c2c_options[] = {
2187 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
2188 "file", "vmlinux pathname"),
2189 OPT_INCR('v', "verbose", &verbose,
2190 "be more verbose (show counter open errors, etc)"),
2191 OPT_STRING('i', "input", &input_name, "file",
2192 "the input file to process"),
1e181b92
JO
2193 OPT_INCR('N', "node-info", &c2c.node_info,
2194 "show extra node info in report (repeat for more info)"),
5a1a99cd
JO
2195#ifdef HAVE_SLANG_SUPPORT
2196 OPT_BOOLEAN(0, "stdio", &c2c.use_stdio, "Use the stdio interface"),
2197#endif
74c63a25
JO
2198 OPT_BOOLEAN(0, "stats", &c2c.stats_only,
2199 "Use the stdio interface"),
dd805768
JO
2200 OPT_CALLBACK_DEFAULT('g', "call-graph", &callchain_param,
2201 "print_type,threshold[,print_limit],order,sort_key[,branch],value",
2202 callchain_help, &parse_callchain_opt,
2203 callchain_default_opt),
903a6f15
JO
2204 OPT_END()
2205 };
2206 int err = 0;
2207
2208 argc = parse_options(argc, argv, c2c_options, report_c2c_usage,
2209 PARSE_OPT_STOP_AT_NON_OPTION);
78b27543 2210 if (argc)
903a6f15
JO
2211 usage_with_options(report_c2c_usage, c2c_options);
2212
74c63a25
JO
2213 if (c2c.stats_only)
2214 c2c.use_stdio = true;
2215
5a1a99cd
JO
2216 if (c2c.use_stdio)
2217 use_browser = 0;
2218 else
2219 use_browser = 1;
2220
2221 setup_browser(false);
2222
78b27543
JO
2223 if (!input_name || !strlen(input_name))
2224 input_name = "perf.data";
2225
903a6f15
JO
2226 file.path = input_name;
2227
1d62fcd6 2228 err = c2c_hists__init(&c2c.hists, "dcacheline", 2);
c75540e3
JO
2229 if (err) {
2230 pr_debug("Failed to initialize hists\n");
2231 goto out;
2232 }
2233
903a6f15
JO
2234 session = perf_session__new(&file, 0, &c2c.tool);
2235 if (session == NULL) {
2236 pr_debug("No memory for session\n");
2237 goto out;
2238 }
1e181b92
JO
2239 err = setup_nodes(session);
2240 if (err) {
2241 pr_err("Failed setup nodes\n");
2242 goto out;
2243 }
903a6f15 2244
dd805768
JO
2245 err = setup_callchain(session->evlist);
2246 if (err)
2247 goto out_session;
2248
903a6f15
JO
2249 if (symbol__init(&session->header.env) < 0)
2250 goto out_session;
2251
2252 /* No pipe support at the moment. */
2253 if (perf_data_file__is_pipe(session->file)) {
2254 pr_debug("No pipe support at the moment.\n");
2255 goto out_session;
2256 }
2257
78b27543
JO
2258 err = perf_session__process_events(session);
2259 if (err) {
2260 pr_err("failed to process sample\n");
2261 goto out_session;
2262 }
2263
22dd59d1
JO
2264 c2c_hists__reinit(&c2c.hists,
2265 "dcacheline,"
2266 "tot_recs,"
2267 "percent_hitm,"
2268 "tot_hitm,lcl_hitm,rmt_hitm,"
2269 "stores,stores_l1hit,stores_l1miss,"
2270 "dram_lcl,dram_rmt,"
2271 "ld_llcmiss,"
2272 "tot_loads,"
2273 "ld_fbhit,ld_l1hit,ld_l2hit,"
2274 "ld_lclhit,ld_rmthit",
2275 "rmt_hitm"
2276 );
2277
78b27543
JO
2278 ui_progress__init(&prog, c2c.hists.hists.nr_entries, "Sorting...");
2279
2280 hists__collapse_resort(&c2c.hists.hists, NULL);
7ef2efaa
JO
2281 hists__output_resort_cb(&c2c.hists.hists, &prog, resort_hitm_cb);
2282 hists__iterate_cb(&c2c.hists.hists, resort_cl_cb);
78b27543
JO
2283
2284 ui_progress__finish();
2285
5a1a99cd
JO
2286 ui_quirks();
2287
2709b97d 2288 perf_c2c_display(session);
2d388bd0 2289
903a6f15
JO
2290out_session:
2291 perf_session__delete(session);
2292out:
2293 return err;
2294}
2295
39bcd4a4
JO
2296static int parse_record_events(const struct option *opt __maybe_unused,
2297 const char *str, int unset __maybe_unused)
2298{
2299 bool *event_set = (bool *) opt->value;
2300
2301 *event_set = true;
2302 return perf_mem_events__parse(str);
2303}
2304
2305
2306static const char * const __usage_record[] = {
2307 "perf c2c record [<options>] [<command>]",
2308 "perf c2c record [<options>] -- <command> [<options>]",
2309 NULL
2310};
2311
2312static const char * const *record_mem_usage = __usage_record;
2313
2314static int perf_c2c__record(int argc, const char **argv)
2315{
2316 int rec_argc, i = 0, j;
2317 const char **rec_argv;
2318 int ret;
2319 bool all_user = false, all_kernel = false;
2320 bool event_set = false;
2321 struct option options[] = {
2322 OPT_CALLBACK('e', "event", &event_set, "event",
2323 "event selector. Use 'perf mem record -e list' to list available events",
2324 parse_record_events),
2325 OPT_INCR('v', "verbose", &verbose,
2326 "be more verbose (show counter open errors, etc)"),
2327 OPT_BOOLEAN('u', "all-user", &all_user, "collect only user level data"),
2328 OPT_BOOLEAN('k', "all-kernel", &all_kernel, "collect only kernel level data"),
2329 OPT_UINTEGER('l', "ldlat", &perf_mem_events__loads_ldlat, "setup mem-loads latency"),
2330 OPT_END()
2331 };
2332
2333 if (perf_mem_events__init()) {
2334 pr_err("failed: memory events not supported\n");
2335 return -1;
2336 }
2337
2338 argc = parse_options(argc, argv, options, record_mem_usage,
2339 PARSE_OPT_KEEP_UNKNOWN);
2340
2341 rec_argc = argc + 10; /* max number of arguments */
2342 rec_argv = calloc(rec_argc + 1, sizeof(char *));
2343 if (!rec_argv)
2344 return -1;
2345
2346 rec_argv[i++] = "record";
2347
2348 if (!event_set) {
2349 perf_mem_events[PERF_MEM_EVENTS__LOAD].record = true;
2350 perf_mem_events[PERF_MEM_EVENTS__STORE].record = true;
2351 }
2352
2353 if (perf_mem_events[PERF_MEM_EVENTS__LOAD].record)
2354 rec_argv[i++] = "-W";
2355
2356 rec_argv[i++] = "-d";
2357 rec_argv[i++] = "--sample-cpu";
2358
2359 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
2360 if (!perf_mem_events[j].record)
2361 continue;
2362
2363 if (!perf_mem_events[j].supported) {
2364 pr_err("failed: event '%s' not supported\n",
2365 perf_mem_events[j].name);
2366 return -1;
2367 }
2368
2369 rec_argv[i++] = "-e";
2370 rec_argv[i++] = perf_mem_events__name(j);
2371 };
2372
2373 if (all_user)
2374 rec_argv[i++] = "--all-user";
2375
2376 if (all_kernel)
2377 rec_argv[i++] = "--all-kernel";
2378
2379 for (j = 0; j < argc; j++, i++)
2380 rec_argv[i] = argv[j];
2381
2382 if (verbose > 0) {
2383 pr_debug("calling: ");
2384
2385 j = 0;
2386
2387 while (rec_argv[j]) {
2388 pr_debug("%s ", rec_argv[j]);
2389 j++;
2390 }
2391 pr_debug("\n");
2392 }
2393
2394 ret = cmd_record(i, rec_argv, NULL);
2395 free(rec_argv);
2396 return ret;
2397}
2398
7aef3bf3
JO
2399int cmd_c2c(int argc, const char **argv, const char *prefix __maybe_unused)
2400{
2401 const struct option c2c_options[] = {
2402 OPT_INCR('v', "verbose", &verbose, "be more verbose"),
2403 OPT_END()
2404 };
2405
2406 argc = parse_options(argc, argv, c2c_options, c2c_usage,
2407 PARSE_OPT_STOP_AT_NON_OPTION);
39bcd4a4
JO
2408
2409 if (!argc)
2410 usage_with_options(c2c_usage, c2c_options);
2411
2412 if (!strncmp(argv[0], "rec", 3)) {
2413 return perf_c2c__record(argc, argv);
903a6f15
JO
2414 } else if (!strncmp(argv[0], "rep", 3)) {
2415 return perf_c2c__report(argc, argv);
39bcd4a4
JO
2416 } else {
2417 usage_with_options(c2c_usage, c2c_options);
2418 }
2419
7aef3bf3
JO
2420 return 0;
2421}