xref: /openbmc/linux/tools/perf/util/mem-events.c (revision f4c3b83b)
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 }
491