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