]>
Commit | Line | Data |
---|---|---|
1c6a800c ACM |
1 | /* |
2 | * builtin-test.c | |
3 | * | |
4 | * Builtin regression testing command: ever growing number of sanity tests | |
5 | */ | |
6 | #include "builtin.h" | |
7 | ||
8 | #include "util/cache.h" | |
9a8e85ad | 9 | #include "util/color.h" |
1c6a800c | 10 | #include "util/debug.h" |
ebf294bf | 11 | #include "util/debugfs.h" |
de5fa3a8 | 12 | #include "util/evlist.h" |
69d2591a | 13 | #include "util/machine.h" |
1c6a800c | 14 | #include "util/parse-options.h" |
de5fa3a8 | 15 | #include "util/parse-events.h" |
1c6a800c | 16 | #include "util/symbol.h" |
fd78260b | 17 | #include "util/thread_map.h" |
cd82a32e | 18 | #include "util/pmu.h" |
6a6cd11d | 19 | #include "event-parse.h" |
13b62567 | 20 | #include "../../include/linux/hw_breakpoint.h" |
1c6a800c | 21 | |
08aa0d1f PZ |
22 | #include <sys/mman.h> |
23 | ||
0252208e | 24 | #include "util/cpumap.h" |
d854861c ACM |
25 | #include "util/evsel.h" |
26 | #include <sys/types.h> | |
27 | ||
0a4e1ae6 JO |
28 | #include "tests.h" |
29 | ||
0252208e ACM |
30 | #include <sched.h> |
31 | ||
0252208e | 32 | |
cd82a32e JO |
33 | static int test__perf_pmu(void) |
34 | { | |
35 | return perf_pmu__test(); | |
36 | } | |
37 | ||
49f20d72 ACM |
38 | static int perf_evsel__roundtrip_cache_name_test(void) |
39 | { | |
40 | char name[128]; | |
41 | int type, op, err = 0, ret = 0, i, idx; | |
42 | struct perf_evsel *evsel; | |
43 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | |
44 | ||
45 | if (evlist == NULL) | |
46 | return -ENOMEM; | |
47 | ||
48 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | |
49 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | |
50 | /* skip invalid cache type */ | |
51 | if (!perf_evsel__is_cache_op_valid(type, op)) | |
52 | continue; | |
53 | ||
54 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | |
55 | __perf_evsel__hw_cache_type_op_res_name(type, op, i, | |
56 | name, sizeof(name)); | |
57 | err = parse_events(evlist, name, 0); | |
58 | if (err) | |
59 | ret = err; | |
60 | } | |
61 | } | |
62 | } | |
63 | ||
64 | idx = 0; | |
65 | evsel = perf_evlist__first(evlist); | |
66 | ||
67 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | |
68 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | |
69 | /* skip invalid cache type */ | |
70 | if (!perf_evsel__is_cache_op_valid(type, op)) | |
71 | continue; | |
72 | ||
73 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | |
74 | __perf_evsel__hw_cache_type_op_res_name(type, op, i, | |
75 | name, sizeof(name)); | |
76 | if (evsel->idx != idx) | |
77 | continue; | |
78 | ||
79 | ++idx; | |
80 | ||
81 | if (strcmp(perf_evsel__name(evsel), name)) { | |
82 | pr_debug("%s != %s\n", perf_evsel__name(evsel), name); | |
83 | ret = -1; | |
84 | } | |
85 | ||
86 | evsel = perf_evsel__next(evsel); | |
87 | } | |
88 | } | |
89 | } | |
90 | ||
91 | perf_evlist__delete(evlist); | |
92 | return ret; | |
93 | } | |
94 | ||
8ad7013b ACM |
95 | static int __perf_evsel__name_array_test(const char *names[], int nr_names) |
96 | { | |
97 | int i, err; | |
98 | struct perf_evsel *evsel; | |
99 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | |
100 | ||
101 | if (evlist == NULL) | |
102 | return -ENOMEM; | |
103 | ||
104 | for (i = 0; i < nr_names; ++i) { | |
105 | err = parse_events(evlist, names[i], 0); | |
106 | if (err) { | |
107 | pr_debug("failed to parse event '%s', err %d\n", | |
108 | names[i], err); | |
109 | goto out_delete_evlist; | |
110 | } | |
111 | } | |
112 | ||
113 | err = 0; | |
114 | list_for_each_entry(evsel, &evlist->entries, node) { | |
115 | if (strcmp(perf_evsel__name(evsel), names[evsel->idx])) { | |
116 | --err; | |
117 | pr_debug("%s != %s\n", perf_evsel__name(evsel), names[evsel->idx]); | |
118 | } | |
119 | } | |
120 | ||
121 | out_delete_evlist: | |
122 | perf_evlist__delete(evlist); | |
123 | return err; | |
124 | } | |
125 | ||
126 | #define perf_evsel__name_array_test(names) \ | |
127 | __perf_evsel__name_array_test(names, ARRAY_SIZE(names)) | |
128 | ||
129 | static int perf_evsel__roundtrip_name_test(void) | |
130 | { | |
131 | int err = 0, ret = 0; | |
132 | ||
133 | err = perf_evsel__name_array_test(perf_evsel__hw_names); | |
134 | if (err) | |
135 | ret = err; | |
136 | ||
137 | err = perf_evsel__name_array_test(perf_evsel__sw_names); | |
138 | if (err) | |
139 | ret = err; | |
140 | ||
49f20d72 ACM |
141 | err = perf_evsel__roundtrip_cache_name_test(); |
142 | if (err) | |
143 | ret = err; | |
144 | ||
8ad7013b ACM |
145 | return ret; |
146 | } | |
147 | ||
6a6cd11d ACM |
148 | static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name, |
149 | int size, bool should_be_signed) | |
150 | { | |
151 | struct format_field *field = perf_evsel__field(evsel, name); | |
152 | int is_signed; | |
153 | int ret = 0; | |
154 | ||
155 | if (field == NULL) { | |
156 | pr_debug("%s: \"%s\" field not found!\n", evsel->name, name); | |
157 | return -1; | |
158 | } | |
159 | ||
160 | is_signed = !!(field->flags | FIELD_IS_SIGNED); | |
161 | if (should_be_signed && !is_signed) { | |
162 | pr_debug("%s: \"%s\" signedness(%d) is wrong, should be %d\n", | |
163 | evsel->name, name, is_signed, should_be_signed); | |
164 | ret = -1; | |
165 | } | |
166 | ||
167 | if (field->size != size) { | |
168 | pr_debug("%s: \"%s\" size (%d) should be %d!\n", | |
169 | evsel->name, name, field->size, size); | |
170 | ret = -1; | |
171 | } | |
172 | ||
af9da88f | 173 | return ret; |
6a6cd11d ACM |
174 | } |
175 | ||
176 | static int perf_evsel__tp_sched_test(void) | |
177 | { | |
178 | struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch", 0); | |
179 | int ret = 0; | |
180 | ||
181 | if (evsel == NULL) { | |
182 | pr_debug("perf_evsel__new\n"); | |
183 | return -1; | |
184 | } | |
185 | ||
186 | if (perf_evsel__test_field(evsel, "prev_comm", 16, true)) | |
187 | ret = -1; | |
188 | ||
189 | if (perf_evsel__test_field(evsel, "prev_pid", 4, true)) | |
190 | ret = -1; | |
191 | ||
192 | if (perf_evsel__test_field(evsel, "prev_prio", 4, true)) | |
193 | ret = -1; | |
194 | ||
195 | if (perf_evsel__test_field(evsel, "prev_state", 8, true)) | |
196 | ret = -1; | |
197 | ||
198 | if (perf_evsel__test_field(evsel, "next_comm", 16, true)) | |
199 | ret = -1; | |
200 | ||
201 | if (perf_evsel__test_field(evsel, "next_pid", 4, true)) | |
202 | ret = -1; | |
203 | ||
204 | if (perf_evsel__test_field(evsel, "next_prio", 4, true)) | |
205 | ret = -1; | |
206 | ||
207 | perf_evsel__delete(evsel); | |
208 | ||
209 | evsel = perf_evsel__newtp("sched", "sched_wakeup", 0); | |
210 | ||
211 | if (perf_evsel__test_field(evsel, "comm", 16, true)) | |
212 | ret = -1; | |
213 | ||
214 | if (perf_evsel__test_field(evsel, "pid", 4, true)) | |
215 | ret = -1; | |
216 | ||
217 | if (perf_evsel__test_field(evsel, "prio", 4, true)) | |
218 | ret = -1; | |
219 | ||
220 | if (perf_evsel__test_field(evsel, "success", 4, true)) | |
221 | ret = -1; | |
222 | ||
223 | if (perf_evsel__test_field(evsel, "target_cpu", 4, true)) | |
224 | ret = -1; | |
225 | ||
af9da88f | 226 | return ret; |
6a6cd11d ACM |
227 | } |
228 | ||
eb2f2703 ACM |
229 | static int test__syscall_open_tp_fields(void) |
230 | { | |
231 | struct perf_record_opts opts = { | |
232 | .target = { | |
233 | .uid = UINT_MAX, | |
234 | .uses_mmap = true, | |
235 | }, | |
236 | .no_delay = true, | |
237 | .freq = 1, | |
238 | .mmap_pages = 256, | |
239 | .raw_samples = true, | |
240 | }; | |
241 | const char *filename = "/etc/passwd"; | |
242 | int flags = O_RDONLY | O_DIRECTORY; | |
243 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | |
244 | struct perf_evsel *evsel; | |
245 | int err = -1, i, nr_events = 0, nr_polls = 0; | |
246 | ||
247 | if (evlist == NULL) { | |
248 | pr_debug("%s: perf_evlist__new\n", __func__); | |
249 | goto out; | |
250 | } | |
251 | ||
252 | evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); | |
253 | if (evsel == NULL) { | |
254 | pr_debug("%s: perf_evsel__newtp\n", __func__); | |
255 | goto out_delete_evlist; | |
256 | } | |
257 | ||
258 | perf_evlist__add(evlist, evsel); | |
259 | ||
260 | err = perf_evlist__create_maps(evlist, &opts.target); | |
261 | if (err < 0) { | |
262 | pr_debug("%s: perf_evlist__create_maps\n", __func__); | |
263 | goto out_delete_evlist; | |
264 | } | |
265 | ||
266 | perf_evsel__config(evsel, &opts, evsel); | |
267 | ||
268 | evlist->threads->map[0] = getpid(); | |
269 | ||
270 | err = perf_evlist__open(evlist); | |
271 | if (err < 0) { | |
272 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | |
273 | goto out_delete_evlist; | |
274 | } | |
275 | ||
276 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | |
277 | if (err < 0) { | |
278 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | |
279 | goto out_delete_evlist; | |
280 | } | |
281 | ||
282 | perf_evlist__enable(evlist); | |
283 | ||
284 | /* | |
945aea22 JO |
285 | * Generate the event: |
286 | */ | |
eb2f2703 ACM |
287 | open(filename, flags); |
288 | ||
289 | while (1) { | |
290 | int before = nr_events; | |
291 | ||
292 | for (i = 0; i < evlist->nr_mmaps; i++) { | |
293 | union perf_event *event; | |
294 | ||
295 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | |
296 | const u32 type = event->header.type; | |
297 | int tp_flags; | |
298 | struct perf_sample sample; | |
299 | ||
300 | ++nr_events; | |
301 | ||
302 | if (type != PERF_RECORD_SAMPLE) | |
303 | continue; | |
304 | ||
305 | err = perf_evsel__parse_sample(evsel, event, &sample); | |
306 | if (err) { | |
307 | pr_err("Can't parse sample, err = %d\n", err); | |
308 | goto out_munmap; | |
309 | } | |
310 | ||
311 | tp_flags = perf_evsel__intval(evsel, &sample, "flags"); | |
312 | ||
313 | if (flags != tp_flags) { | |
314 | pr_debug("%s: Expected flags=%#x, got %#x\n", | |
315 | __func__, flags, tp_flags); | |
316 | goto out_munmap; | |
317 | } | |
318 | ||
319 | goto out_ok; | |
320 | } | |
321 | } | |
322 | ||
323 | if (nr_events == before) | |
324 | poll(evlist->pollfd, evlist->nr_fds, 10); | |
325 | ||
326 | if (++nr_polls > 5) { | |
327 | pr_debug("%s: no events!\n", __func__); | |
328 | goto out_munmap; | |
329 | } | |
330 | } | |
331 | out_ok: | |
332 | err = 0; | |
333 | out_munmap: | |
334 | perf_evlist__munmap(evlist); | |
335 | out_delete_evlist: | |
336 | perf_evlist__delete(evlist); | |
337 | out: | |
338 | return err; | |
339 | } | |
340 | ||
1c6a800c ACM |
341 | static struct test { |
342 | const char *desc; | |
343 | int (*func)(void); | |
344 | } tests[] = { | |
345 | { | |
346 | .desc = "vmlinux symtab matches kallsyms", | |
347 | .func = test__vmlinux_matches_kallsyms, | |
348 | }, | |
d854861c ACM |
349 | { |
350 | .desc = "detect open syscall event", | |
351 | .func = test__open_syscall_event, | |
352 | }, | |
0252208e ACM |
353 | { |
354 | .desc = "detect open syscall event on all cpus", | |
355 | .func = test__open_syscall_event_on_all_cpus, | |
356 | }, | |
de5fa3a8 ACM |
357 | { |
358 | .desc = "read samples using the mmap interface", | |
359 | .func = test__basic_mmap, | |
360 | }, | |
13b62567 JO |
361 | { |
362 | .desc = "parse events tests", | |
f50246e2 | 363 | .func = parse_events__test, |
13b62567 | 364 | }, |
08aa0d1f PZ |
365 | #if defined(__x86_64__) || defined(__i386__) |
366 | { | |
367 | .desc = "x86 rdpmc test", | |
368 | .func = test__rdpmc, | |
369 | }, | |
370 | #endif | |
3e7c439a ACM |
371 | { |
372 | .desc = "Validate PERF_RECORD_* events & perf_sample fields", | |
373 | .func = test__PERF_RECORD, | |
374 | }, | |
cd82a32e JO |
375 | { |
376 | .desc = "Test perf pmu format parsing", | |
377 | .func = test__perf_pmu, | |
378 | }, | |
f7add556 JO |
379 | { |
380 | .desc = "Test dso data interface", | |
381 | .func = dso__test_data, | |
382 | }, | |
8ad7013b ACM |
383 | { |
384 | .desc = "roundtrip evsel->name check", | |
385 | .func = perf_evsel__roundtrip_name_test, | |
386 | }, | |
6a6cd11d ACM |
387 | { |
388 | .desc = "Check parsing of sched tracepoints fields", | |
389 | .func = perf_evsel__tp_sched_test, | |
390 | }, | |
eb2f2703 ACM |
391 | { |
392 | .desc = "Generate and check syscalls:sys_enter_open event fields", | |
393 | .func = test__syscall_open_tp_fields, | |
394 | }, | |
d898b241 JO |
395 | { |
396 | .desc = "struct perf_event_attr setup", | |
397 | .func = test_attr__run, | |
398 | }, | |
1c6a800c ACM |
399 | { |
400 | .func = NULL, | |
401 | }, | |
402 | }; | |
403 | ||
e60770a0 | 404 | static bool perf_test__matches(int curr, int argc, const char *argv[]) |
1c6a800c | 405 | { |
e60770a0 ACM |
406 | int i; |
407 | ||
408 | if (argc == 0) | |
409 | return true; | |
410 | ||
411 | for (i = 0; i < argc; ++i) { | |
412 | char *end; | |
413 | long nr = strtoul(argv[i], &end, 10); | |
414 | ||
415 | if (*end == '\0') { | |
416 | if (nr == curr + 1) | |
417 | return true; | |
418 | continue; | |
419 | } | |
1c6a800c | 420 | |
e60770a0 ACM |
421 | if (strstr(tests[curr].desc, argv[i])) |
422 | return true; | |
423 | } | |
424 | ||
425 | return false; | |
426 | } | |
427 | ||
428 | static int __cmd_test(int argc, const char *argv[]) | |
429 | { | |
430 | int i = 0; | |
9a8e85ad | 431 | int width = 0; |
1c6a800c | 432 | |
9a8e85ad ACM |
433 | while (tests[i].func) { |
434 | int len = strlen(tests[i].desc); | |
435 | ||
436 | if (width < len) | |
437 | width = len; | |
438 | ++i; | |
439 | } | |
945aea22 | 440 | |
9a8e85ad | 441 | i = 0; |
1c6a800c | 442 | while (tests[i].func) { |
e60770a0 ACM |
443 | int curr = i++, err; |
444 | ||
445 | if (!perf_test__matches(curr, argc, argv)) | |
446 | continue; | |
447 | ||
9a8e85ad | 448 | pr_info("%2d: %-*s:", i, width, tests[curr].desc); |
1c6a800c | 449 | pr_debug("\n--- start ---\n"); |
e60770a0 ACM |
450 | err = tests[curr].func(); |
451 | pr_debug("---- end ----\n%s:", tests[curr].desc); | |
9a8e85ad ACM |
452 | if (err) |
453 | color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n"); | |
454 | else | |
455 | pr_info(" Ok\n"); | |
1c6a800c ACM |
456 | } |
457 | ||
458 | return 0; | |
459 | } | |
460 | ||
e60770a0 ACM |
461 | static int perf_test__list(int argc, const char **argv) |
462 | { | |
463 | int i = 0; | |
464 | ||
465 | while (tests[i].func) { | |
466 | int curr = i++; | |
467 | ||
468 | if (argc > 1 && !strstr(tests[curr].desc, argv[1])) | |
469 | continue; | |
470 | ||
471 | pr_info("%2d: %s\n", i, tests[curr].desc); | |
472 | } | |
473 | ||
474 | return 0; | |
475 | } | |
1c6a800c | 476 | |
1d037ca1 | 477 | int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) |
e60770a0 ACM |
478 | { |
479 | const char * const test_usage[] = { | |
480 | "perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]", | |
481 | NULL, | |
482 | }; | |
483 | const struct option test_options[] = { | |
c30ab8aa | 484 | OPT_INCR('v', "verbose", &verbose, |
1c6a800c ACM |
485 | "be more verbose (show symbol address, etc)"), |
486 | OPT_END() | |
e60770a0 | 487 | }; |
1c6a800c | 488 | |
1c6a800c | 489 | argc = parse_options(argc, argv, test_options, test_usage, 0); |
e60770a0 ACM |
490 | if (argc >= 1 && !strcmp(argv[0], "list")) |
491 | return perf_test__list(argc, argv); | |
1c6a800c ACM |
492 | |
493 | symbol_conf.priv_size = sizeof(int); | |
494 | symbol_conf.sort_by_name = true; | |
495 | symbol_conf.try_vmlinux_path = true; | |
496 | ||
497 | if (symbol__init() < 0) | |
498 | return -1; | |
499 | ||
e60770a0 | 500 | return __cmd_test(argc, argv); |
1c6a800c | 501 | } |