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