blob: 5e7320f1e1679fdf4caa15d9b729534425b49bc6 [file] [log] [blame]
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#ifndef ANDROID
#include <sys/timex.h>
#else
/*
* As of 4.0.4, Bionic doesn't provide the timex/adjtimex interface However, the kernel does.
*/
#include <linux/timex.h> /* for struct timex */
#include <asm/unistd.h> /* for __NR_adjtimex */
static int adjtimex(struct timex *buf)
{
return syscall(__NR_adjtimex, buf);
}
#endif
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <sched.h>
#include <signal.h>
#define DURATION 120 /* seconds */
#define SEC_TO_USEC(d) ((d) * 1000 * 1000)
static int sigon;
void sighandler(int sig)
{
sigon = 1;
}
void timeout(int sig)
{
printf("Test duration exceeded\n");
exit(1);
}
int tick_me(int nrsleeps, int delay, int cpu)
{
int i;
cpu_set_t mask;
unsigned long counter = 0;
struct itimerval t = { };
CPU_ZERO(&mask);
CPU_SET(cpu, &mask);
/* stick on the specified cpu */
if (sched_setaffinity(getpid(), sizeof(mask), &mask)) {
fprintf(stderr, "sched_setaffinity (%d): %m", cpu);
return -1;
}
signal(SIGALRM, sighandler);
for (i = 0; i < nrsleeps / 2; i++) {
usleep(delay);
sigon = 0;
t.it_value.tv_sec = 0;
t.it_value.tv_usec = delay;
if (setitimer(ITIMER_REAL, &t, NULL)) {
perror("setitimer");
return 1;
}
while (!sigon)
counter++;
}
fprintf(stderr, "counter value %lu\n", counter);
return 0;
}
int isonline(int cpu)
{
FILE *f;
char path[MAXPATHLEN];
int online;
if (!cpu)
return 1;
sprintf(path, "/sys/devices/system/cpu/cpu%d/online", cpu);
f = fopen(path, "r");
if (!f) {
perror("fopen");
return -1;
}
fscanf(f, "%d", &online);
fclose(f);
return online;
}
int main(int argc, char *argv[])
{
int ret, i, nrcpus = 2;
int nrsleeps, delay;
pid_t pids[nrcpus];
struct timex timex = { 0 };
if (adjtimex(&timex) < 0) {
perror("adjtimex");
return 1;
}
fprintf(stderr, "jiffies are : %ld usecs\n", timex.tick);
nrcpus = sysconf(_SC_NPROCESSORS_CONF);
if (nrcpus < 0) {
perror("sysconf");
return 1;
}
fprintf(stderr, "found %d cpu(s)\n", nrcpus);
for (i = 0; i < nrcpus; i++) {
ret = isonline(i);
if (!ret) {
fprintf(stderr, "cpu%d is offline, ignoring\n", i);
continue;
}
pids[i] = fork();
if (pids[i] < 0) {
perror("fork");
return 1;
}
if (!pids[i]) {
struct timeval before, after;
long duration;
float deviation;
nrsleeps = SEC_TO_USEC(DURATION) / (timex.tick * 10);
delay = SEC_TO_USEC(DURATION) / nrsleeps;
fprintf(stderr, "duration: %d secs, #sleep: %d, delay: %d us\n",
DURATION, nrsleeps, delay);
gettimeofday(&before, NULL);
if (tick_me(nrsleeps, delay, i))
return 1;
gettimeofday(&after, NULL);
duration = SEC_TO_USEC(after.tv_sec - before.tv_sec) +
(after.tv_usec - before.tv_usec);
fprintf(stderr, "test duration: %f secs\n",
(float)(duration) / (float)SEC_TO_USEC(1));
deviation = ((float)duration - (float)SEC_TO_USEC(DURATION)) /
(float)SEC_TO_USEC(DURATION);
fprintf(stderr, "deviation %f\n", deviation);
if (deviation > 0.5) {
fprintf(stderr, "expected test duration too long\n");
return 1;
}
return 0;
}
}
ret = 0;
signal(SIGALRM, timeout);
alarm(DURATION + 5);
for (i = 0; i < nrcpus; i++) {
int status;
waitpid(pids[i], &status, 0);
if (status != 0) {
fprintf(stderr, "test for cpu %d has failed\n", i);
ret = 1;
}
}
return ret;
}