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