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