blob: a118c5f9b78c5327efcf1bceedce5dd400de25ba [file] [log] [blame]
/* Copyright (C) 2003,2004 Andi Kleen, SuSE Labs.
numamon is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; version
2.
numamon is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should find a copy of v2 of the GNU General Public License somewhere
on your Linux system; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Display some numa statistics collected by the CPU.
Opteron specific. Also not reliable because the counters
are not quite correct in hardware. */
#define _LARGE_FILE_SOURCE 1
#define _GNU_SOURCE 1
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <dirent.h>
#include <getopt.h>
#include <stdarg.h>
#include <stdlib.h>
#include <sys/fcntl.h>
enum { LOCALLOCAL = 0, LOCALREMOTE = 1, REMOTELOCAL = 2 };
static int mem[] = { [LOCALLOCAL] = 0xa8, [LOCALREMOTE] = 0x98, [REMOTELOCAL] = 0x68 };
static int io[] = { [LOCALLOCAL] = 0xa4, [LOCALREMOTE] = 0x94, [REMOTELOCAL] = 0x64 };
static int *masks = mem;
#define err(x) perror(x),exit(1)
#define PERFEVTSEL0 0xc0010000
#define PERFEVTSEL1 0xc0010001
#define PERFEVTSEL2 0xc0010002
#define PERFEVTSEL3 0xc0010003
#define PERFCTR0 0xc0010004
#define PERFCTR1 0xc0010005
#define PERFCTR2 0xc0010006
#define PERFCTR3 0xc0010007
#define EVENT 0xe9
#define PERFEVTSEL_EN (1 << 22)
#define PERFEVTSEL_OS (1 << 17)
#define PERFEVTSEL_USR (1 << 16)
#define BASE (EVENT | PERFEVTSEL_EN | PERFEVTSEL_OS | PERFEVTSEL_USR)
#define MAXCPU 8
int force = 0;
int msrfd[MAXCPU];
int delay;
int absolute;
char *cfilter;
int verbose;
void usage(void);
void Vprintf(char *fmt, ...)
{
va_list ap;
va_start(ap,fmt);
if (verbose)
vfprintf(stderr,fmt,ap);
va_end(ap);
}
unsigned long long rdmsr(int cpu, unsigned long msr)
{
unsigned long long val;
if (pread(msrfd[cpu], &val, 8, msr) != 8) {
fprintf(stderr, "rdmsr of %lx failed: %s\n", msr, strerror(errno));
exit(1);
}
return val;
}
void wrmsr(int cpu, unsigned long msr, unsigned long long value)
{
if (pwrite(msrfd[cpu], &value, 8, msr) != 8) {
fprintf(stderr, "wdmsr of %lx failed: %s\n", msr, strerror(errno));
exit(1);
}
}
int cpufilter(int cpu)
{
long num;
char *end;
char *s;
if (!cfilter)
return 1;
for (s = cfilter;;) {
num = strtoul(s, &end, 0);
if (end == s)
usage();
if (cpu == num)
return 1;
if (*end == ',')
s = end+1;
else if (*end == 0)
break;
else
usage();
}
return 0;
}
void checkcounter(int cpu, int clear)
{
int i;
for (i = 1; i < 4; i++) {
int clear_this = clear;
unsigned long long evtsel = rdmsr(cpu, PERFEVTSEL0 + i);
Vprintf("%d: %x %Lx\n", cpu, PERFEVTSEL0 + i, evtsel);
if (!(evtsel & PERFEVTSEL_EN)) {
Vprintf("reinit %d\n", cpu);
wrmsr(cpu, PERFEVTSEL0 + i, BASE | masks[i - 1]);
clear_this = 1;
} else if (evtsel == (BASE | (masks[i-1] << 8))) {
/* everything fine */
} else if (force) {
Vprintf("reinit force %d\n", cpu);
wrmsr(cpu, PERFEVTSEL0 + i, BASE | (masks[i - 1] << 8));
clear_this = 1;
} else {
fprintf(stderr, "perfctr %d cpu %d already used with %Lx\n",
i, cpu, evtsel);
fprintf(stderr, "Consider using -f if you know what you're doing.\n");
exit(1);
}
if (clear_this) {
Vprintf("clearing %d\n", cpu);
wrmsr(cpu, PERFCTR0 + i, 0);
}
}
}
void setup(int clear)
{
DIR *dir;
struct dirent *d;
int numcpus = 0;
memset(msrfd, -1, sizeof(msrfd));
dir = opendir("/dev/cpu");
if (!dir)
err("cannot open /dev/cpu");
while ((d = readdir(dir)) != NULL) {
char buf[64];
char *end;
long cpunum = strtoul(d->d_name, &end, 0);
if (*end != 0)
continue;
if (cpunum > MAXCPU) {
fprintf(stderr, "too many cpus %ld %s\n", cpunum, d->d_name);
continue;
}
if (!cpufilter(cpunum))
continue;
snprintf(buf, 63, "/dev/cpu/%ld/msr", cpunum);
msrfd[cpunum] = open64(buf, O_RDWR);
if (msrfd[cpunum] < 0)
continue;
numcpus++;
checkcounter(cpunum, clear);
}
closedir(dir);
if (numcpus == 0) {
fprintf(stderr, "No CPU found using MSR driver.\n");
exit(1);
}
}
void printf_padded(int pad, char *fmt, ...)
{
char buf[pad + 1];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, pad, fmt, ap);
printf("%-*s", pad, buf);
va_end(ap);
}
void print_header(void)
{
printf_padded(4, "CPU ");
printf_padded(16, "LOCAL");
printf_padded(16, "LOCAL->REMOTE");
printf_padded(16, "REMOTE->LOCAL");
putchar('\n');
}
void print_cpu(int cpu)
{
int i;
static unsigned long long lastval[4];
printf_padded(4, "%d", cpu);
for (i = 1; i < 4; i++) {
unsigned long long val = rdmsr(cpu, PERFCTR0 + i);
if (absolute)
printf_padded(16, "%Lu", val);
else
printf_padded(16, "%Lu", val - lastval[i]);
lastval[i] = val;
}
putchar('\n');
}
void dumpall(void)
{
int cnt = 0;
int cpu;
print_header();
for (;;) {
for (cpu = 0; cpu < MAXCPU; ++cpu) {
if (msrfd[cpu] < 0)
continue;
print_cpu(cpu);
}
if (!delay)
break;
sleep(delay);
if (++cnt > 40) {
cnt = 0;
print_header();
}
}
}
void checkk8(void)
{
char *line = NULL;
size_t size = 0;
int bad = 0;
FILE *f = fopen("/proc/cpuinfo", "r");
if (!f)
return;
while (getline(&line, &size, f) > 0) {
if (!strncmp("vendor_id", line, 9)) {
if (!strstr(line, "AMD"))
bad++;
}
if (!strncmp("cpu family", line, 10)) {
char *s = line + strcspn(line,":");
int family;
if (*s == ':') ++s;
family = strtoul(s, NULL, 0);
if (family != 15)
bad++;
}
}
if (bad) {
printf("not a opteron cpu\n");
exit(1);
}
free(line);
fclose(f);
}
void usage(void)
{
fprintf(stderr, "usage: numamon [args] [delay]\n");
fprintf(stderr, " -f forcibly overwrite counters\n");
fprintf(stderr, " -i count IO (default memory)\n");
fprintf(stderr, " -a print absolute counter values (with delay)\n");
fprintf(stderr, " -s setup counters and exit\n");
fprintf(stderr, " -c clear counters and exit\n");
fprintf(stderr, " -m Print memory traffic (default)\n");
fprintf(stderr, " -C cpu{,cpu} only print for cpus\n");
fprintf(stderr, " -v Be verbose\n");
exit(1);
}
int main(int ac, char **av)
{
int opt;
checkk8();
while ((opt = getopt(ac,av,"ifscmaC:v")) != -1) {
switch (opt) {
case 'f':
force = 1;
break;
case 'c':
setup(1);
exit(0);
case 's':
setup(0);
exit(0);
case 'm':
masks = mem;
break;
case 'i':
masks = io;
break;
case 'a':
absolute = 1;
break;
case 'C':
cfilter = optarg;
break;
case 'v':
verbose = 1;
break;
default:
usage();
}
}
if (av[optind]) {
char *end;
delay = strtoul(av[optind], &end, 10);
if (*end)
usage();
if (av[optind+1])
usage();
}
setup(0);
dumpall();
return 0;
}