perf stat aggregation: Add separate core member
[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 static struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {
21         E("ldlat-loads",        "cpu/mem-loads,ldlat=%u/P",     "cpu/events/mem-loads"),
22         E("ldlat-stores",       "cpu/mem-stores/P",             "cpu/events/mem-stores"),
23         E(NULL,                 NULL,                           NULL),
24 };
25 #undef E
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)
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 int perf_mem_events__parse(const char *str)
60 {
61         char *tok, *saveptr = NULL;
62         bool found = false;
63         char *buf;
64         int j;
65
66         /* We need buffer that we know we can write to. */
67         buf = malloc(strlen(str) + 1);
68         if (!buf)
69                 return -ENOMEM;
70
71         strcpy(buf, str);
72
73         tok = strtok_r((char *)buf, ",", &saveptr);
74
75         while (tok) {
76                 for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
77                         struct perf_mem_event *e = perf_mem_events__ptr(j);
78
79                         if (!e->tag)
80                                 continue;
81
82                         if (strstr(e->tag, tok))
83                                 e->record = found = true;
84                 }
85
86                 tok = strtok_r(NULL, ",", &saveptr);
87         }
88
89         free(buf);
90
91         if (found)
92                 return 0;
93
94         pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str);
95         return -1;
96 }
97
98 int perf_mem_events__init(void)
99 {
100         const char *mnt = sysfs__mount();
101         bool found = false;
102         int j;
103
104         if (!mnt)
105                 return -ENOENT;
106
107         for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
108                 char path[PATH_MAX];
109                 struct perf_mem_event *e = perf_mem_events__ptr(j);
110                 struct stat st;
111
112                 /*
113                  * If the event entry isn't valid, skip initialization
114                  * and "e->supported" will keep false.
115                  */
116                 if (!e->tag)
117                         continue;
118
119                 scnprintf(path, PATH_MAX, "%s/devices/%s",
120                           mnt, e->sysfs_name);
121
122                 if (!stat(path, &st))
123                         e->supported = found = true;
124         }
125
126         return found ? 0 : -ENOENT;
127 }
128
129 void perf_mem_events__list(void)
130 {
131         int j;
132
133         for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
134                 struct perf_mem_event *e = perf_mem_events__ptr(j);
135
136                 fprintf(stderr, "%-13s%-*s%s\n",
137                         e->tag ?: "",
138                         verbose > 0 ? 25 : 0,
139                         verbose > 0 ? perf_mem_events__name(j) : "",
140                         e->supported ? ": available" : "");
141         }
142 }
143
144 static const char * const tlb_access[] = {
145         "N/A",
146         "HIT",
147         "MISS",
148         "L1",
149         "L2",
150         "Walker",
151         "Fault",
152 };
153
154 int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
155 {
156         size_t l = 0, i;
157         u64 m = PERF_MEM_TLB_NA;
158         u64 hit, miss;
159
160         sz -= 1; /* -1 for null termination */
161         out[0] = '\0';
162
163         if (mem_info)
164                 m = mem_info->data_src.mem_dtlb;
165
166         hit = m & PERF_MEM_TLB_HIT;
167         miss = m & PERF_MEM_TLB_MISS;
168
169         /* already taken care of */
170         m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
171
172         for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
173                 if (!(m & 0x1))
174                         continue;
175                 if (l) {
176                         strcat(out, " or ");
177                         l += 4;
178                 }
179                 l += scnprintf(out + l, sz - l, tlb_access[i]);
180         }
181         if (*out == '\0')
182                 l += scnprintf(out, sz - l, "N/A");
183         if (hit)
184                 l += scnprintf(out + l, sz - l, " hit");
185         if (miss)
186                 l += scnprintf(out + l, sz - l, " miss");
187
188         return l;
189 }
190
191 static const char * const mem_lvl[] = {
192         "N/A",
193         "HIT",
194         "MISS",
195         "L1",
196         "LFB",
197         "L2",
198         "L3",
199         "Local RAM",
200         "Remote RAM (1 hop)",
201         "Remote RAM (2 hops)",
202         "Remote Cache (1 hop)",
203         "Remote Cache (2 hops)",
204         "I/O",
205         "Uncached",
206 };
207
208 static const char * const mem_lvlnum[] = {
209         [PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache",
210         [PERF_MEM_LVLNUM_LFB] = "LFB",
211         [PERF_MEM_LVLNUM_RAM] = "RAM",
212         [PERF_MEM_LVLNUM_PMEM] = "PMEM",
213         [PERF_MEM_LVLNUM_NA] = "N/A",
214 };
215
216 int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
217 {
218         size_t i, l = 0;
219         u64 m =  PERF_MEM_LVL_NA;
220         u64 hit, miss;
221         int printed;
222
223         if (mem_info)
224                 m  = mem_info->data_src.mem_lvl;
225
226         sz -= 1; /* -1 for null termination */
227         out[0] = '\0';
228
229         hit = m & PERF_MEM_LVL_HIT;
230         miss = m & PERF_MEM_LVL_MISS;
231
232         /* already taken care of */
233         m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
234
235
236         if (mem_info && mem_info->data_src.mem_remote) {
237                 strcat(out, "Remote ");
238                 l += 7;
239         }
240
241         printed = 0;
242         for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
243                 if (!(m & 0x1))
244                         continue;
245                 if (printed++) {
246                         strcat(out, " or ");
247                         l += 4;
248                 }
249                 l += scnprintf(out + l, sz - l, mem_lvl[i]);
250         }
251
252         if (mem_info && mem_info->data_src.mem_lvl_num) {
253                 int lvl = mem_info->data_src.mem_lvl_num;
254                 if (printed++) {
255                         strcat(out, " or ");
256                         l += 4;
257                 }
258                 if (mem_lvlnum[lvl])
259                         l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
260                 else
261                         l += scnprintf(out + l, sz - l, "L%d", lvl);
262         }
263
264         if (l == 0)
265                 l += scnprintf(out + l, sz - l, "N/A");
266         if (hit)
267                 l += scnprintf(out + l, sz - l, " hit");
268         if (miss)
269                 l += scnprintf(out + l, sz - l, " miss");
270
271         return l;
272 }
273
274 static const char * const snoop_access[] = {
275         "N/A",
276         "None",
277         "Hit",
278         "Miss",
279         "HitM",
280 };
281
282 int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
283 {
284         size_t i, l = 0;
285         u64 m = PERF_MEM_SNOOP_NA;
286
287         sz -= 1; /* -1 for null termination */
288         out[0] = '\0';
289
290         if (mem_info)
291                 m = mem_info->data_src.mem_snoop;
292
293         for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
294                 if (!(m & 0x1))
295                         continue;
296                 if (l) {
297                         strcat(out, " or ");
298                         l += 4;
299                 }
300                 l += scnprintf(out + l, sz - l, snoop_access[i]);
301         }
302         if (mem_info &&
303              (mem_info->data_src.mem_snoopx & PERF_MEM_SNOOPX_FWD)) {
304                 if (l) {
305                         strcat(out, " or ");
306                         l += 4;
307                 }
308                 l += scnprintf(out + l, sz - l, "Fwd");
309         }
310
311         if (*out == '\0')
312                 l += scnprintf(out, sz - l, "N/A");
313
314         return l;
315 }
316
317 int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
318 {
319         u64 mask = PERF_MEM_LOCK_NA;
320         int l;
321
322         if (mem_info)
323                 mask = mem_info->data_src.mem_lock;
324
325         if (mask & PERF_MEM_LOCK_NA)
326                 l = scnprintf(out, sz, "N/A");
327         else if (mask & PERF_MEM_LOCK_LOCKED)
328                 l = scnprintf(out, sz, "Yes");
329         else
330                 l = scnprintf(out, sz, "No");
331
332         return l;
333 }
334
335 int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
336 {
337         int i = 0;
338
339         i += perf_mem__lvl_scnprintf(out, sz, mem_info);
340         i += scnprintf(out + i, sz - i, "|SNP ");
341         i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
342         i += scnprintf(out + i, sz - i, "|TLB ");
343         i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
344         i += scnprintf(out + i, sz - i, "|LCK ");
345         i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
346
347         return i;
348 }
349
350 int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
351 {
352         union perf_mem_data_src *data_src = &mi->data_src;
353         u64 daddr  = mi->daddr.addr;
354         u64 op     = data_src->mem_op;
355         u64 lvl    = data_src->mem_lvl;
356         u64 snoop  = data_src->mem_snoop;
357         u64 lock   = data_src->mem_lock;
358         /*
359          * Skylake might report unknown remote level via this
360          * bit, consider it when evaluating remote HITMs.
361          */
362         bool mrem  = data_src->mem_remote;
363         int err = 0;
364
365 #define HITM_INC(__f)           \
366 do {                            \
367         stats->__f++;           \
368         stats->tot_hitm++;      \
369 } while (0)
370
371 #define P(a, b) PERF_MEM_##a##_##b
372
373         stats->nr_entries++;
374
375         if (lock & P(LOCK, LOCKED)) stats->locks++;
376
377         if (op & P(OP, LOAD)) {
378                 /* load */
379                 stats->load++;
380
381                 if (!daddr) {
382                         stats->ld_noadrs++;
383                         return -1;
384                 }
385
386                 if (lvl & P(LVL, HIT)) {
387                         if (lvl & P(LVL, UNC)) stats->ld_uncache++;
388                         if (lvl & P(LVL, IO))  stats->ld_io++;
389                         if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
390                         if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
391                         if (lvl & P(LVL, L2 )) stats->ld_l2hit++;
392                         if (lvl & P(LVL, L3 )) {
393                                 if (snoop & P(SNOOP, HITM))
394                                         HITM_INC(lcl_hitm);
395                                 else
396                                         stats->ld_llchit++;
397                         }
398
399                         if (lvl & P(LVL, LOC_RAM)) {
400                                 stats->lcl_dram++;
401                                 if (snoop & P(SNOOP, HIT))
402                                         stats->ld_shared++;
403                                 else
404                                         stats->ld_excl++;
405                         }
406
407                         if ((lvl & P(LVL, REM_RAM1)) ||
408                             (lvl & P(LVL, REM_RAM2)) ||
409                              mrem) {
410                                 stats->rmt_dram++;
411                                 if (snoop & P(SNOOP, HIT))
412                                         stats->ld_shared++;
413                                 else
414                                         stats->ld_excl++;
415                         }
416                 }
417
418                 if ((lvl & P(LVL, REM_CCE1)) ||
419                     (lvl & P(LVL, REM_CCE2)) ||
420                      mrem) {
421                         if (snoop & P(SNOOP, HIT))
422                                 stats->rmt_hit++;
423                         else if (snoop & P(SNOOP, HITM))
424                                 HITM_INC(rmt_hitm);
425                 }
426
427                 if ((lvl & P(LVL, MISS)))
428                         stats->ld_miss++;
429
430         } else if (op & P(OP, STORE)) {
431                 /* store */
432                 stats->store++;
433
434                 if (!daddr) {
435                         stats->st_noadrs++;
436                         return -1;
437                 }
438
439                 if (lvl & P(LVL, HIT)) {
440                         if (lvl & P(LVL, UNC)) stats->st_uncache++;
441                         if (lvl & P(LVL, L1 )) stats->st_l1hit++;
442                 }
443                 if (lvl & P(LVL, MISS))
444                         if (lvl & P(LVL, L1)) stats->st_l1miss++;
445         } else {
446                 /* unparsable data_src? */
447                 stats->noparse++;
448                 return -1;
449         }
450
451         if (!mi->daddr.ms.map || !mi->iaddr.ms.map) {
452                 stats->nomap++;
453                 return -1;
454         }
455
456 #undef P
457 #undef HITM_INC
458         return err;
459 }
460
461 void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
462 {
463         stats->nr_entries       += add->nr_entries;
464
465         stats->locks            += add->locks;
466         stats->store            += add->store;
467         stats->st_uncache       += add->st_uncache;
468         stats->st_noadrs        += add->st_noadrs;
469         stats->st_l1hit         += add->st_l1hit;
470         stats->st_l1miss        += add->st_l1miss;
471         stats->load             += add->load;
472         stats->ld_excl          += add->ld_excl;
473         stats->ld_shared        += add->ld_shared;
474         stats->ld_uncache       += add->ld_uncache;
475         stats->ld_io            += add->ld_io;
476         stats->ld_miss          += add->ld_miss;
477         stats->ld_noadrs        += add->ld_noadrs;
478         stats->ld_fbhit         += add->ld_fbhit;
479         stats->ld_l1hit         += add->ld_l1hit;
480         stats->ld_l2hit         += add->ld_l2hit;
481         stats->ld_llchit        += add->ld_llchit;
482         stats->lcl_hitm         += add->lcl_hitm;
483         stats->rmt_hitm         += add->rmt_hitm;
484         stats->tot_hitm         += add->tot_hitm;
485         stats->rmt_hit          += add->rmt_hit;
486         stats->lcl_dram         += add->lcl_dram;
487         stats->rmt_dram         += add->rmt_dram;
488         stats->nomap            += add->nomap;
489         stats->noparse          += add->noparse;
490 }