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