]>
Commit | Line | Data |
---|---|---|
8ad8db37 IM |
1 | |
2 | #include "../perf.h" | |
3 | #include "util.h" | |
4 | #include "parse-options.h" | |
5 | #include "parse-events.h" | |
6 | #include "exec_cmd.h" | |
a0055ae2 | 7 | #include "string.h" |
8ad8db37 | 8 | |
8326f44d IM |
9 | extern char *strcasestr(const char *haystack, const char *needle); |
10 | ||
a21ca2ca | 11 | int nr_counters; |
8ad8db37 | 12 | |
a21ca2ca | 13 | struct perf_counter_attr attrs[MAX_COUNTERS]; |
8ad8db37 IM |
14 | |
15 | struct event_symbol { | |
9cffa8d5 PM |
16 | u8 type; |
17 | u64 config; | |
a21ca2ca | 18 | char *symbol; |
74d5b588 | 19 | char *alias; |
8ad8db37 IM |
20 | }; |
21 | ||
51e26842 JSR |
22 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x |
23 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x | |
a21ca2ca | 24 | |
8ad8db37 | 25 | static struct event_symbol event_symbols[] = { |
74d5b588 JSR |
26 | { CHW(CPU_CYCLES), "cpu-cycles", "cycles" }, |
27 | { CHW(INSTRUCTIONS), "instructions", "" }, | |
28 | { CHW(CACHE_REFERENCES), "cache-references", "" }, | |
29 | { CHW(CACHE_MISSES), "cache-misses", "" }, | |
30 | { CHW(BRANCH_INSTRUCTIONS), "branch-instructions", "branches" }, | |
31 | { CHW(BRANCH_MISSES), "branch-misses", "" }, | |
32 | { CHW(BUS_CYCLES), "bus-cycles", "" }, | |
33 | ||
34 | { CSW(CPU_CLOCK), "cpu-clock", "" }, | |
35 | { CSW(TASK_CLOCK), "task-clock", "" }, | |
c0c22dbf | 36 | { CSW(PAGE_FAULTS), "page-faults", "faults" }, |
74d5b588 JSR |
37 | { CSW(PAGE_FAULTS_MIN), "minor-faults", "" }, |
38 | { CSW(PAGE_FAULTS_MAJ), "major-faults", "" }, | |
39 | { CSW(CONTEXT_SWITCHES), "context-switches", "cs" }, | |
40 | { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" }, | |
8ad8db37 IM |
41 | }; |
42 | ||
5242519b IM |
43 | #define __PERF_COUNTER_FIELD(config, name) \ |
44 | ((config & PERF_COUNTER_##name##_MASK) >> PERF_COUNTER_##name##_SHIFT) | |
45 | ||
46 | #define PERF_COUNTER_RAW(config) __PERF_COUNTER_FIELD(config, RAW) | |
47 | #define PERF_COUNTER_CONFIG(config) __PERF_COUNTER_FIELD(config, CONFIG) | |
48 | #define PERF_COUNTER_TYPE(config) __PERF_COUNTER_FIELD(config, TYPE) | |
49 | #define PERF_COUNTER_ID(config) __PERF_COUNTER_FIELD(config, EVENT) | |
50 | ||
51 | static char *hw_event_names[] = { | |
8faf3b54 | 52 | "cycles", |
5242519b | 53 | "instructions", |
8faf3b54 IM |
54 | "cache-references", |
55 | "cache-misses", | |
5242519b | 56 | "branches", |
8faf3b54 IM |
57 | "branch-misses", |
58 | "bus-cycles", | |
5242519b IM |
59 | }; |
60 | ||
61 | static char *sw_event_names[] = { | |
44175b6f IM |
62 | "cpu-clock-msecs", |
63 | "task-clock-msecs", | |
8faf3b54 IM |
64 | "page-faults", |
65 | "context-switches", | |
66 | "CPU-migrations", | |
67 | "minor-faults", | |
68 | "major-faults", | |
5242519b IM |
69 | }; |
70 | ||
8326f44d IM |
71 | #define MAX_ALIASES 8 |
72 | ||
c0c22dbf JSR |
73 | static char *hw_cache[][MAX_ALIASES] = { |
74 | { "L1-data", "l1-d", "l1d" }, | |
75 | { "L1-instruction", "l1-i", "l1i" }, | |
76 | { "L2", "l2" }, | |
77 | { "Data-TLB", "dtlb", "d-tlb" }, | |
78 | { "Instruction-TLB", "itlb", "i-tlb" }, | |
79 | { "Branch", "bpu" , "btb", "bpc" }, | |
8326f44d IM |
80 | }; |
81 | ||
c0c22dbf JSR |
82 | static char *hw_cache_op[][MAX_ALIASES] = { |
83 | { "Load", "read" }, | |
84 | { "Store", "write" }, | |
85 | { "Prefetch", "speculative-read", "speculative-load" }, | |
8326f44d IM |
86 | }; |
87 | ||
c0c22dbf JSR |
88 | static char *hw_cache_result[][MAX_ALIASES] = { |
89 | { "Reference", "ops", "access" }, | |
90 | { "Miss" }, | |
8326f44d IM |
91 | }; |
92 | ||
a21ca2ca | 93 | char *event_name(int counter) |
5242519b | 94 | { |
9cffa8d5 | 95 | u64 config = attrs[counter].config; |
a21ca2ca | 96 | int type = attrs[counter].type; |
5242519b IM |
97 | static char buf[32]; |
98 | ||
a21ca2ca IM |
99 | if (attrs[counter].type == PERF_TYPE_RAW) { |
100 | sprintf(buf, "raw 0x%llx", config); | |
5242519b IM |
101 | return buf; |
102 | } | |
103 | ||
104 | switch (type) { | |
105 | case PERF_TYPE_HARDWARE: | |
f4dbfa8f | 106 | if (config < PERF_COUNT_HW_MAX) |
a21ca2ca | 107 | return hw_event_names[config]; |
5242519b IM |
108 | return "unknown-hardware"; |
109 | ||
8326f44d | 110 | case PERF_TYPE_HW_CACHE: { |
9cffa8d5 | 111 | u8 cache_type, cache_op, cache_result; |
8326f44d IM |
112 | static char name[100]; |
113 | ||
114 | cache_type = (config >> 0) & 0xff; | |
115 | if (cache_type > PERF_COUNT_HW_CACHE_MAX) | |
116 | return "unknown-ext-hardware-cache-type"; | |
117 | ||
118 | cache_op = (config >> 8) & 0xff; | |
8faf3b54 IM |
119 | if (cache_op > PERF_COUNT_HW_CACHE_OP_MAX) |
120 | return "unknown-ext-hardware-cache-op"; | |
8326f44d IM |
121 | |
122 | cache_result = (config >> 16) & 0xff; | |
8faf3b54 IM |
123 | if (cache_result > PERF_COUNT_HW_CACHE_RESULT_MAX) |
124 | return "unknown-ext-hardware-cache-result"; | |
8326f44d | 125 | |
8faf3b54 | 126 | sprintf(name, "%s-Cache-%s-%ses", |
8326f44d IM |
127 | hw_cache[cache_type][0], |
128 | hw_cache_op[cache_op][0], | |
129 | hw_cache_result[cache_result][0]); | |
130 | ||
131 | return name; | |
132 | } | |
133 | ||
5242519b | 134 | case PERF_TYPE_SOFTWARE: |
f4dbfa8f | 135 | if (config < PERF_COUNT_SW_MAX) |
a21ca2ca | 136 | return sw_event_names[config]; |
5242519b IM |
137 | return "unknown-software"; |
138 | ||
139 | default: | |
140 | break; | |
141 | } | |
142 | ||
143 | return "unknown"; | |
144 | } | |
145 | ||
8326f44d IM |
146 | static int parse_aliases(const char *str, char *names[][MAX_ALIASES], int size) |
147 | { | |
148 | int i, j; | |
149 | ||
150 | for (i = 0; i < size; i++) { | |
151 | for (j = 0; j < MAX_ALIASES; j++) { | |
152 | if (!names[i][j]) | |
153 | break; | |
154 | if (strcasestr(str, names[i][j])) | |
155 | return i; | |
156 | } | |
157 | } | |
158 | ||
8953645f | 159 | return -1; |
8326f44d IM |
160 | } |
161 | ||
c0c22dbf JSR |
162 | static int |
163 | parse_generic_hw_symbols(const char *str, struct perf_counter_attr *attr) | |
8326f44d | 164 | { |
8953645f | 165 | int cache_type = -1, cache_op = 0, cache_result = 0; |
8326f44d IM |
166 | |
167 | cache_type = parse_aliases(str, hw_cache, PERF_COUNT_HW_CACHE_MAX); | |
168 | /* | |
169 | * No fallback - if we cannot get a clear cache type | |
170 | * then bail out: | |
171 | */ | |
172 | if (cache_type == -1) | |
173 | return -EINVAL; | |
174 | ||
175 | cache_op = parse_aliases(str, hw_cache_op, PERF_COUNT_HW_CACHE_OP_MAX); | |
176 | /* | |
177 | * Fall back to reads: | |
178 | */ | |
8953645f IM |
179 | if (cache_op == -1) |
180 | cache_op = PERF_COUNT_HW_CACHE_OP_READ; | |
8326f44d IM |
181 | |
182 | cache_result = parse_aliases(str, hw_cache_result, | |
183 | PERF_COUNT_HW_CACHE_RESULT_MAX); | |
184 | /* | |
185 | * Fall back to accesses: | |
186 | */ | |
187 | if (cache_result == -1) | |
188 | cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS; | |
189 | ||
190 | attr->config = cache_type | (cache_op << 8) | (cache_result << 16); | |
191 | attr->type = PERF_TYPE_HW_CACHE; | |
192 | ||
193 | return 0; | |
194 | } | |
195 | ||
74d5b588 JSR |
196 | static int check_events(const char *str, unsigned int i) |
197 | { | |
198 | if (!strncmp(str, event_symbols[i].symbol, | |
199 | strlen(event_symbols[i].symbol))) | |
200 | return 1; | |
201 | ||
202 | if (strlen(event_symbols[i].alias)) | |
203 | if (!strncmp(str, event_symbols[i].alias, | |
c0c22dbf | 204 | strlen(event_symbols[i].alias))) |
74d5b588 JSR |
205 | return 1; |
206 | return 0; | |
207 | } | |
208 | ||
8ad8db37 IM |
209 | /* |
210 | * Each event can have multiple symbolic names. | |
211 | * Symbolic names are (almost) exactly matched. | |
212 | */ | |
8326f44d | 213 | static int parse_event_symbols(const char *str, struct perf_counter_attr *attr) |
8ad8db37 | 214 | { |
9cffa8d5 | 215 | u64 config, id; |
8ad8db37 IM |
216 | int type; |
217 | unsigned int i; | |
a0055ae2 | 218 | const char *sep, *pstr; |
8ad8db37 | 219 | |
a21ca2ca IM |
220 | if (str[0] == 'r' && hex2u64(str + 1, &config) > 0) { |
221 | attr->type = PERF_TYPE_RAW; | |
222 | attr->config = config; | |
223 | ||
224 | return 0; | |
225 | } | |
8ad8db37 | 226 | |
a0055ae2 ACM |
227 | pstr = str; |
228 | sep = strchr(pstr, ':'); | |
229 | if (sep) { | |
230 | type = atoi(pstr); | |
231 | pstr = sep + 1; | |
232 | id = atoi(pstr); | |
233 | sep = strchr(pstr, ':'); | |
234 | if (sep) { | |
235 | pstr = sep + 1; | |
236 | if (strchr(pstr, 'k')) | |
a21ca2ca | 237 | attr->exclude_user = 1; |
a0055ae2 | 238 | if (strchr(pstr, 'u')) |
a21ca2ca | 239 | attr->exclude_kernel = 1; |
a0055ae2 | 240 | } |
a21ca2ca IM |
241 | attr->type = type; |
242 | attr->config = id; | |
243 | ||
244 | return 0; | |
5242519b | 245 | } |
8ad8db37 IM |
246 | |
247 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { | |
74d5b588 | 248 | if (check_events(str, i)) { |
a21ca2ca IM |
249 | attr->type = event_symbols[i].type; |
250 | attr->config = event_symbols[i].config; | |
251 | ||
252 | return 0; | |
253 | } | |
8ad8db37 IM |
254 | } |
255 | ||
8326f44d | 256 | return parse_generic_hw_symbols(str, attr); |
8ad8db37 IM |
257 | } |
258 | ||
259 | int parse_events(const struct option *opt, const char *str, int unset) | |
260 | { | |
a21ca2ca IM |
261 | struct perf_counter_attr attr; |
262 | int ret; | |
8ad8db37 | 263 | |
a21ca2ca | 264 | memset(&attr, 0, sizeof(attr)); |
8ad8db37 IM |
265 | again: |
266 | if (nr_counters == MAX_COUNTERS) | |
267 | return -1; | |
268 | ||
8326f44d | 269 | ret = parse_event_symbols(str, &attr); |
a21ca2ca IM |
270 | if (ret < 0) |
271 | return ret; | |
8ad8db37 | 272 | |
a21ca2ca | 273 | attrs[nr_counters] = attr; |
8ad8db37 IM |
274 | nr_counters++; |
275 | ||
276 | str = strstr(str, ","); | |
277 | if (str) { | |
278 | str++; | |
279 | goto again; | |
280 | } | |
281 | ||
282 | return 0; | |
283 | } | |
284 | ||
86847b62 TG |
285 | static const char * const event_type_descriptors[] = { |
286 | "", | |
287 | "Hardware event", | |
288 | "Software event", | |
289 | "Tracepoint event", | |
290 | "Hardware cache event", | |
291 | }; | |
292 | ||
8ad8db37 | 293 | /* |
86847b62 | 294 | * Print the help text for the event symbols: |
8ad8db37 | 295 | */ |
86847b62 | 296 | void print_events(void) |
8ad8db37 | 297 | { |
86847b62 TG |
298 | struct event_symbol *syms = event_symbols; |
299 | unsigned int i, type, prev_type = -1; | |
74d5b588 | 300 | char name[40]; |
8ad8db37 | 301 | |
86847b62 TG |
302 | fprintf(stderr, "\n"); |
303 | fprintf(stderr, "List of pre-defined events (to be used in -e):\n"); | |
8ad8db37 | 304 | |
86847b62 TG |
305 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
306 | type = syms->type + 1; | |
307 | if (type > ARRAY_SIZE(event_type_descriptors)) | |
308 | type = 0; | |
8ad8db37 | 309 | |
86847b62 TG |
310 | if (type != prev_type) |
311 | fprintf(stderr, "\n"); | |
8ad8db37 | 312 | |
74d5b588 JSR |
313 | if (strlen(syms->alias)) |
314 | sprintf(name, "%s OR %s", syms->symbol, syms->alias); | |
315 | else | |
316 | strcpy(name, syms->symbol); | |
317 | fprintf(stderr, " %-40s [%s]\n", name, | |
86847b62 | 318 | event_type_descriptors[type]); |
8ad8db37 | 319 | |
86847b62 | 320 | prev_type = type; |
8ad8db37 IM |
321 | } |
322 | ||
86847b62 | 323 | fprintf(stderr, "\n"); |
74d5b588 | 324 | fprintf(stderr, " %-40s [raw hardware event descriptor]\n", |
86847b62 TG |
325 | "rNNN"); |
326 | fprintf(stderr, "\n"); | |
327 | ||
328 | exit(129); | |
8ad8db37 | 329 | } |