diff options
Diffstat (limited to 'tools/perf/util/mem-events.c')
-rw-r--r-- | tools/perf/util/mem-events.c | 255 |
1 files changed, 255 insertions, 0 deletions
diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c new file mode 100644 index 000000000000..75465f89a413 --- /dev/null +++ b/tools/perf/util/mem-events.c | |||
@@ -0,0 +1,255 @@ | |||
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 | } | ||