From 711ca12865ed9548ed9de07b26eea6ec984fa964 Mon Sep 17 00:00:00 2001 From: Anders Roxell Date: Sat, 8 Feb 2014 20:03:32 +0100 Subject: Initial version from Montavista Signed-off-by: Anders Roxell --- use_cpu.c | 454 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 454 insertions(+) create mode 100644 use_cpu.c diff --git a/use_cpu.c b/use_cpu.c new file mode 100644 index 0000000..16e69b2 --- /dev/null +++ b/use_cpu.c @@ -0,0 +1,454 @@ +/* + * use_cpu - A program for using specific amounts of CPU. + * Copyright (C) 2012 MontaVista Software, LLC + * + * 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 + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *help_opt = "\ + use_cpu [opts]\n\ +Use cpu time on the system\n\ +Options are:\n\ + --percent - Specify the default percentage to use on each CPU. If this is\n\ + not specified the default is 50%.\n\ + --prio - Specify the priority to run the threads at. This is a real-time\n\ + priority, or 0 for normal priority. Default it 0.\n\ + --cpus - Specify the CPUs to run on, and optionally the percent per CPU.\n\ + The format is [:][,[:]]...\n\ + If this is not specified then the default is all CPUs at the default\n\ + percentage.\n\ + --duration - The amount of time in seconds to run the test. 0 means\n\ + forever. Default is 0.\n\ + --nogettodspin - Normally the program spins in each thread calling\n\ + gettimeofday. If using LTT, this can generate a lot of noise.\n\ + With this, it uses a volatile to control spinning and that gets set\n\ + from the main thread. This is not quite as reliable, and it may\n\ + have issues when getting close to 100%.\n\ +"; + +struct option opts[] = +{ + { "percent", 1, NULL, 'p' }, + { "prio", 1, NULL, 'r' }, + { "cpus", 1, NULL, 'c' }, + { "duration", 1, NULL, 'd' }, + { "nogettodspin", 0, NULL, 's' }, + { NULL } +}; + +void +exit_err(char *format, ...) +{ + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + putc('\n', stdout); + va_end(ap); + exit(1); +} + +struct proc { + unsigned int num; + int valid; + int percent_given; + int use; + struct procinfo *info; + unsigned int prio; + double percent; + pthread_t thread; + volatile unsigned int missed_sched; + unsigned int last_missed_sched; + volatile int waiting; +}; + +struct procinfo { + unsigned int maxproc; + int use_end_time; + struct timeval end_time; + volatile int end_now; + struct proc *procs; +}; + +struct procinfo *g_pinfo; + +void end_op_sig(int sig) +{ + g_pinfo->end_now = 1; +} + +#define CPUINFO "/proc/cpuinfo" + +struct procinfo *read_processors(void) +{ + FILE *f = fopen(CPUINFO, "r"); + char line[81]; + char *endptr; + char *rv; + unsigned int lineno = 0; + unsigned int i; + struct procinfo *pinfo; + + pinfo = malloc(sizeof(*pinfo)); + if (!pinfo) + exit_err("Out of memory"); + pinfo->maxproc = 0; + + if (!f) + exit_err("Unable to open %s", CPUINFO); + + /* Find the maximum processor id on this system. */ + rv = fgets(line, sizeof(line), f); + while (rv) { + lineno++; + if (strncmp(line, "processor", 9) == 0) { + char *p = strchr(line, ':'); + int unsigned v; + if (!p) + exit_err("%s:%u - Invalid processor line" + " format", CPUINFO, lineno); + p++; + while (isspace(*p)) + p++; + if (strlen(p) == 0) + exit_err("%s:%u - Invalid processor line" + " format", CPUINFO, lineno); + v = strtoul(p, &endptr, 10); + if (*endptr != '\n') + exit_err("%s:%u - Invalid processor line" + " format", CPUINFO, lineno); + if (v > pinfo->maxproc) + pinfo->maxproc = v; + } + rv = fgets(line, sizeof(line), f); + } + + /* Now allocate memory and fill in the valid processors. */ + pinfo->procs = malloc(sizeof(*pinfo->procs) * (pinfo->maxproc + 1)); + if (!pinfo) + exit_err("Out of memory"); + for (i = 0; i <= pinfo->maxproc; i++) { + pinfo->procs[i].num = i; + pinfo->procs[i].valid = 0; + pinfo->procs[i].percent_given = 0; + pinfo->procs[i].use = 0; + pinfo->procs[i].info = pinfo; + pinfo->procs[i].prio = 0; + pinfo->procs[i].missed_sched = 0; + pinfo->procs[i].last_missed_sched = 0; + pinfo->procs[i].waiting = 0; + } + rewind(f); + lineno = 0; + rv = fgets(line, sizeof(line), f); + while (rv > 0) { + lineno++; + if (strncmp(line, "processor", 9) == 0) { + char *p = strchr(line, ':'); + int unsigned v; + if (!p) + exit_err("%s:%u - Invalid processor line" + " format", CPUINFO, lineno); + p++; + while (isspace(*p)) + p++; + if (strlen(p) == 0) + exit_err("%s:%u - Invalid processor line" + " format", CPUINFO, lineno); + v = strtoul(p, &endptr, 10); + if (*endptr != '\n') + exit_err("%s:%u - Invalid processor line" + " format", CPUINFO, lineno); + if (v > pinfo->maxproc) + exit_err("%s:%u - Processor number after max", + CPUINFO, lineno); + pinfo->procs[v].valid = 1; + } + rv = fgets(line, sizeof(line), f); + } + + return pinfo; +} + +void handle_cpus(struct procinfo *pinfo, char *cpuspec) +{ + char *ncpu; + unsigned int count = 0; + + while (*cpuspec != '\0') { + unsigned int n; + char *endptr; + + count++; + ncpu = strchr(cpuspec, ','); + if (!ncpu) + ncpu = cpuspec + strlen(cpuspec); + n = strtoul(cpuspec, &endptr, 10); + if (endptr == cpuspec) + exit_err("cpu specifier %u is invalid", count); + if (n > pinfo->maxproc) + exit_err("cpu specifier %u is larger than the number" + " of processors", count); + if (!pinfo->procs[n].valid) + exit_err("cpu specifier %u is not a valid processor", + count); + pinfo->procs[n].use = 1; + if (*endptr == ':') { + cpuspec = endptr + 1; + pinfo->procs[n].percent = (strtod(cpuspec, &endptr) + / 100.0); + if (endptr == cpuspec) + exit_err("cpu specifier %u is invalid", count); + if (pinfo->procs[n].percent >= 1.0) + exit_err("cpu percent must be < 100"); + pinfo->procs[n].percent_given = 1; + } + cpuspec = endptr; + if (*cpuspec == ',') + cpuspec++; + else if (*endptr != '\0') + exit_err("cpu specifier %u is invalid", count); + } +} + +long subtime(struct timeval *l, struct timeval *r) +{ + return (((l->tv_sec - r->tv_sec) * 1000000) + + (l->tv_usec - r->tv_usec)); +} + +int use_spin; +volatile int spin_now; + +void *use_cpu(void *arg) +{ + struct proc *p = arg; + struct procinfo *pinfo = p->info; + struct timeval now, end; + long time_per_sec; + cpu_set_t set; + int rv; + + CPU_ZERO(&set); + CPU_SET(p->num, &set); + rv = pthread_setaffinity_np(p->thread, sizeof(set), &set); + if (rv) + exit_err("pthread_setaffinity_np: %s", strerror(rv)); + time_per_sec = 1000000 * p->percent; + gettimeofday(&now, NULL); + p->waiting = 1; + while (!pinfo->end_now && (!pinfo->use_end_time + || subtime(&now, &pinfo->end_time) < 0)) { + /* Wait til the top of the second. */ + usleep(1000000 - now.tv_usec); + p->waiting = 0; + + end.tv_sec = now.tv_sec + 1; + end.tv_usec = time_per_sec; + if (use_spin) { + while (spin_now) + ; + gettimeofday(&now, NULL); + } else { + gettimeofday(&now, NULL); + while (subtime(&now, &end) < 0) + gettimeofday(&now, NULL); + } + if (now.tv_sec > end.tv_sec) + p->missed_sched++; + p->waiting = 1; + } + return NULL; +} + +void start_proc(struct proc *p) +{ + int rv; + pthread_attr_t attr; + struct sched_param param; + int policy; + + rv = pthread_attr_init(&attr); + if (rv) + exit_err("pthread_attr_init: %s", strerror(rv)); + rv = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + if (rv) + exit_err("pthread_attr_setinheritsched: %s", + strerror(rv)); + param.sched_priority = p->prio; + if (p->prio > 0) + policy = SCHED_FIFO; + else + policy = SCHED_OTHER; + rv = pthread_setschedparam(pthread_self(), policy, ¶m); + if (rv) + exit_err("pthread_setschedparam: %s", strerror(rv)); + rv = pthread_attr_setschedpolicy(&attr, policy); + if (rv) + exit_err("pthread_attr_setschedpolicy: %s", + strerror(rv)); + rv = pthread_attr_setschedparam(&attr, ¶m); + if (rv) + exit_err("pthread_attr_setschedparam: %s", + strerror(rv)); + rv = pthread_create(&p->thread, &attr, use_cpu, p); + if (rv) + exit_err("pthread_create: %s", strerror(rv)); + rv = pthread_attr_destroy(&attr); + if (rv) + exit_err("pthread_attr_destroy: %s", strerror(rv)); +} + +int main(int argc, char *argv[]) +{ + double default_percent = 0.5; + char *endptr; + struct procinfo *pinfo = read_processors(); + unsigned int i; + int opt; + int got_cpuspec = 0; + unsigned int default_priority = 0; + unsigned int duration = 0; + struct timeval now; + long time_per_sec; + + while ((opt = getopt_long(argc, argv, "p:c:r:d:s", opts, NULL)) != -1) { + switch (opt) { + case 'p': + default_percent = strtod(optarg, &endptr) / 100.0; + if (*endptr != '\0') + exit_err("Invalid percent: %s", optarg); + if (default_percent >= 1.0) + exit_err("Percent must be < 100"); + break; + + case 'r': + default_priority = strtoul(optarg, &endptr, 10); + if (*endptr != '\0') + exit_err("Invalid priority: %s", optarg); + break; + + case 'd': + duration = strtoul(optarg, &endptr, 10); + if (*endptr != '\0') + exit_err("Invalid duration: %s", optarg); + break; + + case 'c': + handle_cpus(pinfo, optarg); + got_cpuspec = 1; + break; + + case 's': + use_spin = 1; + break; + + case '?': + puts(help_opt); + exit(1); + } + } + + pinfo->end_now = 0; + if (duration == 0) { + pinfo->use_end_time = 0; + memset(&pinfo->end_time, 0, sizeof(pinfo->end_time)); + } else { + pinfo->use_end_time = 1; + gettimeofday(&pinfo->end_time, NULL); + pinfo->end_time.tv_sec += duration; + } + + for (i = 0; i <= pinfo->maxproc; i++) { + if (pinfo->procs[i].valid) { + if (!pinfo->procs[i].percent_given) + pinfo->procs[i].percent = default_percent; + if (!got_cpuspec) + pinfo->procs[i].use = 1; + pinfo->procs[i].prio = default_priority; + } + } + + g_pinfo = pinfo; + signal(SIGINT, end_op_sig); + + for (i = 0; i <= pinfo->maxproc; i++) { + if (pinfo->procs[i].use) + start_proc(&pinfo->procs[i]); + } + + time_per_sec = 1000000 * default_percent; + gettimeofday(&now, NULL); + while (!pinfo->end_now && (!pinfo->use_end_time + || subtime(&now, &pinfo->end_time) < 0)) { + /* Wait til the top of the second. */ + usleep(1000000 - now.tv_usec); + + if (use_spin) { + struct timeval end; + int not_waiting = 1; + + end.tv_sec = now.tv_sec + 1; + end.tv_usec = time_per_sec; + gettimeofday(&now, NULL); + usleep(subtime(&end, &now)); + /* Tell everyone to stop. */ + spin_now = 0; + /* Wait for them to stop and go into waiting mode. */ + while (not_waiting > 0) { + not_waiting = 0; + for (i = 0; i <= pinfo->maxproc; i++) + not_waiting += !pinfo->procs[i].waiting; + } + /* Now let everyone go when they wake up next */ + spin_now = 1; + } + + for (i = 0; i <= pinfo->maxproc; i++) { + struct proc *p = &pinfo->procs[i]; + unsigned int missed = p->missed_sched; + if (missed > p->last_missed_sched) { + printf("Missed scheduling %d times on CPU %d\n", + missed - p->last_missed_sched, + i); + p->last_missed_sched = p->missed_sched; + } + } + + gettimeofday(&now, NULL); + } + + for (i = 0; i <= pinfo->maxproc; i++) + pthread_join(pinfo->procs[i].thread, NULL); + + for (i = 0; i <= pinfo->maxproc; i++) { + if (pinfo->procs[i].missed_sched) + printf(" CPU %d: Missed scheduling %d times.\n", + i, pinfo->procs[i].missed_sched); + } + printf("Finished using CPU\n"); + return 0; +} -- cgit v1.2.3