Linux Audio

Check our new training course

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