]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - tools/perf/builtin-c2c.c
perf c2c report: Set final resort fields
[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"
903a6f15 16
c75540e3
JO
17struct c2c_hists {
18 struct hists hists;
19 struct perf_hpp_list list;
b2252ae6 20 struct c2c_stats stats;
c75540e3
JO
21};
22
92062d54
JO
23struct compute_stats {
24 struct stats lcl_hitm;
25 struct stats rmt_hitm;
26 struct stats load;
27};
28
78b27543
JO
29struct c2c_hist_entry {
30 struct c2c_hists *hists;
b2252ae6 31 struct c2c_stats stats;
1e181b92
JO
32 unsigned long *cpuset;
33 struct c2c_stats *node_stats;
92062d54
JO
34
35 struct compute_stats cstats;
36
78b27543
JO
37 /*
38 * must be at the end,
39 * because of its callchain dynamic entry
40 */
41 struct hist_entry he;
42};
43
903a6f15 44struct perf_c2c {
c75540e3
JO
45 struct perf_tool tool;
46 struct c2c_hists hists;
1e181b92
JO
47
48 unsigned long **nodes;
49 int nodes_cnt;
50 int cpus_cnt;
51 int *cpu2node;
52 int node_info;
89d9ba8f
JO
53
54 bool show_src;
903a6f15
JO
55};
56
57static struct perf_c2c c2c;
7aef3bf3 58
78b27543
JO
59static void *c2c_he_zalloc(size_t size)
60{
61 struct c2c_hist_entry *c2c_he;
62
63 c2c_he = zalloc(size + sizeof(*c2c_he));
64 if (!c2c_he)
65 return NULL;
66
1e181b92
JO
67 c2c_he->cpuset = bitmap_alloc(c2c.cpus_cnt);
68 if (!c2c_he->cpuset)
69 return NULL;
70
71 c2c_he->node_stats = zalloc(c2c.nodes_cnt * sizeof(*c2c_he->node_stats));
72 if (!c2c_he->node_stats)
73 return NULL;
74
92062d54
JO
75 init_stats(&c2c_he->cstats.lcl_hitm);
76 init_stats(&c2c_he->cstats.rmt_hitm);
77 init_stats(&c2c_he->cstats.load);
78
78b27543
JO
79 return &c2c_he->he;
80}
81
82static void c2c_he_free(void *he)
83{
84 struct c2c_hist_entry *c2c_he;
85
86 c2c_he = container_of(he, struct c2c_hist_entry, he);
87 if (c2c_he->hists) {
88 hists__delete_entries(&c2c_he->hists->hists);
89 free(c2c_he->hists);
90 }
91
1e181b92
JO
92 free(c2c_he->cpuset);
93 free(c2c_he->node_stats);
78b27543
JO
94 free(c2c_he);
95}
96
97static struct hist_entry_ops c2c_entry_ops = {
98 .new = c2c_he_zalloc,
99 .free = c2c_he_free,
100};
101
ec06f9b9 102static int c2c_hists__init(struct c2c_hists *hists,
1d62fcd6
JO
103 const char *sort,
104 int nr_header_lines);
ec06f9b9 105
b2252ae6
JO
106static struct c2c_hists*
107he__get_c2c_hists(struct hist_entry *he,
1d62fcd6
JO
108 const char *sort,
109 int nr_header_lines)
ec06f9b9
JO
110{
111 struct c2c_hist_entry *c2c_he;
112 struct c2c_hists *hists;
113 int ret;
114
115 c2c_he = container_of(he, struct c2c_hist_entry, he);
116 if (c2c_he->hists)
b2252ae6 117 return c2c_he->hists;
ec06f9b9
JO
118
119 hists = c2c_he->hists = zalloc(sizeof(*hists));
120 if (!hists)
121 return NULL;
122
1d62fcd6 123 ret = c2c_hists__init(hists, sort, nr_header_lines);
ec06f9b9
JO
124 if (ret) {
125 free(hists);
126 return NULL;
127 }
128
b2252ae6 129 return hists;
ec06f9b9
JO
130}
131
1e181b92
JO
132static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he,
133 struct perf_sample *sample)
134{
135 if (WARN_ONCE(sample->cpu == (unsigned int) -1,
136 "WARNING: no sample cpu value"))
137 return;
138
139 set_bit(sample->cpu, c2c_he->cpuset);
140}
141
92062d54
JO
142static void compute_stats(struct c2c_hist_entry *c2c_he,
143 struct c2c_stats *stats,
144 u64 weight)
145{
146 struct compute_stats *cstats = &c2c_he->cstats;
147
148 if (stats->rmt_hitm)
149 update_stats(&cstats->rmt_hitm, weight);
150 else if (stats->lcl_hitm)
151 update_stats(&cstats->lcl_hitm, weight);
152 else if (stats->load)
153 update_stats(&cstats->load, weight);
154}
155
78b27543
JO
156static int process_sample_event(struct perf_tool *tool __maybe_unused,
157 union perf_event *event,
158 struct perf_sample *sample,
159 struct perf_evsel *evsel __maybe_unused,
160 struct machine *machine)
161{
b2252ae6
JO
162 struct c2c_hists *c2c_hists = &c2c.hists;
163 struct c2c_hist_entry *c2c_he;
164 struct c2c_stats stats = { .nr_entries = 0, };
78b27543
JO
165 struct hist_entry *he;
166 struct addr_location al;
ec06f9b9 167 struct mem_info *mi, *mi_dup;
78b27543
JO
168 int ret;
169
170 if (machine__resolve(machine, &al, sample) < 0) {
171 pr_debug("problem processing %d event, skipping it.\n",
172 event->header.type);
173 return -1;
174 }
175
176 mi = sample__resolve_mem(sample, &al);
177 if (mi == NULL)
178 return -ENOMEM;
179
ec06f9b9
JO
180 mi_dup = memdup(mi, sizeof(*mi));
181 if (!mi_dup)
182 goto free_mi;
183
b2252ae6
JO
184 c2c_decode_stats(&stats, mi);
185
186 he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
78b27543
JO
187 &al, NULL, NULL, mi,
188 sample, true);
ec06f9b9
JO
189 if (he == NULL)
190 goto free_mi_dup;
78b27543 191
b2252ae6
JO
192 c2c_he = container_of(he, struct c2c_hist_entry, he);
193 c2c_add_stats(&c2c_he->stats, &stats);
194 c2c_add_stats(&c2c_hists->stats, &stats);
195
1e181b92
JO
196 c2c_he__set_cpu(c2c_he, sample);
197
b2252ae6 198 hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
78b27543
JO
199 ret = hist_entry__append_callchain(he, sample);
200
ec06f9b9 201 if (!ret) {
1e181b92
JO
202 /*
203 * There's already been warning about missing
204 * sample's cpu value. Let's account all to
205 * node 0 in this case, without any further
206 * warning.
207 *
208 * Doing node stats only for single callchain data.
209 */
210 int cpu = sample->cpu == (unsigned int) -1 ? 0 : sample->cpu;
211 int node = c2c.cpu2node[cpu];
212
ec06f9b9
JO
213 mi = mi_dup;
214
215 mi_dup = memdup(mi, sizeof(*mi));
216 if (!mi_dup)
217 goto free_mi;
218
1d62fcd6 219 c2c_hists = he__get_c2c_hists(he, "offset", 2);
b2252ae6 220 if (!c2c_hists)
ec06f9b9
JO
221 goto free_mi_dup;
222
b2252ae6 223 he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
ec06f9b9
JO
224 &al, NULL, NULL, mi,
225 sample, true);
226 if (he == NULL)
227 goto free_mi_dup;
228
b2252ae6
JO
229 c2c_he = container_of(he, struct c2c_hist_entry, he);
230 c2c_add_stats(&c2c_he->stats, &stats);
231 c2c_add_stats(&c2c_hists->stats, &stats);
1e181b92
JO
232 c2c_add_stats(&c2c_he->node_stats[node], &stats);
233
92062d54
JO
234 compute_stats(c2c_he, &stats, sample->weight);
235
1e181b92 236 c2c_he__set_cpu(c2c_he, sample);
b2252ae6
JO
237
238 hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
ec06f9b9
JO
239 ret = hist_entry__append_callchain(he, sample);
240 }
241
242out:
78b27543
JO
243 addr_location__put(&al);
244 return ret;
ec06f9b9
JO
245
246free_mi_dup:
247 free(mi_dup);
248free_mi:
249 free(mi);
250 ret = -ENOMEM;
251 goto out;
78b27543
JO
252}
253
254static struct perf_c2c c2c = {
255 .tool = {
256 .sample = process_sample_event,
257 .mmap = perf_event__process_mmap,
258 .mmap2 = perf_event__process_mmap2,
259 .comm = perf_event__process_comm,
260 .exit = perf_event__process_exit,
261 .fork = perf_event__process_fork,
262 .lost = perf_event__process_lost,
263 .ordered_events = true,
264 .ordering_requires_timestamps = true,
265 },
266};
267
7aef3bf3 268static const char * const c2c_usage[] = {
903a6f15 269 "perf c2c {record|report}",
7aef3bf3
JO
270 NULL
271};
272
903a6f15
JO
273static const char * const __usage_report[] = {
274 "perf c2c report",
275 NULL
276};
277
278static const char * const *report_c2c_usage = __usage_report;
279
c75540e3
JO
280#define C2C_HEADER_MAX 2
281
282struct c2c_header {
283 struct {
284 const char *text;
285 int span;
286 } line[C2C_HEADER_MAX];
287};
288
289struct c2c_dimension {
290 struct c2c_header header;
291 const char *name;
292 int width;
8d3f938d 293 struct sort_entry *se;
c75540e3
JO
294
295 int64_t (*cmp)(struct perf_hpp_fmt *fmt,
296 struct hist_entry *, struct hist_entry *);
297 int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
298 struct hist_entry *he);
299 int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
300 struct hist_entry *he);
301};
302
303struct c2c_fmt {
304 struct perf_hpp_fmt fmt;
305 struct c2c_dimension *dim;
306};
307
308static int c2c_width(struct perf_hpp_fmt *fmt,
309 struct perf_hpp *hpp __maybe_unused,
310 struct hists *hists __maybe_unused)
311{
312 struct c2c_fmt *c2c_fmt;
8d3f938d 313 struct c2c_dimension *dim;
c75540e3
JO
314
315 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
8d3f938d
JO
316 dim = c2c_fmt->dim;
317
318 return dim->se ? hists__col_len(hists, dim->se->se_width_idx) :
319 c2c_fmt->dim->width;
c75540e3
JO
320}
321
322static int c2c_header(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
8d3f938d 323 struct hists *hists, int line, int *span)
c75540e3 324{
8d3f938d 325 struct perf_hpp_list *hpp_list = hists->hpp_list;
c75540e3
JO
326 struct c2c_fmt *c2c_fmt;
327 struct c2c_dimension *dim;
8d3f938d
JO
328 const char *text = NULL;
329 int width = c2c_width(fmt, hpp, hists);
c75540e3
JO
330
331 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
332 dim = c2c_fmt->dim;
333
8d3f938d
JO
334 if (dim->se) {
335 text = dim->header.line[line].text;
336 /* Use the last line from sort_entry if not defined. */
337 if (!text && (line == hpp_list->nr_header_lines - 1))
338 text = dim->se->se_header;
c75540e3 339 } else {
8d3f938d
JO
340 text = dim->header.line[line].text;
341
342 if (*span) {
343 (*span)--;
344 return 0;
345 } else {
346 *span = dim->header.line[line].span;
347 }
c75540e3
JO
348 }
349
8d3f938d
JO
350 if (text == NULL)
351 text = "";
352
353 return scnprintf(hpp->buf, hpp->size, "%*s", width, text);
c75540e3
JO
354}
355
cbb88500
JO
356#define HEX_STR(__s, __v) \
357({ \
358 scnprintf(__s, sizeof(__s), "0x%" PRIx64, __v); \
359 __s; \
360})
361
362static int64_t
363dcacheline_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
364 struct hist_entry *left, struct hist_entry *right)
365{
366 return sort__dcacheline_cmp(left, right);
367}
368
369static int dcacheline_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
370 struct hist_entry *he)
371{
372 uint64_t addr = 0;
373 int width = c2c_width(fmt, hpp, he->hists);
374 char buf[20];
375
376 if (he->mem_info)
377 addr = cl_address(he->mem_info->daddr.addr);
378
379 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
380}
381
48acdebd
JO
382static int offset_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
383 struct hist_entry *he)
384{
385 uint64_t addr = 0;
386 int width = c2c_width(fmt, hpp, he->hists);
387 char buf[20];
388
389 if (he->mem_info)
390 addr = cl_offset(he->mem_info->daddr.al_addr);
391
392 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
393}
394
395static int64_t
396offset_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
397 struct hist_entry *left, struct hist_entry *right)
398{
399 uint64_t l = 0, r = 0;
400
401 if (left->mem_info)
402 l = cl_offset(left->mem_info->daddr.addr);
403 if (right->mem_info)
404 r = cl_offset(right->mem_info->daddr.addr);
405
406 return (int64_t)(r - l);
407}
408
43575a95
JO
409static int
410iaddr_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
411 struct hist_entry *he)
412{
413 uint64_t addr = 0;
414 int width = c2c_width(fmt, hpp, he->hists);
415 char buf[20];
416
417 if (he->mem_info)
418 addr = he->mem_info->iaddr.addr;
419
420 return scnprintf(hpp->buf, hpp->size, "%*s", width, HEX_STR(buf, addr));
421}
422
423static int64_t
424iaddr_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
425 struct hist_entry *left, struct hist_entry *right)
426{
427 return sort__iaddr_cmp(left, right);
428}
429
97cb486e
JO
430static int
431tot_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
432 struct hist_entry *he)
433{
434 struct c2c_hist_entry *c2c_he;
435 int width = c2c_width(fmt, hpp, he->hists);
436 unsigned int tot_hitm;
437
438 c2c_he = container_of(he, struct c2c_hist_entry, he);
439 tot_hitm = c2c_he->stats.lcl_hitm + c2c_he->stats.rmt_hitm;
440
441 return scnprintf(hpp->buf, hpp->size, "%*u", width, tot_hitm);
442}
443
444static int64_t
445tot_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
446 struct hist_entry *left, struct hist_entry *right)
447{
448 struct c2c_hist_entry *c2c_left;
449 struct c2c_hist_entry *c2c_right;
450 unsigned int tot_hitm_left;
451 unsigned int tot_hitm_right;
452
453 c2c_left = container_of(left, struct c2c_hist_entry, he);
454 c2c_right = container_of(right, struct c2c_hist_entry, he);
455
456 tot_hitm_left = c2c_left->stats.lcl_hitm + c2c_left->stats.rmt_hitm;
457 tot_hitm_right = c2c_right->stats.lcl_hitm + c2c_right->stats.rmt_hitm;
458
459 return tot_hitm_left - tot_hitm_right;
460}
461
462#define STAT_FN_ENTRY(__f) \
463static int \
464__f ## _entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, \
465 struct hist_entry *he) \
466{ \
467 struct c2c_hist_entry *c2c_he; \
468 int width = c2c_width(fmt, hpp, he->hists); \
469 \
470 c2c_he = container_of(he, struct c2c_hist_entry, he); \
471 return scnprintf(hpp->buf, hpp->size, "%*u", width, \
472 c2c_he->stats.__f); \
473}
474
475#define STAT_FN_CMP(__f) \
476static int64_t \
477__f ## _cmp(struct perf_hpp_fmt *fmt __maybe_unused, \
478 struct hist_entry *left, struct hist_entry *right) \
479{ \
480 struct c2c_hist_entry *c2c_left, *c2c_right; \
481 \
482 c2c_left = container_of(left, struct c2c_hist_entry, he); \
483 c2c_right = container_of(right, struct c2c_hist_entry, he); \
484 return c2c_left->stats.__f - c2c_right->stats.__f; \
485}
486
487#define STAT_FN(__f) \
488 STAT_FN_ENTRY(__f) \
489 STAT_FN_CMP(__f)
490
491STAT_FN(rmt_hitm)
492STAT_FN(lcl_hitm)
0f18896d
JO
493STAT_FN(store)
494STAT_FN(st_l1hit)
495STAT_FN(st_l1miss)
1295f685
JO
496STAT_FN(ld_fbhit)
497STAT_FN(ld_l1hit)
498STAT_FN(ld_l2hit)
4d08910c
JO
499STAT_FN(ld_llchit)
500STAT_FN(rmt_hit)
97cb486e 501
04402d20
JO
502static uint64_t llc_miss(struct c2c_stats *stats)
503{
504 uint64_t llcmiss;
505
506 llcmiss = stats->lcl_dram +
507 stats->rmt_dram +
508 stats->rmt_hitm +
509 stats->rmt_hit;
510
511 return llcmiss;
512}
513
514static int
515ld_llcmiss_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
516 struct hist_entry *he)
517{
518 struct c2c_hist_entry *c2c_he;
519 int width = c2c_width(fmt, hpp, he->hists);
520
521 c2c_he = container_of(he, struct c2c_hist_entry, he);
522
523 return scnprintf(hpp->buf, hpp->size, "%*lu", width,
524 llc_miss(&c2c_he->stats));
525}
526
527static int64_t
528ld_llcmiss_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
529 struct hist_entry *left, struct hist_entry *right)
530{
531 struct c2c_hist_entry *c2c_left;
532 struct c2c_hist_entry *c2c_right;
533
534 c2c_left = container_of(left, struct c2c_hist_entry, he);
535 c2c_right = container_of(right, struct c2c_hist_entry, he);
536
537 return llc_miss(&c2c_left->stats) - llc_miss(&c2c_right->stats);
538}
539
01b84d76
JO
540static uint64_t total_records(struct c2c_stats *stats)
541{
542 uint64_t lclmiss, ldcnt, total;
543
544 lclmiss = stats->lcl_dram +
545 stats->rmt_dram +
546 stats->rmt_hitm +
547 stats->rmt_hit;
548
549 ldcnt = lclmiss +
550 stats->ld_fbhit +
551 stats->ld_l1hit +
552 stats->ld_l2hit +
553 stats->ld_llchit +
554 stats->lcl_hitm;
555
556 total = ldcnt +
557 stats->st_l1hit +
558 stats->st_l1miss;
559
560 return total;
561}
562
563static int
564tot_recs_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
565 struct hist_entry *he)
566{
567 struct c2c_hist_entry *c2c_he;
568 int width = c2c_width(fmt, hpp, he->hists);
569 uint64_t tot_recs;
570
571 c2c_he = container_of(he, struct c2c_hist_entry, he);
572 tot_recs = total_records(&c2c_he->stats);
573
574 return scnprintf(hpp->buf, hpp->size, "%*" PRIu64, width, tot_recs);
575}
576
577static int64_t
578tot_recs_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
579 struct hist_entry *left, struct hist_entry *right)
580{
581 struct c2c_hist_entry *c2c_left;
582 struct c2c_hist_entry *c2c_right;
583 uint64_t tot_recs_left;
584 uint64_t tot_recs_right;
585
586 c2c_left = container_of(left, struct c2c_hist_entry, he);
587 c2c_right = container_of(right, struct c2c_hist_entry, he);
588
589 tot_recs_left = total_records(&c2c_left->stats);
590 tot_recs_right = total_records(&c2c_right->stats);
591
592 return tot_recs_left - tot_recs_right;
593}
594
55177c4e
JO
595static uint64_t total_loads(struct c2c_stats *stats)
596{
597 uint64_t lclmiss, ldcnt;
598
599 lclmiss = stats->lcl_dram +
600 stats->rmt_dram +
601 stats->rmt_hitm +
602 stats->rmt_hit;
603
604 ldcnt = lclmiss +
605 stats->ld_fbhit +
606 stats->ld_l1hit +
607 stats->ld_l2hit +
608 stats->ld_llchit +
609 stats->lcl_hitm;
610
611 return ldcnt;
612}
613
614static int
615tot_loads_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
616 struct hist_entry *he)
617{
618 struct c2c_hist_entry *c2c_he;
619 int width = c2c_width(fmt, hpp, he->hists);
620 uint64_t tot_recs;
621
622 c2c_he = container_of(he, struct c2c_hist_entry, he);
623 tot_recs = total_loads(&c2c_he->stats);
624
625 return scnprintf(hpp->buf, hpp->size, "%*" PRIu64, width, tot_recs);
626}
627
628static int64_t
629tot_loads_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
630 struct hist_entry *left, struct hist_entry *right)
631{
632 struct c2c_hist_entry *c2c_left;
633 struct c2c_hist_entry *c2c_right;
634 uint64_t tot_recs_left;
635 uint64_t tot_recs_right;
636
637 c2c_left = container_of(left, struct c2c_hist_entry, he);
638 c2c_right = container_of(right, struct c2c_hist_entry, he);
639
640 tot_recs_left = total_loads(&c2c_left->stats);
641 tot_recs_right = total_loads(&c2c_right->stats);
642
643 return tot_recs_left - tot_recs_right;
644}
645
f0c50c15
JO
646typedef double (get_percent_cb)(struct c2c_hist_entry *);
647
648static int
649percent_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
650 struct hist_entry *he, get_percent_cb get_percent)
651{
652 struct c2c_hist_entry *c2c_he;
653 int width = c2c_width(fmt, hpp, he->hists);
654 double per;
655
656 c2c_he = container_of(he, struct c2c_hist_entry, he);
657 per = get_percent(c2c_he);
658
659 return hpp_color_scnprintf(hpp, "%*.2f%%", width - 1, per);
660}
661
662static double percent_hitm(struct c2c_hist_entry *c2c_he)
663{
664 struct c2c_hists *hists;
665 struct c2c_stats *stats;
666 struct c2c_stats *total;
667 int tot, st;
668 double p;
669
670 hists = container_of(c2c_he->he.hists, struct c2c_hists, hists);
671 stats = &c2c_he->stats;
672 total = &hists->stats;
673
674 st = stats->rmt_hitm;
675 tot = total->rmt_hitm;
676
677 p = tot ? (double) st / tot : 0;
678
679 return 100 * p;
680}
681
682#define PERC_STR(__s, __v) \
683({ \
684 scnprintf(__s, sizeof(__s), "%.2F%%", __v); \
685 __s; \
686})
687
688static int
689percent_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
690 struct hist_entry *he)
691{
692 struct c2c_hist_entry *c2c_he;
693 int width = c2c_width(fmt, hpp, he->hists);
694 char buf[10];
695 double per;
696
697 c2c_he = container_of(he, struct c2c_hist_entry, he);
698 per = percent_hitm(c2c_he);
699 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
700}
701
702static int
703percent_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
704 struct hist_entry *he)
705{
706 return percent_color(fmt, hpp, he, percent_hitm);
707}
708
709static int64_t
710percent_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
711 struct hist_entry *left, struct hist_entry *right)
712{
713 struct c2c_hist_entry *c2c_left;
714 struct c2c_hist_entry *c2c_right;
715 double per_left;
716 double per_right;
717
718 c2c_left = container_of(left, struct c2c_hist_entry, he);
719 c2c_right = container_of(right, struct c2c_hist_entry, he);
720
721 per_left = percent_hitm(c2c_left);
722 per_right = percent_hitm(c2c_right);
723
724 return per_left - per_right;
725}
726
9cb3500a
JO
727static struct c2c_stats *he_stats(struct hist_entry *he)
728{
729 struct c2c_hist_entry *c2c_he;
730
731 c2c_he = container_of(he, struct c2c_hist_entry, he);
732 return &c2c_he->stats;
733}
734
735static struct c2c_stats *total_stats(struct hist_entry *he)
736{
737 struct c2c_hists *hists;
738
739 hists = container_of(he->hists, struct c2c_hists, hists);
740 return &hists->stats;
741}
742
743static double percent(int st, int tot)
744{
745 return tot ? 100. * (double) st / (double) tot : 0;
746}
747
748#define PERCENT(__h, __f) percent(he_stats(__h)->__f, total_stats(__h)->__f)
749
750#define PERCENT_FN(__f) \
751static double percent_ ## __f(struct c2c_hist_entry *c2c_he) \
752{ \
753 struct c2c_hists *hists; \
754 \
755 hists = container_of(c2c_he->he.hists, struct c2c_hists, hists); \
756 return percent(c2c_he->stats.__f, hists->stats.__f); \
757}
758
759PERCENT_FN(rmt_hitm)
760PERCENT_FN(lcl_hitm)
761PERCENT_FN(st_l1hit)
762PERCENT_FN(st_l1miss)
763
764static int
765percent_rmt_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
766 struct hist_entry *he)
767{
768 int width = c2c_width(fmt, hpp, he->hists);
769 double per = PERCENT(he, rmt_hitm);
770 char buf[10];
771
772 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
773}
774
775static int
776percent_rmt_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
777 struct hist_entry *he)
778{
779 return percent_color(fmt, hpp, he, percent_rmt_hitm);
780}
781
782static int64_t
783percent_rmt_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
784 struct hist_entry *left, struct hist_entry *right)
785{
786 double per_left;
787 double per_right;
788
789 per_left = PERCENT(left, lcl_hitm);
790 per_right = PERCENT(right, lcl_hitm);
791
792 return per_left - per_right;
793}
794
795static int
796percent_lcl_hitm_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
797 struct hist_entry *he)
798{
799 int width = c2c_width(fmt, hpp, he->hists);
800 double per = PERCENT(he, lcl_hitm);
801 char buf[10];
802
803 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
804}
805
806static int
807percent_lcl_hitm_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
808 struct hist_entry *he)
809{
810 return percent_color(fmt, hpp, he, percent_lcl_hitm);
811}
812
813static int64_t
814percent_lcl_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
815 struct hist_entry *left, struct hist_entry *right)
816{
817 double per_left;
818 double per_right;
819
820 per_left = PERCENT(left, lcl_hitm);
821 per_right = PERCENT(right, lcl_hitm);
822
823 return per_left - per_right;
824}
825
826static int
827percent_stores_l1hit_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
828 struct hist_entry *he)
829{
830 int width = c2c_width(fmt, hpp, he->hists);
831 double per = PERCENT(he, st_l1hit);
832 char buf[10];
833
834 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
835}
836
837static int
838percent_stores_l1hit_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
839 struct hist_entry *he)
840{
841 return percent_color(fmt, hpp, he, percent_st_l1hit);
842}
843
844static int64_t
845percent_stores_l1hit_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
846 struct hist_entry *left, struct hist_entry *right)
847{
848 double per_left;
849 double per_right;
850
851 per_left = PERCENT(left, st_l1hit);
852 per_right = PERCENT(right, st_l1hit);
853
854 return per_left - per_right;
855}
856
857static int
858percent_stores_l1miss_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
859 struct hist_entry *he)
860{
861 int width = c2c_width(fmt, hpp, he->hists);
862 double per = PERCENT(he, st_l1miss);
863 char buf[10];
864
865 return scnprintf(hpp->buf, hpp->size, "%*s", width, PERC_STR(buf, per));
866}
867
868static int
869percent_stores_l1miss_color(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
870 struct hist_entry *he)
871{
872 return percent_color(fmt, hpp, he, percent_st_l1miss);
873}
874
875static int64_t
876percent_stores_l1miss_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
877 struct hist_entry *left, struct hist_entry *right)
878{
879 double per_left;
880 double per_right;
881
882 per_left = PERCENT(left, st_l1miss);
883 per_right = PERCENT(right, st_l1miss);
884
885 return per_left - per_right;
886}
887
6c70f54c
JO
888STAT_FN(lcl_dram)
889STAT_FN(rmt_dram)
890
36d3deb9
JO
891static int
892pid_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
893 struct hist_entry *he)
894{
895 int width = c2c_width(fmt, hpp, he->hists);
896
897 return scnprintf(hpp->buf, hpp->size, "%*d", width, he->thread->pid_);
898}
899
900static int64_t
901pid_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
902 struct hist_entry *left, struct hist_entry *right)
903{
904 return left->thread->pid_ - right->thread->pid_;
905}
906
1e181b92
JO
907static int64_t
908empty_cmp(struct perf_hpp_fmt *fmt __maybe_unused,
909 struct hist_entry *left __maybe_unused,
910 struct hist_entry *right __maybe_unused)
911{
912 return 0;
913}
914
915static int
916node_entry(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
917 struct hist_entry *he)
918{
919 struct c2c_hist_entry *c2c_he;
920 bool first = true;
921 int node;
922 int ret = 0;
923
924 c2c_he = container_of(he, struct c2c_hist_entry, he);
925
926 for (node = 0; node < c2c.nodes_cnt; node++) {
927 DECLARE_BITMAP(set, c2c.cpus_cnt);
928
929 bitmap_zero(set, c2c.cpus_cnt);
930 bitmap_and(set, c2c_he->cpuset, c2c.nodes[node], c2c.cpus_cnt);
931
932 if (!bitmap_weight(set, c2c.cpus_cnt)) {
933 if (c2c.node_info == 1) {
934 ret = scnprintf(hpp->buf, hpp->size, "%21s", " ");
935 advance_hpp(hpp, ret);
936 }
937 continue;
938 }
939
940 if (!first) {
941 ret = scnprintf(hpp->buf, hpp->size, " ");
942 advance_hpp(hpp, ret);
943 }
944
945 switch (c2c.node_info) {
946 case 0:
947 ret = scnprintf(hpp->buf, hpp->size, "%2d", node);
948 advance_hpp(hpp, ret);
949 break;
950 case 1:
951 {
952 int num = bitmap_weight(c2c_he->cpuset, c2c.cpus_cnt);
953 struct c2c_stats *stats = &c2c_he->node_stats[node];
954
955 ret = scnprintf(hpp->buf, hpp->size, "%2d{%2d ", node, num);
956 advance_hpp(hpp, ret);
957
958
959 if (c2c_he->stats.rmt_hitm > 0) {
960 ret = scnprintf(hpp->buf, hpp->size, "%5.1f%% ",
961 percent(stats->rmt_hitm, c2c_he->stats.rmt_hitm));
962 } else {
963 ret = scnprintf(hpp->buf, hpp->size, "%6s ", "n/a");
964 }
965
966 advance_hpp(hpp, ret);
967
968 if (c2c_he->stats.store > 0) {
969 ret = scnprintf(hpp->buf, hpp->size, "%5.1f%%}",
970 percent(stats->store, c2c_he->stats.store));
971 } else {
972 ret = scnprintf(hpp->buf, hpp->size, "%6s}", "n/a");
973 }
974
975 advance_hpp(hpp, ret);
976 break;
977 }
978 case 2:
979 ret = scnprintf(hpp->buf, hpp->size, "%2d{", node);
980 advance_hpp(hpp, ret);
981
982 ret = bitmap_scnprintf(set, c2c.cpus_cnt, hpp->buf, hpp->size);
983 advance_hpp(hpp, ret);
984
985 ret = scnprintf(hpp->buf, hpp->size, "}");
986 advance_hpp(hpp, ret);
987 break;
988 default:
989 break;
990 }
991
992 first = false;
993 }
994
995 return 0;
996}
997
92062d54
JO
998static int
999mean_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1000 struct hist_entry *he, double mean)
1001{
1002 int width = c2c_width(fmt, hpp, he->hists);
1003 char buf[10];
1004
1005 scnprintf(buf, 10, "%6.0f", mean);
1006 return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1007}
1008
1009#define MEAN_ENTRY(__func, __val) \
1010static int \
1011__func(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, struct hist_entry *he) \
1012{ \
1013 struct c2c_hist_entry *c2c_he; \
1014 c2c_he = container_of(he, struct c2c_hist_entry, he); \
1015 return mean_entry(fmt, hpp, he, avg_stats(&c2c_he->cstats.__val)); \
1016}
1017
1018MEAN_ENTRY(mean_rmt_entry, rmt_hitm);
1019MEAN_ENTRY(mean_lcl_entry, lcl_hitm);
1020MEAN_ENTRY(mean_load_entry, load);
1021
b6fe2bbc
JO
1022static int
1023cpucnt_entry(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp,
1024 struct hist_entry *he)
1025{
1026 struct c2c_hist_entry *c2c_he;
1027 int width = c2c_width(fmt, hpp, he->hists);
1028 char buf[10];
1029
1030 c2c_he = container_of(he, struct c2c_hist_entry, he);
1031
1032 scnprintf(buf, 10, "%d", bitmap_weight(c2c_he->cpuset, c2c.cpus_cnt));
1033 return scnprintf(hpp->buf, hpp->size, "%*s", width, buf);
1034}
1035
600a8cf4
JO
1036#define HEADER_LOW(__h) \
1037 { \
1038 .line[1] = { \
1039 .text = __h, \
1040 }, \
1041 }
1042
1043#define HEADER_BOTH(__h0, __h1) \
1044 { \
1045 .line[0] = { \
1046 .text = __h0, \
1047 }, \
1048 .line[1] = { \
1049 .text = __h1, \
1050 }, \
1051 }
1052
1053#define HEADER_SPAN(__h0, __h1, __s) \
1054 { \
1055 .line[0] = { \
1056 .text = __h0, \
1057 .span = __s, \
1058 }, \
1059 .line[1] = { \
1060 .text = __h1, \
1061 }, \
1062 }
1063
1064#define HEADER_SPAN_LOW(__h) \
1065 { \
1066 .line[1] = { \
1067 .text = __h, \
1068 }, \
1069 }
1070
cbb88500
JO
1071static struct c2c_dimension dim_dcacheline = {
1072 .header = HEADER_LOW("Cacheline"),
1073 .name = "dcacheline",
1074 .cmp = dcacheline_cmp,
1075 .entry = dcacheline_entry,
1076 .width = 18,
1077};
1078
48acdebd
JO
1079static struct c2c_dimension dim_offset = {
1080 .header = HEADER_BOTH("Data address", "Offset"),
1081 .name = "offset",
1082 .cmp = offset_cmp,
1083 .entry = offset_entry,
1084 .width = 18,
1085};
1086
43575a95
JO
1087static struct c2c_dimension dim_iaddr = {
1088 .header = HEADER_LOW("Code address"),
1089 .name = "iaddr",
1090 .cmp = iaddr_cmp,
1091 .entry = iaddr_entry,
1092 .width = 18,
1093};
1094
97cb486e
JO
1095static struct c2c_dimension dim_tot_hitm = {
1096 .header = HEADER_SPAN("----- LLC Load Hitm -----", "Total", 2),
1097 .name = "tot_hitm",
1098 .cmp = tot_hitm_cmp,
1099 .entry = tot_hitm_entry,
1100 .width = 7,
1101};
1102
1103static struct c2c_dimension dim_lcl_hitm = {
1104 .header = HEADER_SPAN_LOW("Lcl"),
1105 .name = "lcl_hitm",
1106 .cmp = lcl_hitm_cmp,
1107 .entry = lcl_hitm_entry,
1108 .width = 7,
1109};
1110
1111static struct c2c_dimension dim_rmt_hitm = {
1112 .header = HEADER_SPAN_LOW("Rmt"),
1113 .name = "rmt_hitm",
1114 .cmp = rmt_hitm_cmp,
1115 .entry = rmt_hitm_entry,
1116 .width = 7,
1117};
1118
1119static struct c2c_dimension dim_cl_rmt_hitm = {
1120 .header = HEADER_SPAN("----- HITM -----", "Rmt", 1),
1121 .name = "cl_rmt_hitm",
1122 .cmp = rmt_hitm_cmp,
1123 .entry = rmt_hitm_entry,
1124 .width = 7,
1125};
1126
1127static struct c2c_dimension dim_cl_lcl_hitm = {
1128 .header = HEADER_SPAN_LOW("Lcl"),
1129 .name = "cl_lcl_hitm",
1130 .cmp = lcl_hitm_cmp,
1131 .entry = lcl_hitm_entry,
1132 .width = 7,
1133};
1134
0f18896d
JO
1135static struct c2c_dimension dim_stores = {
1136 .header = HEADER_SPAN("---- Store Reference ----", "Total", 2),
1137 .name = "stores",
1138 .cmp = store_cmp,
1139 .entry = store_entry,
1140 .width = 7,
1141};
1142
1143static struct c2c_dimension dim_stores_l1hit = {
1144 .header = HEADER_SPAN_LOW("L1Hit"),
1145 .name = "stores_l1hit",
1146 .cmp = st_l1hit_cmp,
1147 .entry = st_l1hit_entry,
1148 .width = 7,
1149};
1150
1151static struct c2c_dimension dim_stores_l1miss = {
1152 .header = HEADER_SPAN_LOW("L1Miss"),
1153 .name = "stores_l1miss",
1154 .cmp = st_l1miss_cmp,
1155 .entry = st_l1miss_entry,
1156 .width = 7,
1157};
1158
1159static struct c2c_dimension dim_cl_stores_l1hit = {
1160 .header = HEADER_SPAN("-- Store Refs --", "L1 Hit", 1),
1161 .name = "cl_stores_l1hit",
1162 .cmp = st_l1hit_cmp,
1163 .entry = st_l1hit_entry,
1164 .width = 7,
1165};
1166
1167static struct c2c_dimension dim_cl_stores_l1miss = {
1168 .header = HEADER_SPAN_LOW("L1 Miss"),
1169 .name = "cl_stores_l1miss",
1170 .cmp = st_l1miss_cmp,
1171 .entry = st_l1miss_entry,
1172 .width = 7,
1173};
1174
1295f685
JO
1175static struct c2c_dimension dim_ld_fbhit = {
1176 .header = HEADER_SPAN("----- Core Load Hit -----", "FB", 2),
1177 .name = "ld_fbhit",
1178 .cmp = ld_fbhit_cmp,
1179 .entry = ld_fbhit_entry,
1180 .width = 7,
1181};
1182
1183static struct c2c_dimension dim_ld_l1hit = {
1184 .header = HEADER_SPAN_LOW("L1"),
1185 .name = "ld_l1hit",
1186 .cmp = ld_l1hit_cmp,
1187 .entry = ld_l1hit_entry,
1188 .width = 7,
1189};
1190
1191static struct c2c_dimension dim_ld_l2hit = {
1192 .header = HEADER_SPAN_LOW("L2"),
1193 .name = "ld_l2hit",
1194 .cmp = ld_l2hit_cmp,
1195 .entry = ld_l2hit_entry,
1196 .width = 7,
1197};
1198
4d08910c
JO
1199static struct c2c_dimension dim_ld_llchit = {
1200 .header = HEADER_SPAN("-- LLC Load Hit --", "Llc", 1),
1201 .name = "ld_lclhit",
1202 .cmp = ld_llchit_cmp,
1203 .entry = ld_llchit_entry,
1204 .width = 8,
1205};
1206
1207static struct c2c_dimension dim_ld_rmthit = {
1208 .header = HEADER_SPAN_LOW("Rmt"),
1209 .name = "ld_rmthit",
1210 .cmp = rmt_hit_cmp,
1211 .entry = rmt_hit_entry,
1212 .width = 8,
1213};
1214
04402d20
JO
1215static struct c2c_dimension dim_ld_llcmiss = {
1216 .header = HEADER_BOTH("LLC", "Ld Miss"),
1217 .name = "ld_llcmiss",
1218 .cmp = ld_llcmiss_cmp,
1219 .entry = ld_llcmiss_entry,
1220 .width = 7,
1221};
1222
01b84d76
JO
1223static struct c2c_dimension dim_tot_recs = {
1224 .header = HEADER_BOTH("Total", "records"),
1225 .name = "tot_recs",
1226 .cmp = tot_recs_cmp,
1227 .entry = tot_recs_entry,
1228 .width = 7,
1229};
1230
55177c4e
JO
1231static struct c2c_dimension dim_tot_loads = {
1232 .header = HEADER_BOTH("Total", "Loads"),
1233 .name = "tot_loads",
1234 .cmp = tot_loads_cmp,
1235 .entry = tot_loads_entry,
1236 .width = 7,
1237};
1238
f0c50c15
JO
1239static struct c2c_dimension dim_percent_hitm = {
1240 .header = HEADER_LOW("%hitm"),
1241 .name = "percent_hitm",
1242 .cmp = percent_hitm_cmp,
1243 .entry = percent_hitm_entry,
1244 .color = percent_hitm_color,
1245 .width = 7,
1246};
1247
9cb3500a
JO
1248static struct c2c_dimension dim_percent_rmt_hitm = {
1249 .header = HEADER_SPAN("----- HITM -----", "Rmt", 1),
1250 .name = "percent_rmt_hitm",
1251 .cmp = percent_rmt_hitm_cmp,
1252 .entry = percent_rmt_hitm_entry,
1253 .color = percent_rmt_hitm_color,
1254 .width = 7,
1255};
1256
1257static struct c2c_dimension dim_percent_lcl_hitm = {
1258 .header = HEADER_SPAN_LOW("Lcl"),
1259 .name = "percent_lcl_hitm",
1260 .cmp = percent_lcl_hitm_cmp,
1261 .entry = percent_lcl_hitm_entry,
1262 .color = percent_lcl_hitm_color,
1263 .width = 7,
1264};
1265
1266static struct c2c_dimension dim_percent_stores_l1hit = {
1267 .header = HEADER_SPAN("-- Store Refs --", "L1 Hit", 1),
1268 .name = "percent_stores_l1hit",
1269 .cmp = percent_stores_l1hit_cmp,
1270 .entry = percent_stores_l1hit_entry,
1271 .color = percent_stores_l1hit_color,
1272 .width = 7,
1273};
1274
1275static struct c2c_dimension dim_percent_stores_l1miss = {
1276 .header = HEADER_SPAN_LOW("L1 Miss"),
1277 .name = "percent_stores_l1miss",
1278 .cmp = percent_stores_l1miss_cmp,
1279 .entry = percent_stores_l1miss_entry,
1280 .color = percent_stores_l1miss_color,
1281 .width = 7,
1282};
1283
6c70f54c
JO
1284static struct c2c_dimension dim_dram_lcl = {
1285 .header = HEADER_SPAN("--- Load Dram ----", "Lcl", 1),
1286 .name = "dram_lcl",
1287 .cmp = lcl_dram_cmp,
1288 .entry = lcl_dram_entry,
1289 .width = 8,
1290};
1291
1292static struct c2c_dimension dim_dram_rmt = {
1293 .header = HEADER_SPAN_LOW("Rmt"),
1294 .name = "dram_rmt",
1295 .cmp = rmt_dram_cmp,
1296 .entry = rmt_dram_entry,
1297 .width = 8,
1298};
1299
36d3deb9
JO
1300static struct c2c_dimension dim_pid = {
1301 .header = HEADER_LOW("Pid"),
1302 .name = "pid",
1303 .cmp = pid_cmp,
1304 .entry = pid_entry,
1305 .width = 7,
1306};
1307
e87019c5
JO
1308static struct c2c_dimension dim_tid = {
1309 .header = HEADER_LOW("Tid"),
1310 .name = "tid",
1311 .se = &sort_thread,
1312};
1313
51dedaa4
JO
1314static struct c2c_dimension dim_symbol = {
1315 .name = "symbol",
1316 .se = &sort_sym,
1317};
1318
1319static struct c2c_dimension dim_dso = {
1320 .header = HEADER_BOTH("Shared", "Object"),
1321 .name = "dso",
1322 .se = &sort_dso,
1323};
1324
1e181b92
JO
1325static struct c2c_header header_node[3] = {
1326 HEADER_LOW("Node"),
1327 HEADER_LOW("Node{cpus %hitms %stores}"),
1328 HEADER_LOW("Node{cpu list}"),
1329};
1330
1331static struct c2c_dimension dim_node = {
1332 .name = "node",
1333 .cmp = empty_cmp,
1334 .entry = node_entry,
1335 .width = 4,
1336};
1337
92062d54
JO
1338static struct c2c_dimension dim_mean_rmt = {
1339 .header = HEADER_SPAN("---------- cycles ----------", "rmt hitm", 2),
1340 .name = "mean_rmt",
1341 .cmp = empty_cmp,
1342 .entry = mean_rmt_entry,
1343 .width = 8,
1344};
1345
1346static struct c2c_dimension dim_mean_lcl = {
1347 .header = HEADER_SPAN_LOW("lcl hitm"),
1348 .name = "mean_lcl",
1349 .cmp = empty_cmp,
1350 .entry = mean_lcl_entry,
1351 .width = 8,
1352};
1353
1354static struct c2c_dimension dim_mean_load = {
1355 .header = HEADER_SPAN_LOW("load"),
1356 .name = "mean_load",
1357 .cmp = empty_cmp,
1358 .entry = mean_load_entry,
1359 .width = 8,
1360};
1361
b6fe2bbc
JO
1362static struct c2c_dimension dim_cpucnt = {
1363 .header = HEADER_BOTH("cpu", "cnt"),
1364 .name = "cpucnt",
1365 .cmp = empty_cmp,
1366 .entry = cpucnt_entry,
1367 .width = 8,
1368};
1369
89d9ba8f
JO
1370static struct c2c_dimension dim_srcline = {
1371 .name = "cl_srcline",
1372 .se = &sort_srcline,
1373};
1374
c75540e3 1375static struct c2c_dimension *dimensions[] = {
cbb88500 1376 &dim_dcacheline,
48acdebd 1377 &dim_offset,
43575a95 1378 &dim_iaddr,
97cb486e
JO
1379 &dim_tot_hitm,
1380 &dim_lcl_hitm,
1381 &dim_rmt_hitm,
1382 &dim_cl_lcl_hitm,
1383 &dim_cl_rmt_hitm,
0f18896d
JO
1384 &dim_stores,
1385 &dim_stores_l1hit,
1386 &dim_stores_l1miss,
1387 &dim_cl_stores_l1hit,
1388 &dim_cl_stores_l1miss,
1295f685
JO
1389 &dim_ld_fbhit,
1390 &dim_ld_l1hit,
1391 &dim_ld_l2hit,
4d08910c
JO
1392 &dim_ld_llchit,
1393 &dim_ld_rmthit,
04402d20 1394 &dim_ld_llcmiss,
01b84d76 1395 &dim_tot_recs,
55177c4e 1396 &dim_tot_loads,
f0c50c15 1397 &dim_percent_hitm,
9cb3500a
JO
1398 &dim_percent_rmt_hitm,
1399 &dim_percent_lcl_hitm,
1400 &dim_percent_stores_l1hit,
1401 &dim_percent_stores_l1miss,
6c70f54c
JO
1402 &dim_dram_lcl,
1403 &dim_dram_rmt,
36d3deb9 1404 &dim_pid,
e87019c5 1405 &dim_tid,
51dedaa4
JO
1406 &dim_symbol,
1407 &dim_dso,
1e181b92 1408 &dim_node,
92062d54
JO
1409 &dim_mean_rmt,
1410 &dim_mean_lcl,
1411 &dim_mean_load,
b6fe2bbc 1412 &dim_cpucnt,
89d9ba8f 1413 &dim_srcline,
c75540e3
JO
1414 NULL,
1415};
1416
1417static void fmt_free(struct perf_hpp_fmt *fmt)
1418{
1419 struct c2c_fmt *c2c_fmt;
1420
1421 c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1422 free(c2c_fmt);
1423}
1424
1425static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b)
1426{
1427 struct c2c_fmt *c2c_a = container_of(a, struct c2c_fmt, fmt);
1428 struct c2c_fmt *c2c_b = container_of(b, struct c2c_fmt, fmt);
1429
1430 return c2c_a->dim == c2c_b->dim;
1431}
1432
1433static struct c2c_dimension *get_dimension(const char *name)
1434{
1435 unsigned int i;
1436
1437 for (i = 0; dimensions[i]; i++) {
1438 struct c2c_dimension *dim = dimensions[i];
1439
1440 if (!strcmp(dim->name, name))
1441 return dim;
1442 };
1443
1444 return NULL;
1445}
1446
8d3f938d
JO
1447static int c2c_se_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
1448 struct hist_entry *he)
1449{
1450 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1451 struct c2c_dimension *dim = c2c_fmt->dim;
1452 size_t len = fmt->user_len;
1453
1454 if (!len)
1455 len = hists__col_len(he->hists, dim->se->se_width_idx);
1456
1457 return dim->se->se_snprintf(he, hpp->buf, hpp->size, len);
1458}
1459
1460static int64_t c2c_se_cmp(struct perf_hpp_fmt *fmt,
1461 struct hist_entry *a, struct hist_entry *b)
1462{
1463 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1464 struct c2c_dimension *dim = c2c_fmt->dim;
1465
1466 return dim->se->se_cmp(a, b);
1467}
1468
1469static int64_t c2c_se_collapse(struct perf_hpp_fmt *fmt,
1470 struct hist_entry *a, struct hist_entry *b)
1471{
1472 struct c2c_fmt *c2c_fmt = container_of(fmt, struct c2c_fmt, fmt);
1473 struct c2c_dimension *dim = c2c_fmt->dim;
1474 int64_t (*collapse_fn)(struct hist_entry *, struct hist_entry *);
1475
1476 collapse_fn = dim->se->se_collapse ?: dim->se->se_cmp;
1477 return collapse_fn(a, b);
1478}
1479
c75540e3
JO
1480static struct c2c_fmt *get_format(const char *name)
1481{
1482 struct c2c_dimension *dim = get_dimension(name);
1483 struct c2c_fmt *c2c_fmt;
1484 struct perf_hpp_fmt *fmt;
1485
1486 if (!dim)
1487 return NULL;
1488
1489 c2c_fmt = zalloc(sizeof(*c2c_fmt));
1490 if (!c2c_fmt)
1491 return NULL;
1492
1493 c2c_fmt->dim = dim;
1494
1495 fmt = &c2c_fmt->fmt;
1496 INIT_LIST_HEAD(&fmt->list);
1497 INIT_LIST_HEAD(&fmt->sort_list);
1498
8d3f938d
JO
1499 fmt->cmp = dim->se ? c2c_se_cmp : dim->cmp;
1500 fmt->sort = dim->se ? c2c_se_cmp : dim->cmp;
9cb3500a 1501 fmt->color = dim->se ? NULL : dim->color;
8d3f938d 1502 fmt->entry = dim->se ? c2c_se_entry : dim->entry;
c75540e3
JO
1503 fmt->header = c2c_header;
1504 fmt->width = c2c_width;
8d3f938d 1505 fmt->collapse = dim->se ? c2c_se_collapse : dim->cmp;
c75540e3
JO
1506 fmt->equal = fmt_equal;
1507 fmt->free = fmt_free;
1508
1509 return c2c_fmt;
1510}
1511
1512static int c2c_hists__init_output(struct perf_hpp_list *hpp_list, char *name)
1513{
1514 struct c2c_fmt *c2c_fmt = get_format(name);
1515
5f2eca83
JO
1516 if (!c2c_fmt) {
1517 reset_dimensions();
1518 return output_field_add(hpp_list, name);
1519 }
c75540e3
JO
1520
1521 perf_hpp_list__column_register(hpp_list, &c2c_fmt->fmt);
1522 return 0;
1523}
1524
1525static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name)
1526{
1527 struct c2c_fmt *c2c_fmt = get_format(name);
51dedaa4 1528 struct c2c_dimension *dim;
c75540e3 1529
5f2eca83
JO
1530 if (!c2c_fmt) {
1531 reset_dimensions();
1532 return sort_dimension__add(hpp_list, name, NULL, 0);
1533 }
c75540e3 1534
51dedaa4
JO
1535 dim = c2c_fmt->dim;
1536 if (dim == &dim_dso)
1537 hpp_list->dso = 1;
1538
c75540e3
JO
1539 perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
1540 return 0;
1541}
1542
1543#define PARSE_LIST(_list, _fn) \
1544 do { \
1545 char *tmp, *tok; \
1546 ret = 0; \
1547 \
1548 if (!_list) \
1549 break; \
1550 \
1551 for (tok = strtok_r((char *)_list, ", ", &tmp); \
1552 tok; tok = strtok_r(NULL, ", ", &tmp)) { \
1553 ret = _fn(hpp_list, tok); \
1554 if (ret == -EINVAL) { \
1555 error("Invalid --fields key: `%s'", tok); \
1556 break; \
1557 } else if (ret == -ESRCH) { \
1558 error("Unknown --fields key: `%s'", tok); \
1559 break; \
1560 } \
1561 } \
1562 } while (0)
1563
1564static int hpp_list__parse(struct perf_hpp_list *hpp_list,
1565 const char *output_,
1566 const char *sort_)
1567{
1568 char *output = output_ ? strdup(output_) : NULL;
1569 char *sort = sort_ ? strdup(sort_) : NULL;
1570 int ret;
1571
1572 PARSE_LIST(output, c2c_hists__init_output);
1573 PARSE_LIST(sort, c2c_hists__init_sort);
1574
1575 /* copy sort keys to output fields */
1576 perf_hpp__setup_output_field(hpp_list);
1577
1578 /*
1579 * We dont need other sorting keys other than those
1580 * we already specified. It also really slows down
1581 * the processing a lot with big number of output
1582 * fields, so switching this off for c2c.
1583 */
1584
1585#if 0
1586 /* and then copy output fields to sort keys */
1587 perf_hpp__append_sort_keys(&hists->list);
1588#endif
1589
1590 free(output);
1591 free(sort);
1592 return ret;
1593}
1594
1595static int c2c_hists__init(struct c2c_hists *hists,
1d62fcd6
JO
1596 const char *sort,
1597 int nr_header_lines)
c75540e3
JO
1598{
1599 __hists__init(&hists->hists, &hists->list);
1600
1601 /*
1602 * Initialize only with sort fields, we need to resort
1603 * later anyway, and that's where we add output fields
1604 * as well.
1605 */
1606 perf_hpp_list__init(&hists->list);
1607
1d62fcd6
JO
1608 /* Overload number of header lines.*/
1609 hists->list.nr_header_lines = nr_header_lines;
1610
c75540e3
JO
1611 return hpp_list__parse(&hists->list, NULL, sort);
1612}
1613
1614__maybe_unused
1615static int c2c_hists__reinit(struct c2c_hists *c2c_hists,
1616 const char *output,
1617 const char *sort)
1618{
1619 perf_hpp__reset_output_field(&c2c_hists->list);
1620 return hpp_list__parse(&c2c_hists->list, output, sort);
1621}
1622
89d9ba8f 1623static int filter_cb(struct hist_entry *he)
ec06f9b9 1624{
89d9ba8f
JO
1625 if (c2c.show_src && !he->srcline)
1626 he->srcline = hist_entry__get_srcline(he);
1627
ec06f9b9
JO
1628 return 0;
1629}
1630
1631static int resort_cl_cb(struct hist_entry *he)
1632{
1633 struct c2c_hist_entry *c2c_he;
1634 struct c2c_hists *c2c_hists;
1635
1636 c2c_he = container_of(he, struct c2c_hist_entry, he);
1637 c2c_hists = c2c_he->hists;
1638
1639 if (c2c_hists) {
22dd59d1
JO
1640 c2c_hists__reinit(c2c_hists,
1641 "percent_rmt_hitm,"
1642 "percent_lcl_hitm,"
1643 "percent_stores_l1hit,"
1644 "percent_stores_l1miss,"
1645 "offset,"
1646 "pid,"
1647 "tid,"
1648 "mean_rmt,"
1649 "mean_lcl,"
1650 "mean_load,"
1651 "cpucnt,"
1652 "symbol,"
1653 "dso,"
1654 "node",
1655 "offset,rmt_hitm,lcl_hitm");
1656
ec06f9b9
JO
1657 hists__collapse_resort(&c2c_hists->hists, NULL);
1658 hists__output_resort_cb(&c2c_hists->hists, NULL, filter_cb);
1659 }
1660
1661 return 0;
1662}
1663
1e181b92
JO
1664static void setup_nodes_header(void)
1665{
1666 dim_node.header = header_node[c2c.node_info];
1667}
1668
1669static int setup_nodes(struct perf_session *session)
1670{
1671 struct numa_node *n;
1672 unsigned long **nodes;
1673 int node, cpu;
1674 int *cpu2node;
1675
1676 if (c2c.node_info > 2)
1677 c2c.node_info = 2;
1678
1679 c2c.nodes_cnt = session->header.env.nr_numa_nodes;
1680 c2c.cpus_cnt = session->header.env.nr_cpus_online;
1681
1682 n = session->header.env.numa_nodes;
1683 if (!n)
1684 return -EINVAL;
1685
1686 nodes = zalloc(sizeof(unsigned long *) * c2c.nodes_cnt);
1687 if (!nodes)
1688 return -ENOMEM;
1689
1690 c2c.nodes = nodes;
1691
1692 cpu2node = zalloc(sizeof(int) * c2c.cpus_cnt);
1693 if (!cpu2node)
1694 return -ENOMEM;
1695
1696 for (cpu = 0; cpu < c2c.cpus_cnt; cpu++)
1697 cpu2node[cpu] = -1;
1698
1699 c2c.cpu2node = cpu2node;
1700
1701 for (node = 0; node < c2c.nodes_cnt; node++) {
1702 struct cpu_map *map = n[node].map;
1703 unsigned long *set;
1704
1705 set = bitmap_alloc(c2c.cpus_cnt);
1706 if (!set)
1707 return -ENOMEM;
1708
1709 for (cpu = 0; cpu < map->nr; cpu++) {
1710 set_bit(map->map[cpu], set);
1711
1712 if (WARN_ONCE(cpu2node[map->map[cpu]] != -1, "node/cpu topology bug"))
1713 return -EINVAL;
1714
1715 cpu2node[map->map[cpu]] = node;
1716 }
1717
1718 nodes[node] = set;
1719 }
1720
1721 setup_nodes_header();
1722 return 0;
1723}
1724
1725
903a6f15
JO
1726static int perf_c2c__report(int argc, const char **argv)
1727{
1728 struct perf_session *session;
78b27543 1729 struct ui_progress prog;
903a6f15
JO
1730 struct perf_data_file file = {
1731 .mode = PERF_DATA_MODE_READ,
1732 };
1733 const struct option c2c_options[] = {
1734 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
1735 "file", "vmlinux pathname"),
1736 OPT_INCR('v', "verbose", &verbose,
1737 "be more verbose (show counter open errors, etc)"),
1738 OPT_STRING('i', "input", &input_name, "file",
1739 "the input file to process"),
1e181b92
JO
1740 OPT_INCR('N', "node-info", &c2c.node_info,
1741 "show extra node info in report (repeat for more info)"),
903a6f15
JO
1742 OPT_END()
1743 };
1744 int err = 0;
1745
1746 argc = parse_options(argc, argv, c2c_options, report_c2c_usage,
1747 PARSE_OPT_STOP_AT_NON_OPTION);
78b27543 1748 if (argc)
903a6f15
JO
1749 usage_with_options(report_c2c_usage, c2c_options);
1750
78b27543
JO
1751 if (!input_name || !strlen(input_name))
1752 input_name = "perf.data";
1753
903a6f15
JO
1754 file.path = input_name;
1755
1d62fcd6 1756 err = c2c_hists__init(&c2c.hists, "dcacheline", 2);
c75540e3
JO
1757 if (err) {
1758 pr_debug("Failed to initialize hists\n");
1759 goto out;
1760 }
1761
903a6f15
JO
1762 session = perf_session__new(&file, 0, &c2c.tool);
1763 if (session == NULL) {
1764 pr_debug("No memory for session\n");
1765 goto out;
1766 }
1e181b92
JO
1767 err = setup_nodes(session);
1768 if (err) {
1769 pr_err("Failed setup nodes\n");
1770 goto out;
1771 }
903a6f15
JO
1772
1773 if (symbol__init(&session->header.env) < 0)
1774 goto out_session;
1775
1776 /* No pipe support at the moment. */
1777 if (perf_data_file__is_pipe(session->file)) {
1778 pr_debug("No pipe support at the moment.\n");
1779 goto out_session;
1780 }
1781
78b27543
JO
1782 err = perf_session__process_events(session);
1783 if (err) {
1784 pr_err("failed to process sample\n");
1785 goto out_session;
1786 }
1787
22dd59d1
JO
1788 c2c_hists__reinit(&c2c.hists,
1789 "dcacheline,"
1790 "tot_recs,"
1791 "percent_hitm,"
1792 "tot_hitm,lcl_hitm,rmt_hitm,"
1793 "stores,stores_l1hit,stores_l1miss,"
1794 "dram_lcl,dram_rmt,"
1795 "ld_llcmiss,"
1796 "tot_loads,"
1797 "ld_fbhit,ld_l1hit,ld_l2hit,"
1798 "ld_lclhit,ld_rmthit",
1799 "rmt_hitm"
1800 );
1801
78b27543
JO
1802 ui_progress__init(&prog, c2c.hists.hists.nr_entries, "Sorting...");
1803
1804 hists__collapse_resort(&c2c.hists.hists, NULL);
ec06f9b9 1805 hists__output_resort_cb(&c2c.hists.hists, &prog, resort_cl_cb);
78b27543
JO
1806
1807 ui_progress__finish();
1808
903a6f15
JO
1809out_session:
1810 perf_session__delete(session);
1811out:
1812 return err;
1813}
1814
39bcd4a4
JO
1815static int parse_record_events(const struct option *opt __maybe_unused,
1816 const char *str, int unset __maybe_unused)
1817{
1818 bool *event_set = (bool *) opt->value;
1819
1820 *event_set = true;
1821 return perf_mem_events__parse(str);
1822}
1823
1824
1825static const char * const __usage_record[] = {
1826 "perf c2c record [<options>] [<command>]",
1827 "perf c2c record [<options>] -- <command> [<options>]",
1828 NULL
1829};
1830
1831static const char * const *record_mem_usage = __usage_record;
1832
1833static int perf_c2c__record(int argc, const char **argv)
1834{
1835 int rec_argc, i = 0, j;
1836 const char **rec_argv;
1837 int ret;
1838 bool all_user = false, all_kernel = false;
1839 bool event_set = false;
1840 struct option options[] = {
1841 OPT_CALLBACK('e', "event", &event_set, "event",
1842 "event selector. Use 'perf mem record -e list' to list available events",
1843 parse_record_events),
1844 OPT_INCR('v', "verbose", &verbose,
1845 "be more verbose (show counter open errors, etc)"),
1846 OPT_BOOLEAN('u', "all-user", &all_user, "collect only user level data"),
1847 OPT_BOOLEAN('k', "all-kernel", &all_kernel, "collect only kernel level data"),
1848 OPT_UINTEGER('l', "ldlat", &perf_mem_events__loads_ldlat, "setup mem-loads latency"),
1849 OPT_END()
1850 };
1851
1852 if (perf_mem_events__init()) {
1853 pr_err("failed: memory events not supported\n");
1854 return -1;
1855 }
1856
1857 argc = parse_options(argc, argv, options, record_mem_usage,
1858 PARSE_OPT_KEEP_UNKNOWN);
1859
1860 rec_argc = argc + 10; /* max number of arguments */
1861 rec_argv = calloc(rec_argc + 1, sizeof(char *));
1862 if (!rec_argv)
1863 return -1;
1864
1865 rec_argv[i++] = "record";
1866
1867 if (!event_set) {
1868 perf_mem_events[PERF_MEM_EVENTS__LOAD].record = true;
1869 perf_mem_events[PERF_MEM_EVENTS__STORE].record = true;
1870 }
1871
1872 if (perf_mem_events[PERF_MEM_EVENTS__LOAD].record)
1873 rec_argv[i++] = "-W";
1874
1875 rec_argv[i++] = "-d";
1876 rec_argv[i++] = "--sample-cpu";
1877
1878 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
1879 if (!perf_mem_events[j].record)
1880 continue;
1881
1882 if (!perf_mem_events[j].supported) {
1883 pr_err("failed: event '%s' not supported\n",
1884 perf_mem_events[j].name);
1885 return -1;
1886 }
1887
1888 rec_argv[i++] = "-e";
1889 rec_argv[i++] = perf_mem_events__name(j);
1890 };
1891
1892 if (all_user)
1893 rec_argv[i++] = "--all-user";
1894
1895 if (all_kernel)
1896 rec_argv[i++] = "--all-kernel";
1897
1898 for (j = 0; j < argc; j++, i++)
1899 rec_argv[i] = argv[j];
1900
1901 if (verbose > 0) {
1902 pr_debug("calling: ");
1903
1904 j = 0;
1905
1906 while (rec_argv[j]) {
1907 pr_debug("%s ", rec_argv[j]);
1908 j++;
1909 }
1910 pr_debug("\n");
1911 }
1912
1913 ret = cmd_record(i, rec_argv, NULL);
1914 free(rec_argv);
1915 return ret;
1916}
1917
7aef3bf3
JO
1918int cmd_c2c(int argc, const char **argv, const char *prefix __maybe_unused)
1919{
1920 const struct option c2c_options[] = {
1921 OPT_INCR('v', "verbose", &verbose, "be more verbose"),
1922 OPT_END()
1923 };
1924
1925 argc = parse_options(argc, argv, c2c_options, c2c_usage,
1926 PARSE_OPT_STOP_AT_NON_OPTION);
39bcd4a4
JO
1927
1928 if (!argc)
1929 usage_with_options(c2c_usage, c2c_options);
1930
1931 if (!strncmp(argv[0], "rec", 3)) {
1932 return perf_c2c__record(argc, argv);
903a6f15
JO
1933 } else if (!strncmp(argv[0], "rep", 3)) {
1934 return perf_c2c__report(argc, argv);
39bcd4a4
JO
1935 } else {
1936 usage_with_options(c2c_usage, c2c_options);
1937 }
1938
7aef3bf3
JO
1939 return 0;
1940}