xref: /openbmc/linux/tools/perf/util/mem-events.c (revision bb0eb050)
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 int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
170 {
171 	size_t i, l = 0;
172 	u64 m =  PERF_MEM_LVL_NA;
173 	u64 hit, miss;
174 
175 	if (mem_info)
176 		m  = mem_info->data_src.mem_lvl;
177 
178 	sz -= 1; /* -1 for null termination */
179 	out[0] = '\0';
180 
181 	hit = m & PERF_MEM_LVL_HIT;
182 	miss = m & PERF_MEM_LVL_MISS;
183 
184 	/* already taken care of */
185 	m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
186 
187 	for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
188 		if (!(m & 0x1))
189 			continue;
190 		if (l) {
191 			strcat(out, " or ");
192 			l += 4;
193 		}
194 		l += scnprintf(out + l, sz - l, mem_lvl[i]);
195 	}
196 	if (*out == '\0')
197 		l += scnprintf(out, sz - l, "N/A");
198 	if (hit)
199 		l += scnprintf(out + l, sz - l, " hit");
200 	if (miss)
201 		l += scnprintf(out + l, sz - l, " miss");
202 
203 	return l;
204 }
205 
206 static const char * const snoop_access[] = {
207 	"N/A",
208 	"None",
209 	"Hit",
210 	"Miss",
211 	"HitM",
212 };
213 
214 int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
215 {
216 	size_t i, l = 0;
217 	u64 m = PERF_MEM_SNOOP_NA;
218 
219 	sz -= 1; /* -1 for null termination */
220 	out[0] = '\0';
221 
222 	if (mem_info)
223 		m = mem_info->data_src.mem_snoop;
224 
225 	for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
226 		if (!(m & 0x1))
227 			continue;
228 		if (l) {
229 			strcat(out, " or ");
230 			l += 4;
231 		}
232 		l += scnprintf(out + l, sz - l, snoop_access[i]);
233 	}
234 
235 	if (*out == '\0')
236 		l += scnprintf(out, sz - l, "N/A");
237 
238 	return l;
239 }
240 
241 int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
242 {
243 	u64 mask = PERF_MEM_LOCK_NA;
244 	int l;
245 
246 	if (mem_info)
247 		mask = mem_info->data_src.mem_lock;
248 
249 	if (mask & PERF_MEM_LOCK_NA)
250 		l = scnprintf(out, sz, "N/A");
251 	else if (mask & PERF_MEM_LOCK_LOCKED)
252 		l = scnprintf(out, sz, "Yes");
253 	else
254 		l = scnprintf(out, sz, "No");
255 
256 	return l;
257 }
258 
259 int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
260 {
261 	int i = 0;
262 
263 	i += perf_mem__lvl_scnprintf(out, sz, mem_info);
264 	i += scnprintf(out + i, sz - i, "|SNP ");
265 	i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
266 	i += scnprintf(out + i, sz - i, "|TLB ");
267 	i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
268 	i += scnprintf(out + i, sz - i, "|LCK ");
269 	i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
270 
271 	return i;
272 }
273 
274 int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
275 {
276 	union perf_mem_data_src *data_src = &mi->data_src;
277 	u64 daddr  = mi->daddr.addr;
278 	u64 op     = data_src->mem_op;
279 	u64 lvl    = data_src->mem_lvl;
280 	u64 snoop  = data_src->mem_snoop;
281 	u64 lock   = data_src->mem_lock;
282 	int err = 0;
283 
284 #define HITM_INC(__f)		\
285 do {				\
286 	stats->__f++;		\
287 	stats->tot_hitm++;	\
288 } while (0)
289 
290 #define P(a, b) PERF_MEM_##a##_##b
291 
292 	stats->nr_entries++;
293 
294 	if (lock & P(LOCK, LOCKED)) stats->locks++;
295 
296 	if (op & P(OP, LOAD)) {
297 		/* load */
298 		stats->load++;
299 
300 		if (!daddr) {
301 			stats->ld_noadrs++;
302 			return -1;
303 		}
304 
305 		if (lvl & P(LVL, HIT)) {
306 			if (lvl & P(LVL, UNC)) stats->ld_uncache++;
307 			if (lvl & P(LVL, IO))  stats->ld_io++;
308 			if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
309 			if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
310 			if (lvl & P(LVL, L2 )) stats->ld_l2hit++;
311 			if (lvl & P(LVL, L3 )) {
312 				if (snoop & P(SNOOP, HITM))
313 					HITM_INC(lcl_hitm);
314 				else
315 					stats->ld_llchit++;
316 			}
317 
318 			if (lvl & P(LVL, LOC_RAM)) {
319 				stats->lcl_dram++;
320 				if (snoop & P(SNOOP, HIT))
321 					stats->ld_shared++;
322 				else
323 					stats->ld_excl++;
324 			}
325 
326 			if ((lvl & P(LVL, REM_RAM1)) ||
327 			    (lvl & P(LVL, REM_RAM2))) {
328 				stats->rmt_dram++;
329 				if (snoop & P(SNOOP, HIT))
330 					stats->ld_shared++;
331 				else
332 					stats->ld_excl++;
333 			}
334 		}
335 
336 		if ((lvl & P(LVL, REM_CCE1)) ||
337 		    (lvl & P(LVL, REM_CCE2))) {
338 			if (snoop & P(SNOOP, HIT))
339 				stats->rmt_hit++;
340 			else if (snoop & P(SNOOP, HITM))
341 				HITM_INC(rmt_hitm);
342 		}
343 
344 		if ((lvl & P(LVL, MISS)))
345 			stats->ld_miss++;
346 
347 	} else if (op & P(OP, STORE)) {
348 		/* store */
349 		stats->store++;
350 
351 		if (!daddr) {
352 			stats->st_noadrs++;
353 			return -1;
354 		}
355 
356 		if (lvl & P(LVL, HIT)) {
357 			if (lvl & P(LVL, UNC)) stats->st_uncache++;
358 			if (lvl & P(LVL, L1 )) stats->st_l1hit++;
359 		}
360 		if (lvl & P(LVL, MISS))
361 			if (lvl & P(LVL, L1)) stats->st_l1miss++;
362 	} else {
363 		/* unparsable data_src? */
364 		stats->noparse++;
365 		return -1;
366 	}
367 
368 	if (!mi->daddr.map || !mi->iaddr.map) {
369 		stats->nomap++;
370 		return -1;
371 	}
372 
373 #undef P
374 #undef HITM_INC
375 	return err;
376 }
377 
378 void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
379 {
380 	stats->nr_entries	+= add->nr_entries;
381 
382 	stats->locks		+= add->locks;
383 	stats->store		+= add->store;
384 	stats->st_uncache	+= add->st_uncache;
385 	stats->st_noadrs	+= add->st_noadrs;
386 	stats->st_l1hit		+= add->st_l1hit;
387 	stats->st_l1miss	+= add->st_l1miss;
388 	stats->load		+= add->load;
389 	stats->ld_excl		+= add->ld_excl;
390 	stats->ld_shared	+= add->ld_shared;
391 	stats->ld_uncache	+= add->ld_uncache;
392 	stats->ld_io		+= add->ld_io;
393 	stats->ld_miss		+= add->ld_miss;
394 	stats->ld_noadrs	+= add->ld_noadrs;
395 	stats->ld_fbhit		+= add->ld_fbhit;
396 	stats->ld_l1hit		+= add->ld_l1hit;
397 	stats->ld_l2hit		+= add->ld_l2hit;
398 	stats->ld_llchit	+= add->ld_llchit;
399 	stats->lcl_hitm		+= add->lcl_hitm;
400 	stats->rmt_hitm		+= add->rmt_hitm;
401 	stats->tot_hitm		+= add->tot_hitm;
402 	stats->rmt_hit		+= add->rmt_hit;
403 	stats->lcl_dram		+= add->lcl_dram;
404 	stats->rmt_dram		+= add->rmt_dram;
405 	stats->nomap		+= add->nomap;
406 	stats->noparse		+= add->noparse;
407 }
408