Linux Audio

Check our new training course

Loading...
v4.6
 
  1#include <stddef.h>
  2#include <stdlib.h>
  3#include <string.h>
  4#include <errno.h>
  5#include <sys/types.h>
  6#include <sys/stat.h>
  7#include <unistd.h>
  8#include <api/fs/fs.h>
 
 
  9#include "mem-events.h"
 10#include "debug.h"
 11#include "symbol.h"
 
 
 
 
 12
 13#define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
 14
 15struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {
 16	E("ldlat-loads",	"cpu/mem-loads,ldlat=30/P",	"mem-loads"),
 17	E("ldlat-stores",	"cpu/mem-stores/P",		"mem-stores"),
 
 18};
 19#undef E
 20
 21#undef E
 
 22
 23char *perf_mem_events__name(int i)
 24{
 25	return (char *)perf_mem_events[i].name;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 26}
 27
 28int perf_mem_events__parse(const char *str)
 29{
 30	char *tok, *saveptr = NULL;
 31	bool found = false;
 32	char *buf;
 33	int j;
 34
 35	/* We need buffer that we know we can write to. */
 36	buf = malloc(strlen(str) + 1);
 37	if (!buf)
 38		return -ENOMEM;
 39
 40	strcpy(buf, str);
 41
 42	tok = strtok_r((char *)buf, ",", &saveptr);
 43
 44	while (tok) {
 45		for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
 46			struct perf_mem_event *e = &perf_mem_events[j];
 
 
 
 47
 48			if (strstr(e->tag, tok))
 49				e->record = found = true;
 50		}
 51
 52		tok = strtok_r(NULL, ",", &saveptr);
 53	}
 54
 55	free(buf);
 56
 57	if (found)
 58		return 0;
 59
 60	pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str);
 61	return -1;
 62}
 63
 
 
 
 
 
 
 
 
 
 
 
 
 64int perf_mem_events__init(void)
 65{
 66	const char *mnt = sysfs__mount();
 67	bool found = false;
 68	int j;
 69
 70	if (!mnt)
 71		return -ENOENT;
 72
 73	for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
 74		char path[PATH_MAX];
 75		struct perf_mem_event *e = &perf_mem_events[j];
 76		struct stat st;
 77
 78		scnprintf(path, PATH_MAX, "%s/devices/cpu/events/%s",
 79			  mnt, e->sysfs_name);
 
 
 
 
 80
 81		if (!stat(path, &st))
 82			e->supported = found = true;
 
 
 
 
 
 
 
 
 
 
 83	}
 84
 85	return found ? 0 : -ENOENT;
 86}
 87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 88static const char * const tlb_access[] = {
 89	"N/A",
 90	"HIT",
 91	"MISS",
 92	"L1",
 93	"L2",
 94	"Walker",
 95	"Fault",
 96};
 97
 98int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
 99{
100	size_t l = 0, i;
101	u64 m = PERF_MEM_TLB_NA;
102	u64 hit, miss;
103
104	sz -= 1; /* -1 for null termination */
105	out[0] = '\0';
106
107	if (mem_info)
108		m = mem_info->data_src.mem_dtlb;
109
110	hit = m & PERF_MEM_TLB_HIT;
111	miss = m & PERF_MEM_TLB_MISS;
112
113	/* already taken care of */
114	m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
115
116	for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
117		if (!(m & 0x1))
118			continue;
119		if (l) {
120			strcat(out, " or ");
121			l += 4;
122		}
123		l += scnprintf(out + l, sz - l, tlb_access[i]);
124	}
125	if (*out == '\0')
126		l += scnprintf(out, sz - l, "N/A");
127	if (hit)
128		l += scnprintf(out + l, sz - l, " hit");
129	if (miss)
130		l += scnprintf(out + l, sz - l, " miss");
131
132	return l;
133}
134
135static const char * const mem_lvl[] = {
136	"N/A",
137	"HIT",
138	"MISS",
139	"L1",
140	"LFB",
141	"L2",
142	"L3",
143	"Local RAM",
144	"Remote RAM (1 hop)",
145	"Remote RAM (2 hops)",
146	"Remote Cache (1 hop)",
147	"Remote Cache (2 hops)",
148	"I/O",
149	"Uncached",
150};
151
152int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153{
154	size_t i, l = 0;
155	u64 m =  PERF_MEM_LVL_NA;
156	u64 hit, miss;
157
158	if (mem_info)
159		m  = mem_info->data_src.mem_lvl;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
161	sz -= 1; /* -1 for null termination */
162	out[0] = '\0';
163
164	hit = m & PERF_MEM_LVL_HIT;
165	miss = m & PERF_MEM_LVL_MISS;
166
167	/* already taken care of */
168	m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
169
170	for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
171		if (!(m & 0x1))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172			continue;
173		if (l) {
174			strcat(out, " or ");
175			l += 4;
176		}
177		l += scnprintf(out + l, sz - l, mem_lvl[i]);
178	}
179	if (*out == '\0')
180		l += scnprintf(out, sz - l, "N/A");
181	if (hit)
182		l += scnprintf(out + l, sz - l, " hit");
183	if (miss)
184		l += scnprintf(out + l, sz - l, " miss");
185
186	return l;
 
 
 
 
 
 
 
187}
188
189static const char * const snoop_access[] = {
190	"N/A",
191	"None",
192	"Miss",
193	"Hit",
 
194	"HitM",
195};
196
 
 
 
 
 
197int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
198{
199	size_t i, l = 0;
200	u64 m = PERF_MEM_SNOOP_NA;
201
202	sz -= 1; /* -1 for null termination */
203	out[0] = '\0';
204
205	if (mem_info)
206		m = mem_info->data_src.mem_snoop;
207
208	for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
209		if (!(m & 0x1))
210			continue;
211		if (l) {
212			strcat(out, " or ");
213			l += 4;
214		}
215		l += scnprintf(out + l, sz - l, snoop_access[i]);
216	}
217
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218	if (*out == '\0')
219		l += scnprintf(out, sz - l, "N/A");
220
221	return l;
222}
223
224int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
225{
226	u64 mask = PERF_MEM_LOCK_NA;
227	int l;
228
229	if (mem_info)
230		mask = mem_info->data_src.mem_lock;
231
232	if (mask & PERF_MEM_LOCK_NA)
233		l = scnprintf(out, sz, "N/A");
234	else if (mask & PERF_MEM_LOCK_LOCKED)
235		l = scnprintf(out, sz, "Yes");
236	else
237		l = scnprintf(out, sz, "No");
238
239	return l;
240}
241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
243{
244	int i = 0;
245
246	i += perf_mem__lvl_scnprintf(out, sz, mem_info);
 
 
 
247	i += scnprintf(out + i, sz - i, "|SNP ");
248	i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
249	i += scnprintf(out + i, sz - i, "|TLB ");
250	i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
251	i += scnprintf(out + i, sz - i, "|LCK ");
252	i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
 
 
253
254	return i;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255}
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}