Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 16unsigned int perf_mem_events__loads_ldlat = 30;
 17
 18#define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
 19
 20struct 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
 28static char mem_loads_name[100];
 29static bool mem_loads_name__init;
 30
 31char * __weak 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
 46int 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
 82int 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
106void perf_mem_events__list(void)
107{
108	int j;
109
110	for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
111		struct perf_mem_event *e = &perf_mem_events[j];
112
113		fprintf(stderr, "%-13s%-*s%s\n",
114			e->tag,
115			verbose > 0 ? 25 : 0,
116			verbose > 0 ? perf_mem_events__name(j) : "",
117			e->supported ? ": available" : "");
118	}
119}
120
121static const char * const tlb_access[] = {
122	"N/A",
123	"HIT",
124	"MISS",
125	"L1",
126	"L2",
127	"Walker",
128	"Fault",
129};
130
131int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
132{
133	size_t l = 0, i;
134	u64 m = PERF_MEM_TLB_NA;
135	u64 hit, miss;
136
137	sz -= 1; /* -1 for null termination */
138	out[0] = '\0';
139
140	if (mem_info)
141		m = mem_info->data_src.mem_dtlb;
142
143	hit = m & PERF_MEM_TLB_HIT;
144	miss = m & PERF_MEM_TLB_MISS;
145
146	/* already taken care of */
147	m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
148
149	for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
150		if (!(m & 0x1))
151			continue;
152		if (l) {
153			strcat(out, " or ");
154			l += 4;
155		}
156		l += scnprintf(out + l, sz - l, tlb_access[i]);
157	}
158	if (*out == '\0')
159		l += scnprintf(out, sz - l, "N/A");
160	if (hit)
161		l += scnprintf(out + l, sz - l, " hit");
162	if (miss)
163		l += scnprintf(out + l, sz - l, " miss");
164
165	return l;
166}
167
168static const char * const mem_lvl[] = {
169	"N/A",
170	"HIT",
171	"MISS",
172	"L1",
173	"LFB",
174	"L2",
175	"L3",
176	"Local RAM",
177	"Remote RAM (1 hop)",
178	"Remote RAM (2 hops)",
179	"Remote Cache (1 hop)",
180	"Remote Cache (2 hops)",
181	"I/O",
182	"Uncached",
183};
184
185static const char * const mem_lvlnum[] = {
186	[PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache",
187	[PERF_MEM_LVLNUM_LFB] = "LFB",
188	[PERF_MEM_LVLNUM_RAM] = "RAM",
189	[PERF_MEM_LVLNUM_PMEM] = "PMEM",
190	[PERF_MEM_LVLNUM_NA] = "N/A",
191};
192
193int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
194{
195	size_t i, l = 0;
196	u64 m =  PERF_MEM_LVL_NA;
197	u64 hit, miss;
198	int printed;
199
200	if (mem_info)
201		m  = mem_info->data_src.mem_lvl;
202
203	sz -= 1; /* -1 for null termination */
204	out[0] = '\0';
205
206	hit = m & PERF_MEM_LVL_HIT;
207	miss = m & PERF_MEM_LVL_MISS;
208
209	/* already taken care of */
210	m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
211
212
213	if (mem_info && mem_info->data_src.mem_remote) {
214		strcat(out, "Remote ");
215		l += 7;
216	}
217
218	printed = 0;
219	for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
220		if (!(m & 0x1))
221			continue;
222		if (printed++) {
223			strcat(out, " or ");
224			l += 4;
225		}
226		l += scnprintf(out + l, sz - l, mem_lvl[i]);
227	}
228
229	if (mem_info && mem_info->data_src.mem_lvl_num) {
230		int lvl = mem_info->data_src.mem_lvl_num;
231		if (printed++) {
232			strcat(out, " or ");
233			l += 4;
234		}
235		if (mem_lvlnum[lvl])
236			l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
237		else
238			l += scnprintf(out + l, sz - l, "L%d", lvl);
239	}
240
241	if (l == 0)
242		l += scnprintf(out + l, sz - l, "N/A");
243	if (hit)
244		l += scnprintf(out + l, sz - l, " hit");
245	if (miss)
246		l += scnprintf(out + l, sz - l, " miss");
247
248	return l;
249}
250
251static const char * const snoop_access[] = {
252	"N/A",
253	"None",
254	"Hit",
255	"Miss",
256	"HitM",
257};
258
259int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
260{
261	size_t i, l = 0;
262	u64 m = PERF_MEM_SNOOP_NA;
263
264	sz -= 1; /* -1 for null termination */
265	out[0] = '\0';
266
267	if (mem_info)
268		m = mem_info->data_src.mem_snoop;
269
270	for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
271		if (!(m & 0x1))
272			continue;
273		if (l) {
274			strcat(out, " or ");
275			l += 4;
276		}
277		l += scnprintf(out + l, sz - l, snoop_access[i]);
278	}
279	if (mem_info &&
280	     (mem_info->data_src.mem_snoopx & PERF_MEM_SNOOPX_FWD)) {
281		if (l) {
282			strcat(out, " or ");
283			l += 4;
284		}
285		l += scnprintf(out + l, sz - l, "Fwd");
286	}
287
288	if (*out == '\0')
289		l += scnprintf(out, sz - l, "N/A");
290
291	return l;
292}
293
294int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
295{
296	u64 mask = PERF_MEM_LOCK_NA;
297	int l;
298
299	if (mem_info)
300		mask = mem_info->data_src.mem_lock;
301
302	if (mask & PERF_MEM_LOCK_NA)
303		l = scnprintf(out, sz, "N/A");
304	else if (mask & PERF_MEM_LOCK_LOCKED)
305		l = scnprintf(out, sz, "Yes");
306	else
307		l = scnprintf(out, sz, "No");
308
309	return l;
310}
311
312int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
313{
314	int i = 0;
315
316	i += perf_mem__lvl_scnprintf(out, sz, mem_info);
317	i += scnprintf(out + i, sz - i, "|SNP ");
318	i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
319	i += scnprintf(out + i, sz - i, "|TLB ");
320	i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
321	i += scnprintf(out + i, sz - i, "|LCK ");
322	i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
323
324	return i;
325}
326
327int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
328{
329	union perf_mem_data_src *data_src = &mi->data_src;
330	u64 daddr  = mi->daddr.addr;
331	u64 op     = data_src->mem_op;
332	u64 lvl    = data_src->mem_lvl;
333	u64 snoop  = data_src->mem_snoop;
334	u64 lock   = data_src->mem_lock;
335	/*
336	 * Skylake might report unknown remote level via this
337	 * bit, consider it when evaluating remote HITMs.
338	 */
339	bool mrem  = data_src->mem_remote;
340	int err = 0;
341
342#define HITM_INC(__f)		\
343do {				\
344	stats->__f++;		\
345	stats->tot_hitm++;	\
346} while (0)
347
348#define P(a, b) PERF_MEM_##a##_##b
349
350	stats->nr_entries++;
351
352	if (lock & P(LOCK, LOCKED)) stats->locks++;
353
354	if (op & P(OP, LOAD)) {
355		/* load */
356		stats->load++;
357
358		if (!daddr) {
359			stats->ld_noadrs++;
360			return -1;
361		}
362
363		if (lvl & P(LVL, HIT)) {
364			if (lvl & P(LVL, UNC)) stats->ld_uncache++;
365			if (lvl & P(LVL, IO))  stats->ld_io++;
366			if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
367			if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
368			if (lvl & P(LVL, L2 )) stats->ld_l2hit++;
369			if (lvl & P(LVL, L3 )) {
370				if (snoop & P(SNOOP, HITM))
371					HITM_INC(lcl_hitm);
372				else
373					stats->ld_llchit++;
374			}
375
376			if (lvl & P(LVL, LOC_RAM)) {
377				stats->lcl_dram++;
378				if (snoop & P(SNOOP, HIT))
379					stats->ld_shared++;
380				else
381					stats->ld_excl++;
382			}
383
384			if ((lvl & P(LVL, REM_RAM1)) ||
385			    (lvl & P(LVL, REM_RAM2)) ||
386			     mrem) {
387				stats->rmt_dram++;
388				if (snoop & P(SNOOP, HIT))
389					stats->ld_shared++;
390				else
391					stats->ld_excl++;
392			}
393		}
394
395		if ((lvl & P(LVL, REM_CCE1)) ||
396		    (lvl & P(LVL, REM_CCE2)) ||
397		     mrem) {
398			if (snoop & P(SNOOP, HIT))
399				stats->rmt_hit++;
400			else if (snoop & P(SNOOP, HITM))
401				HITM_INC(rmt_hitm);
402		}
403
404		if ((lvl & P(LVL, MISS)))
405			stats->ld_miss++;
406
407	} else if (op & P(OP, STORE)) {
408		/* store */
409		stats->store++;
410
411		if (!daddr) {
412			stats->st_noadrs++;
413			return -1;
414		}
415
416		if (lvl & P(LVL, HIT)) {
417			if (lvl & P(LVL, UNC)) stats->st_uncache++;
418			if (lvl & P(LVL, L1 )) stats->st_l1hit++;
419		}
420		if (lvl & P(LVL, MISS))
421			if (lvl & P(LVL, L1)) stats->st_l1miss++;
422	} else {
423		/* unparsable data_src? */
424		stats->noparse++;
425		return -1;
426	}
427
428	if (!mi->daddr.ms.map || !mi->iaddr.ms.map) {
429		stats->nomap++;
430		return -1;
431	}
432
433#undef P
434#undef HITM_INC
435	return err;
436}
437
438void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
439{
440	stats->nr_entries	+= add->nr_entries;
441
442	stats->locks		+= add->locks;
443	stats->store		+= add->store;
444	stats->st_uncache	+= add->st_uncache;
445	stats->st_noadrs	+= add->st_noadrs;
446	stats->st_l1hit		+= add->st_l1hit;
447	stats->st_l1miss	+= add->st_l1miss;
448	stats->load		+= add->load;
449	stats->ld_excl		+= add->ld_excl;
450	stats->ld_shared	+= add->ld_shared;
451	stats->ld_uncache	+= add->ld_uncache;
452	stats->ld_io		+= add->ld_io;
453	stats->ld_miss		+= add->ld_miss;
454	stats->ld_noadrs	+= add->ld_noadrs;
455	stats->ld_fbhit		+= add->ld_fbhit;
456	stats->ld_l1hit		+= add->ld_l1hit;
457	stats->ld_l2hit		+= add->ld_l2hit;
458	stats->ld_llchit	+= add->ld_llchit;
459	stats->lcl_hitm		+= add->lcl_hitm;
460	stats->rmt_hitm		+= add->rmt_hitm;
461	stats->tot_hitm		+= add->tot_hitm;
462	stats->rmt_hit		+= add->rmt_hit;
463	stats->lcl_dram		+= add->lcl_dram;
464	stats->rmt_dram		+= add->rmt_dram;
465	stats->nomap		+= add->nomap;
466	stats->noparse		+= add->noparse;
467}