Linux Audio

Check our new training course

Loading...
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}
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}