Created
March 7, 2021 22:38
-
-
Save cpackham/6356a3a943accebb228135dc10daf721 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @file cpuload.c | |
* | |
* Simple binary that creates CPU load. This allows developers to artificially | |
* induce a specified level of CPU load (e.g. to test how well certain code | |
* responds under high load). | |
* | |
* Copyright 2008 Allied Telesis Labs, New Zealand | |
*/ | |
#include <stdlib.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <sched.h> | |
#include <string.h> | |
#include <sys/time.h> | |
#include <pthread.h> | |
#define MAX_CPUS 64 | |
static uint32_t msec_delay = UINT32_MAX; | |
static uint32_t cpu_util = 20; | |
static uint32_t | |
diff_time_in_ms (struct timeval start, struct timeval end) | |
{ | |
uint32_t elapsed_ms; | |
/* get any difference in secs since epoch first, then the difference | |
* in microsecs will be the elapsed time (convert everything to ms) */ | |
elapsed_ms = (end.tv_sec - start.tv_sec) * 1000; | |
elapsed_ms += end.tv_usec / 1000; | |
elapsed_ms -= start.tv_usec / 1000; | |
return elapsed_ms; | |
} /* diff_time_in_ms */ | |
static void | |
do_tight_loop_for_100ms (uint32_t utilisation) | |
{ | |
uint32_t elapsed_ms = 0; | |
uint32_t remainder; | |
struct timeval start, now; | |
remainder = (100 - utilisation) * 1000; | |
gettimeofday (&start, NULL); | |
/* go into a tight loop continually checking the time. | |
* we want to eat up the CPU for <utilisation> ms out of every 100 ms */ | |
while (elapsed_ms < utilisation) | |
{ | |
gettimeofday (&now, NULL); | |
elapsed_ms = diff_time_in_ms (start, now); | |
} | |
/* sleep the remainder */ | |
usleep (remainder); | |
} | |
static void * | |
gobble_cpu (void *unused) | |
{ | |
struct sched_param sched_param; | |
uint32_t elapsed_ms; | |
struct timeval start, now; | |
/* set our priority REALLY low */ | |
sched_param.sched_priority = 1; | |
sched_setscheduler (0, SCHED_RR, &sched_param); | |
/* now cause the cpu load - loop until we've used up the desired CPU */ | |
gettimeofday (&start, NULL); | |
elapsed_ms = 0; | |
while (elapsed_ms <= msec_delay) | |
{ | |
/* go into a tight loop to eat up the CPU for 100 ms */ | |
do_tight_loop_for_100ms (cpu_util); | |
/* check how much longer we need to loop for */ | |
gettimeofday (&now, NULL); | |
elapsed_ms = diff_time_in_ms (start, now); | |
} | |
return NULL; | |
} | |
int | |
main (int argc, char *argv[]) | |
{ | |
pthread_t cputhread[MAX_CPUS]; | |
int cpu; | |
int num_cpus; | |
if (argc >= 2) | |
{ | |
/* get the CPU utilization % specified (default 20%) */ | |
cpu_util = atoi (argv[1]); | |
/* print help if needed */ | |
if (strstr (argv[1], "?") != NULL || | |
strstr (argv[1], "help") != NULL || cpu_util > 100) | |
{ | |
fprintf (stderr, "Usage: %s CPU-utilization [duration-ms]\n" | |
"Default: 20%% [no-timeout]\n", argv[0]); | |
return EXIT_FAILURE; | |
} | |
} | |
if (argc >= 3) | |
{ | |
/* get the msec delay specified (default no timeout) */ | |
msec_delay = atoi (argv[2]); | |
} | |
for (cpu = 1; cpu < MAX_CPUS; cpu++) | |
{ | |
char cpupath[80]; | |
snprintf (cpupath, sizeof (cpupath), "/sys/devices/system/cpu/cpu%d", cpu); | |
/* out of CPU cores? */ | |
if (access (cpupath, F_OK) < 0) | |
{ | |
break; | |
} | |
/* Create a new thread */ | |
pthread_create (&cputhread[cpu], NULL, gobble_cpu, NULL); | |
} | |
num_cpus = cpu; | |
/* Finally, chew up CPU in the main thread */ | |
gobble_cpu (NULL); | |
/* Wait for threads to exit */ | |
for (cpu = 1; cpu < num_cpus; cpu++) | |
{ | |
pthread_join (cputhread[cpu], NULL); | |
} | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment