xref: /openbmc/linux/tools/perf/util/mem-events.c (revision 6aa7de05)
1 #include <stddef.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <unistd.h>
8 #include <api/fs/fs.h>
9 #include <linux/kernel.h>
10 #include "mem-events.h"
11 #include "debug.h"
12 #include "symbol.h"
13 #include "sort.h"
14 
15 unsigned int perf_mem_events__loads_ldlat = 30;
16 
17 #define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
18 
19 struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {
20 	E("ldlat-loads",	"cpu/mem-loads,ldlat=%u/P",	"mem-loads"),
21 	E("ldlat-stores",	"cpu/mem-stores/P",		"mem-stores"),
22 };
23 #undef E
24 
25 #undef E
26 
27 static char mem_loads_name[100];
28 static bool mem_loads_name__init;
29 
30 char *perf_mem_events__name(int i)
31 {
32 	if (i == PERF_MEM_EVENTS__LOAD) {
33 		if (!mem_loads_name__init) {
34 			mem_loads_name__init = true;
35 			scnprintf(mem_loads_name, sizeof(mem_loads_name),
36 				  perf_mem_events[i].name,
37 				  perf_mem_events__loads_ldlat);
38 		}
39 		return mem_loads_name;
40 	}
41 
42 	return (char *)perf_mem_events[i].name;
43 }
44 
45 int perf_mem_events__parse(const char *str)
46 {
47 	char *tok, *saveptr = NULL;
48 	bool found = false;
49 	char *buf;
50 	int j;
51 
52 	/* We need buffer that we know we can write to. */
53 	buf = malloc(strlen(str) + 1);
54 	if (!buf)
55 		return -ENOMEM;
56 
57 	strcpy(buf, str);
58 
59 	tok = strtok_r((char *)buf, ",", &saveptr);
60 
61 	while (tok) {
62 		for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
63 			struct perf_mem_event *e = &perf_mem_events[j];
64 
65 			if (strstr(e->tag, tok))
66 				e->record = found = true;
67 		}
68 
69 		tok = strtok_r(NULL, ",", &saveptr);
70 	}
71 
72 	free(buf);
73 
74 	if (found)
75 		return 0;
76 
77 	pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str);
78 	return -1;
79 }
80 
81 int perf_mem_events__init(void)
82 {
83 	const char *mnt = sysfs__mount();
84 	bool found = false;
85 	int j;
86 
87 	if (!mnt)
88 		return -ENOENT;
89 
90 	for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
91 		char path[PATH_MAX];
92 		struct perf_mem_event *e = &perf_mem_events[j];
93 		struct stat st;
94 
95 		scnprintf(path, PATH_MAX, "%s/devices/cpu/events/%s",
96 			  mnt, e->sysfs_name);
97 
98 		if (!stat(path, &st))
99 			e->supported = found = true;
100 	}
101 
102 	return found ? 0 : -ENOENT;
103 }
104 
105 static const char * const tlb_access[] = {
106 	"N/A",
107 	"HIT",
108 	"MISS",
109 	"L1",
110 	"L2",
111 	"Walker",
112 	"Fault",
113 };
114 
115 int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
116 {
117 	size_t l = 0, i;
118 	u64 m = PERF_MEM_TLB_NA;
119 	u64 hit, miss;
120 
121 	sz -= 1; /* -1 for null termination */
122 	out[0] = '\0';
123 
124 	if (mem_info)
125 		m = mem_info->data_src.mem_dtlb;
126 
127 	hit = m & PERF_MEM_TLB_HIT;
128 	miss = m & PERF_MEM_TLB_MISS;
129 
130 	/* already taken care of */
131 	m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
132 
133 	for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
134 		if (!(m & 0x1))
135 			continue;
136 		if (l) {
137 			strcat(out, " or ");
138 			l += 4;
139 		}
140 		l += scnprintf(out + l, sz - l, tlb_access[i]);
141 	}
142 	if (*out == '\0')
143 		l += scnprintf(out, sz - l, "N/A");
144 	if (hit)
145 		l += scnprintf(out + l, sz - l, " hit");
146 	if (miss)
147 		l += scnprintf(out + l, sz - l, " miss");
148 
149 	return l;
150 }
151 
152 static const char * const mem_lvl[] = {
153 	"N/A",
154 	"HIT",
155 	"MISS",
156 	"L1",
157 	"LFB",
158 	"L2",
159 	"L3",
160 	"Local RAM",
161 	"Remote RAM (1 hop)",
162 	"Remote RAM (2 hops)",
163 	"Remote Cache (1 hop)",
164 	"Remote Cache (2 hops)",
165 	"I/O",
166 	"Uncached",
167 };
168 
169 static const char * const mem_lvlnum[] = {
170 	[PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache",
171 	[PERF_MEM_LVLNUM_LFB] = "LFB",
172 	[PERF_MEM_LVLNUM_RAM] = "RAM",
173 	[PERF_MEM_LVLNUM_PMEM] = "PMEM",
174 	[PERF_MEM_LVLNUM_NA] = "N/A",
175 };
176 
177 int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
178 {
179 	size_t i, l = 0;
180 	u64 m =  PERF_MEM_LVL_NA;
181 	u64 hit, miss;
182 	int printed;
183 
184 	if (mem_info)
185 		m  = mem_info->data_src.mem_lvl;
186 
187 	sz -= 1; /* -1 for null termination */
188 	out[0] = '\0';
189 
190 	hit = m & PERF_MEM_LVL_HIT;
191 	miss = m & PERF_MEM_LVL_MISS;
192 
193 	/* already taken care of */
194 	m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
195 
196 
197 	if (mem_info && mem_info->data_src.mem_remote) {
198 		strcat(out, "Remote ");
199 		l += 7;
200 	}
201 
202 	printed = 0;
203 	for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
204 		if (!(m & 0x1))
205 			continue;
206 		if (printed++) {
207 			strcat(out, " or ");
208 			l += 4;
209 		}
210 		l += scnprintf(out + l, sz - l, mem_lvl[i]);
211 	}
212 
213 	if (mem_info && mem_info->data_src.mem_lvl_num) {
214 		int lvl = mem_info->data_src.mem_lvl_num;
215 		if (printed++) {
216 			strcat(out, " or ");
217 			l += 4;
218 		}
219 		if (mem_lvlnum[lvl])
220 			l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
221 		else
222 			l += scnprintf(out + l, sz - l, "L%d", lvl);
223 	}
224 
225 	if (l == 0)
226 		l += scnprintf(out + l, sz - l, "N/A");
227 	if (hit)
228 		l += scnprintf(out + l, sz - l, " hit");
229 	if (miss)
230 		l += scnprintf(out + l, sz - l, " miss");
231 
232 	return l;
233 }
234 
235 static const char * const snoop_access[] = {
236 	"N/A",
237 	"None",
238 	"Hit",
239 	"Miss",
240 	"HitM",
241 };
242 
243 int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
244 {
245 	size_t i, l = 0;
246 	u64 m = PERF_MEM_SNOOP_NA;
247 
248 	sz -= 1; /* -1 for null termination */
249 	out[0] = '\0';
250 
251 	if (mem_info)
252 		m = mem_info->data_src.mem_snoop;
253 
254 	for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
255 		if (!(m & 0x1))
256 			continue;
257 		if (l) {
258 			strcat(out, " or ");
259 			l += 4;
260 		}
261 		l += scnprintf(out + l, sz - l, snoop_access[i]);
262 	}
263 	if (mem_info &&
264 	     (mem_info->data_src.mem_snoopx & PERF_MEM_SNOOPX_FWD)) {
265 		if (l) {
266 			strcat(out, " or ");
267 			l += 4;
268 		}
269 		l += scnprintf(out + l, sz - l, "Fwd");
270 	}
271 
272 	if (*out == '\0')
273 		l += scnprintf(out, sz - l, "N/A");
274 
275 	return l;
276 }
277 
278 int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
279 {
280 	u64 mask = PERF_MEM_LOCK_NA;
281 	int l;
282 
283 	if (mem_info)
284 		mask = mem_info->data_src.mem_lock;
285 
286 	if (mask & PERF_MEM_LOCK_NA)
287 		l = scnprintf(out, sz, "N/A");
288 	else if (mask & PERF_MEM_LOCK_LOCKED)
289 		l = scnprintf(out, sz, "Yes");
290 	else
291 		l = scnprintf(out, sz, "No");
292 
293 	return l;
294 }
295 
296 int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
297 {
298 	int i = 0;
299 
300 	i += perf_mem__lvl_scnprintf(out, sz, mem_info);
301 	i += scnprintf(out + i, sz - i, "|SNP ");
302 	i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
303 	i += scnprintf(out + i, sz - i, "|TLB ");
304 	i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
305 	i += scnprintf(out + i, sz - i, "|LCK ");
306 	i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
307 
308 	return i;
309 }
310 
311 int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
312 {
313 	union perf_mem_data_src *data_src = &mi->data_src;
314 	u64 daddr  = mi->daddr.addr;
315 	u64 op     = data_src->mem_op;
316 	u64 lvl    = data_src->mem_lvl;
317 	u64 snoop  = data_src->mem_snoop;
318 	u64 lock   = data_src->mem_lock;
319 	/*
320 	 * Skylake might report unknown remote level via this
321 	 * bit, consider it when evaluating remote HITMs.
322 	 */
323 	bool mrem  = data_src->mem_remote;
324 	int err = 0;
325 
326 #define HITM_INC(__f)		\
327 do {				\
328 	stats->__f++;		\
329 	stats->tot_hitm++;	\
330 } while (0)
331 
332 #define P(a, b) PERF_MEM_##a##_##b
333 
334 	stats->nr_entries++;
335 
336 	if (lock & P(LOCK, LOCKED)) stats->locks++;
337 
338 	if (op & P(OP, LOAD)) {
339 		/* load */
340 		stats->load++;
341 
342 		if (!daddr) {
343 			stats->ld_noadrs++;
344 			return -1;
345 		}
346 
347 		if (lvl & P(LVL, HIT)) {
348 			if (lvl & P(LVL, UNC)) stats->ld_uncache++;
349 			if (lvl & P(LVL, IO))  stats->ld_io++;
350 			if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
351 			if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
352 			if (lvl & P(LVL, L2 )) stats->ld_l2hit++;
353 			if (lvl & P(LVL, L3 )) {
354 				if (snoop & P(SNOOP, HITM))
355 					HITM_INC(lcl_hitm);
356 				else
357 					stats->ld_llchit++;
358 			}
359 
360 			if (lvl & P(LVL, LOC_RAM)) {
361 				stats->lcl_dram++;
362 				if (snoop & P(SNOOP, HIT))
363 					stats->ld_shared++;
364 				else
365 					stats->ld_excl++;
366 			}
367 
368 			if ((lvl & P(LVL, REM_RAM1)) ||
369 			    (lvl & P(LVL, REM_RAM2)) ||
370 			     mrem) {
371 				stats->rmt_dram++;
372 				if (snoop & P(SNOOP, HIT))
373 					stats->ld_shared++;
374 				else
375 					stats->ld_excl++;
376 			}
377 		}
378 
379 		if ((lvl & P(LVL, REM_CCE1)) ||
380 		    (lvl & P(LVL, REM_CCE2)) ||
381 		     mrem) {
382 			if (snoop & P(SNOOP, HIT))
383 				stats->rmt_hit++;
384 			else if (snoop & P(SNOOP, HITM))
385 				HITM_INC(rmt_hitm);
386 		}
387 
388 		if ((lvl & P(LVL, MISS)))
389 			stats->ld_miss++;
390 
391 	} else if (op & P(OP, STORE)) {
392 		/* store */
393 		stats->store++;
394 
395 		if (!daddr) {
396 			stats->st_noadrs++;
397 			return -1;
398 		}
399 
400 		if (lvl & P(LVL, HIT)) {
401 			if (lvl & P(LVL, UNC)) stats->st_uncache++;
402 			if (lvl & P(LVL, L1 )) stats->st_l1hit++;
403 		}
404 		if (lvl & P(LVL, MISS))
405 			if (lvl & P(LVL, L1)) stats->st_l1miss++;
406 	} else {
407 		/* unparsable data_src? */
408 		stats->noparse++;
409 		return -1;
410 	}
411 
412 	if (!mi->daddr.map || !mi->iaddr.map) {
413 		stats->nomap++;
414 		return -1;
415 	}
416 
417 #undef P
418 #undef HITM_INC
419 	return err;
420 }
421 
422 void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
423 {
424 	stats->nr_entries	+= add->nr_entries;
425 
426 	stats->locks		+= add->locks;
427 	stats->store		+= add->store;
428 	stats->st_uncache	+= add->st_uncache;
429 	stats->st_noadrs	+= add->st_noadrs;
430 	stats->st_l1hit		+= add->st_l1hit;
431 	stats->st_l1miss	+= add->st_l1miss;
432 	stats->load		+= add->load;
433 	stats->ld_excl		+= add->ld_excl;
434 	stats->ld_shared	+= add->ld_shared;
435 	stats->ld_uncache	+= add->ld_uncache;
436 	stats->ld_io		+= add->ld_io;
437 	stats->ld_miss		+= add->ld_miss;
438 	stats->ld_noadrs	+= add->ld_noadrs;
439 	stats->ld_fbhit		+= add->ld_fbhit;
440 	stats->ld_l1hit		+= add->ld_l1hit;
441 	stats->ld_l2hit		+= add->ld_l2hit;
442 	stats->ld_llchit	+= add->ld_llchit;
443 	stats->lcl_hitm		+= add->lcl_hitm;
444 	stats->rmt_hitm		+= add->rmt_hitm;
445 	stats->tot_hitm		+= add->tot_hitm;
446 	stats->rmt_hit		+= add->rmt_hit;
447 	stats->lcl_dram		+= add->lcl_dram;
448 	stats->rmt_dram		+= add->rmt_dram;
449 	stats->nomap		+= add->nomap;
450 	stats->noparse		+= add->noparse;
451 }
452