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