blob: 8f0c9431363664447abde64b69fcdd33349c4fec [file] [log] [blame]
#define _GNU_SOURCE
#include <stdio.h>
#include <sys/prctl.h>
#include <asm/prctl.h>
#include <sys/ucontext.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <err.h>
#include <linux/perf_event.h>
#include <sys/mman.h>
#include <sys/syscall.h>
static volatile sig_atomic_t got_sigbus = 0;
static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
int flags)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO | flags;
sigemptyset(&sa.sa_mask);
if (sigaction(sig, &sa, 0))
err(1, "sigaction");
}
#ifdef __x86_64__
# define REG_IP REG_RIP
#else
# define REG_IP REG_EIP
#endif
extern const char rdpmc_insn[];
static void sigsegv(int sig, siginfo_t *si, void *ctx_void)
{
ucontext_t *ctx = (ucontext_t*)ctx_void;
if (ctx->uc_mcontext.gregs[REG_IP] == (unsigned long)rdpmc_insn) {
got_sigbus = 1;
ctx->uc_mcontext.gregs[REG_IP] += 2;
} else {
raise(SIGABRT);
}
}
__attribute__((noinline, noclone)) static void do_rdpmc(unsigned int ecx)
{
unsigned int eax, edx;
got_sigbus = 0;
asm volatile ("rdpmc_insn: rdpmc" : "=a" (eax), "=d" (edx) : "c" (ecx));
if (got_sigbus)
return;
printf("PMC 0x%08X: 0x%08X%08X\n", ecx, edx, eax);
}
static int perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
int cpu, int group_fd, unsigned long flags)
{
return syscall(__NR_perf_event_open, hw_event, pid, cpu,
group_fd, flags);
}
static void* map_one(void)
{
struct perf_event_attr pe;
int fd;
memset(&pe, 0, sizeof(struct perf_event_attr));
pe.type = PERF_TYPE_HARDWARE;
pe.size = sizeof(struct perf_event_attr);
pe.config = PERF_COUNT_HW_INSTRUCTIONS;
pe.disabled = 0;
pe.exclude_kernel = 1;
pe.exclude_hv = 1;
fd = perf_event_open(&pe, getpid(), -1, -1, 0);
if (fd == -1)
err(1, "perf_event_open");
return mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, 0);
}
int main(int argc, char **argv)
{
if (argc == 2 && !strcmp(argv[1], "try_harder")) {
map_one();
munmap(map_one(), 4096);
}
sethandler(SIGSEGV, sigsegv, 0);
for (unsigned int i = 0; i < 4096; i++)
do_rdpmc(i);
for (unsigned int i = 0; i < 4096; i++)
do_rdpmc(i | (1<<30));
return 0;
}