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