Linux Audio

Check our new training course

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