Linux Audio

Check our new training course

Loading...
v4.6
 
  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
 
 
 13#define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
 14
 15struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {
 16	E("ldlat-loads",	"cpu/mem-loads,ldlat=30/P",	"mem-loads"),
 17	E("ldlat-stores",	"cpu/mem-stores/P",		"mem-stores"),
 18};
 19#undef E
 20
 21#undef E
 22
 23char *perf_mem_events__name(int i)
 
 
 
 24{
 
 
 
 
 
 
 
 
 
 
 25	return (char *)perf_mem_events[i].name;
 26}
 27
 28int perf_mem_events__parse(const char *str)
 29{
 30	char *tok, *saveptr = NULL;
 31	bool found = false;
 32	char *buf;
 33	int j;
 34
 35	/* We need buffer that we know we can write to. */
 36	buf = malloc(strlen(str) + 1);
 37	if (!buf)
 38		return -ENOMEM;
 39
 40	strcpy(buf, str);
 41
 42	tok = strtok_r((char *)buf, ",", &saveptr);
 43
 44	while (tok) {
 45		for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
 46			struct perf_mem_event *e = &perf_mem_events[j];
 47
 48			if (strstr(e->tag, tok))
 49				e->record = found = true;
 50		}
 51
 52		tok = strtok_r(NULL, ",", &saveptr);
 53	}
 54
 55	free(buf);
 56
 57	if (found)
 58		return 0;
 59
 60	pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str);
 61	return -1;
 62}
 63
 64int perf_mem_events__init(void)
 65{
 66	const char *mnt = sysfs__mount();
 67	bool found = false;
 68	int j;
 69
 70	if (!mnt)
 71		return -ENOENT;
 72
 73	for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
 74		char path[PATH_MAX];
 75		struct perf_mem_event *e = &perf_mem_events[j];
 76		struct stat st;
 77
 78		scnprintf(path, PATH_MAX, "%s/devices/cpu/events/%s",
 79			  mnt, e->sysfs_name);
 80
 81		if (!stat(path, &st))
 82			e->supported = found = true;
 83	}
 84
 85	return found ? 0 : -ENOENT;
 86}
 87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 88static const char * const tlb_access[] = {
 89	"N/A",
 90	"HIT",
 91	"MISS",
 92	"L1",
 93	"L2",
 94	"Walker",
 95	"Fault",
 96};
 97
 98int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
 99{
100	size_t l = 0, i;
101	u64 m = PERF_MEM_TLB_NA;
102	u64 hit, miss;
103
104	sz -= 1; /* -1 for null termination */
105	out[0] = '\0';
106
107	if (mem_info)
108		m = mem_info->data_src.mem_dtlb;
109
110	hit = m & PERF_MEM_TLB_HIT;
111	miss = m & PERF_MEM_TLB_MISS;
112
113	/* already taken care of */
114	m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
115
116	for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
117		if (!(m & 0x1))
118			continue;
119		if (l) {
120			strcat(out, " or ");
121			l += 4;
122		}
123		l += scnprintf(out + l, sz - l, tlb_access[i]);
124	}
125	if (*out == '\0')
126		l += scnprintf(out, sz - l, "N/A");
127	if (hit)
128		l += scnprintf(out + l, sz - l, " hit");
129	if (miss)
130		l += scnprintf(out + l, sz - l, " miss");
131
132	return l;
133}
134
135static const char * const mem_lvl[] = {
136	"N/A",
137	"HIT",
138	"MISS",
139	"L1",
140	"LFB",
141	"L2",
142	"L3",
143	"Local RAM",
144	"Remote RAM (1 hop)",
145	"Remote RAM (2 hops)",
146	"Remote Cache (1 hop)",
147	"Remote Cache (2 hops)",
148	"I/O",
149	"Uncached",
150};
151
 
 
 
 
 
 
 
 
152int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
153{
154	size_t i, l = 0;
155	u64 m =  PERF_MEM_LVL_NA;
156	u64 hit, miss;
 
157
158	if (mem_info)
159		m  = mem_info->data_src.mem_lvl;
160
161	sz -= 1; /* -1 for null termination */
162	out[0] = '\0';
163
164	hit = m & PERF_MEM_LVL_HIT;
165	miss = m & PERF_MEM_LVL_MISS;
166
167	/* already taken care of */
168	m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
169
 
 
 
 
 
 
 
170	for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
171		if (!(m & 0x1))
172			continue;
173		if (l) {
174			strcat(out, " or ");
175			l += 4;
176		}
177		l += scnprintf(out + l, sz - l, mem_lvl[i]);
178	}
179	if (*out == '\0')
180		l += scnprintf(out, sz - l, "N/A");
 
 
 
 
 
 
 
 
 
 
 
 
 
181	if (hit)
182		l += scnprintf(out + l, sz - l, " hit");
183	if (miss)
184		l += scnprintf(out + l, sz - l, " miss");
185
186	return l;
187}
188
189static const char * const snoop_access[] = {
190	"N/A",
191	"None",
192	"Miss",
193	"Hit",
 
194	"HitM",
195};
196
197int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
198{
199	size_t i, l = 0;
200	u64 m = PERF_MEM_SNOOP_NA;
201
202	sz -= 1; /* -1 for null termination */
203	out[0] = '\0';
204
205	if (mem_info)
206		m = mem_info->data_src.mem_snoop;
207
208	for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
209		if (!(m & 0x1))
210			continue;
211		if (l) {
212			strcat(out, " or ");
213			l += 4;
214		}
215		l += scnprintf(out + l, sz - l, snoop_access[i]);
216	}
 
 
 
 
 
 
 
 
217
218	if (*out == '\0')
219		l += scnprintf(out, sz - l, "N/A");
220
221	return l;
222}
223
224int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
225{
226	u64 mask = PERF_MEM_LOCK_NA;
227	int l;
228
229	if (mem_info)
230		mask = mem_info->data_src.mem_lock;
231
232	if (mask & PERF_MEM_LOCK_NA)
233		l = scnprintf(out, sz, "N/A");
234	else if (mask & PERF_MEM_LOCK_LOCKED)
235		l = scnprintf(out, sz, "Yes");
236	else
237		l = scnprintf(out, sz, "No");
238
239	return l;
240}
241
242int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
243{
244	int i = 0;
245
246	i += perf_mem__lvl_scnprintf(out, sz, mem_info);
247	i += scnprintf(out + i, sz - i, "|SNP ");
248	i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
249	i += scnprintf(out + i, sz - i, "|TLB ");
250	i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
251	i += scnprintf(out + i, sz - i, "|LCK ");
252	i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
253
254	return i;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255}
v5.9
  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}