Roger Larsson wrote: > > ... > An alternative would be Andrew Mortons amlat - it is simpler, possibly better > in an automated environment.
That would require a kernel patch. I always use a mucked-with version of Mark Hahn's `realfeel' application. Simple, accurate. Attached here.
/* * This was originally written by Mark Hahn. Obtained from * http://brain.mcmaster.ca/~hahn/realfeel.c */ #include <linux/rtc.h> #include <sys/ioctl.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <math.h> #include <stdlib.h> #include <stdio.h> #include <sys/time.h> #include <sys/mman.h> #include <sched.h> #include <sys/signal.h> #include <string.h> #define _GNU_SOURCE #include <getopt.h> /* global vars */ int stopit; /* set to stop measuring */ #define SAMPLES 10000 int histogram[SAMPLES]; /* Milliseconds */ #define PAGE_SIZE 4096UL /* virtual memory page size */ #define OSCR_HZ 3686400 /* frequency of clock ticks */ #define OSCR 0x90000010 /* physical address of OSCR register */ unsigned long *oscr; /* ptr to OSCR */ void setup_clock (void) { int fd; void *map_base; off_t target; off_t page; #ifndef ARCHARM return; #endif fd = open ("/dev/mem", O_RDWR | O_SYNC); if (-1 == fd) { perror ("open of /dev/mem failed\n"); exit (1); } /* Map one page */ target = OSCR; page = target & ~(PAGE_SIZE - 1); map_base = mmap (0, PAGE_SIZE, PROT_READ, MAP_SHARED, fd, page); if (MAP_FAILED == map_base) { perror ("mmap failed"); (void) close (fd); exit (2); } oscr = map_base + (target - page); /* This does not end the mmap, * the mmap will go away when the process dies */ close (fd); } double second() { struct timeval tv; gettimeofday(&tv,0); return tv.tv_sec + 1e-6 * tv.tv_usec; } typedef unsigned long long u64; u64 rdtsc() { u64 tsc; #ifdef ARCHARM tsc = *oscr; #else __asm__ __volatile__("rdtsc" : "=A" (tsc)); #endif return tsc; } void selectsleep(unsigned us) { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = us; select(0,0,0,0,&tv); } double secondsPerTick, ticksPerSecond; void calibrate() { double sumx = 0; double sumy = 0; double sumxx = 0; double sumxy = 0; double slope; // least squares linear regression of ticks onto real time // as returned by gettimeofday. const unsigned n = 30; unsigned i; for (i=0; i<n; i++) { double breal,real,ticks; u64 bticks; breal = second(); bticks = rdtsc(); selectsleep((unsigned)(10000 + drand48() * 200000)); ticks = rdtsc() - bticks; real = second() - breal; sumx += real; sumxx += real * real; sumxy += real * ticks; sumy += ticks; } slope = ((sumxy - (sumx*sumy) / n) / (sumxx - (sumx*sumx) / n)); ticksPerSecond = slope; secondsPerTick = 1.0 / slope; printf("%3.3f MHz\n",ticksPerSecond*1e-6); } void fatal(char *msg) { perror(msg); exit(1); } int set_realtime_priority(void) { struct sched_param schp; /* * set the process to realtime privs */ memset(&schp, 0, sizeof(schp)); schp.sched_priority = sched_get_priority_max(SCHED_FIFO); if (sched_setscheduler(0, SCHED_FIFO, &schp) != 0) { perror("sched_setscheduler"); exit(1); } return 0; } void hist(char *file) { int i; FILE *f; f = fopen(file, "w"); if (f == 0) { fprintf(stderr, "realfeel: can't open `%s':%s\n", file, strerror(errno)); exit(1); } for (i = 0; i < SAMPLES; i++) { if (histogram[i]) { fprintf(f, "%d.%d %d\n", i / 10, i % 10, histogram[i]); } } fclose(f); exit(0); } void signalled(int sig) { stopit = 1; } static void usage(void) { fprintf(stderr, "Usage: realfeel [--samples n] [--hertz n] filename.hist\n"); exit(1); } uint bounded=0; uint ncycles=0; uint current=0; int hz = 2048; char *parse_options(int argc, char **argv) { int c, option_index; static struct option long_options[] = { {"samples", required_argument, 0, 0}, {"hertz", required_argument, 0, 0}, {0, 0, 0, 0}, }; while ((c = getopt_long (argc, argv, "b:", long_options, &option_index)) != -1) { switch (option_index) { default: case -1: fprintf (stderr, "Bad option (%s)\n", optarg); usage(); exit (1); break; case 0: bounded = 1; ncycles = atoi(optarg); break; case 1: hz = atoi(optarg); if (hz > 2048) { fprintf(stderr, "max allowable interrupt frequency is 2048 Hz\n"); hz = 2048; } else if (hz <= 0) { fprintf(stderr, "zero or negative frequency doesn't make sense!\n"); hz = 1; } } } if (argv[optind] == NULL) { fprintf(stderr, "histogram file name required\n"); usage(); exit (2); } return argv[optind]; } #define msec(f) (1e3 * (f)) int main(int argc, char *argv[]) { int fd; double ideal; u64 last; double max_delay = 0; char *histfile = parse_options(argc, argv); if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0) { perror("mlockall"); exit(1); } setup_clock (); set_realtime_priority(); calibrate(); printf("secondsPerTick=%f\n", secondsPerTick); printf("ticksPerSecond=%f\n", ticksPerSecond); fd = open("/dev/rtc",O_RDONLY); if (fd == -1) fatal("failed to open /dev/rtc"); ideal = 1.0 / hz; if (ioctl(fd, RTC_IRQP_SET, hz) == -1) fatal("ioctl(RTC_IRQP_SET) failed"); printf("Interrupt frequency: %d Hz\n",hz); if (bounded) { printf("running for %d samples\n", ncycles); } /* Enable periodic interrupts */ if (ioctl(fd, RTC_PIE_ON, 0) == -1) fatal("ioctl(RTC_PIE_ON) failed"); signal(SIGINT, signalled); last = rdtsc(); while (!stopit) { u64 now; double delay; int data; int ms; if (read(fd, &data, sizeof(data)) == -1) fatal("blocking read failed"); now = rdtsc(); delay = secondsPerTick * (now - last); if (delay > max_delay) { max_delay = delay; // printf("%.3f msec\n", -(1e3 * (ideal - delay))); } ms = (-(ideal - delay) + 1.0/20000.0) * 10000; if (ms < 0) ms = 0; /* hmmm */ if (ms >= SAMPLES) ms = SAMPLES; histogram[ms]++; if (bounded) { if (++current >= ncycles) { printf ("finished collecting %d samples\n", ncycles); printf ("maximum cycle time: %.3fms\n", -msec(ideal - max_delay)); break; } if ((current % 10000) == 0) { printf("%d cycles (max cycle time so far: %.3fms)\n", current, -(msec(ideal - max_delay))); } } last = now; } if (ioctl(fd, RTC_PIE_OFF, 0) == -1) fatal("ioctl(RTC_PIE_OFF) failed"); hist(histfile); return 0; }