xref: /linux/tools/perf/util/mem-events.c (revision 0883c2c06fb5bcf5b9e008270827e63c09a88c1e)
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 
15 struct 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 
23 char *perf_mem_events__name(int i)
24 {
25 	return (char *)perf_mem_events[i].name;
26 }
27 
28 int 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 
64 int 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 
88 static const char * const tlb_access[] = {
89 	"N/A",
90 	"HIT",
91 	"MISS",
92 	"L1",
93 	"L2",
94 	"Walker",
95 	"Fault",
96 };
97 
98 int 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 
135 static 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 
152 int 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 
189 static const char * const snoop_access[] = {
190 	"N/A",
191 	"None",
192 	"Miss",
193 	"Hit",
194 	"HitM",
195 };
196 
197 int 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 
224 int 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 
242 int 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 }
256