|
Packit |
577717 |
/* $Id: misc.c,v 1.26 2006/08/27 07:34:41 mikpe Exp $
|
|
Packit |
577717 |
* Miscellaneous perfctr operations.
|
|
Packit |
577717 |
*
|
|
Packit |
577717 |
* Copyright (C) 1999-2004 Mikael Pettersson
|
|
Packit |
577717 |
*/
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#include <sys/utsname.h>
|
|
Packit |
577717 |
#include <errno.h>
|
|
Packit |
577717 |
#include <stddef.h>
|
|
Packit |
577717 |
#include <stdio.h>
|
|
Packit |
577717 |
#include <stdlib.h>
|
|
Packit |
577717 |
#include <string.h>
|
|
Packit |
577717 |
#include "libperfctr.h"
|
|
Packit |
577717 |
#include "arch.h"
|
|
Packit |
577717 |
|
|
Packit |
577717 |
#define SYS_CLASS_PERFCTR "/sys/class/perfctr/"
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int read_uint(const char *path, unsigned int *dst)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
FILE *fp;
|
|
Packit |
577717 |
int ret;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
fp = fopen(path, "r");
|
|
Packit |
577717 |
if (!fp)
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
ret = fscanf(fp, "%i", (int*)dst);
|
|
Packit |
577717 |
fclose(fp);
|
|
Packit |
577717 |
return (ret == 1) ? 0 : -1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int read_string(const char *path, char *dst, size_t dstlen)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
FILE *fp;
|
|
Packit |
577717 |
int len;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
fp = fopen(path, "r");
|
|
Packit |
577717 |
if (!fp)
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
len = 0;
|
|
Packit |
577717 |
while (len < dstlen) {
|
|
Packit |
577717 |
int ch = fgetc(fp);
|
|
Packit |
577717 |
switch (ch) {
|
|
Packit |
577717 |
case '\n':
|
|
Packit |
577717 |
case '\0':
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
default:
|
|
Packit |
577717 |
dst[len++] = ch;
|
|
Packit |
577717 |
continue;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
break;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if (len < dstlen)
|
|
Packit |
577717 |
dst[len] = '\0';
|
|
Packit |
577717 |
fclose(fp);
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int read_cpumask(const char *path, struct perfctr_cpu_mask *mask)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int nrwords;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (!mask)
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
/* XXX: fake */
|
|
Packit |
577717 |
nrwords = mask->nrwords;
|
|
Packit |
577717 |
mask->nrwords = 1;
|
|
Packit |
577717 |
if (nrwords) {
|
|
Packit |
577717 |
mask->mask[0] = 0;
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
} else {
|
|
Packit |
577717 |
errno = EOVERFLOW;
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static int read_info(struct perfctr_info *info)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int err = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (!info)
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
#if 0
|
|
Packit |
577717 |
err |= read_uint(SYS_CLASS_PERFCTR "abi_version", &info->abi_version);
|
|
Packit |
577717 |
#else
|
|
Packit |
577717 |
info->abi_version = PERFCTR_ABI_VERSION;
|
|
Packit |
577717 |
#endif
|
|
Packit |
577717 |
// err |= read_uint(SYS_CLASS_PERFCTR "cpu_type", &info->cpu_type);
|
|
Packit |
577717 |
err |= read_uint(SYS_CLASS_PERFCTR "cpu_features", &info->cpu_features);
|
|
Packit |
577717 |
err |= read_uint(SYS_CLASS_PERFCTR "cpu_khz", &info->cpu_khz);
|
|
Packit |
577717 |
err |= read_uint(SYS_CLASS_PERFCTR "tsc_to_cpu_mult", &info->tsc_to_cpu_mult);
|
|
Packit |
577717 |
err |= read_string(SYS_CLASS_PERFCTR "driver_version",
|
|
Packit |
577717 |
info->driver_version, sizeof(info->driver_version));
|
|
Packit |
577717 |
return err;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int _perfctr_get_state_user_offset(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int offset;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (read_uint(SYS_CLASS_PERFCTR "state_user_offset", &offset) != 0)
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
return offset;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int _sys_perfctr_info(int fd_unused,
|
|
Packit |
577717 |
struct perfctr_info *info,
|
|
Packit |
577717 |
struct perfctr_cpu_mask *cpus,
|
|
Packit |
577717 |
struct perfctr_cpu_mask *forbidden)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
int err = 0;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
err |= read_info(info);
|
|
Packit |
577717 |
err |= read_cpumask(SYS_CLASS_PERFCTR "cpus_online", cpus);
|
|
Packit |
577717 |
err |= read_cpumask(SYS_CLASS_PERFCTR "cpus_forbidden", forbidden);
|
|
Packit |
577717 |
return err;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int _perfctr_abi_check_fd(int fd, unsigned int user_abi_version)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct perfctr_info info;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if( _sys_perfctr_info(fd, &info, NULL, NULL) < 0 ) {
|
|
Packit |
577717 |
perror("perfctr_abi_check");
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if( (info.abi_version ^ user_abi_version) & 0xFF00FF00 ) {
|
|
Packit |
577717 |
fprintf(stderr, "Error: perfctr ABI major version mismatch: "
|
|
Packit |
577717 |
"driver ABI 0x%08X, user ABI 0x%08X\n",
|
|
Packit |
577717 |
info.abi_version, user_abi_version);
|
|
Packit |
577717 |
errno = EPROTO;
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
int perfctr_info(int fd, struct perfctr_info *info)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
if( _sys_perfctr_info(fd, info, NULL, NULL) < 0 )
|
|
Packit |
577717 |
return -1;
|
|
Packit |
577717 |
perfctr_info_cpu_init(info);
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
struct perfctr_cpus_info *perfctr_cpus_info(int fd)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct perfctr_cpu_mask dummy;
|
|
Packit |
577717 |
struct perfctr_cpus_info *info;
|
|
Packit |
577717 |
unsigned int cpu_mask_bytes;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
dummy.nrwords = 0;
|
|
Packit |
577717 |
if( _sys_perfctr_info(fd, NULL, &dummy, NULL) >= 0 ||
|
|
Packit |
577717 |
errno != EOVERFLOW ||
|
|
Packit |
577717 |
dummy.nrwords == 0 ) {
|
|
Packit |
577717 |
perror("PERFCTR_CPUS");
|
|
Packit |
577717 |
return NULL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
cpu_mask_bytes = offsetof(struct perfctr_cpu_mask, mask[dummy.nrwords]);
|
|
Packit |
577717 |
info = malloc(sizeof(struct perfctr_cpus_info) + 2*cpu_mask_bytes);
|
|
Packit |
577717 |
if( !info ) {
|
|
Packit |
577717 |
perror("malloc");
|
|
Packit |
577717 |
return NULL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
info->cpus = (struct perfctr_cpu_mask*)(info + 1);
|
|
Packit |
577717 |
info->cpus->nrwords = dummy.nrwords;
|
|
Packit |
577717 |
info->cpus_forbidden = (struct perfctr_cpu_mask*)((char*)(info + 1) + cpu_mask_bytes);
|
|
Packit |
577717 |
info->cpus_forbidden->nrwords = dummy.nrwords;
|
|
Packit |
577717 |
if( _sys_perfctr_info(fd, NULL, info->cpus, info->cpus_forbidden) < 0 ) {
|
|
Packit |
577717 |
perror("PERFCTR_CPUS");
|
|
Packit |
577717 |
free(info);
|
|
Packit |
577717 |
return NULL;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return info;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void perfctr_info_print(const struct perfctr_info *info)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
static const char * const features[] = { "rdpmc", "rdtsc", "pcint" };
|
|
Packit |
577717 |
int fi, comma;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
printf("abi_version\t\t0x%08X\n", info->abi_version);
|
|
Packit |
577717 |
printf("driver_version\t\t%s\n", info->driver_version);
|
|
Packit |
577717 |
printf("cpu_type\t\t%u (%s)\n", info->cpu_type, perfctr_info_cpu_name(info));
|
|
Packit |
577717 |
printf("cpu_features\t\t%#x (", info->cpu_features);
|
|
Packit |
577717 |
for(comma = 0, fi = 0; fi < sizeof features / sizeof features[0]; ++fi) {
|
|
Packit |
577717 |
unsigned fmask = 1 << fi;
|
|
Packit |
577717 |
if( info->cpu_features & fmask ) {
|
|
Packit |
577717 |
if( comma )
|
|
Packit |
577717 |
printf(",");
|
|
Packit |
577717 |
printf("%s", features[fi]);
|
|
Packit |
577717 |
comma = 1;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
printf(")\n");
|
|
Packit |
577717 |
printf("cpu_khz\t\t\t%u\n", info->cpu_khz);
|
|
Packit |
577717 |
printf("tsc_to_cpu_mult\t\t%u%s\n",
|
|
Packit |
577717 |
info->tsc_to_cpu_mult,
|
|
Packit |
577717 |
info->tsc_to_cpu_mult ? "" : " (unspecified, assume 1)");
|
|
Packit |
577717 |
printf("cpu_nrctrs\t\t%u\n", perfctr_info_nrctrs(info));
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
static void print_cpus(const struct perfctr_cpu_mask *cpus)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
unsigned int nrcpus, nr, i, cpumask, bitmask;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
printf("[");
|
|
Packit |
577717 |
nrcpus = 0;
|
|
Packit |
577717 |
for(i = 0; i < cpus->nrwords; ++i) {
|
|
Packit |
577717 |
cpumask = cpus->mask[i];
|
|
Packit |
577717 |
nr = i * 8 * sizeof(int);
|
|
Packit |
577717 |
for(bitmask = 1; cpumask != 0; ++nr, bitmask <<= 1) {
|
|
Packit |
577717 |
if( cpumask & bitmask ) {
|
|
Packit |
577717 |
cpumask &= ~bitmask;
|
|
Packit |
577717 |
if( nrcpus )
|
|
Packit |
577717 |
printf(",");
|
|
Packit |
577717 |
++nrcpus;
|
|
Packit |
577717 |
printf("%u", nr);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
printf("], total: %u\n", nrcpus);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
void perfctr_cpus_info_print(const struct perfctr_cpus_info *info)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
printf("cpus\t\t\t"); print_cpus(info->cpus);
|
|
Packit |
577717 |
printf("cpus_forbidden\t\t"); print_cpus(info->cpus_forbidden);
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
|
|
Packit |
577717 |
unsigned int perfctr_linux_version_code(void)
|
|
Packit |
577717 |
{
|
|
Packit |
577717 |
struct utsname utsname;
|
|
Packit |
577717 |
unsigned int version, patchlevel, sublevel;
|
|
Packit |
577717 |
|
|
Packit |
577717 |
if (uname(&utsname) < 0) {
|
|
Packit |
577717 |
fprintf(stderr, "uname: %s\n", strerror(errno));
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
if (sscanf(utsname.release, "%u.%u.%u", &version, &patchlevel, &sublevel) != 3) {
|
|
Packit |
577717 |
fprintf(stderr, "uname: unexpected release '%s'\n", utsname.release);
|
|
Packit |
577717 |
return 0;
|
|
Packit |
577717 |
}
|
|
Packit |
577717 |
return PERFCTR_KERNEL_VERSION(version,patchlevel,sublevel);
|
|
Packit |
577717 |
}
|