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