Implement a simple test for the self-monitoring data from the mmap-control page. 6: x86 rdpmc test: Ok 0: 6042 2481 1: 60042 23669 2: 605410 245588 3: 6060818 2396231 XXX: come up with logic to automagically turn these numbers into OK/FAIL. Cc: Stephane Eranian Cc: Arun Sharma Signed-off-by: Peter Zijlstra --- tools/perf/builtin-test.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) Index: linux-2.6/tools/perf/builtin-test.c =================================================================== --- linux-2.6.orig/tools/perf/builtin-test.c +++ linux-2.6/tools/perf/builtin-test.c @@ -14,6 +14,8 @@ #include "util/thread_map.h" #include "../../include/linux/hw_breakpoint.h" +#include + static long page_size; static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym) @@ -841,6 +843,127 @@ static int test__parse_events(void) return ret; } + +#if defined(__x86_64__) || defined(__i386__) + +#define barrier() asm volatile("" ::: "memory") + +static u64 rdpmc(unsigned int counter) +{ + unsigned int low, high; + + asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter)); + + return low | ((u64)high) << 32; +} + +static u64 rdtsc(void) +{ + unsigned int low, high; + + asm volatile("rdtsc" : "=a" (low), "=d" (high)); + + return low | ((u64)high) << 32; +} + +static u64 mmap_read_self(void *addr, u64 *enabled, u64 *running) +{ + struct perf_event_mmap_page *pc = addr; + u32 seq; + u64 count, delta; + + do { +again: + seq = pc->lock; + barrier(); + if (seq & 1) + goto again; + + if ((enabled || running) && pc->time_mult) { + u64 cyc = rdtsc(); + u64 rem, quot; + + quot = (cyc >> pc->time_shift); + rem = cyc & ((1 << pc->time_shift) - 1); + delta = pc->time_offset + + quot * pc->time_mult + + ((rem * pc->time_mult) >> pc->time_shift); + } + + if (enabled) + *enabled = pc->time_enabled + delta; + + if (running) + *running = pc->time_running + delta; + + if (pc->index) { + count = rdpmc(pc->index - 1); + count += pc->offset; + } else + goto fail; + + barrier(); + } while (pc->lock != seq); + + return count; + +fail: + /* + * XXX do read() here + */ + printf("FAIL FAIL FAIL\n"); + return 0; +} + +static int test__rdpmc(void) +{ + volatile int tmp = 0; + int loops = 1000; + int n, i; + int fd; + void *addr; + struct perf_event_attr attr = { + .type = PERF_TYPE_HARDWARE, + .config = PERF_COUNT_HW_INSTRUCTIONS, + }; + + + fd = sys_perf_event_open(&attr, 0, -1, -1, 0); + if (fd < 0) { + die("Error: sys_perf_event_open() syscall returned " + "with %d (%s)\n", fd, strerror(errno)); + } + + addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); + if (addr == (void *)(-1)) { + die("Error: mmap() syscall returned " + "with (%s)\n", strerror(errno)); + } + + for (n = 0; n < 4; n++) { + + u64 stamp, now; + u64 stamp2, now2; + + stamp = mmap_read_self(addr, NULL, &stamp2); + + for (i = 0; i < loops; i++) + tmp++; + + now = mmap_read_self(addr, NULL, &now2); + loops *= 10; + + printf("%d: %Lu %Lu\n", n, (unsigned long long)(now - stamp), + (unsigned long long)(now2 - stamp2)); + } + + munmap(addr, page_size); + close(fd); + + return 0; +} +#endif + static struct test { const char *desc; int (*func)(void); @@ -865,6 +988,12 @@ static struct test { .desc = "parse events tests", .func = test__parse_events, }, +#if defined(__x86_64__) || defined(__i386__) + { + .desc = "x86 rdpmc test", + .func = test__rdpmc, + }, +#endif { .func = NULL, }, -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/