xref: /openbmc/linux/tools/perf/util/mem-events.c (revision 7fbddf40)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2ce1e22b0SJiri Olsa #include <stddef.h>
3ce1e22b0SJiri Olsa #include <stdlib.h>
4ce1e22b0SJiri Olsa #include <string.h>
5ce1e22b0SJiri Olsa #include <errno.h>
654fbad54SJiri Olsa #include <sys/types.h>
754fbad54SJiri Olsa #include <sys/stat.h>
854fbad54SJiri Olsa #include <unistd.h>
954fbad54SJiri Olsa #include <api/fs/fs.h>
10877a7a11SArnaldo Carvalho de Melo #include <linux/kernel.h>
11d3300a3cSArnaldo Carvalho de Melo #include "map_symbol.h"
12acbe613eSJiri Olsa #include "mem-events.h"
13ce1e22b0SJiri Olsa #include "debug.h"
140c877d75SJiri Olsa #include "symbol.h"
15e7ce8d11SJin Yao #include "pmu.h"
16e7ce8d11SJin Yao #include "pmu-hybrid.h"
17acbe613eSJiri Olsa 
18b0d745b3SJiri Olsa unsigned int perf_mem_events__loads_ldlat = 30;
19b0d745b3SJiri Olsa 
2054fbad54SJiri Olsa #define E(t, n, s) { .tag = t, .name = n, .sysfs_name = s }
21acbe613eSJiri Olsa 
22eaf6aaeeSLeo Yan static struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = {
23f9f16dfbSLeo Yan 	E("ldlat-loads",	"cpu/mem-loads,ldlat=%u/P",	"cpu/events/mem-loads"),
24f9f16dfbSLeo Yan 	E("ldlat-stores",	"cpu/mem-stores/P",		"cpu/events/mem-stores"),
254ba2452cSLeo Yan 	E(NULL,			NULL,				NULL),
26acbe613eSJiri Olsa };
2754fbad54SJiri Olsa #undef E
28acbe613eSJiri Olsa 
29b0d745b3SJiri Olsa static char mem_loads_name[100];
30b0d745b3SJiri Olsa static bool mem_loads_name__init;
31b0d745b3SJiri Olsa 
32eaf6aaeeSLeo Yan struct perf_mem_event * __weak perf_mem_events__ptr(int i)
33eaf6aaeeSLeo Yan {
34eaf6aaeeSLeo Yan 	if (i >= PERF_MEM_EVENTS__MAX)
35eaf6aaeeSLeo Yan 		return NULL;
36eaf6aaeeSLeo Yan 
37eaf6aaeeSLeo Yan 	return &perf_mem_events[i];
38eaf6aaeeSLeo Yan }
39eaf6aaeeSLeo Yan 
40d2f327acSJin Yao char * __weak perf_mem_events__name(int i, char *pmu_name  __maybe_unused)
412ba7ac58SJiri Olsa {
42eaf6aaeeSLeo Yan 	struct perf_mem_event *e = perf_mem_events__ptr(i);
43eaf6aaeeSLeo Yan 
44eaf6aaeeSLeo Yan 	if (!e)
45eaf6aaeeSLeo Yan 		return NULL;
46eaf6aaeeSLeo Yan 
47b0d745b3SJiri Olsa 	if (i == PERF_MEM_EVENTS__LOAD) {
48b0d745b3SJiri Olsa 		if (!mem_loads_name__init) {
49b0d745b3SJiri Olsa 			mem_loads_name__init = true;
50b0d745b3SJiri Olsa 			scnprintf(mem_loads_name, sizeof(mem_loads_name),
51eaf6aaeeSLeo Yan 				  e->name, perf_mem_events__loads_ldlat);
52b0d745b3SJiri Olsa 		}
53b0d745b3SJiri Olsa 		return mem_loads_name;
54b0d745b3SJiri Olsa 	}
55b0d745b3SJiri Olsa 
56eaf6aaeeSLeo Yan 	return (char *)e->name;
572ba7ac58SJiri Olsa }
582ba7ac58SJiri Olsa 
592a57d408SKan Liang __weak bool is_mem_loads_aux_event(struct evsel *leader __maybe_unused)
602a57d408SKan Liang {
612a57d408SKan Liang 	return false;
622a57d408SKan Liang }
632a57d408SKan Liang 
64ce1e22b0SJiri Olsa int perf_mem_events__parse(const char *str)
65ce1e22b0SJiri Olsa {
66ce1e22b0SJiri Olsa 	char *tok, *saveptr = NULL;
67ce1e22b0SJiri Olsa 	bool found = false;
68ce1e22b0SJiri Olsa 	char *buf;
69ce1e22b0SJiri Olsa 	int j;
70ce1e22b0SJiri Olsa 
71ce1e22b0SJiri Olsa 	/* We need buffer that we know we can write to. */
72ce1e22b0SJiri Olsa 	buf = malloc(strlen(str) + 1);
73ce1e22b0SJiri Olsa 	if (!buf)
74ce1e22b0SJiri Olsa 		return -ENOMEM;
75ce1e22b0SJiri Olsa 
76ce1e22b0SJiri Olsa 	strcpy(buf, str);
77ce1e22b0SJiri Olsa 
78ce1e22b0SJiri Olsa 	tok = strtok_r((char *)buf, ",", &saveptr);
79ce1e22b0SJiri Olsa 
80ce1e22b0SJiri Olsa 	while (tok) {
81ce1e22b0SJiri Olsa 		for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
82eaf6aaeeSLeo Yan 			struct perf_mem_event *e = perf_mem_events__ptr(j);
83ce1e22b0SJiri Olsa 
844ba2452cSLeo Yan 			if (!e->tag)
854ba2452cSLeo Yan 				continue;
864ba2452cSLeo Yan 
87ce1e22b0SJiri Olsa 			if (strstr(e->tag, tok))
88ce1e22b0SJiri Olsa 				e->record = found = true;
89ce1e22b0SJiri Olsa 		}
90ce1e22b0SJiri Olsa 
91ce1e22b0SJiri Olsa 		tok = strtok_r(NULL, ",", &saveptr);
92ce1e22b0SJiri Olsa 	}
93ce1e22b0SJiri Olsa 
94ce1e22b0SJiri Olsa 	free(buf);
95ce1e22b0SJiri Olsa 
96ce1e22b0SJiri Olsa 	if (found)
97ce1e22b0SJiri Olsa 		return 0;
98ce1e22b0SJiri Olsa 
99ce1e22b0SJiri Olsa 	pr_err("failed: event '%s' not found, use '-e list' to get list of available events\n", str);
100ce1e22b0SJiri Olsa 	return -1;
101ce1e22b0SJiri Olsa }
10254fbad54SJiri Olsa 
103e7ce8d11SJin Yao static bool perf_mem_event__supported(const char *mnt, char *sysfs_name)
104e7ce8d11SJin Yao {
105e7ce8d11SJin Yao 	char path[PATH_MAX];
106e7ce8d11SJin Yao 	struct stat st;
107e7ce8d11SJin Yao 
108e7ce8d11SJin Yao 	scnprintf(path, PATH_MAX, "%s/devices/%s", mnt, sysfs_name);
109e7ce8d11SJin Yao 	return !stat(path, &st);
110e7ce8d11SJin Yao }
111e7ce8d11SJin Yao 
11254fbad54SJiri Olsa int perf_mem_events__init(void)
11354fbad54SJiri Olsa {
11454fbad54SJiri Olsa 	const char *mnt = sysfs__mount();
11554fbad54SJiri Olsa 	bool found = false;
11654fbad54SJiri Olsa 	int j;
11754fbad54SJiri Olsa 
11854fbad54SJiri Olsa 	if (!mnt)
11954fbad54SJiri Olsa 		return -ENOENT;
12054fbad54SJiri Olsa 
12154fbad54SJiri Olsa 	for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
122eaf6aaeeSLeo Yan 		struct perf_mem_event *e = perf_mem_events__ptr(j);
123e7ce8d11SJin Yao 		struct perf_pmu *pmu;
124e7ce8d11SJin Yao 		char sysfs_name[100];
12554fbad54SJiri Olsa 
1264ba2452cSLeo Yan 		/*
1274ba2452cSLeo Yan 		 * If the event entry isn't valid, skip initialization
1284ba2452cSLeo Yan 		 * and "e->supported" will keep false.
1294ba2452cSLeo Yan 		 */
1304ba2452cSLeo Yan 		if (!e->tag)
1314ba2452cSLeo Yan 			continue;
1324ba2452cSLeo Yan 
133e7ce8d11SJin Yao 		if (!perf_pmu__has_hybrid()) {
134e7ce8d11SJin Yao 			scnprintf(sysfs_name, sizeof(sysfs_name),
135e7ce8d11SJin Yao 				  e->sysfs_name, "cpu");
136e7ce8d11SJin Yao 			e->supported = perf_mem_event__supported(mnt, sysfs_name);
137e7ce8d11SJin Yao 		} else {
138e7ce8d11SJin Yao 			perf_pmu__for_each_hybrid_pmu(pmu) {
139e7ce8d11SJin Yao 				scnprintf(sysfs_name, sizeof(sysfs_name),
140e7ce8d11SJin Yao 					  e->sysfs_name, pmu->name);
141e7ce8d11SJin Yao 				e->supported |= perf_mem_event__supported(mnt, sysfs_name);
142e7ce8d11SJin Yao 			}
143e7ce8d11SJin Yao 		}
14454fbad54SJiri Olsa 
145e7ce8d11SJin Yao 		if (e->supported)
146e7ce8d11SJin Yao 			found = true;
14754fbad54SJiri Olsa 	}
14854fbad54SJiri Olsa 
14954fbad54SJiri Olsa 	return found ? 0 : -ENOENT;
15054fbad54SJiri Olsa }
1510c877d75SJiri Olsa 
152b027cc6fSIan Rogers void perf_mem_events__list(void)
153b027cc6fSIan Rogers {
154b027cc6fSIan Rogers 	int j;
155b027cc6fSIan Rogers 
156b027cc6fSIan Rogers 	for (j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
157eaf6aaeeSLeo Yan 		struct perf_mem_event *e = perf_mem_events__ptr(j);
158b027cc6fSIan Rogers 
159b027cc6fSIan Rogers 		fprintf(stderr, "%-13s%-*s%s\n",
1604ba2452cSLeo Yan 			e->tag ?: "",
161b027cc6fSIan Rogers 			verbose > 0 ? 25 : 0,
162d2f327acSJin Yao 			verbose > 0 ? perf_mem_events__name(j, NULL) : "",
163b027cc6fSIan Rogers 			e->supported ? ": available" : "");
164b027cc6fSIan Rogers 	}
165b027cc6fSIan Rogers }
166b027cc6fSIan Rogers 
1674a9086adSJin Yao static void perf_mem_events__print_unsupport_hybrid(struct perf_mem_event *e,
1684a9086adSJin Yao 						    int idx)
1694a9086adSJin Yao {
1704a9086adSJin Yao 	const char *mnt = sysfs__mount();
1714a9086adSJin Yao 	char sysfs_name[100];
1724a9086adSJin Yao 	struct perf_pmu *pmu;
1734a9086adSJin Yao 
1744a9086adSJin Yao 	perf_pmu__for_each_hybrid_pmu(pmu) {
1754a9086adSJin Yao 		scnprintf(sysfs_name, sizeof(sysfs_name), e->sysfs_name,
1764a9086adSJin Yao 			  pmu->name);
1774a9086adSJin Yao 		if (!perf_mem_event__supported(mnt, sysfs_name)) {
1784a9086adSJin Yao 			pr_err("failed: event '%s' not supported\n",
1794a9086adSJin Yao 			       perf_mem_events__name(idx, pmu->name));
1804a9086adSJin Yao 		}
1814a9086adSJin Yao 	}
1824a9086adSJin Yao }
1834a9086adSJin Yao 
1844a9086adSJin Yao int perf_mem_events__record_args(const char **rec_argv, int *argv_nr,
1854a9086adSJin Yao 				 char **rec_tmp, int *tmp_nr)
1864a9086adSJin Yao {
1874a9086adSJin Yao 	int i = *argv_nr, k = 0;
1884a9086adSJin Yao 	struct perf_mem_event *e;
1894a9086adSJin Yao 	struct perf_pmu *pmu;
1904a9086adSJin Yao 	char *s;
1914a9086adSJin Yao 
1924a9086adSJin Yao 	for (int j = 0; j < PERF_MEM_EVENTS__MAX; j++) {
1934a9086adSJin Yao 		e = perf_mem_events__ptr(j);
1944a9086adSJin Yao 		if (!e->record)
1954a9086adSJin Yao 			continue;
1964a9086adSJin Yao 
1974a9086adSJin Yao 		if (!perf_pmu__has_hybrid()) {
1984a9086adSJin Yao 			if (!e->supported) {
1994a9086adSJin Yao 				pr_err("failed: event '%s' not supported\n",
2004a9086adSJin Yao 				       perf_mem_events__name(j, NULL));
2014a9086adSJin Yao 				return -1;
2024a9086adSJin Yao 			}
2034a9086adSJin Yao 
2044a9086adSJin Yao 			rec_argv[i++] = "-e";
2054a9086adSJin Yao 			rec_argv[i++] = perf_mem_events__name(j, NULL);
2064a9086adSJin Yao 		} else {
2074a9086adSJin Yao 			if (!e->supported) {
2084a9086adSJin Yao 				perf_mem_events__print_unsupport_hybrid(e, j);
2094a9086adSJin Yao 				return -1;
2104a9086adSJin Yao 			}
2114a9086adSJin Yao 
2124a9086adSJin Yao 			perf_pmu__for_each_hybrid_pmu(pmu) {
2134a9086adSJin Yao 				rec_argv[i++] = "-e";
2144a9086adSJin Yao 				s = perf_mem_events__name(j, pmu->name);
2154a9086adSJin Yao 				if (s) {
2164a9086adSJin Yao 					s = strdup(s);
2174a9086adSJin Yao 					if (!s)
2184a9086adSJin Yao 						return -1;
2194a9086adSJin Yao 
2204a9086adSJin Yao 					rec_argv[i++] = s;
2214a9086adSJin Yao 					rec_tmp[k++] = s;
2224a9086adSJin Yao 				}
2234a9086adSJin Yao 			}
2244a9086adSJin Yao 		}
2254a9086adSJin Yao 	}
2264a9086adSJin Yao 
2274a9086adSJin Yao 	*argv_nr = i;
2284a9086adSJin Yao 	*tmp_nr = k;
2294a9086adSJin Yao 	return 0;
2304a9086adSJin Yao }
2314a9086adSJin Yao 
2320c877d75SJiri Olsa static const char * const tlb_access[] = {
2330c877d75SJiri Olsa 	"N/A",
2340c877d75SJiri Olsa 	"HIT",
2350c877d75SJiri Olsa 	"MISS",
2360c877d75SJiri Olsa 	"L1",
2370c877d75SJiri Olsa 	"L2",
2380c877d75SJiri Olsa 	"Walker",
2390c877d75SJiri Olsa 	"Fault",
2400c877d75SJiri Olsa };
2410c877d75SJiri Olsa 
242b1a5fbeaSJiri Olsa int perf_mem__tlb_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
2430c877d75SJiri Olsa {
2440c877d75SJiri Olsa 	size_t l = 0, i;
2450c877d75SJiri Olsa 	u64 m = PERF_MEM_TLB_NA;
2460c877d75SJiri Olsa 	u64 hit, miss;
2470c877d75SJiri Olsa 
2480c877d75SJiri Olsa 	sz -= 1; /* -1 for null termination */
2490c877d75SJiri Olsa 	out[0] = '\0';
2500c877d75SJiri Olsa 
2510c877d75SJiri Olsa 	if (mem_info)
2520c877d75SJiri Olsa 		m = mem_info->data_src.mem_dtlb;
2530c877d75SJiri Olsa 
2540c877d75SJiri Olsa 	hit = m & PERF_MEM_TLB_HIT;
2550c877d75SJiri Olsa 	miss = m & PERF_MEM_TLB_MISS;
2560c877d75SJiri Olsa 
2570c877d75SJiri Olsa 	/* already taken care of */
2580c877d75SJiri Olsa 	m &= ~(PERF_MEM_TLB_HIT|PERF_MEM_TLB_MISS);
2590c877d75SJiri Olsa 
2600c877d75SJiri Olsa 	for (i = 0; m && i < ARRAY_SIZE(tlb_access); i++, m >>= 1) {
2610c877d75SJiri Olsa 		if (!(m & 0x1))
2620c877d75SJiri Olsa 			continue;
2630c877d75SJiri Olsa 		if (l) {
2640c877d75SJiri Olsa 			strcat(out, " or ");
2650c877d75SJiri Olsa 			l += 4;
2660c877d75SJiri Olsa 		}
267b1a5fbeaSJiri Olsa 		l += scnprintf(out + l, sz - l, tlb_access[i]);
2680c877d75SJiri Olsa 	}
2690c877d75SJiri Olsa 	if (*out == '\0')
270b1a5fbeaSJiri Olsa 		l += scnprintf(out, sz - l, "N/A");
2710c877d75SJiri Olsa 	if (hit)
272b1a5fbeaSJiri Olsa 		l += scnprintf(out + l, sz - l, " hit");
2730c877d75SJiri Olsa 	if (miss)
274b1a5fbeaSJiri Olsa 		l += scnprintf(out + l, sz - l, " miss");
275b1a5fbeaSJiri Olsa 
276b1a5fbeaSJiri Olsa 	return l;
2770c877d75SJiri Olsa }
278071e9a1eSJiri Olsa 
279071e9a1eSJiri Olsa static const char * const mem_lvl[] = {
280071e9a1eSJiri Olsa 	"N/A",
281071e9a1eSJiri Olsa 	"HIT",
282071e9a1eSJiri Olsa 	"MISS",
283071e9a1eSJiri Olsa 	"L1",
284071e9a1eSJiri Olsa 	"LFB",
285071e9a1eSJiri Olsa 	"L2",
286071e9a1eSJiri Olsa 	"L3",
287071e9a1eSJiri Olsa 	"Local RAM",
288071e9a1eSJiri Olsa 	"Remote RAM (1 hop)",
289071e9a1eSJiri Olsa 	"Remote RAM (2 hops)",
290071e9a1eSJiri Olsa 	"Remote Cache (1 hop)",
291071e9a1eSJiri Olsa 	"Remote Cache (2 hops)",
292071e9a1eSJiri Olsa 	"I/O",
293071e9a1eSJiri Olsa 	"Uncached",
294071e9a1eSJiri Olsa };
295071e9a1eSJiri Olsa 
29652839e65SAndi Kleen static const char * const mem_lvlnum[] = {
29752839e65SAndi Kleen 	[PERF_MEM_LVLNUM_ANY_CACHE] = "Any cache",
29852839e65SAndi Kleen 	[PERF_MEM_LVLNUM_LFB] = "LFB",
29952839e65SAndi Kleen 	[PERF_MEM_LVLNUM_RAM] = "RAM",
30052839e65SAndi Kleen 	[PERF_MEM_LVLNUM_PMEM] = "PMEM",
30152839e65SAndi Kleen 	[PERF_MEM_LVLNUM_NA] = "N/A",
30252839e65SAndi Kleen };
30352839e65SAndi Kleen 
304cae1d759SKajol Jain static const char * const mem_hops[] = {
305cae1d759SKajol Jain 	"N/A",
306cae1d759SKajol Jain 	/*
307cae1d759SKajol Jain 	 * While printing, 'Remote' will be added to represent
308cae1d759SKajol Jain 	 * 'Remote core, same node' accesses as remote field need
309cae1d759SKajol Jain 	 * to be set with mem_hops field.
310cae1d759SKajol Jain 	 */
311cae1d759SKajol Jain 	"core, same node",
312*7fbddf40SKajol Jain 	"node, same socket",
313*7fbddf40SKajol Jain 	"socket, same board",
314*7fbddf40SKajol Jain 	"board",
315cae1d759SKajol Jain };
316cae1d759SKajol Jain 
31796907563SJiri Olsa int perf_mem__lvl_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
318071e9a1eSJiri Olsa {
319071e9a1eSJiri Olsa 	size_t i, l = 0;
320071e9a1eSJiri Olsa 	u64 m =  PERF_MEM_LVL_NA;
321071e9a1eSJiri Olsa 	u64 hit, miss;
322*7fbddf40SKajol Jain 	int printed = 0;
323071e9a1eSJiri Olsa 
324071e9a1eSJiri Olsa 	if (mem_info)
325071e9a1eSJiri Olsa 		m  = mem_info->data_src.mem_lvl;
326071e9a1eSJiri Olsa 
327071e9a1eSJiri Olsa 	sz -= 1; /* -1 for null termination */
328071e9a1eSJiri Olsa 	out[0] = '\0';
329071e9a1eSJiri Olsa 
330071e9a1eSJiri Olsa 	hit = m & PERF_MEM_LVL_HIT;
331071e9a1eSJiri Olsa 	miss = m & PERF_MEM_LVL_MISS;
332071e9a1eSJiri Olsa 
333071e9a1eSJiri Olsa 	/* already taken care of */
334071e9a1eSJiri Olsa 	m &= ~(PERF_MEM_LVL_HIT|PERF_MEM_LVL_MISS);
335071e9a1eSJiri Olsa 
33652839e65SAndi Kleen 	if (mem_info && mem_info->data_src.mem_remote) {
33752839e65SAndi Kleen 		strcat(out, "Remote ");
33852839e65SAndi Kleen 		l += 7;
33952839e65SAndi Kleen 	}
34052839e65SAndi Kleen 
341*7fbddf40SKajol Jain 	/*
342*7fbddf40SKajol Jain 	 * Incase mem_hops field is set, we can skip printing data source via
343*7fbddf40SKajol Jain 	 * PERF_MEM_LVL namespace.
344*7fbddf40SKajol Jain 	 */
345*7fbddf40SKajol Jain 	if (mem_info && mem_info->data_src.mem_hops) {
346cae1d759SKajol Jain 		l += scnprintf(out + l, sz - l, "%s ", mem_hops[mem_info->data_src.mem_hops]);
347*7fbddf40SKajol Jain 	} else {
348071e9a1eSJiri Olsa 		for (i = 0; m && i < ARRAY_SIZE(mem_lvl); i++, m >>= 1) {
349071e9a1eSJiri Olsa 			if (!(m & 0x1))
350071e9a1eSJiri Olsa 				continue;
35152839e65SAndi Kleen 			if (printed++) {
352071e9a1eSJiri Olsa 				strcat(out, " or ");
353071e9a1eSJiri Olsa 				l += 4;
354071e9a1eSJiri Olsa 			}
35596907563SJiri Olsa 			l += scnprintf(out + l, sz - l, mem_lvl[i]);
356071e9a1eSJiri Olsa 		}
357*7fbddf40SKajol Jain 	}
35852839e65SAndi Kleen 
35952839e65SAndi Kleen 	if (mem_info && mem_info->data_src.mem_lvl_num) {
36052839e65SAndi Kleen 		int lvl = mem_info->data_src.mem_lvl_num;
36152839e65SAndi Kleen 		if (printed++) {
36252839e65SAndi Kleen 			strcat(out, " or ");
36352839e65SAndi Kleen 			l += 4;
36452839e65SAndi Kleen 		}
36552839e65SAndi Kleen 		if (mem_lvlnum[lvl])
36652839e65SAndi Kleen 			l += scnprintf(out + l, sz - l, mem_lvlnum[lvl]);
36752839e65SAndi Kleen 		else
36852839e65SAndi Kleen 			l += scnprintf(out + l, sz - l, "L%d", lvl);
36952839e65SAndi Kleen 	}
37052839e65SAndi Kleen 
37152839e65SAndi Kleen 	if (l == 0)
37252839e65SAndi Kleen 		l += scnprintf(out + l, sz - l, "N/A");
373071e9a1eSJiri Olsa 	if (hit)
37496907563SJiri Olsa 		l += scnprintf(out + l, sz - l, " hit");
375071e9a1eSJiri Olsa 	if (miss)
37696907563SJiri Olsa 		l += scnprintf(out + l, sz - l, " miss");
37796907563SJiri Olsa 
37896907563SJiri Olsa 	return l;
379071e9a1eSJiri Olsa }
3802c07af13SJiri Olsa 
3812c07af13SJiri Olsa static const char * const snoop_access[] = {
3822c07af13SJiri Olsa 	"N/A",
3832c07af13SJiri Olsa 	"None",
3842c07af13SJiri Olsa 	"Hit",
385166ebdd2SAndi Kleen 	"Miss",
3862c07af13SJiri Olsa 	"HitM",
3872c07af13SJiri Olsa };
3882c07af13SJiri Olsa 
389149d7507SJiri Olsa int perf_mem__snp_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
3902c07af13SJiri Olsa {
3912c07af13SJiri Olsa 	size_t i, l = 0;
3922c07af13SJiri Olsa 	u64 m = PERF_MEM_SNOOP_NA;
3932c07af13SJiri Olsa 
3942c07af13SJiri Olsa 	sz -= 1; /* -1 for null termination */
3952c07af13SJiri Olsa 	out[0] = '\0';
3962c07af13SJiri Olsa 
3972c07af13SJiri Olsa 	if (mem_info)
3982c07af13SJiri Olsa 		m = mem_info->data_src.mem_snoop;
3992c07af13SJiri Olsa 
4002c07af13SJiri Olsa 	for (i = 0; m && i < ARRAY_SIZE(snoop_access); i++, m >>= 1) {
4012c07af13SJiri Olsa 		if (!(m & 0x1))
4022c07af13SJiri Olsa 			continue;
4032c07af13SJiri Olsa 		if (l) {
4042c07af13SJiri Olsa 			strcat(out, " or ");
4052c07af13SJiri Olsa 			l += 4;
4062c07af13SJiri Olsa 		}
407149d7507SJiri Olsa 		l += scnprintf(out + l, sz - l, snoop_access[i]);
4082c07af13SJiri Olsa 	}
40952839e65SAndi Kleen 	if (mem_info &&
41052839e65SAndi Kleen 	     (mem_info->data_src.mem_snoopx & PERF_MEM_SNOOPX_FWD)) {
41152839e65SAndi Kleen 		if (l) {
41252839e65SAndi Kleen 			strcat(out, " or ");
41352839e65SAndi Kleen 			l += 4;
41452839e65SAndi Kleen 		}
41552839e65SAndi Kleen 		l += scnprintf(out + l, sz - l, "Fwd");
41652839e65SAndi Kleen 	}
4172c07af13SJiri Olsa 
4182c07af13SJiri Olsa 	if (*out == '\0')
419149d7507SJiri Olsa 		l += scnprintf(out, sz - l, "N/A");
420149d7507SJiri Olsa 
421149d7507SJiri Olsa 	return l;
4222c07af13SJiri Olsa }
42369a77275SJiri Olsa 
4248b0819c8SJiri Olsa int perf_mem__lck_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
42569a77275SJiri Olsa {
42669a77275SJiri Olsa 	u64 mask = PERF_MEM_LOCK_NA;
4278b0819c8SJiri Olsa 	int l;
42869a77275SJiri Olsa 
42969a77275SJiri Olsa 	if (mem_info)
43069a77275SJiri Olsa 		mask = mem_info->data_src.mem_lock;
43169a77275SJiri Olsa 
43269a77275SJiri Olsa 	if (mask & PERF_MEM_LOCK_NA)
4338b0819c8SJiri Olsa 		l = scnprintf(out, sz, "N/A");
43469a77275SJiri Olsa 	else if (mask & PERF_MEM_LOCK_LOCKED)
4358b0819c8SJiri Olsa 		l = scnprintf(out, sz, "Yes");
43669a77275SJiri Olsa 	else
4378b0819c8SJiri Olsa 		l = scnprintf(out, sz, "No");
4388b0819c8SJiri Olsa 
4398b0819c8SJiri Olsa 	return l;
44069a77275SJiri Olsa }
441c19ac912SJiri Olsa 
442a054c298SKan Liang int perf_mem__blk_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
443a054c298SKan Liang {
444a054c298SKan Liang 	size_t l = 0;
445a054c298SKan Liang 	u64 mask = PERF_MEM_BLK_NA;
446a054c298SKan Liang 
447a054c298SKan Liang 	sz -= 1; /* -1 for null termination */
448a054c298SKan Liang 	out[0] = '\0';
449a054c298SKan Liang 
450a054c298SKan Liang 	if (mem_info)
451a054c298SKan Liang 		mask = mem_info->data_src.mem_blk;
452a054c298SKan Liang 
453a054c298SKan Liang 	if (!mask || (mask & PERF_MEM_BLK_NA)) {
454a054c298SKan Liang 		l += scnprintf(out + l, sz - l, " N/A");
455a054c298SKan Liang 		return l;
456a054c298SKan Liang 	}
457a054c298SKan Liang 	if (mask & PERF_MEM_BLK_DATA)
458a054c298SKan Liang 		l += scnprintf(out + l, sz - l, " Data");
459a054c298SKan Liang 	if (mask & PERF_MEM_BLK_ADDR)
460a054c298SKan Liang 		l += scnprintf(out + l, sz - l, " Addr");
461a054c298SKan Liang 
462a054c298SKan Liang 	return l;
463a054c298SKan Liang }
464a054c298SKan Liang 
465c19ac912SJiri Olsa int perf_script__meminfo_scnprintf(char *out, size_t sz, struct mem_info *mem_info)
466c19ac912SJiri Olsa {
467c19ac912SJiri Olsa 	int i = 0;
468c19ac912SJiri Olsa 
469c19ac912SJiri Olsa 	i += perf_mem__lvl_scnprintf(out, sz, mem_info);
470c19ac912SJiri Olsa 	i += scnprintf(out + i, sz - i, "|SNP ");
471c19ac912SJiri Olsa 	i += perf_mem__snp_scnprintf(out + i, sz - i, mem_info);
472c19ac912SJiri Olsa 	i += scnprintf(out + i, sz - i, "|TLB ");
473c19ac912SJiri Olsa 	i += perf_mem__tlb_scnprintf(out + i, sz - i, mem_info);
474c19ac912SJiri Olsa 	i += scnprintf(out + i, sz - i, "|LCK ");
475c19ac912SJiri Olsa 	i += perf_mem__lck_scnprintf(out + i, sz - i, mem_info);
476a054c298SKan Liang 	i += scnprintf(out + i, sz - i, "|BLK ");
477a054c298SKan Liang 	i += perf_mem__blk_scnprintf(out + i, sz - i, mem_info);
478c19ac912SJiri Olsa 
479c19ac912SJiri Olsa 	return i;
480c19ac912SJiri Olsa }
481aadddd68SJiri Olsa 
482aadddd68SJiri Olsa int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi)
483aadddd68SJiri Olsa {
484aadddd68SJiri Olsa 	union perf_mem_data_src *data_src = &mi->data_src;
485aadddd68SJiri Olsa 	u64 daddr  = mi->daddr.addr;
486aadddd68SJiri Olsa 	u64 op     = data_src->mem_op;
487aadddd68SJiri Olsa 	u64 lvl    = data_src->mem_lvl;
488aadddd68SJiri Olsa 	u64 snoop  = data_src->mem_snoop;
489aadddd68SJiri Olsa 	u64 lock   = data_src->mem_lock;
490d9d5d767SKan Liang 	u64 blk    = data_src->mem_blk;
49112c15302SJiri Olsa 	/*
49212c15302SJiri Olsa 	 * Skylake might report unknown remote level via this
49312c15302SJiri Olsa 	 * bit, consider it when evaluating remote HITMs.
494cae1d759SKajol Jain 	 *
495cae1d759SKajol Jain 	 * Incase of power, remote field can also be used to denote cache
496cae1d759SKajol Jain 	 * accesses from the another core of same node. Hence, setting
497cae1d759SKajol Jain 	 * mrem only when HOPS is zero along with set remote field.
49812c15302SJiri Olsa 	 */
499cae1d759SKajol Jain 	bool mrem  = (data_src->mem_remote && !data_src->mem_hops);
500aadddd68SJiri Olsa 	int err = 0;
501aadddd68SJiri Olsa 
502dba8ab93SJiri Olsa #define HITM_INC(__f)		\
503dba8ab93SJiri Olsa do {				\
504dba8ab93SJiri Olsa 	stats->__f++;		\
505dba8ab93SJiri Olsa 	stats->tot_hitm++;	\
506dba8ab93SJiri Olsa } while (0)
507dba8ab93SJiri Olsa 
508aadddd68SJiri Olsa #define P(a, b) PERF_MEM_##a##_##b
509aadddd68SJiri Olsa 
510aadddd68SJiri Olsa 	stats->nr_entries++;
511aadddd68SJiri Olsa 
512aadddd68SJiri Olsa 	if (lock & P(LOCK, LOCKED)) stats->locks++;
513aadddd68SJiri Olsa 
514d9d5d767SKan Liang 	if (blk & P(BLK, DATA)) stats->blk_data++;
515d9d5d767SKan Liang 	if (blk & P(BLK, ADDR)) stats->blk_addr++;
516d9d5d767SKan Liang 
517aadddd68SJiri Olsa 	if (op & P(OP, LOAD)) {
518aadddd68SJiri Olsa 		/* load */
519aadddd68SJiri Olsa 		stats->load++;
520aadddd68SJiri Olsa 
521aadddd68SJiri Olsa 		if (!daddr) {
522aadddd68SJiri Olsa 			stats->ld_noadrs++;
523aadddd68SJiri Olsa 			return -1;
524aadddd68SJiri Olsa 		}
525aadddd68SJiri Olsa 
526aadddd68SJiri Olsa 		if (lvl & P(LVL, HIT)) {
527aadddd68SJiri Olsa 			if (lvl & P(LVL, UNC)) stats->ld_uncache++;
528aadddd68SJiri Olsa 			if (lvl & P(LVL, IO))  stats->ld_io++;
529aadddd68SJiri Olsa 			if (lvl & P(LVL, LFB)) stats->ld_fbhit++;
530aadddd68SJiri Olsa 			if (lvl & P(LVL, L1 )) stats->ld_l1hit++;
531aadddd68SJiri Olsa 			if (lvl & P(LVL, L2 )) stats->ld_l2hit++;
532aadddd68SJiri Olsa 			if (lvl & P(LVL, L3 )) {
533aadddd68SJiri Olsa 				if (snoop & P(SNOOP, HITM))
534dba8ab93SJiri Olsa 					HITM_INC(lcl_hitm);
535aadddd68SJiri Olsa 				else
536aadddd68SJiri Olsa 					stats->ld_llchit++;
537aadddd68SJiri Olsa 			}
538aadddd68SJiri Olsa 
539aadddd68SJiri Olsa 			if (lvl & P(LVL, LOC_RAM)) {
540aadddd68SJiri Olsa 				stats->lcl_dram++;
541aadddd68SJiri Olsa 				if (snoop & P(SNOOP, HIT))
542aadddd68SJiri Olsa 					stats->ld_shared++;
543aadddd68SJiri Olsa 				else
544aadddd68SJiri Olsa 					stats->ld_excl++;
545aadddd68SJiri Olsa 			}
546aadddd68SJiri Olsa 
547aadddd68SJiri Olsa 			if ((lvl & P(LVL, REM_RAM1)) ||
54812c15302SJiri Olsa 			    (lvl & P(LVL, REM_RAM2)) ||
54912c15302SJiri Olsa 			     mrem) {
550aadddd68SJiri Olsa 				stats->rmt_dram++;
551aadddd68SJiri Olsa 				if (snoop & P(SNOOP, HIT))
552aadddd68SJiri Olsa 					stats->ld_shared++;
553aadddd68SJiri Olsa 				else
554aadddd68SJiri Olsa 					stats->ld_excl++;
555aadddd68SJiri Olsa 			}
556aadddd68SJiri Olsa 		}
557aadddd68SJiri Olsa 
558aadddd68SJiri Olsa 		if ((lvl & P(LVL, REM_CCE1)) ||
55912c15302SJiri Olsa 		    (lvl & P(LVL, REM_CCE2)) ||
56012c15302SJiri Olsa 		     mrem) {
561aadddd68SJiri Olsa 			if (snoop & P(SNOOP, HIT))
562aadddd68SJiri Olsa 				stats->rmt_hit++;
563aadddd68SJiri Olsa 			else if (snoop & P(SNOOP, HITM))
564dba8ab93SJiri Olsa 				HITM_INC(rmt_hitm);
565aadddd68SJiri Olsa 		}
566aadddd68SJiri Olsa 
567aadddd68SJiri Olsa 		if ((lvl & P(LVL, MISS)))
568aadddd68SJiri Olsa 			stats->ld_miss++;
569aadddd68SJiri Olsa 
570aadddd68SJiri Olsa 	} else if (op & P(OP, STORE)) {
571aadddd68SJiri Olsa 		/* store */
572aadddd68SJiri Olsa 		stats->store++;
573aadddd68SJiri Olsa 
574aadddd68SJiri Olsa 		if (!daddr) {
575aadddd68SJiri Olsa 			stats->st_noadrs++;
576aadddd68SJiri Olsa 			return -1;
577aadddd68SJiri Olsa 		}
578aadddd68SJiri Olsa 
579aadddd68SJiri Olsa 		if (lvl & P(LVL, HIT)) {
580aadddd68SJiri Olsa 			if (lvl & P(LVL, UNC)) stats->st_uncache++;
581aadddd68SJiri Olsa 			if (lvl & P(LVL, L1 )) stats->st_l1hit++;
582aadddd68SJiri Olsa 		}
583aadddd68SJiri Olsa 		if (lvl & P(LVL, MISS))
584aadddd68SJiri Olsa 			if (lvl & P(LVL, L1)) stats->st_l1miss++;
585aadddd68SJiri Olsa 	} else {
586aadddd68SJiri Olsa 		/* unparsable data_src? */
587aadddd68SJiri Olsa 		stats->noparse++;
588aadddd68SJiri Olsa 		return -1;
589aadddd68SJiri Olsa 	}
590aadddd68SJiri Olsa 
591d46a4cdfSArnaldo Carvalho de Melo 	if (!mi->daddr.ms.map || !mi->iaddr.ms.map) {
592aadddd68SJiri Olsa 		stats->nomap++;
593aadddd68SJiri Olsa 		return -1;
594aadddd68SJiri Olsa 	}
595aadddd68SJiri Olsa 
596aadddd68SJiri Olsa #undef P
597dba8ab93SJiri Olsa #undef HITM_INC
598aadddd68SJiri Olsa 	return err;
599aadddd68SJiri Olsa }
6000a9a24ccSJiri Olsa 
6010a9a24ccSJiri Olsa void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add)
6020a9a24ccSJiri Olsa {
6030a9a24ccSJiri Olsa 	stats->nr_entries	+= add->nr_entries;
6040a9a24ccSJiri Olsa 
6050a9a24ccSJiri Olsa 	stats->locks		+= add->locks;
6060a9a24ccSJiri Olsa 	stats->store		+= add->store;
6070a9a24ccSJiri Olsa 	stats->st_uncache	+= add->st_uncache;
6080a9a24ccSJiri Olsa 	stats->st_noadrs	+= add->st_noadrs;
6090a9a24ccSJiri Olsa 	stats->st_l1hit		+= add->st_l1hit;
6100a9a24ccSJiri Olsa 	stats->st_l1miss	+= add->st_l1miss;
6110a9a24ccSJiri Olsa 	stats->load		+= add->load;
6120a9a24ccSJiri Olsa 	stats->ld_excl		+= add->ld_excl;
6130a9a24ccSJiri Olsa 	stats->ld_shared	+= add->ld_shared;
6140a9a24ccSJiri Olsa 	stats->ld_uncache	+= add->ld_uncache;
6150a9a24ccSJiri Olsa 	stats->ld_io		+= add->ld_io;
6160a9a24ccSJiri Olsa 	stats->ld_miss		+= add->ld_miss;
6170a9a24ccSJiri Olsa 	stats->ld_noadrs	+= add->ld_noadrs;
6180a9a24ccSJiri Olsa 	stats->ld_fbhit		+= add->ld_fbhit;
6190a9a24ccSJiri Olsa 	stats->ld_l1hit		+= add->ld_l1hit;
6200a9a24ccSJiri Olsa 	stats->ld_l2hit		+= add->ld_l2hit;
6210a9a24ccSJiri Olsa 	stats->ld_llchit	+= add->ld_llchit;
6220a9a24ccSJiri Olsa 	stats->lcl_hitm		+= add->lcl_hitm;
6230a9a24ccSJiri Olsa 	stats->rmt_hitm		+= add->rmt_hitm;
624dba8ab93SJiri Olsa 	stats->tot_hitm		+= add->tot_hitm;
6250a9a24ccSJiri Olsa 	stats->rmt_hit		+= add->rmt_hit;
6260a9a24ccSJiri Olsa 	stats->lcl_dram		+= add->lcl_dram;
6270a9a24ccSJiri Olsa 	stats->rmt_dram		+= add->rmt_dram;
628d9d5d767SKan Liang 	stats->blk_data		+= add->blk_data;
629d9d5d767SKan Liang 	stats->blk_addr		+= add->blk_addr;
6300a9a24ccSJiri Olsa 	stats->nomap		+= add->nomap;
6310a9a24ccSJiri Olsa 	stats->noparse		+= add->noparse;
6320a9a24ccSJiri Olsa }
633