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