Loading...
1// SPDX-License-Identifier: GPL-2.0
2#include <stddef.h>
3#include <stdlib.h>
4#include <string.h>
5#include <errno.h>
6#include <sys/types.h>
7#include <sys/stat.h>
8#include <unistd.h>
9#include <api/fs/fs.h>
10#include <linux/kernel.h>
11#include "map_symbol.h"
12#include "mem-events.h"
13#include "debug.h"
14#include "symbol.h"
15#include "pmu.h"
16#include "pmu-hybrid.h"
17
18unsigned int perf_mem_events__loads_ldlat = 30;
19
20#define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
21
22static struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {
23 E("ldlat-loads", "cpu/mem-loads,ldlat=%u/P", "cpu/events/mem-loads"),
24 E("ldlat-stores", "cpu/mem-stores/P", "cpu/events/mem-stores"),
25 E(NULL, NULL, NULL),
26};
27#undef E
28
29static char mem_loads_name[100];
30static bool mem_loads_name__init;
31
32struct perf_mem_event * __weak perf_mem_events__ptr(int i)
33{
34 if (i >= PERF_MEM_EVENTS__MAX)
35 return NULL;
36
37 return &perf_mem_events[i];
38}
39
40char * __weak perf_mem_events__name(int i, char *pmu_name __maybe_unused)
41{
42 struct perf_mem_event *e = perf_mem_events__ptr(i);
43
44 if (!e)
45 return NULL;
46
47 if (i == PERF_MEM_EVENTS__LOAD) {
48 if (!mem_loads_name__init) {
49 mem_loads_name__init = true;
50 scnprintf(mem_loads_name, sizeof(mem_loads_name),
51 e->name, perf_mem_events__loads_ldlat);
52 }
53 return mem_loads_name;
54 }
55
56 return (char *)e->name;
57}
58
59__weak bool is_mem_loads_aux_event(struct evsel *leader __maybe_unused)
60{
61 return false;
62}
63
64int perf_mem_events__parse(const char *str)
65{
66 char *tok, *saveptr = NULL;
67 bool found = false;
68 char *buf;
69 int j;
70
71 /* We need buffer that we know we can write to. */
72 buf = malloc(strlen(str) + 1);
73 if (!buf)
74 return -ENOMEM;
75
76 strcpy(buf, str);
77
78 tok = strtok_r((char *)buf, ",", &saveptr);
79
80 while (tok) {
81 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
82 struct perf_mem_event *e = perf_mem_events__ptr(j);
83
84 if (!e->tag)
85 continue;
86
87 if (strstr(e->tag, tok))
88 e->record = found = true;
89 }
90
91 tok = strtok_r(NULL, ",", &saveptr);
92 }
93
94 free(buf);
95
96 if (found)
97 return 0;
98
99 pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str);
100 return -1;
101}
102
103static bool perf_mem_event__supported(const char *mnt, char *sysfs_name)
104{
105 char path[PATH_MAX];
106 struct stat st;
107
108 scnprintf(path, PATH_MAX, "%s/devices/%s", mnt, sysfs_name);
109 return !stat(path, &st);
110}
111
112int perf_mem_events__init(void)
113{
114 const char *mnt = sysfs__mount();
115 bool found = false;
116 int j;
117
118 if (!mnt)
119 return -ENOENT;
120
121 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
122 struct perf_mem_event *e = perf_mem_events__ptr(j);
123 struct perf_pmu *pmu;
124 char sysfs_name[100];
125
126 /*
127 * If the event entry isn't valid, skip initialization
128 * and "e->supported" will keep false.
129 */
130 if (!e->tag)
131 continue;
132
133 if (!perf_pmu__has_hybrid()) {
134 scnprintf(sysfs_name, sizeof(sysfs_name),
135 e->sysfs_name, "cpu");
136 e->supported = perf_mem_event__supported(mnt, sysfs_name);
137 } else {
138 perf_pmu__for_each_hybrid_pmu(pmu) {
139 scnprintf(sysfs_name, sizeof(sysfs_name),
140 e->sysfs_name, pmu->name);
141 e->supported |= perf_mem_event__supported(mnt, sysfs_name);
142 }
143 }
144
145 if (e->supported)
146 found = true;
147 }
148
149 return found ? 0 : -ENOENT;
150}
151
152void perf_mem_events__list(void)
153{
154 int j;
155
156 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
157 struct perf_mem_event *e = perf_mem_events__ptr(j);
158
159 fprintf(stderr, "%-*s%-*s%s",
160 e->tag ? 13 : 0,
161 e->tag ? : "",
162 e->tag && verbose > 0 ? 25 : 0,
163 e->tag && verbose > 0 ? perf_mem_events__name(j, NULL) : "",
164 e->supported ? ": available\n" : "");
165 }
166}
167
168static void perf_mem_events__print_unsupport_hybrid(struct perf_mem_event *e,
169 int idx)
170{
171 const char *mnt = sysfs__mount();
172 char sysfs_name[100];
173 struct perf_pmu *pmu;
174
175 perf_pmu__for_each_hybrid_pmu(pmu) {
176 scnprintf(sysfs_name, sizeof(sysfs_name), e->sysfs_name,
177 pmu->name);
178 if (!perf_mem_event__supported(mnt, sysfs_name)) {
179 pr_err("failed: event '%s' not supported\n",
180 perf_mem_events__name(idx, pmu->name));
181 }
182 }
183}
184
185int perf_mem_events__record_args(const char **rec_argv, int *argv_nr,
186 char **rec_tmp, int *tmp_nr)
187{
188 int i = *argv_nr, k = 0;
189 struct perf_mem_event *e;
190 struct perf_pmu *pmu;
191 char *s;
192
193 for (int j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
194 e = perf_mem_events__ptr(j);
195 if (!e->record)
196 continue;
197
198 if (!perf_pmu__has_hybrid()) {
199 if (!e->supported) {
200 pr_err("failed: event '%s' not supported\n",
201 perf_mem_events__name(j, NULL));
202 return -1;
203 }
204
205 rec_argv[i++] = "-e";
206 rec_argv[i++] = perf_mem_events__name(j, NULL);
207 } else {
208 if (!e->supported) {
209 perf_mem_events__print_unsupport_hybrid(e, j);
210 return -1;
211 }
212
213 perf_pmu__for_each_hybrid_pmu(pmu) {
214 rec_argv[i++] = "-e";
215 s = perf_mem_events__name(j, pmu->name);
216 if (s) {
217 s = strdup(s);
218 if (!s)
219 return -1;
220
221 rec_argv[i++] = s;
222 rec_tmp[k++] = s;
223 }
224 }
225 }
226 }
227
228 *argv_nr = i;
229 *tmp_nr = k;
230 return 0;
231}
232
233static const char * const tlb_access[] = {
234 "N/A",
235 "HIT",
236 "MISS",
237 "L1",
238 "L2",
239 "Walker",
240 "Fault",
241};
242
243int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
244{
245 size_t l = 0, i;
246 u64 m = PERF_MEM_TLB_NA;
247 u64 hit, miss;
248
249 sz -= 1; /* -1 for null termination */
250 out[0] = '\0';
251
252 if (mem_info)
253 m = mem_info->data_src.mem_dtlb;
254
255 hit = m & PERF_MEM_TLB_HIT;
256 miss = m & PERF_MEM_TLB_MISS;
257
258 /* already taken care of */
259 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
260
261 for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
262 if (!(m & 0x1))
263 continue;
264 if (l) {
265 strcat(out, " or ");
266 l += 4;
267 }
268 l += scnprintf(out + l, sz - l, tlb_access[i]);
269 }
270 if (*out == '\0')
271 l += scnprintf(out, sz - l, "N/A");
272 if (hit)
273 l += scnprintf(out + l, sz - l, " hit");
274 if (miss)
275 l += scnprintf(out + l, sz - l, " miss");
276
277 return l;
278}
279
280static const char * const mem_lvl[] = {
281 "N/A",
282 "HIT",
283 "MISS",
284 "L1",
285 "LFB/MAB",
286 "L2",
287 "L3",
288 "Local RAM",
289 "Remote RAM (1 hop)",
290 "Remote RAM (2 hops)",
291 "Remote Cache (1 hop)",
292 "Remote Cache (2 hops)",
293 "I/O",
294 "Uncached",
295};
296
297static const char * const mem_lvlnum[] = {
298 [PERF_MEM_LVLNUM_CXL] = "CXL",
299 [PERF_MEM_LVLNUM_IO] = "I/O",
300 [PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache",
301 [PERF_MEM_LVLNUM_LFB] = "LFB/MAB",
302 [PERF_MEM_LVLNUM_RAM] = "RAM",
303 [PERF_MEM_LVLNUM_PMEM] = "PMEM",
304 [PERF_MEM_LVLNUM_NA] = "N/A",
305};
306
307static const char * const mem_hops[] = {
308 "N/A",
309 /*
310 * While printing, 'Remote' will be added to represent
311 * 'Remote core, same node' accesses as remote field need
312 * to be set with mem_hops field.
313 */
314 "core, same node",
315 "node, same socket",
316 "socket, same board",
317 "board",
318};
319
320static int perf_mem__op_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
321{
322 u64 op = PERF_MEM_LOCK_NA;
323 int l;
324
325 if (mem_info)
326 op = mem_info->data_src.mem_op;
327
328 if (op & PERF_MEM_OP_NA)
329 l = scnprintf(out, sz, "N/A");
330 else if (op & PERF_MEM_OP_LOAD)
331 l = scnprintf(out, sz, "LOAD");
332 else if (op & PERF_MEM_OP_STORE)
333 l = scnprintf(out, sz, "STORE");
334 else if (op & PERF_MEM_OP_PFETCH)
335 l = scnprintf(out, sz, "PFETCH");
336 else if (op & PERF_MEM_OP_EXEC)
337 l = scnprintf(out, sz, "EXEC");
338 else
339 l = scnprintf(out, sz, "No");
340
341 return l;
342}
343
344int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
345{
346 size_t i, l = 0;
347 u64 m = PERF_MEM_LVL_NA;
348 u64 hit, miss;
349 int printed = 0;
350
351 if (mem_info)
352 m = mem_info->data_src.mem_lvl;
353
354 sz -= 1; /* -1 for null termination */
355 out[0] = '\0';
356
357 hit = m & PERF_MEM_LVL_HIT;
358 miss = m & PERF_MEM_LVL_MISS;
359
360 /* already taken care of */
361 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
362
363 if (mem_info && mem_info->data_src.mem_remote) {
364 strcat(out, "Remote ");
365 l += 7;
366 }
367
368 /*
369 * Incase mem_hops field is set, we can skip printing data source via
370 * PERF_MEM_LVL namespace.
371 */
372 if (mem_info && mem_info->data_src.mem_hops) {
373 l += scnprintf(out + l, sz - l, "%s ", mem_hops[mem_info->data_src.mem_hops]);
374 } else {
375 for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
376 if (!(m & 0x1))
377 continue;
378 if (printed++) {
379 strcat(out, " or ");
380 l += 4;
381 }
382 l += scnprintf(out + l, sz - l, mem_lvl[i]);
383 }
384 }
385
386 if (mem_info && mem_info->data_src.mem_lvl_num) {
387 int lvl = mem_info->data_src.mem_lvl_num;
388 if (printed++) {
389 strcat(out, " or ");
390 l += 4;
391 }
392 if (mem_lvlnum[lvl])
393 l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
394 else
395 l += scnprintf(out + l, sz - l, "L%d", lvl);
396 }
397
398 if (l == 0)
399 l += scnprintf(out + l, sz - l, "N/A");
400 if (hit)
401 l += scnprintf(out + l, sz - l, " hit");
402 if (miss)
403 l += scnprintf(out + l, sz - l, " miss");
404
405 return l;
406}
407
408static const char * const snoop_access[] = {
409 "N/A",
410 "None",
411 "Hit",
412 "Miss",
413 "HitM",
414};
415
416static const char * const snoopx_access[] = {
417 "Fwd",
418 "Peer",
419};
420
421int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
422{
423 size_t i, l = 0;
424 u64 m = PERF_MEM_SNOOP_NA;
425
426 sz -= 1; /* -1 for null termination */
427 out[0] = '\0';
428
429 if (mem_info)
430 m = mem_info->data_src.mem_snoop;
431
432 for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
433 if (!(m & 0x1))
434 continue;
435 if (l) {
436 strcat(out, " or ");
437 l += 4;
438 }
439 l += scnprintf(out + l, sz - l, snoop_access[i]);
440 }
441
442 m = 0;
443 if (mem_info)
444 m = mem_info->data_src.mem_snoopx;
445
446 for (i = 0; m && i < ARRAY_SIZE(snoopx_access); i++, m >>= 1) {
447 if (!(m & 0x1))
448 continue;
449
450 if (l) {
451 strcat(out, " or ");
452 l += 4;
453 }
454 l += scnprintf(out + l, sz - l, snoopx_access[i]);
455 }
456
457 if (*out == '\0')
458 l += scnprintf(out, sz - l, "N/A");
459
460 return l;
461}
462
463int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
464{
465 u64 mask = PERF_MEM_LOCK_NA;
466 int l;
467
468 if (mem_info)
469 mask = mem_info->data_src.mem_lock;
470
471 if (mask & PERF_MEM_LOCK_NA)
472 l = scnprintf(out, sz, "N/A");
473 else if (mask & PERF_MEM_LOCK_LOCKED)
474 l = scnprintf(out, sz, "Yes");
475 else
476 l = scnprintf(out, sz, "No");
477
478 return l;
479}
480
481int perf_mem__blk_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
482{
483 size_t l = 0;
484 u64 mask = PERF_MEM_BLK_NA;
485
486 sz -= 1; /* -1 for null termination */
487 out[0] = '\0';
488
489 if (mem_info)
490 mask = mem_info->data_src.mem_blk;
491
492 if (!mask || (mask & PERF_MEM_BLK_NA)) {
493 l += scnprintf(out + l, sz - l, " N/A");
494 return l;
495 }
496 if (mask & PERF_MEM_BLK_DATA)
497 l += scnprintf(out + l, sz - l, " Data");
498 if (mask & PERF_MEM_BLK_ADDR)
499 l += scnprintf(out + l, sz - l, " Addr");
500
501 return l;
502}
503
504int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
505{
506 int i = 0;
507
508 i += scnprintf(out, sz, "|OP ");
509 i += perf_mem__op_scnprintf(out + i, sz - i, mem_info);
510 i += scnprintf(out + i, sz - i, "|LVL ");
511 i += perf_mem__lvl_scnprintf(out + i, sz, mem_info);
512 i += scnprintf(out + i, sz - i, "|SNP ");
513 i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
514 i += scnprintf(out + i, sz - i, "|TLB ");
515 i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
516 i += scnprintf(out + i, sz - i, "|LCK ");
517 i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
518 i += scnprintf(out + i, sz - i, "|BLK ");
519 i += perf_mem__blk_scnprintf(out + i, sz - i, mem_info);
520
521 return i;
522}
523
524int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
525{
526 union perf_mem_data_src *data_src = &mi->data_src;
527 u64 daddr = mi->daddr.addr;
528 u64 op = data_src->mem_op;
529 u64 lvl = data_src->mem_lvl;
530 u64 snoop = data_src->mem_snoop;
531 u64 snoopx = data_src->mem_snoopx;
532 u64 lock = data_src->mem_lock;
533 u64 blk = data_src->mem_blk;
534 /*
535 * Skylake might report unknown remote level via this
536 * bit, consider it when evaluating remote HITMs.
537 *
538 * Incase of power, remote field can also be used to denote cache
539 * accesses from the another core of same node. Hence, setting
540 * mrem only when HOPS is zero along with set remote field.
541 */
542 bool mrem = (data_src->mem_remote && !data_src->mem_hops);
543 int err = 0;
544
545#define HITM_INC(__f) \
546do { \
547 stats->__f++; \
548 stats->tot_hitm++; \
549} while (0)
550
551#define PEER_INC(__f) \
552do { \
553 stats->__f++; \
554 stats->tot_peer++; \
555} while (0)
556
557#define P(a, b) PERF_MEM_##a##_##b
558
559 stats->nr_entries++;
560
561 if (lock & P(LOCK, LOCKED)) stats->locks++;
562
563 if (blk & P(BLK, DATA)) stats->blk_data++;
564 if (blk & P(BLK, ADDR)) stats->blk_addr++;
565
566 if (op & P(OP, LOAD)) {
567 /* load */
568 stats->load++;
569
570 if (!daddr) {
571 stats->ld_noadrs++;
572 return -1;
573 }
574
575 if (lvl & P(LVL, HIT)) {
576 if (lvl & P(LVL, UNC)) stats->ld_uncache++;
577 if (lvl & P(LVL, IO)) stats->ld_io++;
578 if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
579 if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
580 if (lvl & P(LVL, L2)) {
581 stats->ld_l2hit++;
582
583 if (snoopx & P(SNOOPX, PEER))
584 PEER_INC(lcl_peer);
585 }
586 if (lvl & P(LVL, L3 )) {
587 if (snoop & P(SNOOP, HITM))
588 HITM_INC(lcl_hitm);
589 else
590 stats->ld_llchit++;
591
592 if (snoopx & P(SNOOPX, PEER))
593 PEER_INC(lcl_peer);
594 }
595
596 if (lvl & P(LVL, LOC_RAM)) {
597 stats->lcl_dram++;
598 if (snoop & P(SNOOP, HIT))
599 stats->ld_shared++;
600 else
601 stats->ld_excl++;
602 }
603
604 if ((lvl & P(LVL, REM_RAM1)) ||
605 (lvl & P(LVL, REM_RAM2)) ||
606 mrem) {
607 stats->rmt_dram++;
608 if (snoop & P(SNOOP, HIT))
609 stats->ld_shared++;
610 else
611 stats->ld_excl++;
612 }
613 }
614
615 if ((lvl & P(LVL, REM_CCE1)) ||
616 (lvl & P(LVL, REM_CCE2)) ||
617 mrem) {
618 if (snoop & P(SNOOP, HIT)) {
619 stats->rmt_hit++;
620 } else if (snoop & P(SNOOP, HITM)) {
621 HITM_INC(rmt_hitm);
622 } else if (snoopx & P(SNOOPX, PEER)) {
623 stats->rmt_hit++;
624 PEER_INC(rmt_peer);
625 }
626 }
627
628 if ((lvl & P(LVL, MISS)))
629 stats->ld_miss++;
630
631 } else if (op & P(OP, STORE)) {
632 /* store */
633 stats->store++;
634
635 if (!daddr) {
636 stats->st_noadrs++;
637 return -1;
638 }
639
640 if (lvl & P(LVL, HIT)) {
641 if (lvl & P(LVL, UNC)) stats->st_uncache++;
642 if (lvl & P(LVL, L1 )) stats->st_l1hit++;
643 }
644 if (lvl & P(LVL, MISS))
645 if (lvl & P(LVL, L1)) stats->st_l1miss++;
646 if (lvl & P(LVL, NA))
647 stats->st_na++;
648 } else {
649 /* unparsable data_src? */
650 stats->noparse++;
651 return -1;
652 }
653
654 if (!mi->daddr.ms.map || !mi->iaddr.ms.map) {
655 stats->nomap++;
656 return -1;
657 }
658
659#undef P
660#undef HITM_INC
661 return err;
662}
663
664void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
665{
666 stats->nr_entries += add->nr_entries;
667
668 stats->locks += add->locks;
669 stats->store += add->store;
670 stats->st_uncache += add->st_uncache;
671 stats->st_noadrs += add->st_noadrs;
672 stats->st_l1hit += add->st_l1hit;
673 stats->st_l1miss += add->st_l1miss;
674 stats->st_na += add->st_na;
675 stats->load += add->load;
676 stats->ld_excl += add->ld_excl;
677 stats->ld_shared += add->ld_shared;
678 stats->ld_uncache += add->ld_uncache;
679 stats->ld_io += add->ld_io;
680 stats->ld_miss += add->ld_miss;
681 stats->ld_noadrs += add->ld_noadrs;
682 stats->ld_fbhit += add->ld_fbhit;
683 stats->ld_l1hit += add->ld_l1hit;
684 stats->ld_l2hit += add->ld_l2hit;
685 stats->ld_llchit += add->ld_llchit;
686 stats->lcl_hitm += add->lcl_hitm;
687 stats->rmt_hitm += add->rmt_hitm;
688 stats->tot_hitm += add->tot_hitm;
689 stats->lcl_peer += add->lcl_peer;
690 stats->rmt_peer += add->rmt_peer;
691 stats->tot_peer += add->tot_peer;
692 stats->rmt_hit += add->rmt_hit;
693 stats->lcl_dram += add->lcl_dram;
694 stats->rmt_dram += add->rmt_dram;
695 stats->blk_data += add->blk_data;
696 stats->blk_addr += add->blk_addr;
697 stats->nomap += add->nomap;
698 stats->noparse += add->noparse;
699}
1// SPDX-License-Identifier: GPL-2.0
2#include <stddef.h>
3#include <stdlib.h>
4#include <string.h>
5#include <errno.h>
6#include <sys/types.h>
7#include <sys/stat.h>
8#include <unistd.h>
9#include <api/fs/fs.h>
10#include <linux/kernel.h>
11#include "map_symbol.h"
12#include "mem-events.h"
13#include "debug.h"
14#include "symbol.h"
15
16unsigned int perf_mem_events__loads_ldlat = 30;
17
18#define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
19
20struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {
21 E("ldlat-loads", "cpu/mem-loads,ldlat=%u/P", "mem-loads"),
22 E("ldlat-stores", "cpu/mem-stores/P", "mem-stores"),
23};
24#undef E
25
26#undef E
27
28static char mem_loads_name[100];
29static bool mem_loads_name__init;
30
31char * __weak perf_mem_events__name(int i)
32{
33 if (i == PERF_MEM_EVENTS__LOAD) {
34 if (!mem_loads_name__init) {
35 mem_loads_name__init = true;
36 scnprintf(mem_loads_name, sizeof(mem_loads_name),
37 perf_mem_events[i].name,
38 perf_mem_events__loads_ldlat);
39 }
40 return mem_loads_name;
41 }
42
43 return (char *)perf_mem_events[i].name;
44}
45
46int perf_mem_events__parse(const char *str)
47{
48 char *tok, *saveptr = NULL;
49 bool found = false;
50 char *buf;
51 int j;
52
53 /* We need buffer that we know we can write to. */
54 buf = malloc(strlen(str) + 1);
55 if (!buf)
56 return -ENOMEM;
57
58 strcpy(buf, str);
59
60 tok = strtok_r((char *)buf, ",", &saveptr);
61
62 while (tok) {
63 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
64 struct perf_mem_event *e = &perf_mem_events[j];
65
66 if (strstr(e->tag, tok))
67 e->record = found = true;
68 }
69
70 tok = strtok_r(NULL, ",", &saveptr);
71 }
72
73 free(buf);
74
75 if (found)
76 return 0;
77
78 pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str);
79 return -1;
80}
81
82int perf_mem_events__init(void)
83{
84 const char *mnt = sysfs__mount();
85 bool found = false;
86 int j;
87
88 if (!mnt)
89 return -ENOENT;
90
91 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
92 char path[PATH_MAX];
93 struct perf_mem_event *e = &perf_mem_events[j];
94 struct stat st;
95
96 scnprintf(path, PATH_MAX, "%s/devices/cpu/events/%s",
97 mnt, e->sysfs_name);
98
99 if (!stat(path, &st))
100 e->supported = found = true;
101 }
102
103 return found ? 0 : -ENOENT;
104}
105
106void perf_mem_events__list(void)
107{
108 int j;
109
110 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
111 struct perf_mem_event *e = &perf_mem_events[j];
112
113 fprintf(stderr, "%-13s%-*s%s\n",
114 e->tag,
115 verbose > 0 ? 25 : 0,
116 verbose > 0 ? perf_mem_events__name(j) : "",
117 e->supported ? ": available" : "");
118 }
119}
120
121static const char * const tlb_access[] = {
122 "N/A",
123 "HIT",
124 "MISS",
125 "L1",
126 "L2",
127 "Walker",
128 "Fault",
129};
130
131int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
132{
133 size_t l = 0, i;
134 u64 m = PERF_MEM_TLB_NA;
135 u64 hit, miss;
136
137 sz -= 1; /* -1 for null termination */
138 out[0] = '\0';
139
140 if (mem_info)
141 m = mem_info->data_src.mem_dtlb;
142
143 hit = m & PERF_MEM_TLB_HIT;
144 miss = m & PERF_MEM_TLB_MISS;
145
146 /* already taken care of */
147 m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
148
149 for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
150 if (!(m & 0x1))
151 continue;
152 if (l) {
153 strcat(out, " or ");
154 l += 4;
155 }
156 l += scnprintf(out + l, sz - l, tlb_access[i]);
157 }
158 if (*out == '\0')
159 l += scnprintf(out, sz - l, "N/A");
160 if (hit)
161 l += scnprintf(out + l, sz - l, " hit");
162 if (miss)
163 l += scnprintf(out + l, sz - l, " miss");
164
165 return l;
166}
167
168static const char * const mem_lvl[] = {
169 "N/A",
170 "HIT",
171 "MISS",
172 "L1",
173 "LFB",
174 "L2",
175 "L3",
176 "Local RAM",
177 "Remote RAM (1 hop)",
178 "Remote RAM (2 hops)",
179 "Remote Cache (1 hop)",
180 "Remote Cache (2 hops)",
181 "I/O",
182 "Uncached",
183};
184
185static const char * const mem_lvlnum[] = {
186 [PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache",
187 [PERF_MEM_LVLNUM_LFB] = "LFB",
188 [PERF_MEM_LVLNUM_RAM] = "RAM",
189 [PERF_MEM_LVLNUM_PMEM] = "PMEM",
190 [PERF_MEM_LVLNUM_NA] = "N/A",
191};
192
193int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
194{
195 size_t i, l = 0;
196 u64 m = PERF_MEM_LVL_NA;
197 u64 hit, miss;
198 int printed;
199
200 if (mem_info)
201 m = mem_info->data_src.mem_lvl;
202
203 sz -= 1; /* -1 for null termination */
204 out[0] = '\0';
205
206 hit = m & PERF_MEM_LVL_HIT;
207 miss = m & PERF_MEM_LVL_MISS;
208
209 /* already taken care of */
210 m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
211
212
213 if (mem_info && mem_info->data_src.mem_remote) {
214 strcat(out, "Remote ");
215 l += 7;
216 }
217
218 printed = 0;
219 for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
220 if (!(m & 0x1))
221 continue;
222 if (printed++) {
223 strcat(out, " or ");
224 l += 4;
225 }
226 l += scnprintf(out + l, sz - l, mem_lvl[i]);
227 }
228
229 if (mem_info && mem_info->data_src.mem_lvl_num) {
230 int lvl = mem_info->data_src.mem_lvl_num;
231 if (printed++) {
232 strcat(out, " or ");
233 l += 4;
234 }
235 if (mem_lvlnum[lvl])
236 l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
237 else
238 l += scnprintf(out + l, sz - l, "L%d", lvl);
239 }
240
241 if (l == 0)
242 l += scnprintf(out + l, sz - l, "N/A");
243 if (hit)
244 l += scnprintf(out + l, sz - l, " hit");
245 if (miss)
246 l += scnprintf(out + l, sz - l, " miss");
247
248 return l;
249}
250
251static const char * const snoop_access[] = {
252 "N/A",
253 "None",
254 "Hit",
255 "Miss",
256 "HitM",
257};
258
259int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
260{
261 size_t i, l = 0;
262 u64 m = PERF_MEM_SNOOP_NA;
263
264 sz -= 1; /* -1 for null termination */
265 out[0] = '\0';
266
267 if (mem_info)
268 m = mem_info->data_src.mem_snoop;
269
270 for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
271 if (!(m & 0x1))
272 continue;
273 if (l) {
274 strcat(out, " or ");
275 l += 4;
276 }
277 l += scnprintf(out + l, sz - l, snoop_access[i]);
278 }
279 if (mem_info &&
280 (mem_info->data_src.mem_snoopx & PERF_MEM_SNOOPX_FWD)) {
281 if (l) {
282 strcat(out, " or ");
283 l += 4;
284 }
285 l += scnprintf(out + l, sz - l, "Fwd");
286 }
287
288 if (*out == '\0')
289 l += scnprintf(out, sz - l, "N/A");
290
291 return l;
292}
293
294int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
295{
296 u64 mask = PERF_MEM_LOCK_NA;
297 int l;
298
299 if (mem_info)
300 mask = mem_info->data_src.mem_lock;
301
302 if (mask & PERF_MEM_LOCK_NA)
303 l = scnprintf(out, sz, "N/A");
304 else if (mask & PERF_MEM_LOCK_LOCKED)
305 l = scnprintf(out, sz, "Yes");
306 else
307 l = scnprintf(out, sz, "No");
308
309 return l;
310}
311
312int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
313{
314 int i = 0;
315
316 i += perf_mem__lvl_scnprintf(out, sz, mem_info);
317 i += scnprintf(out + i, sz - i, "|SNP ");
318 i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
319 i += scnprintf(out + i, sz - i, "|TLB ");
320 i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
321 i += scnprintf(out + i, sz - i, "|LCK ");
322 i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
323
324 return i;
325}
326
327int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
328{
329 union perf_mem_data_src *data_src = &mi->data_src;
330 u64 daddr = mi->daddr.addr;
331 u64 op = data_src->mem_op;
332 u64 lvl = data_src->mem_lvl;
333 u64 snoop = data_src->mem_snoop;
334 u64 lock = data_src->mem_lock;
335 /*
336 * Skylake might report unknown remote level via this
337 * bit, consider it when evaluating remote HITMs.
338 */
339 bool mrem = data_src->mem_remote;
340 int err = 0;
341
342#define HITM_INC(__f) \
343do { \
344 stats->__f++; \
345 stats->tot_hitm++; \
346} while (0)
347
348#define P(a, b) PERF_MEM_##a##_##b
349
350 stats->nr_entries++;
351
352 if (lock & P(LOCK, LOCKED)) stats->locks++;
353
354 if (op & P(OP, LOAD)) {
355 /* load */
356 stats->load++;
357
358 if (!daddr) {
359 stats->ld_noadrs++;
360 return -1;
361 }
362
363 if (lvl & P(LVL, HIT)) {
364 if (lvl & P(LVL, UNC)) stats->ld_uncache++;
365 if (lvl & P(LVL, IO)) stats->ld_io++;
366 if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
367 if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
368 if (lvl & P(LVL, L2 )) stats->ld_l2hit++;
369 if (lvl & P(LVL, L3 )) {
370 if (snoop & P(SNOOP, HITM))
371 HITM_INC(lcl_hitm);
372 else
373 stats->ld_llchit++;
374 }
375
376 if (lvl & P(LVL, LOC_RAM)) {
377 stats->lcl_dram++;
378 if (snoop & P(SNOOP, HIT))
379 stats->ld_shared++;
380 else
381 stats->ld_excl++;
382 }
383
384 if ((lvl & P(LVL, REM_RAM1)) ||
385 (lvl & P(LVL, REM_RAM2)) ||
386 mrem) {
387 stats->rmt_dram++;
388 if (snoop & P(SNOOP, HIT))
389 stats->ld_shared++;
390 else
391 stats->ld_excl++;
392 }
393 }
394
395 if ((lvl & P(LVL, REM_CCE1)) ||
396 (lvl & P(LVL, REM_CCE2)) ||
397 mrem) {
398 if (snoop & P(SNOOP, HIT))
399 stats->rmt_hit++;
400 else if (snoop & P(SNOOP, HITM))
401 HITM_INC(rmt_hitm);
402 }
403
404 if ((lvl & P(LVL, MISS)))
405 stats->ld_miss++;
406
407 } else if (op & P(OP, STORE)) {
408 /* store */
409 stats->store++;
410
411 if (!daddr) {
412 stats->st_noadrs++;
413 return -1;
414 }
415
416 if (lvl & P(LVL, HIT)) {
417 if (lvl & P(LVL, UNC)) stats->st_uncache++;
418 if (lvl & P(LVL, L1 )) stats->st_l1hit++;
419 }
420 if (lvl & P(LVL, MISS))
421 if (lvl & P(LVL, L1)) stats->st_l1miss++;
422 } else {
423 /* unparsable data_src? */
424 stats->noparse++;
425 return -1;
426 }
427
428 if (!mi->daddr.ms.map || !mi->iaddr.ms.map) {
429 stats->nomap++;
430 return -1;
431 }
432
433#undef P
434#undef HITM_INC
435 return err;
436}
437
438void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
439{
440 stats->nr_entries += add->nr_entries;
441
442 stats->locks += add->locks;
443 stats->store += add->store;
444 stats->st_uncache += add->st_uncache;
445 stats->st_noadrs += add->st_noadrs;
446 stats->st_l1hit += add->st_l1hit;
447 stats->st_l1miss += add->st_l1miss;
448 stats->load += add->load;
449 stats->ld_excl += add->ld_excl;
450 stats->ld_shared += add->ld_shared;
451 stats->ld_uncache += add->ld_uncache;
452 stats->ld_io += add->ld_io;
453 stats->ld_miss += add->ld_miss;
454 stats->ld_noadrs += add->ld_noadrs;
455 stats->ld_fbhit += add->ld_fbhit;
456 stats->ld_l1hit += add->ld_l1hit;
457 stats->ld_l2hit += add->ld_l2hit;
458 stats->ld_llchit += add->ld_llchit;
459 stats->lcl_hitm += add->lcl_hitm;
460 stats->rmt_hitm += add->rmt_hitm;
461 stats->tot_hitm += add->tot_hitm;
462 stats->rmt_hit += add->rmt_hit;
463 stats->lcl_dram += add->lcl_dram;
464 stats->rmt_dram += add->rmt_dram;
465 stats->nomap += add->nomap;
466 stats->noparse += add->noparse;
467}