diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2011-11-21 08:42:47 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2011-12-21 06:21:42 -0500 |
commit | 08aa0d1f376e9b966568316bd2019b3c1274d885 (patch) | |
tree | 494c1ef502a8c9258a89a6a6c3d95c6137728e64 /tools/perf/builtin-test.c | |
parent | e3f3541c19c89a4daae39300defba68943301949 (diff) |
perf tools: Add x86 RDPMC, RDTSC test
Implement a simple test for the self-monitoring data from the
perf mmap data area control page:
6: x86 rdpmc test:
0: 6053
1: 60053
2: 600059
3: 6000059
4: 60000075
5: 600000247
Ok
The counts are expected to increase monotonically - these
are recovered via RDPMC, without calling into the kernel.
It might be nice to add logic to automagically turn these numbers into OK/FAIL.
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Stephane Eranian <eranian@google.com>
Cc: Arun Sharma <asharma@fb.com>
Link: http://lkml.kernel.org/n/tip-evf5yii88ljdgmaihccbxxw1@git.kernel.org
[ various small improvements ]
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/builtin-test.c')
-rw-r--r-- | tools/perf/builtin-test.c | 177 |
1 files changed, 175 insertions, 2 deletions
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 2b9a7f497a20..439b5ed03e64 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c | |||
@@ -15,6 +15,8 @@ | |||
15 | #include "util/thread_map.h" | 15 | #include "util/thread_map.h" |
16 | #include "../../include/linux/hw_breakpoint.h" | 16 | #include "../../include/linux/hw_breakpoint.h" |
17 | 17 | ||
18 | #include <sys/mman.h> | ||
19 | |||
18 | static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym) | 20 | static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym) |
19 | { | 21 | { |
20 | bool *visited = symbol__priv(sym); | 22 | bool *visited = symbol__priv(sym); |
@@ -1296,6 +1298,173 @@ out: | |||
1296 | return (err < 0 || errs > 0) ? -1 : 0; | 1298 | return (err < 0 || errs > 0) ? -1 : 0; |
1297 | } | 1299 | } |
1298 | 1300 | ||
1301 | |||
1302 | #if defined(__x86_64__) || defined(__i386__) | ||
1303 | |||
1304 | #define barrier() asm volatile("" ::: "memory") | ||
1305 | |||
1306 | static u64 rdpmc(unsigned int counter) | ||
1307 | { | ||
1308 | unsigned int low, high; | ||
1309 | |||
1310 | asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter)); | ||
1311 | |||
1312 | return low | ((u64)high) << 32; | ||
1313 | } | ||
1314 | |||
1315 | static u64 rdtsc(void) | ||
1316 | { | ||
1317 | unsigned int low, high; | ||
1318 | |||
1319 | asm volatile("rdtsc" : "=a" (low), "=d" (high)); | ||
1320 | |||
1321 | return low | ((u64)high) << 32; | ||
1322 | } | ||
1323 | |||
1324 | static u64 mmap_read_self(void *addr) | ||
1325 | { | ||
1326 | struct perf_event_mmap_page *pc = addr; | ||
1327 | u32 seq, idx, time_mult = 0, time_shift = 0; | ||
1328 | u64 count, cyc = 0, time_offset = 0, enabled, running, delta; | ||
1329 | |||
1330 | do { | ||
1331 | seq = pc->lock; | ||
1332 | barrier(); | ||
1333 | |||
1334 | enabled = pc->time_enabled; | ||
1335 | running = pc->time_running; | ||
1336 | |||
1337 | if (enabled != running) { | ||
1338 | cyc = rdtsc(); | ||
1339 | time_mult = pc->time_mult; | ||
1340 | time_shift = pc->time_shift; | ||
1341 | time_offset = pc->time_offset; | ||
1342 | } | ||
1343 | |||
1344 | idx = pc->index; | ||
1345 | count = pc->offset; | ||
1346 | if (idx) | ||
1347 | count += rdpmc(idx - 1); | ||
1348 | |||
1349 | barrier(); | ||
1350 | } while (pc->lock != seq); | ||
1351 | |||
1352 | if (enabled != running) { | ||
1353 | u64 quot, rem; | ||
1354 | |||
1355 | quot = (cyc >> time_shift); | ||
1356 | rem = cyc & ((1 << time_shift) - 1); | ||
1357 | delta = time_offset + quot * time_mult + | ||
1358 | ((rem * time_mult) >> time_shift); | ||
1359 | |||
1360 | enabled += delta; | ||
1361 | if (idx) | ||
1362 | running += delta; | ||
1363 | |||
1364 | quot = count / running; | ||
1365 | rem = count % running; | ||
1366 | count = quot * enabled + (rem * enabled) / running; | ||
1367 | } | ||
1368 | |||
1369 | return count; | ||
1370 | } | ||
1371 | |||
1372 | /* | ||
1373 | * If the RDPMC instruction faults then signal this back to the test parent task: | ||
1374 | */ | ||
1375 | static void segfault_handler(int sig __used, siginfo_t *info __used, void *uc __used) | ||
1376 | { | ||
1377 | exit(-1); | ||
1378 | } | ||
1379 | |||
1380 | static int __test__rdpmc(void) | ||
1381 | { | ||
1382 | long page_size = sysconf(_SC_PAGE_SIZE); | ||
1383 | volatile int tmp = 0; | ||
1384 | u64 i, loops = 1000; | ||
1385 | int n; | ||
1386 | int fd; | ||
1387 | void *addr; | ||
1388 | struct perf_event_attr attr = { | ||
1389 | .type = PERF_TYPE_HARDWARE, | ||
1390 | .config = PERF_COUNT_HW_INSTRUCTIONS, | ||
1391 | .exclude_kernel = 1, | ||
1392 | }; | ||
1393 | u64 delta_sum = 0; | ||
1394 | struct sigaction sa; | ||
1395 | |||
1396 | sigfillset(&sa.sa_mask); | ||
1397 | sa.sa_sigaction = segfault_handler; | ||
1398 | sigaction(SIGSEGV, &sa, NULL); | ||
1399 | |||
1400 | fprintf(stderr, "\n\n"); | ||
1401 | |||
1402 | fd = sys_perf_event_open(&attr, 0, -1, -1, 0); | ||
1403 | if (fd < 0) { | ||
1404 | die("Error: sys_perf_event_open() syscall returned " | ||
1405 | "with %d (%s)\n", fd, strerror(errno)); | ||
1406 | } | ||
1407 | |||
1408 | addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); | ||
1409 | if (addr == (void *)(-1)) { | ||
1410 | die("Error: mmap() syscall returned " | ||
1411 | "with (%s)\n", strerror(errno)); | ||
1412 | } | ||
1413 | |||
1414 | for (n = 0; n < 6; n++) { | ||
1415 | u64 stamp, now, delta; | ||
1416 | |||
1417 | stamp = mmap_read_self(addr); | ||
1418 | |||
1419 | for (i = 0; i < loops; i++) | ||
1420 | tmp++; | ||
1421 | |||
1422 | now = mmap_read_self(addr); | ||
1423 | loops *= 10; | ||
1424 | |||
1425 | delta = now - stamp; | ||
1426 | fprintf(stderr, "%14d: %14Lu\n", n, (long long)delta); | ||
1427 | |||
1428 | delta_sum += delta; | ||
1429 | } | ||
1430 | |||
1431 | munmap(addr, page_size); | ||
1432 | close(fd); | ||
1433 | |||
1434 | fprintf(stderr, " "); | ||
1435 | |||
1436 | if (!delta_sum) | ||
1437 | return -1; | ||
1438 | |||
1439 | return 0; | ||
1440 | } | ||
1441 | |||
1442 | static int test__rdpmc(void) | ||
1443 | { | ||
1444 | int status = 0; | ||
1445 | int wret = 0; | ||
1446 | int ret; | ||
1447 | int pid; | ||
1448 | |||
1449 | pid = fork(); | ||
1450 | if (pid < 0) | ||
1451 | return -1; | ||
1452 | |||
1453 | if (!pid) { | ||
1454 | ret = __test__rdpmc(); | ||
1455 | |||
1456 | exit(ret); | ||
1457 | } | ||
1458 | |||
1459 | wret = waitpid(pid, &status, 0); | ||
1460 | if (wret < 0 || status) | ||
1461 | return -1; | ||
1462 | |||
1463 | return 0; | ||
1464 | } | ||
1465 | |||
1466 | #endif | ||
1467 | |||
1299 | static struct test { | 1468 | static struct test { |
1300 | const char *desc; | 1469 | const char *desc; |
1301 | int (*func)(void); | 1470 | int (*func)(void); |
@@ -1320,6 +1489,12 @@ static struct test { | |||
1320 | .desc = "parse events tests", | 1489 | .desc = "parse events tests", |
1321 | .func = test__parse_events, | 1490 | .func = test__parse_events, |
1322 | }, | 1491 | }, |
1492 | #if defined(__x86_64__) || defined(__i386__) | ||
1493 | { | ||
1494 | .desc = "x86 rdpmc test", | ||
1495 | .func = test__rdpmc, | ||
1496 | }, | ||
1497 | #endif | ||
1323 | { | 1498 | { |
1324 | .desc = "Validate PERF_RECORD_* events & perf_sample fields", | 1499 | .desc = "Validate PERF_RECORD_* events & perf_sample fields", |
1325 | .func = test__PERF_RECORD, | 1500 | .func = test__PERF_RECORD, |
@@ -1412,7 +1587,5 @@ int cmd_test(int argc, const char **argv, const char *prefix __used) | |||
1412 | if (symbol__init() < 0) | 1587 | if (symbol__init() < 0) |
1413 | return -1; | 1588 | return -1; |
1414 | 1589 | ||
1415 | setup_pager(); | ||
1416 | |||
1417 | return __cmd_test(argc, argv); | 1590 | return __cmd_test(argc, argv); |
1418 | } | 1591 | } |