Linux Audio

Check our new training course

Loading...
v6.2
  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#include "pmu.h"
 16#include "pmu-hybrid.h"
 17
 18unsigned int perf_mem_events__loads_ldlat = 30;
 19
 20#define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
 21
 22static struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {
 23	E("ldlat-loads",	"cpu/mem-loads,ldlat=%u/P",	"cpu/events/mem-loads"),
 24	E("ldlat-stores",	"cpu/mem-stores/P",		"cpu/events/mem-stores"),
 25	E(NULL,			NULL,				NULL),
 26};
 27#undef E
 28
 
 
 29static char mem_loads_name[100];
 30static bool mem_loads_name__init;
 31
 32struct perf_mem_event * __weak perf_mem_events__ptr(int i)
 33{
 34	if (i >= PERF_MEM_EVENTS__MAX)
 35		return NULL;
 36
 37	return &perf_mem_events[i];
 38}
 39
 40char * __weak perf_mem_events__name(int i, char *pmu_name  __maybe_unused)
 41{
 42	struct perf_mem_event *e = perf_mem_events__ptr(i);
 43
 44	if (!e)
 45		return NULL;
 46
 47	if (i == PERF_MEM_EVENTS__LOAD) {
 48		if (!mem_loads_name__init) {
 49			mem_loads_name__init = true;
 50			scnprintf(mem_loads_name, sizeof(mem_loads_name),
 51				  e->name, perf_mem_events__loads_ldlat);
 
 52		}
 53		return mem_loads_name;
 54	}
 55
 56	return (char *)e->name;
 57}
 58
 59__weak bool is_mem_loads_aux_event(struct evsel *leader __maybe_unused)
 60{
 61	return false;
 62}
 63
 64int perf_mem_events__parse(const char *str)
 65{
 66	char *tok, *saveptr = NULL;
 67	bool found = false;
 68	char *buf;
 69	int j;
 70
 71	/* We need buffer that we know we can write to. */
 72	buf = malloc(strlen(str) + 1);
 73	if (!buf)
 74		return -ENOMEM;
 75
 76	strcpy(buf, str);
 77
 78	tok = strtok_r((char *)buf, ",", &saveptr);
 79
 80	while (tok) {
 81		for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
 82			struct perf_mem_event *e = perf_mem_events__ptr(j);
 83
 84			if (!e->tag)
 85				continue;
 86
 87			if (strstr(e->tag, tok))
 88				e->record = found = true;
 89		}
 90
 91		tok = strtok_r(NULL, ",", &saveptr);
 92	}
 93
 94	free(buf);
 95
 96	if (found)
 97		return 0;
 98
 99	pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str);
