|
Packit |
577717 |
/* $Id: virtual.c,v 1.22.2.10 2009/01/23 20:25:42 mikpe Exp $
|
|
Packit |
577717 |
* Library interface to virtual per-process performance counters.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Copyright (C) 1999-2009 Mikael Pettersson
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#include <stdio.h>
|
|
Packit |
577717 |
#include <stdlib.h>
|
|
Packit |
577717 |
#include <string.h>
|
|
Packit |
577717 |
#include <errno.h>
|
|
Packit |
577717 |
#include <unistd.h>
|
|
Packit |
577717 |
#include <sys/mman.h>
|
|
Packit |
577717 |
#include <sys/ioctl.h>
|
|
Packit |
577717 |
#include <fcntl.h>
|
|
Packit |
577717 |
#include "libperfctr.h"
|
|
Packit |
577717 |
#include "marshal.h"
|
|
Packit |
577717 |
#include "arch.h"
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* Code to open (with or without creation) per-process perfctrs,
|
|
Packit |
577717 |
* using the ioctl(dev_perfctr_fd, VPERFCTR_{CREAT,OPEN}, pid) API.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int _vperfctr_open_pid(int pid, int try_creat)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int dev_perfctr_fd, fd;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
dev_perfctr_fd = open("/dev/perfctr", O_RDONLY);
|
|
Packit |
577717 |
if (dev_perfctr_fd < 0)
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
if (try_creat)
|
|
Packit |
577717 |
fd = ioctl(dev_perfctr_fd, VPERFCTR_CREAT, pid);
|
|
Packit |
577717 |
else
|
|
Packit |
577717 |
fd = ioctl(dev_perfctr_fd, VPERFCTR_OPEN, pid);
|
|
Packit |
577717 |
close(dev_perfctr_fd);
|
|
Packit |
577717 |
if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
|
|
Packit |
577717 |
perror("fcntl");
|
|
Packit |
577717 |
return fd;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* Operations using raw kernel handles, basically just open()/ioctl() wrappers.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int _vperfctr_open(int creat)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return _vperfctr_open_pid(0, creat);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int _vperfctr_control(int fd, const struct vperfctr_control *control)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return perfctr_ioctl_w(fd, VPERFCTR_CONTROL, control, &vperfctr_control_sdesc);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int _vperfctr_read_control(int fd, struct vperfctr_control *control)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return perfctr_ioctl_r(fd, VPERFCTR_READ_CONTROL, control, &vperfctr_control_sdesc);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int _vperfctr_read_sum(int fd, struct perfctr_sum_ctrs *sum)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return perfctr_ioctl_r(fd, VPERFCTR_READ_SUM, sum, &perfctr_sum_ctrs_sdesc);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* Operations using library objects.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
struct vperfctr {
|
|
Packit |
577717 |
/* XXX: point to &vperfctr_state.cpu_state instead? */
|
|
Packit |
577717 |
volatile const struct vperfctr_state *kstate;
|
|
Packit |
577717 |
int fd;
|
|
Packit |
577717 |
unsigned char have_rdpmc;
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int vperfctr_open_pid(int pid, struct vperfctr *perfctr, unsigned int mode)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int fd, creat;
|
|
Packit |
577717 |
struct perfctr_info info;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (mode == 0)
|
|
Packit |
577717 |
creat = 0;
|
|
Packit |
577717 |
else if (mode == VPERFCTR_OPEN_CREAT_EXCL)
|
|
Packit |
577717 |
creat = 1;
|
|
Packit |
577717 |
else {
|
|
Packit |
577717 |
errno = EINVAL;
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
fd = _vperfctr_open_pid(pid, creat);
|
|
Packit |
577717 |
if (fd < 0)
|
|
Packit |
577717 |
goto out_perfctr;
|
|
Packit |
577717 |
perfctr->fd = fd;
|
|
Packit |
577717 |
if (perfctr_abi_check_fd(perfctr->fd) < 0)
|
|
Packit |
577717 |
goto out_fd;
|
|
Packit |
577717 |
if (perfctr_info(perfctr->fd, &info) < 0)
|
|
Packit |
577717 |
goto out_fd;
|
|
Packit |
577717 |
perfctr->have_rdpmc = (info.cpu_features & PERFCTR_FEATURE_RDPMC) != 0;
|
|
Packit |
577717 |
perfctr->kstate = mmap(NULL, PAGE_SIZE, PROT_READ,
|
|
Packit |
577717 |
MAP_SHARED, perfctr->fd, 0);
|
|
Packit |
577717 |
if (perfctr->kstate != MAP_FAILED)
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
munmap((void*)perfctr->kstate, PAGE_SIZE);
|
|
Packit |
577717 |
out_fd:
|
|
Packit |
577717 |
if (creat)
|
|
Packit |
577717 |
vperfctr_unlink(perfctr);
|
|
Packit |
577717 |
close(perfctr->fd);
|
|
Packit |
577717 |
out_perfctr:
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
struct vperfctr *vperfctr_open_mode(unsigned int mode)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct vperfctr *perfctr;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
perfctr = malloc(sizeof(*perfctr));
|
|
Packit |
577717 |
if (perfctr) {
|
|
Packit |
577717 |
if (vperfctr_open_pid(0, perfctr, mode) == 0)
|
|
Packit |
577717 |
return perfctr;
|
|
Packit |
577717 |
free(perfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return NULL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
struct vperfctr *vperfctr_open(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return vperfctr_open_mode(VPERFCTR_OPEN_CREAT_EXCL);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int vperfctr_info(const struct vperfctr *vperfctr, struct perfctr_info *info)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return perfctr_info(vperfctr->fd, info);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
struct perfctr_cpus_info *vperfctr_cpus_info(const struct vperfctr *vperfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return perfctr_cpus_info(vperfctr->fd);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#if (__GNUC__ < 2) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
|
|
Packit |
577717 |
#define __builtin_expect(x, expected_value) (x)
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define likely(x) __builtin_expect((x),1)
|
|
Packit |
577717 |
#define unlikely(x) __builtin_expect((x),0)
|
|
Packit |
577717 |
|
|
Packit |
577717 |
unsigned long long vperfctr_read_tsc(const struct vperfctr *self)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
#if defined(rdtscl)
|
|
Packit |
577717 |
unsigned long long sum;
|
|
Packit |
577717 |
unsigned int tsc0, tsc1, now;
|
|
Packit |
577717 |
volatile const struct vperfctr_state *kstate;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
kstate = self->kstate;
|
|
Packit |
577717 |
if (likely(kstate->cpu_state.cstatus != 0)) {
|
|
Packit |
577717 |
tsc0 = kstate->cpu_state.tsc_start;
|
|
Packit |
577717 |
retry:
|
|
Packit |
577717 |
rdtscl(now);
|
|
Packit |
577717 |
sum = kstate->cpu_state.tsc_sum;
|
|
Packit |
577717 |
tsc1 = kstate->cpu_state.tsc_start;
|
|
Packit |
577717 |
if (likely(tsc1 == tsc0))
|
|
Packit |
577717 |
return sum += (now - tsc0);
|
|
Packit |
577717 |
tsc0 = tsc1;
|
|
Packit |
577717 |
goto retry; /* better gcc code than with a do{}while() loop */
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return kstate->cpu_state.tsc_sum;
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
struct perfctr_sum_ctrs sum_ctrs;
|
|
Packit |
577717 |
if (_vperfctr_read_sum(self->fd, &sum_ctrs) < 0)
|
|
Packit |
577717 |
perror(__FUNCTION__);
|
|
Packit |
577717 |
return sum_ctrs.tsc;
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
unsigned long long vperfctr_read_pmc(const struct vperfctr *self, unsigned i)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct perfctr_sum_ctrs sum_ctrs;
|
|
Packit |
577717 |
#if defined(rdpmcl)
|
|
Packit |
577717 |
unsigned long long sum;
|
|
Packit |
577717 |
unsigned int start, now;
|
|
Packit |
577717 |
unsigned int tsc0, tsc1;
|
|
Packit |
577717 |
volatile const struct vperfctr_state *kstate;
|
|
Packit |
577717 |
unsigned int cstatus;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
kstate = self->kstate;
|
|
Packit |
577717 |
cstatus = kstate->cpu_state.cstatus;
|
|
Packit |
577717 |
/* gcc 3.0 generates crap code for likely(E1 && E2) :-( */
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(cstatus) && vperfctr_has_rdpmc(self)) {
|
|
Packit |
577717 |
tsc0 = kstate->cpu_state.tsc_start;
|
|
Packit |
577717 |
retry:
|
|
Packit |
577717 |
rdpmcl(kstate->cpu_state.pmc[i].map, now);
|
|
Packit |
577717 |
start = kstate->cpu_state.pmc[i].start;
|
|
Packit |
577717 |
sum = kstate->cpu_state.pmc[i].sum;
|
|
Packit |
577717 |
tsc1 = kstate->cpu_state.tsc_start;
|
|
Packit |
577717 |
if (likely(tsc1 == tsc0)) {
|
|
Packit |
577717 |
return sum += (now - start);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
tsc0 = tsc1;
|
|
Packit |
577717 |
goto retry;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
if (_vperfctr_read_sum(self->fd, &sum_ctrs) < 0)
|
|
Packit |
577717 |
perror(__FUNCTION__);
|
|
Packit |
577717 |
return sum_ctrs.pmc[i];
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int vperfctr_read_ctrs_slow(const struct vperfctr *vperfctr,
|
|
Packit |
577717 |
struct perfctr_sum_ctrs *sum)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return _vperfctr_read_sum(vperfctr->fd, sum);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int vperfctr_read_ctrs(const struct vperfctr *self,
|
|
Packit |
577717 |
struct perfctr_sum_ctrs *sum)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
#if defined(rdtscl) && defined(rdpmcl)
|
|
Packit |
577717 |
unsigned int tsc0, now;
|
|
Packit |
577717 |
unsigned int cstatus, nrctrs;
|
|
Packit |
577717 |
volatile const struct vperfctr_state *kstate;
|
|
Packit |
577717 |
int i;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* Fast path is impossible if the TSC isn't being sampled (bad idea,
|
|
Packit |
577717 |
but on WinChip you don't have a choice), or at least one PMC is
|
|
Packit |
577717 |
enabled but the CPU doesn't have RDPMC. */
|
|
Packit |
577717 |
kstate = self->kstate;
|
|
Packit |
577717 |
cstatus = kstate->cpu_state.cstatus;
|
|
Packit |
577717 |
nrctrs = perfctr_cstatus_nrctrs(cstatus);
|
|
Packit |
577717 |
if (perfctr_cstatus_has_tsc(cstatus) && (!nrctrs || vperfctr_has_rdpmc(self))) {
|
|
Packit |
577717 |
retry:
|
|
Packit |
577717 |
tsc0 = kstate->cpu_state.tsc_start;
|
|
Packit |
577717 |
rdtscl(now);
|
|
Packit |
577717 |
sum->tsc = kstate->cpu_state.tsc_sum + (now - tsc0);
|
|
Packit |
577717 |
for(i = nrctrs; --i >= 0;) {
|
|
Packit |
577717 |
rdpmcl(kstate->cpu_state.pmc[i].map, now);
|
|
Packit |
577717 |
sum->pmc[i] = kstate->cpu_state.pmc[i].sum + (now - kstate->cpu_state.pmc[i].start);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if (likely(tsc0 == kstate->cpu_state.tsc_start))
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
goto retry;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
return vperfctr_read_ctrs_slow(self, sum);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int vperfctr_read_state(const struct vperfctr *self, struct perfctr_sum_ctrs *sum,
|
|
Packit |
577717 |
struct vperfctr_control *control)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if (_vperfctr_read_sum(self->fd, sum) < 0)
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
/* For historical reasons, control may be NULL. */
|
|
Packit |
577717 |
if (control && _vperfctr_read_control(self->fd, control) < 0)
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int vperfctr_control(const struct vperfctr *perfctr,
|
|
Packit |
577717 |
struct vperfctr_control *control)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return _vperfctr_control(perfctr->fd, control);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int vperfctr_stop(const struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct vperfctr_control control;
|
|
Packit |
577717 |
memset(&control, 0, sizeof control);
|
|
Packit |
577717 |
return _vperfctr_control(perfctr->fd, &control);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int vperfctr_is_running(const struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return perfctr->kstate->cpu_state.cstatus != 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int vperfctr_iresume(const struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return ioctl(perfctr->fd, VPERFCTR_IRESUME, NULL);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int vperfctr_unlink(const struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return ioctl(perfctr->fd, VPERFCTR_UNLINK, NULL);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void vperfctr_close(struct vperfctr *perfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
munmap((void*)perfctr->kstate, PAGE_SIZE);
|
|
Packit |
577717 |
close(perfctr->fd);
|
|
Packit |
577717 |
free(perfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/*
|
|
Packit |
577717 |
* Operations on other processes' virtual-mode perfctrs.
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
struct rvperfctr {
|
|
Packit |
577717 |
struct vperfctr vperfctr; /* must be first for the close() operation */
|
|
Packit |
577717 |
int pid;
|
|
Packit |
577717 |
};
|
|
Packit |
577717 |
|
|
Packit |
577717 |
struct rvperfctr *rvperfctr_open(int pid)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct rvperfctr *rvperfctr;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
rvperfctr = malloc(sizeof(*rvperfctr));
|
|
Packit |
577717 |
if (rvperfctr) {
|
|
Packit |
577717 |
if (vperfctr_open_pid(pid, &rvperfctr->vperfctr, VPERFCTR_OPEN_CREAT_EXCL) == 0) {
|
|
Packit |
577717 |
rvperfctr->pid = pid;
|
|
Packit |
577717 |
return rvperfctr;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
free(rvperfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return NULL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int rvperfctr_pid(const struct rvperfctr *rvperfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return rvperfctr->pid;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int rvperfctr_info(const struct rvperfctr *rvperfctr, struct perfctr_info *info)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return vperfctr_info(&rvperfctr->vperfctr, info);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int rvperfctr_read_ctrs(const struct rvperfctr *rvperfctr,
|
|
Packit |
577717 |
struct perfctr_sum_ctrs *sum)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return vperfctr_read_ctrs_slow(&rvperfctr->vperfctr, sum);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int rvperfctr_read_state(const struct rvperfctr *rvperfctr,
|
|
Packit |
577717 |
struct perfctr_sum_ctrs *sum,
|
|
Packit |
577717 |
struct vperfctr_control *control)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return vperfctr_read_state(&rvperfctr->vperfctr, sum, control);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int rvperfctr_control(const struct rvperfctr *rvperfctr,
|
|
Packit |
577717 |
struct vperfctr_control *control)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return vperfctr_control(&rvperfctr->vperfctr, control);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int rvperfctr_stop(const struct rvperfctr *rvperfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return vperfctr_stop(&rvperfctr->vperfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int rvperfctr_iresume(const struct rvperfctr *rvperfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return vperfctr_iresume(&rvperfctr->vperfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int rvperfctr_unlink(const struct rvperfctr *rvperfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
return vperfctr_unlink(&rvperfctr->vperfctr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void rvperfctr_close(struct rvperfctr *rvperfctr)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
/* this relies on offsetof(struct rvperfctr, vperfctr) == 0 */
|
|
Packit |
577717 |
vperfctr_close(&rvperfctr->vperfctr);
|
|
Packit |
577717 |
}
|