Merge tag 'kbuild-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy...
[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 static const char * const tlb_access[] = {
107         "N/A",
108         "HIT",
109         "MISS",
110         "L1",
111         "L2",
112         "Walker",
113         "Fault",
114 };
115
116 int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
117 {
118         size_t l = 0, i;
119         u64 m = PERF_MEM_TLB_NA;
120         u64 hit, miss;
121
122         sz -= 1; /* -1 for null termination */
123         out[0] = '\0';
124
125         if (mem_info)
126                 m = mem_info->data_src.mem_dtlb;
127
128         hit = m & PERF_MEM_TLB_HIT;
129         miss = m & PERF_MEM_TLB_MISS;
130
131         /* already taken care of */
132         m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
133
134         for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
135                 if (!(m & 0x1))
136                         continue;
137                 if (l) {
138                         strcat(out, " or ");
139                         l += 4;
140                 }
141                 l += scnprintf(out + l, sz - l, tlb_access[i]);
142         }
143         if (*out == '\0')
144                 l += scnprintf(out, sz - l, "N/A");
145         if (hit)
146                 l += scnprintf(out + l, sz - l, " hit");
147         if (miss)
148                 l += scnprintf(out + l, sz - l, " miss");
149
150         return l;
151 }
152
153 static const char * const mem_lvl[] = {
154         "N/A",
155         "HIT",
156         "MISS",
157         "L1",
158         "LFB",
159         "L2",
160         "L3",
161         "Local RAM",
162         "Remote RAM (1 hop)",
163         "Remote RAM (2 hops)",
164         "Remote Cache (1 hop)",
165         "Remote Cache (2 hops)",
166         "I/O",
167         "Uncached",
168 };
169
170 static const char * const mem_lvlnum[] = {
171         [PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache",
172         [PERF_MEM_LVLNUM_LFB] = "LFB",
173         [PERF_MEM_LVLNUM_RAM] = "RAM",
174         [PERF_MEM_LVLNUM_PMEM] = "PMEM",
175         [PERF_MEM_LVLNUM_NA] = "N/A",
176 };
177
178 int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
179 {
180         size_t i, l = 0;
181         u64 m =  PERF_MEM_LVL_NA;
182         u64 hit, miss;
183         int printed;
184
185         if (mem_info)
186                 m  = mem_info->data_src.mem_lvl;
187
188         sz -= 1; /* -1 for null termination */
189         out[0] = '\0';
190
191         hit = m & PERF_MEM_LVL_HIT;
192         miss = m & PERF_MEM_LVL_MISS;
193
194         /* already taken care of */
195         m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
196
197
198         if (mem_info && mem_info->data_src.mem_remote) {
199                 strcat(out, "Remote ");
200                 l += 7;
201         }
202
203         printed = 0;
204         for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
205                 if (!(m & 0x1))
206                         continue;
207                 if (printed++) {
208                         strcat(out, " or ");
209                         l += 4;
210                 }
211                 l += scnprintf(out + l, sz - l, mem_lvl[i]);
212         }
213
214         if (mem_info && mem_info->data_src.mem_lvl_num) {
215                 int lvl = mem_info->data_src.mem_lvl_num;
216                 if (printed++) {
217                         strcat(out, " or ");
218                         l += 4;
219                 }
220                 if (mem_lvlnum[lvl])
221                         l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
222                 else
223                         l += scnprintf(out + l, sz - l, "L%d", lvl);
224         }
225
226         if (l == 0)
227                 l += scnprintf(out + l, sz - l, "N/A");
228         if (hit)
229                 l += scnprintf(out + l, sz - l, " hit");
230         if (miss)
231                 l += scnprintf(out + l, sz - l, " miss");
232
233         return l;
234 }
235
236 static const char * const snoop_access[] = {
237         "N/A",
238         "None",
239         "Hit",
240         "Miss",
241         "HitM",
242 };
243
244 int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
245 {
246         size_t i, l = 0;
247         u64 m = PERF_MEM_SNOOP_NA;
248
249         sz -= 1; /* -1 for null termination */
250         out[0] = '\0';
251
252         if (mem_info)
253                 m = mem_info->data_src.mem_snoop;
254
255         for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
256                 if (!(m & 0x1))
257                         continue;
258                 if (l) {
259                         strcat(out, " or ");
260                         l += 4;
261                 }
262                 l += scnprintf(out + l, sz - l, snoop_access[i]);
263         }
264         if (mem_info &&
265              (mem_info->data_src.mem_snoopx & PERF_MEM_SNOOPX_FWD)) {
266                 if (l) {
267                         strcat(out, " or ");
268                         l += 4;
269                 }
270                 l += scnprintf(out + l, sz - l, "Fwd");
271         }
272
273         if (*out == '\0')
274                 l += scnprintf(out, sz - l, "N/A");
275
276         return l;
277 }
278
279 int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
280 {
281         u64 mask = PERF_MEM_LOCK_NA;
282         int l;
283
284         if (mem_info)
285                 mask = mem_info->data_src.mem_lock;
286
287         if (mask & PERF_MEM_LOCK_NA)
288                 l = scnprintf(out, sz, "N/A");
289         else if (mask & PERF_MEM_LOCK_LOCKED)
290                 l = scnprintf(out, sz, "Yes");
291         else
292                 l = scnprintf(out, sz, "No");
293
294         return l;
295 }
296
297 int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
298 {
299         int i = 0;
300
301         i += perf_mem__lvl_scnprintf(out, sz, mem_info);
302         i += scnprintf(out + i, sz - i, "|SNP ");
303         i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
304         i += scnprintf(out + i, sz - i, "|TLB ");
305         i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
306         i += scnprintf(out + i, sz - i, "|LCK ");
307         i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
308
309         return i;
310 }
311
312 int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
313 {
314         union perf_mem_data_src *data_src = &mi->data_src;
315         u64 daddr  = mi->daddr.addr;
316         u64 op     = data_src->mem_op;
317         u64 lvl    = data_src->mem_lvl;
318         u64 snoop  = data_src->mem_snoop;
319         u64 lock   = data_src->mem_lock;
320         /*
321          * Skylake might report unknown remote level via this
322          * bit, consider it when evaluating remote HITMs.
323          */
324         bool mrem  = data_src->mem_remote;
325         int err = 0;
326
327 #define HITM_INC(__f)           \
328 do {                            \
329         stats->__f++;           \
330         stats->tot_hitm++;      \
331 } while (0)
332
333 #define P(a, b) PERF_MEM_##a##_##b
334
335         stats->nr_entries++;
336
337         if (lock & P(LOCK, LOCKED)) stats->locks++;
338
339         if (op & P(OP, LOAD)) {
340                 /* load */
341                 stats->load++;
342
343                 if (!daddr) {
344                         stats->ld_noadrs++;
345                         return -1;
346                 }
347
348                 if (lvl & P(LVL, HIT)) {
349                         if (lvl & P(LVL, UNC)) stats->ld_uncache++;
350                         if (lvl & P(LVL, IO))  stats->ld_io++;
351                         if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
352                         if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
353                         if (lvl & P(LVL, L2 )) stats->ld_l2hit++;
354                         if (lvl & P(LVL, L3 )) {
355                                 if (snoop & P(SNOOP, HITM))
356                                         HITM_INC(lcl_hitm);
357                                 else
358                                         stats->ld_llchit++;
359                         }
360
361                         if (lvl & P(LVL, LOC_RAM)) {
362                                 stats->lcl_dram++;
363                                 if (snoop & P(SNOOP, HIT))
364                                         stats->ld_shared++;
365                                 else
366                                         stats->ld_excl++;
367                         }
368
369                         if ((lvl & P(LVL, REM_RAM1)) ||
370                             (lvl & P(LVL, REM_RAM2)) ||
371                              mrem) {
372                                 stats->rmt_dram++;
373                                 if (snoop & P(SNOOP, HIT))
374                                         stats->ld_shared++;
375                                 else
376                                         stats->ld_excl++;
377                         }
378                 }
379
380                 if ((lvl & P(LVL, REM_CCE1)) ||
381                     (lvl & P(LVL, REM_CCE2)) ||
382                      mrem) {
383                         if (snoop & P(SNOOP, HIT))
384                                 stats->rmt_hit++;
385                         else if (snoop & P(SNOOP, HITM))
386                                 HITM_INC(rmt_hitm);
387                 }
388
389                 if ((lvl & P(LVL, MISS)))
390                         stats->ld_miss++;
391
392         } else if (op & P(OP, STORE)) {
393                 /* store */
394                 stats->store++;
395
396                 if (!daddr) {
397                         stats->st_noadrs++;
398                         return -1;
399                 }
400
401                 if (lvl & P(LVL, HIT)) {
402                         if (lvl & P(LVL, UNC)) stats->st_uncache++;
403                         if (lvl & P(LVL, L1 )) stats->st_l1hit++;
404                 }
405                 if (lvl & P(LVL, MISS))
406                         if (lvl & P(LVL, L1)) stats->st_l1miss++;
407         } else {
408                 /* unparsable data_src? */
409                 stats->noparse++;
410                 return -1;
411         }
412
413         if (!mi->daddr.map || !mi->iaddr.map) {
414                 stats->nomap++;
415                 return -1;
416         }
417
418 #undef P
419 #undef HITM_INC
420         return err;
421 }
422
423 void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
424 {
425         stats->nr_entries       += add->nr_entries;
426
427         stats->locks            += add->locks;
428         stats->store            += add->store;
429         stats->st_uncache       += add->st_uncache;
430         stats->st_noadrs        += add->st_noadrs;
431         stats->st_l1hit         += add->st_l1hit;
432         stats->st_l1miss        += add->st_l1miss;
433         stats->load             += add->load;
434         stats->ld_excl          += add->ld_excl;
435         stats->ld_shared        += add->ld_shared;
436         stats->ld_uncache       += add->ld_uncache;
437         stats->ld_io            += add->ld_io;
438         stats->ld_miss          += add->ld_miss;
439         stats->ld_noadrs        += add->ld_noadrs;
440         stats->ld_fbhit         += add->ld_fbhit;
441         stats->ld_l1hit         += add->ld_l1hit;
442         stats->ld_l2hit         += add->ld_l2hit;
443         stats->ld_llchit        += add->ld_llchit;
444         stats->lcl_hitm         += add->lcl_hitm;
445         stats->rmt_hitm         += add->rmt_hitm;
446         stats->tot_hitm         += add->tot_hitm;
447         stats->rmt_hit          += add->rmt_hit;
448         stats->lcl_dram         += add->lcl_dram;
449         stats->rmt_dram         += add->rmt_dram;
450         stats->nomap            += add->nomap;
451         stats->noparse          += add->noparse;
452 }