100	return -1;
101}
102
103static bool perf_mem_event__supported(const char *mnt, char *sysfs_name)
104{
105	char path[PATH_MAX];
106	struct stat st;
107
108	scnprintf(path, PATH_MAX, "%s/devices/%s", mnt, sysfs_name);
109	return !stat(path, &st);
110}
111
112int perf_mem_events__init(void)
113{
114	const char *mnt = sysfs__mount();
115	bool found = false;
116	int j;
117
118	if (!mnt)
119		return -ENOENT;
120
121	for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
122		struct perf_mem_event *e = perf_mem_events__ptr(j);
123		struct perf_pmu *pmu;
124		char sysfs_name[100];
125
126		/*
127		 * If the event entry isn't valid, skip initialization
128		 * and "e->supported" will keep false.
129		 */
130		if (!e->tag)
131			continue;
132
133		if (!perf_pmu__has_hybrid()) {
134			scnprintf(sysfs_name, sizeof(sysfs_name),
135				  e->sysfs_name, "cpu");
136			e->supported = perf_mem_event__supported(mnt, sysfs_name);
137		} else {
138			perf_pmu__for_each_hybrid_pmu(pmu) {
139				scnprintf(sysfs_name, sizeof(sysfs_name),
140					  e->sysfs_name, pmu->name);
141				e->supported |= perf_mem_event__supported(mnt, sysfs_name);
142			}
143		}
144
145		if (e->supported)
146			found = true;
147	}
148
149	return found ? 0 : -ENOENT;
150}
151
152void perf_mem_events__list(void)
153{
154	int j;
155
156	for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
157		struct perf_mem_event *e = perf_mem_events__ptr(j);
158
159		fprintf(stderr, "%-*s%-*s%s",
160			e->tag ? 13 : 0,
161			e->tag ? : "",
162			e->tag && verbose > 0 ? 25 : 0,
163			e->tag && verbose > 0 ? perf_mem_events__name(j, NULL) : "",
164			e->supported ? ": available\n" : "");
165	}
166}
167
168static void perf_mem_events__print_unsupport_hybrid(struct perf_mem_event *e,
169						    int idx)
170{
171	const char *mnt = sysfs__mount();
172	char sysfs_name[100];
173	struct perf_pmu *pmu;
174
175	perf_pmu__for_each_hybrid_pmu(pmu) {
176		scnprintf(sysfs_name, sizeof(sysfs_name), e->sysfs_name,
177			  pmu->name);
178		if (!perf_mem_event__supported(mnt, sysfs_name)) {
179			pr_err("failed: event '%s' not supported\n",
180			       perf_mem_events__name(idx, pmu->name));
181		}
182	}
183}
184
185int perf_mem_events__record_args(const char **rec_argv, int *argv_nr,
186				 char **rec_tmp, int *tmp_nr)
187{
188	int i = *argv_nr, k = 0;
189	struct perf_mem_event *e;
190	struct perf_pmu *pmu;
191	char *s;
192
193	for (int j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
194		e = perf_mem_events__ptr(j);
195		if (!e->record)
196			continue;
197
198		if (!perf_pmu__has_hybrid()) {
199			if (!e->supported) {
200				pr_err("failed: event '%s' not supported\n",
201				       perf_mem_events__name(j, NULL));
202				return -1;
203			}
204
205			rec_argv[i++] = "-e";
206			rec_argv[i++] = perf_mem_events__name(j, NULL);
207		} else {
208			if (!e->supported) {
209				perf_mem_events__print_unsupport_hybrid(e, j);
210				return -1;
211			}
212
213			perf_pmu__for_each_hybrid_pmu(pmu) {
214				rec_argv[i++] = "-e";
215				s = perf_mem_events__name(j, pmu->name);
216				if (s) {
217					s = strdup(s);
218					if (!s)
219						return -1;
220
221					rec_argv[i++] = s;
222					rec_tmp[k++] = s;
223				}
224			}
225		}
226	}
227
228	*argv_nr = i;
229	*tmp_nr = k;
230	return 0;
231}
232
233static const char * const tlb_access[] = {
234	"N/A",
235	"HIT",
236	"MISS",
237	"L1",
238	"L2",
239	"Walker",
240	"Fault",
241};
242
243int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
244{
245	size_t l = 0, i;
246	u64 m = PERF_MEM_TLB_NA;
247	u64 hit, miss;
248
249	sz -= 1; /* -1 for null termination */
250	out[0] = '\0';
251
252	if (mem_info)
253		m = mem_info->data_src.mem_dtlb;
254
255	hit = m & PERF_MEM_TLB_HIT;
256	miss = m & PERF_MEM_TLB_MISS;
257
258	/* already taken care of */
259	m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
260
261	for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
262		if (!(m & 0x1))
263			continue;
264		if (l) {
265			strcat(out, " or ");
266			l += 4;
267		}
268		l += scnprintf(out + l, sz - l, tlb_access[i]);
269	}
270	if (*out == '\0')
271		l += scnprintf(out, sz - l, "N/A");
272	if (hit)
273		l += scnprintf(out + l, sz - l, " hit");
274	if (miss)
275		l += scnprintf(out + l, sz - l, " miss");
276
277	return l;
278}
279
280static const char * const mem_lvl[] = {
281	"N/A",
282	"HIT",
283	"MISS",
284	"L1",
285	"LFB/MAB",
286	"L2",
287	"L3",
288	"Local RAM",
289	"Remote RAM (1 hop)",
290	"Remote RAM (2 hops)",
291	"Remote Cache (1 hop)",
292	"Remote Cache (2 hops)",
293	"I/O",
294	"Uncached",
295};
296
297static const char * const mem_lvlnum[] = {
298	[PERF_MEM_LVLNUM_CXL] = "CXL",
299	[PERF_MEM_LVLNUM_IO] = "I/O",
300	[PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache",
301	[PERF_MEM_LVLNUM_LFB] = "LFB/MAB",
302	[PERF_MEM_LVLNUM_RAM] = "RAM",
303	[PERF_MEM_LVLNUM_PMEM] = "PMEM",
304	[PERF_MEM_LVLNUM_NA] = "N/A",
305};
306
307static const char * const mem_hops[] = {
308	"N/A",
309	/*
310	 * While printing, 'Remote' will be added to represent
311	 * 'Remote core, same node' accesses as remote field need
312	 * to be set with mem_hops field.
313	 */
314	"core, same node",
315	"node, same socket",
316	"socket, same board",
317	"board",
318};
319
320static int perf_mem__op_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
321{
322	u64 op = PERF_MEM_LOCK_NA;
323	int l;
324
325	if (mem_info)
326		op = mem_info->data_src.mem_op;
327
328	if (op & PERF_MEM_OP_NA)
329		l = scnprintf(out, sz, "N/A");
330	else if (op & PERF_MEM_OP_LOAD)
331		l = scnprintf(out, sz, "LOAD");
332	else if (op & PERF_MEM_OP_STORE)
333		l = scnprintf(out, sz, "STORE");
334	else if (op & PERF_MEM_OP_PFETCH)
335		l = scnprintf(out, sz, "PFETCH");
336	else if (op & PERF_MEM_OP_EXEC)
337		l = scnprintf(out, sz, "EXEC");
338	else
339		l = scnprintf(out, sz, "No");
340
341	return l;
342}
343
344int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
345{
346	size_t i, l = 0;
347	u64 m =  PERF_MEM_LVL_NA;
348	u64 hit, miss;
349	int printed = 0;
350
351	if (mem_info)
352		m  = mem_info->data_src.mem_lvl;
353
354	sz -= 1; /* -1 for null termination */
355	out[0] = '\0';
356
357	hit = m & PERF_MEM_LVL_HIT;
358	miss = m & PERF_MEM_LVL_MISS;
359
360	/* already taken care of */
361	m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
362
 
363	if (mem_info && mem_info->data_src.mem_remote) {
364		strcat(out, "Remote ");
365		l += 7;
366	}
367
368	/*
369	 * Incase mem_hops field is set, we can skip printing data source via
370	 * PERF_MEM_LVL namespace.
371	 */
372	if (mem_info && mem_info->data_src.mem_hops) {
373		l += scnprintf(out + l, sz - l, "%s ", mem_hops[mem_info->data_src.mem_hops]);
374	} else {
375		for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
376			if (!(m & 0x1))
377				continue;
378			if (printed++) {
379				strcat(out, " or ");
380				l += 4;
381			}
382			l += scnprintf(out + l, sz - l, mem_lvl[i]);
383		}
 
384	}
385
386	if (mem_info && mem_info->data_src.mem_lvl_num) {
387		int lvl = mem_info->data_src.mem_lvl_num;
388		if (printed++) {
389			strcat(out, " or ");
390			l += 4;
391		}
392		if (mem_lvlnum[lvl])
393			l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
394		else
395			l += scnprintf(out + l, sz - l, "L%d", lvl);
396	}
397
398	if (l == 0)
399		l += scnprintf(out + l, sz - l, "N/A");
400	if (hit)
401		l += scnprintf(out + l, sz - l, " hit");
402	if (miss)
403		l += scnprintf(out + l, sz - l, " miss");
404
405	return l;
406}
407
408static const char * const snoop_access[] = {
409	"N/A",
410	"None",
411	"Hit",
412	"Miss",
413	"HitM",
414};
415
416static const char * const snoopx_access[] = {
417	"Fwd",
418	"Peer",
419};
420
421int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
422{
423	size_t i, l = 0;
424	u64 m = PERF_MEM_SNOOP_NA;
425
426	sz -= 1; /* -1 for null termination */
427	out[0] = '\0';
428
429	if (mem_info)
430		m = mem_info->data_src.mem_snoop;
431
432	for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
433		if (!(m & 0x1))
434			continue;
435		if (l) {
436			strcat(out, " or ");
437			l += 4;
438		}
439		l += scnprintf(out + l, sz - l, snoop_access[i]);
440	}
441
442	m = 0;
443	if (mem_info)
444		m = mem_info->data_src.mem_snoopx;
445
446	for (i = 0; m && i < ARRAY_SIZE(snoopx_access); i++, m >>= 1) {
447		if (!(m & 0x1))
448			continue;
449
450		if (l) {
451			strcat(out, " or ");
452			l += 4;
453		}
454		l += scnprintf(out + l, sz - l, snoopx_access[i]);
455	}
456
457	if (*out == '\0')
458		l += scnprintf(out, sz - l, "N/A");
459
460	return l;
461}
462
463int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
464{
465	u64 mask = PERF_MEM_LOCK_NA;
466	int l;
467
468	if (mem_info)
469		mask = mem_info->data_src.mem_lock;
470
471	if (mask & PERF_MEM_LOCK_NA)
472		l = scnprintf(out, sz, "N/A");
473	else if (mask & PERF_MEM_LOCK_LOCKED)
474		l = scnprintf(out, sz, "Yes");
475	else
476		l = scnprintf(out, sz, "No");
477
478	return l;
479}
480
481int perf_mem__blk_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
482{
483	size_t l = 0;
484	u64 mask = PERF_MEM_BLK_NA;
485
486	sz -= 1; /* -1 for null termination */
487	out[0] = '\0';
488
489	if (mem_info)
490		mask = mem_info->data_src.mem_blk;
491
492	if (!mask || (mask & PERF_MEM_BLK_NA)) {
493		l += scnprintf(out + l, sz - l, " N/A");
494		return l;
495	}
496	if (mask & PERF_MEM_BLK_DATA)
497		l += scnprintf(out + l, sz - l, " Data");
498	if (mask & PERF_MEM_BLK_ADDR)
499		l += scnprintf(out + l, sz - l, " Addr");
500
501	return l;
502}
503
504int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
505{
506	int i = 0;
507
508	i += scnprintf(out, sz, "|OP ");
509	i += perf_mem__op_scnprintf(out + i, sz - i, mem_info);
510	i += scnprintf(out + i, sz - i, "|LVL ");
511	i += perf_mem__lvl_scnprintf(out + i, sz, mem_info);
512	i += scnprintf(out + i, sz - i, "|SNP ");
513	i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
514	i += scnprintf(out + i, sz - i, "|TLB ");
515	i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
516	i += scnprintf(out + i, sz - i, "|LCK ");
517	i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
518	i += scnprintf(out + i, sz - i, "|BLK ");
519	i += perf_mem__blk_scnprintf(out + i, sz - i, mem_info);
520
521	return i;
522}
523
524int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
525{
526	union perf_mem_data_src *data_src = &mi->data_src;
527	u64 daddr  = mi->daddr.addr;
528	u64 op     = data_src->mem_op;
529	u64 lvl    = data_src->mem_lvl;
530	u64 snoop  = data_src->mem_snoop;
531	u64 snoopx = data_src->mem_snoopx;
532	u64 lock   = data_src->mem_lock;
533	u64 blk    = data_src->mem_blk;
534	/*
535	 * Skylake might report unknown remote level via this
536	 * bit, consider it when evaluating remote HITMs.
537	 *
538	 * Incase of power, remote field can also be used to denote cache
539	 * accesses from the another core of same node. Hence, setting
540	 * mrem only when HOPS is zero along with set remote field.
541	 */
542	bool mrem  = (data_src->mem_remote && !data_src->mem_hops);
543	int err = 0;
544
545#define HITM_INC(__f)		\
546do {				\
547	stats->__f++;		\
548	stats->tot_hitm++;	\
549} while (0)
550
551#define PEER_INC(__f)		\
552do {				\
553	stats->__f++;		\
554	stats->tot_peer++;	\
555} while (0)
556
557#define P(a, b) PERF_MEM_##a##_##b
558
559	stats->nr_entries++;
560
561	if (lock & P(LOCK, LOCKED)) stats->locks++;
562
563	if (blk & P(BLK, DATA)) stats->blk_data++;
564	if (blk & P(BLK, ADDR)) stats->blk_addr++;
565
566	if (op & P(OP, LOAD)) {
567		/* load */
568		stats->load++;
569
570		if (!daddr) {
571			stats->ld_noadrs++;
572			return -1;
573		}
574
575		if (lvl & P(LVL, HIT)) {
576			if (lvl & P(LVL, UNC)) stats->ld_uncache++;
577			if (lvl & P(LVL, IO))  stats->ld_io++;
578			if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
579			if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
580			if (lvl & P(LVL, L2)) {
581				stats->ld_l2hit++;
582
583				if (snoopx & P(SNOOPX, PEER))
584					PEER_INC(lcl_peer);
585			}
586			if (lvl & P(LVL, L3 )) {
587				if (snoop & P(SNOOP, HITM))
588					HITM_INC(lcl_hitm);
589				else
590					stats->ld_llchit++;
591
592				if (snoopx & P(SNOOPX, PEER))
593					PEER_INC(lcl_peer);
594			}
595
596			if (lvl & P(LVL, LOC_RAM)) {
597				stats->lcl_dram++;
598				if (snoop & P(SNOOP, HIT))
599					stats->ld_shared++;
600				else
601					stats->ld_excl++;
602			}
603
604			if ((lvl & P(LVL, REM_RAM1)) ||
605			    (lvl & P(LVL, REM_RAM2)) ||
606			     mrem) {
607				stats->rmt_dram++;
608				if (snoop & P(SNOOP, HIT))
609					stats->ld_shared++;
610				else
611					stats->ld_excl++;
612			}
613		}
614
615		if ((lvl & P(LVL, REM_CCE1)) ||
616		    (lvl & P(LVL, REM_CCE2)) ||
617		     mrem) {
618			if (snoop & P(SNOOP, HIT)) {
619				stats->rmt_hit++;
620			} else if (snoop & P(SNOOP, HITM)) {
621				HITM_INC(rmt_hitm);
622			} else if (snoopx & P(SNOOPX, PEER)) {
623				stats->rmt_hit++;
624				PEER_INC(rmt_peer);
625			}
626		}
627
628		if ((lvl & P(LVL, MISS)))
629			stats->ld_miss++;
630
631	} else if (op & P(OP, STORE)) {
632		/* store */
633		stats->store++;
634
635		if (!daddr) {
636			stats->st_noadrs++;
637			return -1;
638		}
639
640		if (lvl & P(LVL, HIT)) {
641			if (lvl & P(LVL, UNC)) stats->st_uncache++;
642			if (lvl & P(LVL, L1 )) stats->st_l1hit++;
643		}
644		if (lvl & P(LVL, MISS))
645			if (lvl & P(LVL, L1)) stats->st_l1miss++;
646		if (lvl & P(LVL, NA))
647			stats->st_na++;
648	} else {
649		/* unparsable data_src? */
650		stats->noparse++;
651		return -1;
652	}
653
654	if (!mi->daddr.ms.map || !mi->iaddr.ms.map) {
655		stats->nomap++;
656		return -1;
657	}
658
659#undef P
660#undef HITM_INC
661	return err;
662}
663
664void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
665{
666	stats->nr_entries	+= add->nr_entries;
667
668	stats->locks		+= add->locks;
669	stats->store		+= add->store;
670	stats->st_uncache	+= add->st_uncache;
671	stats->st_noadrs	+= add->st_noadrs;
672	stats->st_l1hit		+= add->st_l1hit;
673	stats->st_l1miss	+= add->st_l1miss;
674	stats->st_na		+= add->st_na;
675	stats->load		+= add->load;
676	stats->ld_excl		+= add->ld_excl;
677	stats->ld_shared	+= add->ld_shared;
678	stats->ld_uncache	+= add->ld_uncache;
679	stats->ld_io		+= add->ld_io;
680	stats->ld_miss		+= add->ld_miss;
681	stats->ld_noadrs	+= add->ld_noadrs;
682	stats->ld_fbhit		+= add->ld_fbhit;
683	stats->ld_l1hit		+= add->ld_l1hit;
684	stats->ld_l2hit		+= add->ld_l2hit;
685	stats->ld_llchit	+= add->ld_llchit;
686	stats->lcl_hitm		+= add->lcl_hitm;
687	stats->rmt_hitm		+= add->rmt_hitm;
688	stats->tot_hitm		+= add->tot_hitm;
689	stats->lcl_peer		+= add->lcl_peer;
690	stats->rmt_peer		+= add->rmt_peer;
691	stats->tot_peer		+= add->tot_peer;
692	stats->rmt_hit		+= add->rmt_hit;
693	stats->lcl_dram		+= add->lcl_dram;
694	stats->rmt_dram		+= add->rmt_dram;
695	stats->blk_data		+= add->blk_data;
696	stats->blk_addr		+= add->blk_addr;
697	stats->nomap		+= add->nomap;
698	stats->noparse		+= add->noparse;
699}
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}