Blame | Last modification | View Log | Download | RSS feed
/* ACC -- Automatic Compiler Configuration
Copyright (C) 1996-2004 Markus Franz Xaver Johannes Oberhumer
All Rights Reserved.
This software is a copyrighted work licensed under the terms of
the GNU General Public License. Please consult the file "ACC_LICENSE"
for details.
Markus F.X.J. Oberhumer
<markus@oberhumer.com>
http://www.oberhumer.com/
*/
#define __ACCLIB_PERFCTR_CH_INCLUDED 1
#if !defined(ACCLIB_PUBLIC)
# define ACCLIB_PUBLIC(r,f) r __ACCLIB_FUNCNAME(f)
#endif
#if (ACC_OS_POSIX_LINUX)
/* see http://user.it.uu.se/~mikpe/linux/perfctr/ */
#if defined(__cplusplus)
extern "C" {
#include <libperfctr.h>
}
#else
#include <libperfctr.h>
#endif
#endif
/*************************************************************************
//
**************************************************************************/
ACCLIB_PUBLIC(int, acc_perfctr_open) (acc_perfctr_handle_p h)
{
memset(h, 0, sizeof(*h));
#if (ACC_OS_POSIX_LINUX)
{
struct vperfctr* handle;
struct perfctr_info info;
struct vperfctr_control control;
struct perfctr_cpu_control* const cc = &control.cpu_control;
/* open */
handle = vperfctr_open();
if (!handle) goto error;
/* get info */
if (vperfctr_info(handle, &info) < 0) goto error;
h->cpu_type = info.cpu_type;
h->cpu_features = info.cpu_features;
h->cpu_khz = info.cpu_khz;
h->cpu_nrctrs = perfctr_info_nrctrs(&info);
h->cpu_name = perfctr_info_cpu_name(&info);
/* setup control */
memset(&control, 0, sizeof(control));
switch (h->cpu_type) {
#if (ACC_ARCH_IA32)
case PERFCTR_X86_WINCHIP_C6:
case PERFCTR_X86_WINCHIP_2:
break; /* no working TSC available */
case PERFCTR_X86_AMD_K7:
#endif
#if (ACC_ARCH_AMD64 || ACC_ARCH_IA32)
case PERFCTR_X86_AMD_K8:
case PERFCTR_X86_AMD_K8C:
cc->tsc_on = 1; cc->nractrs = 2;
/* event 0xC0 (RETIRED_INSNS), count at CPL > 0, Enable */
cc->pmc_map[0] = 0;
cc->evntsel[0] = 0xC0 | (1 << 16) | (1 << 22);
/* event 0xC1 (RETIRED_OPS), count at CPL > 0, Enable */
cc->pmc_map[1] = 1;
cc->evntsel[1] = 0xC1 | (1 << 16) | (1 << 22);
break;
#endif
default:
cc->tsc_on = 1;
break;
}
if (cc->nractrs > h->cpu_nrctrs) cc->nractrs = h->cpu_nrctrs;
if (vperfctr_control(handle, &control) < 0) goto error;
/* success */
h->h = (void*) handle;
return 0;
error:
if (handle) {
vperfctr_stop(handle);
vperfctr_close(handle);
}
}
#endif
return -1;
}
ACCLIB_PUBLIC(int, acc_perfctr_close) (acc_perfctr_handle_p h)
{
if (h->h) {
#if (ACC_OS_POSIX_LINUX)
struct vperfctr* handle = (struct vperfctr*) h->h;
vperfctr_stop(handle);
vperfctr_close(handle);
#endif
h->h = 0;
}
return 0;
}
/*************************************************************************
//
**************************************************************************/
ACCLIB_PUBLIC(void, acc_perfctr_read) (acc_perfctr_handle_p h, acc_perfctr_clock_p c)
{
if (h->h) {
#if (ACC_OS_POSIX_LINUX)
struct vperfctr* handle = (struct vperfctr*) h->h;
vperfctr_read_ctrs(handle, (struct perfctr_sum_ctrs*) c);
#else
memset(c, 0, sizeof(*c));
#endif
} else
memset(c, 0, sizeof(*c));
}
/*************************************************************************
//
**************************************************************************/
ACCLIB_PUBLIC(double, acc_perfctr_get_elapsed) (acc_perfctr_handle_p h, const acc_perfctr_clock_p start, const acc_perfctr_clock_p stop)
{
#if (ACC_OS_POSIX_LINUX)
acc_uint64l_t tsc = stop->tsc - start->tsc;
return ((double)tsc / h->cpu_khz) / 1000.0;
#else
ACC_UNUSED(h); ACC_UNUSED(start); ACC_UNUSED(stop); return 0;
#endif
}
ACCLIB_PUBLIC(double, acc_perfctr_get_elapsed_tsc) (acc_perfctr_handle_p h, acc_uint64l_t tsc)
{
#if (ACC_OS_POSIX_LINUX)
return ((double)tsc / h->cpu_khz) / 1000.0;
#else
ACC_UNUSED(h); ACC_UNUSED(tsc); return 0;
#endif
}
/*
vi:ts=4:et
*/