xref: /openbmc/linux/tools/perf/util/mem-events.c (revision 65417d9f)
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 
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 
31 char *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 
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 
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 
106 static const char * const tlb_access[] = {
107 	"N/A",
108 	"HIT",
109 	"MISS",
110 	"L1",
111 	"L2",
112 	"Walker",
113 	"Fault",
114 };
115 
116 int 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 
153 static 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 
170 static 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 
178 int 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 
236 static const char * const snoop_access[] = {
237 	"N/A",
238 	"None",
239 	"Hit",
240 	"Miss",
241 	"HitM",
242 };
243 
244 int 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 
279 int 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 
297 int 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 
312 int 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)		\
328 do {				\
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 
423 void 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 }
453