GNU Linux-libre 5.19-rc6-gnu
[releases.git] / tools / perf / util / mem-events.c
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
18 unsigned int perf_mem_events__loads_ldlat = 30;
19
20 #define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
21
22 static 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
29 static char mem_loads_name[100];
30 static bool mem_loads_name__init;
31
32 struct 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
40 char * __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
64 int 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
103 static 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
112 int 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
152 void 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, "%-13s%-*s%s\n",
160                         e->tag ?: "",
161                         verbose > 0 ? 25 : 0,
162                         verbose > 0 ? perf_mem_events__name(j, NULL) : "",
163                         e->supported ? ": available" : "");
164         }
165 }
166
167 static void perf_mem_events__print_unsupport_hybrid(struct perf_mem_event *e,
168                                                     int idx)
169 {
170         const char *mnt = sysfs__mount();
171         char sysfs_name[100];
172         struct perf_pmu *pmu;
173
174         perf_pmu__for_each_hybrid_pmu(pmu) {
175                 scnprintf(sysfs_name, sizeof(sysfs_name), e->sysfs_name,
176                           pmu->name);
177                 if (!perf_mem_event__supported(mnt, sysfs_name)) {
178                         pr_err("failed: event '%s' not supported\n",
179                                perf_mem_events__name(idx, pmu->name));
180                 }
181         }
182 }
183
184 int perf_mem_events__record_args(const char **rec_argv, int *argv_nr,
185                                  char **rec_tmp, int *tmp_nr)
186 {
187         int i = *argv_nr, k = 0;
188         struct perf_mem_event *e;
189         struct perf_pmu *pmu;
190         char *s;
191
192         for (int j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
193                 e = perf_mem_events__ptr(j);
194                 if (!e->record)
195                         continue;
196
197                 if (!perf_pmu__has_hybrid()) {
198                         if (!e->supported) {
199                                 pr_err("failed: event '%s' not supported\n",
200                                        perf_mem_events__name(j, NULL));
201                                 return -1;
202                         }
203
204                         rec_argv[i++] = "-e";
205                         rec_argv[i++] = perf_mem_events__name(j, NULL);
206                 } else {
207                         if (!e->supported) {
208                                 perf_mem_events__print_unsupport_hybrid(e, j);
209                                 return -1;
210                         }
211
212                         perf_pmu__for_each_hybrid_pmu(pmu) {
213                                 rec_argv[i++] = "-e";
214                                 s = perf_mem_events__name(j, pmu->name);
215                                 if (s) {
216                                         s = strdup(s);
217                                         if (!s)
218                                                 return -1;
219
220                                         rec_argv[i++] = s;
221                                         rec_tmp[k++] = s;
222                                 }
223                         }
224                 }
225         }
226
227         *argv_nr = i;
228         *tmp_nr = k;
229         return 0;
230 }
231
232 static const char * const tlb_access[] = {
233         "N/A",
234         "HIT",
235         "MISS",
236         "L1",
237         "L2",
238         "Walker",
239         "Fault",
240 };
241
242 int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
243 {
244         size_t l = 0, i;
245         u64 m = PERF_MEM_TLB_NA;
246         u64 hit, miss;
247
248         sz -= 1; /* -1 for null termination */
249         out[0] = '\0';
250
251         if (mem_info)
252                 m = mem_info->data_src.mem_dtlb;
253
254         hit = m & PERF_MEM_TLB_HIT;
255         miss = m & PERF_MEM_TLB_MISS;
256
257         /* already taken care of */
258         m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
259
260         for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
261                 if (!(m & 0x1))
262                         continue;
263                 if (l) {
264                         strcat(out, " or ");
265                         l += 4;
266                 }
267                 l += scnprintf(out + l, sz - l, tlb_access[i]);
268         }
269         if (*out == '\0')
270                 l += scnprintf(out, sz - l, "N/A");
271         if (hit)
272                 l += scnprintf(out + l, sz - l, " hit");
273         if (miss)
274                 l += scnprintf(out + l, sz - l, " miss");
275
276         return l;
277 }
278
279 static const char * const mem_lvl[] = {
280         "N/A",
281         "HIT",
282         "MISS",
283         "L1",
284         "LFB",
285         "L2",
286         "L3",
287         "Local RAM",
288         "Remote RAM (1 hop)",
289         "Remote RAM (2 hops)",
290         "Remote Cache (1 hop)",
291         "Remote Cache (2 hops)",
292         "I/O",
293         "Uncached",
294 };
295
296 static const char * const mem_lvlnum[] = {
297         [PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache",
298         [PERF_MEM_LVLNUM_LFB] = "LFB",
299         [PERF_MEM_LVLNUM_RAM] = "RAM",
300         [PERF_MEM_LVLNUM_PMEM] = "PMEM",
301         [PERF_MEM_LVLNUM_NA] = "N/A",
302 };
303
304 static const char * const mem_hops[] = {
305         "N/A",
306         /*
307          * While printing, 'Remote' will be added to represent
308          * 'Remote core, same node' accesses as remote field need
309          * to be set with mem_hops field.
310          */
311         "core, same node",
312         "node, same socket",
313         "socket, same board",
314         "board",
315 };
316
317 static int perf_mem__op_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
318 {
319         u64 op = PERF_MEM_LOCK_NA;
320         int l;
321
322         if (mem_info)
323                 op = mem_info->data_src.mem_op;
324
325         if (op & PERF_MEM_OP_NA)
326                 l = scnprintf(out, sz, "N/A");
327         else if (op & PERF_MEM_OP_LOAD)
328                 l = scnprintf(out, sz, "LOAD");
329         else if (op & PERF_MEM_OP_STORE)
330                 l = scnprintf(out, sz, "STORE");
331         else if (op & PERF_MEM_OP_PFETCH)
332                 l = scnprintf(out, sz, "PFETCH");
333         else if (op & PERF_MEM_OP_EXEC)
334                 l = scnprintf(out, sz, "EXEC");
335         else
336                 l = scnprintf(out, sz, "No");
337
338         return l;
339 }
340
341 int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
342 {
343         size_t i, l = 0;
344         u64 m =  PERF_MEM_LVL_NA;
345         u64 hit, miss;
346         int printed = 0;
347
348         if (mem_info)
349                 m  = mem_info->data_src.mem_lvl;
350
351         sz -= 1; /* -1 for null termination */
352         out[0] = '\0';
353
354         hit = m & PERF_MEM_LVL_HIT;
355         miss = m & PERF_MEM_LVL_MISS;
356
357         /* already taken care of */
358         m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
359
360         if (mem_info && mem_info->data_src.mem_remote) {
361                 strcat(out, "Remote ");
362                 l += 7;
363         }
364
365         /*
366          * Incase mem_hops field is set, we can skip printing data source via
367          * PERF_MEM_LVL namespace.
368          */
369         if (mem_info && mem_info->data_src.mem_hops) {
370                 l += scnprintf(out + l, sz - l, "%s ", mem_hops[mem_info->data_src.mem_hops]);
371         } else {
372                 for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
373                         if (!(m & 0x1))
374                                 continue;
375                         if (printed++) {
376                                 strcat(out, " or ");
377                                 l += 4;
378                         }
379                         l += scnprintf(out + l, sz - l, mem_lvl[i]);
380                 }
381         }
382
383         if (mem_info && mem_info->data_src.mem_lvl_num) {
384                 int lvl = mem_info->data_src.mem_lvl_num;
385                 if (printed++) {
386                         strcat(out, " or ");
387                         l += 4;
388                 }
389                 if (mem_lvlnum[lvl])
390                         l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
391                 else
392                         l += scnprintf(out + l, sz - l, "L%d", lvl);
393         }
394
395         if (l == 0)
396                 l += scnprintf(out + l, sz - l, "N/A");
397         if (hit)
398                 l += scnprintf(out + l, sz - l, " hit");
399         if (miss)
400                 l += scnprintf(out + l, sz - l, " miss");
401
402         return l;
403 }
404
405 static const char * const snoop_access[] = {
406         "N/A",
407         "None",
408         "Hit",
409         "Miss",
410         "HitM",
411 };
412
413 int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
414 {
415         size_t i, l = 0;
416         u64 m = PERF_MEM_SNOOP_NA;
417
418         sz -= 1; /* -1 for null termination */
419         out[0] = '\0';
420
421         if (mem_info)
422                 m = mem_info->data_src.mem_snoop;
423
424         for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
425                 if (!(m & 0x1))
426                         continue;
427                 if (l) {
428                         strcat(out, " or ");
429                         l += 4;
430                 }
431                 l += scnprintf(out + l, sz - l, snoop_access[i]);
432         }
433         if (mem_info &&
434              (mem_info->data_src.mem_snoopx & PERF_MEM_SNOOPX_FWD)) {
435                 if (l) {
436                         strcat(out, " or ");
437                         l += 4;
438                 }
439                 l += scnprintf(out + l, sz - l, "Fwd");
440         }
441
442         if (*out == '\0')
443                 l += scnprintf(out, sz - l, "N/A");
444
445         return l;
446 }
447
448 int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
449 {
450         u64 mask = PERF_MEM_LOCK_NA;
451         int l;
452
453         if (mem_info)
454                 mask = mem_info->data_src.mem_lock;
455
456         if (mask & PERF_MEM_LOCK_NA)
457                 l = scnprintf(out, sz, "N/A");
458         else if (mask & PERF_MEM_LOCK_LOCKED)
459                 l = scnprintf(out, sz, "Yes");
460         else
461                 l = scnprintf(out, sz, "No");
462
463         return l;
464 }
465
466 int perf_mem__blk_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
467 {
468         size_t l = 0;
469         u64 mask = PERF_MEM_BLK_NA;
470
471         sz -= 1; /* -1 for null termination */
472         out[0] = '\0';
473
474         if (mem_info)
475                 mask = mem_info->data_src.mem_blk;
476
477         if (!mask || (mask & PERF_MEM_BLK_NA)) {
478                 l += scnprintf(out + l, sz - l, " N/A");
479                 return l;
480         }
481         if (mask & PERF_MEM_BLK_DATA)
482                 l += scnprintf(out + l, sz - l, " Data");
483         if (mask & PERF_MEM_BLK_ADDR)
484                 l += scnprintf(out + l, sz - l, " Addr");
485
486         return l;
487 }
488
489 int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
490 {
491         int i = 0;
492
493         i += scnprintf(out, sz, "|OP ");
494         i += perf_mem__op_scnprintf(out + i, sz - i, mem_info);
495         i += scnprintf(out + i, sz - i, "|LVL ");
496         i += perf_mem__lvl_scnprintf(out + i, sz, mem_info);
497         i += scnprintf(out + i, sz - i, "|SNP ");
498         i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
499         i += scnprintf(out + i, sz - i, "|TLB ");
500         i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
501         i += scnprintf(out + i, sz - i, "|LCK ");
502         i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
503         i += scnprintf(out + i, sz - i, "|BLK ");
504         i += perf_mem__blk_scnprintf(out + i, sz - i, mem_info);
505
506         return i;
507 }
508
509 int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
510 {
511         union perf_mem_data_src *data_src = &mi->data_src;
512         u64 daddr  = mi->daddr.addr;
513         u64 op     = data_src->mem_op;
514         u64 lvl    = data_src->mem_lvl;
515         u64 snoop  = data_src->mem_snoop;
516         u64 lock   = data_src->mem_lock;
517         u64 blk    = data_src->mem_blk;
518         /*
519          * Skylake might report unknown remote level via this
520          * bit, consider it when evaluating remote HITMs.
521          *
522          * Incase of power, remote field can also be used to denote cache
523          * accesses from the another core of same node. Hence, setting
524          * mrem only when HOPS is zero along with set remote field.
525          */
526         bool mrem  = (data_src->mem_remote && !data_src->mem_hops);
527         int err = 0;
528
529 #define HITM_INC(__f)           \
530 do {                            \
531         stats->__f++;           \
532         stats->tot_hitm++;      \
533 } while (0)
534
535 #define P(a, b) PERF_MEM_##a##_##b
536
537         stats->nr_entries++;
538
539         if (lock & P(LOCK, LOCKED)) stats->locks++;
540
541         if (blk & P(BLK, DATA)) stats->blk_data++;
542         if (blk & P(BLK, ADDR)) stats->blk_addr++;
543
544         if (op & P(OP, LOAD)) {
545                 /* load */
546                 stats->load++;
547
548                 if (!daddr) {
549                         stats->ld_noadrs++;
550                         return -1;
551                 }
552
553                 if (lvl & P(LVL, HIT)) {
554                         if (lvl & P(LVL, UNC)) stats->ld_uncache++;
555                         if (lvl & P(LVL, IO))  stats->ld_io++;
556                         if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
557                         if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
558                         if (lvl & P(LVL, L2 )) stats->ld_l2hit++;
559                         if (lvl & P(LVL, L3 )) {
560                                 if (snoop & P(SNOOP, HITM))
561                                         HITM_INC(lcl_hitm);
562                                 else
563                                         stats->ld_llchit++;
564                         }
565
566                         if (lvl & P(LVL, LOC_RAM)) {
567                                 stats->lcl_dram++;
568                                 if (snoop & P(SNOOP, HIT))
569                                         stats->ld_shared++;
570                                 else
571                                         stats->ld_excl++;
572                         }
573
574                         if ((lvl & P(LVL, REM_RAM1)) ||
575                             (lvl & P(LVL, REM_RAM2)) ||
576                              mrem) {
577                                 stats->rmt_dram++;
578                                 if (snoop & P(SNOOP, HIT))
579                                         stats->ld_shared++;
580                                 else
581                                         stats->ld_excl++;
582                         }
583                 }
584
585                 if ((lvl & P(LVL, REM_CCE1)) ||
586                     (lvl & P(LVL, REM_CCE2)) ||
587                      mrem) {
588                         if (snoop & P(SNOOP, HIT))
589                                 stats->rmt_hit++;
590                         else if (snoop & P(SNOOP, HITM))
591                                 HITM_INC(rmt_hitm);
592                 }
593
594                 if ((lvl & P(LVL, MISS)))
595                         stats->ld_miss++;
596
597         } else if (op & P(OP, STORE)) {
598                 /* store */
599                 stats->store++;
600
601                 if (!daddr) {
602                         stats->st_noadrs++;
603                         return -1;
604                 }
605
606                 if (lvl & P(LVL, HIT)) {
607                         if (lvl & P(LVL, UNC)) stats->st_uncache++;
608                         if (lvl & P(LVL, L1 )) stats->st_l1hit++;
609                 }
610                 if (lvl & P(LVL, MISS))
611                         if (lvl & P(LVL, L1)) stats->st_l1miss++;
612                 if (lvl & P(LVL, NA))
613                         stats->st_na++;
614         } else {
615                 /* unparsable data_src? */
616                 stats->noparse++;
617                 return -1;
618         }
619
620         if (!mi->daddr.ms.map || !mi->iaddr.ms.map) {
621                 stats->nomap++;
622                 return -1;
623         }
624
625 #undef P
626 #undef HITM_INC
627         return err;
628 }
629
630 void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
631 {
632         stats->nr_entries       += add->nr_entries;
633
634         stats->locks            += add->locks;
635         stats->store            += add->store;
636         stats->st_uncache       += add->st_uncache;
637         stats->st_noadrs        += add->st_noadrs;
638         stats->st_l1hit         += add->st_l1hit;
639         stats->st_l1miss        += add->st_l1miss;
640         stats->st_na            += add->st_na;
641         stats->load             += add->load;
642         stats->ld_excl          += add->ld_excl;
643         stats->ld_shared        += add->ld_shared;
644         stats->ld_uncache       += add->ld_uncache;
645         stats->ld_io            += add->ld_io;
646         stats->ld_miss          += add->ld_miss;
647         stats->ld_noadrs        += add->ld_noadrs;
648         stats->ld_fbhit         += add->ld_fbhit;
649         stats->ld_l1hit         += add->ld_l1hit;
650         stats->ld_l2hit         += add->ld_l2hit;
651         stats->ld_llchit        += add->ld_llchit;
652         stats->lcl_hitm         += add->lcl_hitm;
653         stats->rmt_hitm         += add->rmt_hitm;
654         stats->tot_hitm         += add->tot_hitm;
655         stats->rmt_hit          += add->rmt_hit;
656         stats->lcl_dram         += add->lcl_dram;
657         stats->rmt_dram         += add->rmt_dram;
658         stats->blk_data         += add->blk_data;
659         stats->blk_addr         += add->blk_addr;
660         stats->nomap            += add->nomap;
661         stats->noparse          += add->noparse;
662 }