/* * sload by Davide Libenzi (CPU load tool) * Copyright (C) 2007 Davide Libenzi * * This program 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; either version 2 of the License, or * (at your option) any later version. * * This program 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 have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Davide Libenzi * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define AUXSIZE 32 struct slreport { unsigned long missd; unsigned long long time; }; struct sltask { char const *name; int nice; unsigned long bus, sus, wus; unsigned long jbus, jsus, jwus; int count; pid_t *pids; struct slreport *reps; int *pfds; }; static double usloop; static int stop_test; static int debug; static void sig_int(int sig) { ++stop_test; signal(sig, sig_int); } static unsigned long long getustime(void) { struct timeval tm; gettimeofday(&tm, NULL); return tm.tv_sec * 1000000ULL + tm.tv_usec; } static void burn_loop(void) { unsigned long i; static unsigned long long aux[AUXSIZE]; for (i = 0; i < 100; i++) aux[i % AUXSIZE] += i * 7 - 3; } static void burn(unsigned long usecs) { double t, us = usecs; for (t = 0; t < us; t += usloop) burn_loop(); } static unsigned long myrand(const unsigned long *sval) { static unsigned long next; if (sval != NULL) next = *sval; else next = next * 1103515245UL + 12345; return next; } static int ilog2(unsigned long v) { int l; for (l = 0; v; v >>= 1, l++); return l; } static unsigned long jval(unsigned long v, unsigned long jitter) { unsigned long b, r; if (jitter > 0) { b = (v > jitter) ? v - jitter: 0; r = myrand(NULL); r >>= 8 * sizeof(unsigned long) - ilog2(2 * jitter) - 1; v = b + r % (2 * jitter); } return v; } static int next_task(struct sltask *tasks, int n) { if (n >= 0) { if ((tasks[n].pids = (pid_t *) malloc(tasks[n].count * sizeof(pid_t))) == NULL || (tasks[n].reps = (struct slreport *) malloc(tasks[n].count * sizeof(struct slreport))) == NULL || (tasks[n].pfds = (int *) malloc(2 * tasks[n].count * sizeof(int))) == NULL) { perror("malloc"); exit(1); } } return n + 1; } static void calibrate_loop(void) { int i, j, loops, oldpolicy; unsigned long long ts, tn, tl; double ust; struct sched_param sp, osp; if ((oldpolicy = sched_getscheduler(0)) < 0) { perror("sched_getscheduler"); exit(1); } if (sched_getparam(0, &osp)) { perror("sched_getparam"); exit(1); } memset(&sp, 0, sizeof(sp)); sp.sched_priority = 99; if (sched_setscheduler(0, SCHED_RR, &sp)) { if (debug) perror("sched_setscheduler"); loops = 8; } else loops = 2; usloop = DBL_MAX; for (i = 0; i < loops; i++) { tl = 0; ts = getustime(); do { for (j = 0; j < 1024; j++) burn_loop(); tl += j; tn = getustime(); } while (tn - ts < 25000); ust = (double) (tn - ts) / tl; if (ust < usloop) usloop = ust; } sched_setscheduler(0, oldpolicy, &osp); if (debug) fprintf(stderr, "us per loop: %f\n", usloop); } static void usage(const char *prg) { fprintf(stderr, "use: %s [-T NAME] [-s USSLEEP] [-b USBURN] [-w USWAIT] [-n NICE]\n" "\t[-N NTASKS] [-B CPU] [-R NSECS] [-D] [-h]\n", prg); exit(1); } int main(int ac, char **av) { int i, k, nt, n = -1, cpu_bind = -1, status, runfor = 0; unsigned long j, bus, sus, wus; unsigned long long ts, tn, tl, totcpu; const char *jptr; struct sltask *tasks, *ctask; cpu_set_t mask; struct slreport rep; if ((tasks = (struct sltask *) malloc(ac * sizeof(struct sltask))) == NULL) { perror("malloc"); return 1; } memset(tasks, 0, ac * sizeof(struct sltask)); while ((i = getopt(ac, av, "T:b:s:w:n:N:B:R:Dh")) != -1) { switch (i) { case 'T': n = next_task(tasks, n); tasks[n].name = optarg; tasks[n].count = 1; break; case 'b': tasks[n].bus = atol(optarg); if ((jptr = strchr(optarg, ':')) != NULL) tasks[n].jbus = atol(jptr + 1); break; case 's': tasks[n].sus = atol(optarg); if ((jptr = strchr(optarg, ':')) != NULL) tasks[n].jsus = atol(jptr + 1); break; case 'w': tasks[n].wus = atol(optarg); if ((jptr = strchr(optarg, ':')) != NULL) tasks[n].jwus = atol(jptr + 1); break; case 'n': tasks[n].nice = atoi(optarg); break; case 'N': tasks[n].count = atoi(optarg); break; case 'R': runfor = atoi(optarg); break; case 'B': cpu_bind = atol(optarg); break; case 'D': debug = 1; break; case 'h': usage(av[0]); } } if (n < 0) usage(av[0]); n = next_task(tasks, n); if (cpu_bind >= 0) { CPU_ZERO(&mask); CPU_SET(cpu_bind, &mask); if (sched_setaffinity(0, sizeof(mask), &mask) == -1) { perror("sched_setaffinity"); return 1; } } calibrate_loop(); signal(SIGINT, sig_int); signal(SIGALRM, sig_int); for (i = 0; i < n; i++) { ctask = &tasks[i]; for (k = 0; k < ctask->count; k++) { nt = k; if (pipe(&ctask->pfds[2 * nt])) { perror("pipe"); return 1; } if ((ctask->pids[k] = fork()) == 0) goto child; } } for (i = 0, totcpu = 0; i < n; i++) { ctask = &tasks[i]; for (k = 0; k < ctask->count; k++) { if (waitpid(ctask->pids[k], &status, 0) != ctask->pids[k]) { perror("waitpid"); return 1; } if (read(ctask->pfds[2 * k], &ctask->reps[k], sizeof(struct slreport)) != sizeof(struct slreport)) { perror("pipe read"); return 1; } totcpu += ctask->reps[k].time; } } fprintf(stdout, "Total Burnt Time: %llu ms\n", totcpu / 1000); if (totcpu) { for (i = 0; i < n; i++) { ctask = &tasks[i]; for (k = 0; k < ctask->count; k++) { fprintf(stdout, "%s[%3d]{%3d} %llu ms (%.2f%%); missed %lu\n", ctask->name, k, ctask->nice, ctask->reps[k].time / 1000, 100.0 * (double) ctask->reps[k].time / (double) totcpu, ctask->reps[k].missd); } } } return 0; child: if (debug) fprintf(stderr, "[%6lu] %s\n", (unsigned long) getpid(), ctask->name); if (runfor > 0) alarm(runfor); if (setpriority(PRIO_PROCESS, 0, ctask->nice)) perror("setpriority"); rep.missd = 0; rep.time = 0; for (; !stop_test;) { ts = getustime(); if (ctask->bus) { bus = jval(ctask->bus, ctask->jbus); burn(bus); rep.time += bus; } if (ctask->sus) { sus = jval(ctask->sus, ctask->jsus); usleep(sus); } if (ctask->wus) { wus = jval(ctask->wus, ctask->jwus); tn = getustime(); if (ts + wus > tn) usleep(ts + wus - tn); else { rep.missd++; if (debug) fprintf(stderr, "missed deadline: %llu us\n", tn - (ts + wus)); } } } write(ctask->pfds[2 * nt + 1], &rep, sizeof(rep)); return 0; }