Merge tag 'amd-drm-fixes-5.9-2020-08-12' of git://people.freedesktop.org/~agd5f/linux...
[linux-2.6-microblaze.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
16 unsigned int perf_mem_events__loads_ldlat = 30;
17
18 #define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
19
20 struct 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
28 static char mem_loads_name[100];
29 static bool mem_loads_name__init;
30
31 char * __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
46 int 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
82 int 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
106 void 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
121 static const char * const tlb_access[] = {
122         "N/A",
123         "HIT",
124         "MISS",
125         "L1",
126         "L2",
127         "Walker",
128         "Fault",
129 };
130
131 int 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
168 static 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
185 static 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
193 int 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
251 static const char * const snoop_access[] = {
252         "N/A",
253         "None",
254         "Hit",
255         "Miss",
256         "HitM",
257 };
258
259 int 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
294 int 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
312 int 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
327 int 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)           \
343 do {                            \
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
438 void 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 }