326 lines · c
1// SPDX-License-Identifier: GPL-2.0+2 3/*4 * Context switch microbenchmark.5 *6 * Copyright 2018, Anton Blanchard, IBM Corp.7 */8 9#define _GNU_SOURCE10#include <assert.h>11#include <errno.h>12#include <getopt.h>13#include <limits.h>14#include <linux/futex.h>15#include <pthread.h>16#include <sched.h>17#include <signal.h>18#include <stdio.h>19#include <stdlib.h>20#include <string.h>21#include <sys/shm.h>22#include <sys/syscall.h>23#include <sys/time.h>24#include <sys/types.h>25#include <sys/wait.h>26#include <unistd.h>27 28static unsigned int timeout = 30;29 30static void set_cpu(int cpu)31{32 cpu_set_t cpuset;33 34 if (cpu == -1)35 return;36 37 CPU_ZERO(&cpuset);38 CPU_SET(cpu, &cpuset);39 40 if (sched_setaffinity(0, sizeof(cpuset), &cpuset)) {41 perror("sched_setaffinity");42 exit(1);43 }44}45 46static void start_process_on(void *(*fn)(void *), void *arg, int cpu)47{48 int pid;49 50 pid = fork();51 if (pid == -1) {52 perror("fork");53 exit(1);54 }55 56 if (pid)57 return;58 59 set_cpu(cpu);60 61 fn(arg);62 63 exit(0);64}65 66static int cpu;67static int do_fork = 0;68static int do_vfork = 0;69static int do_exec = 0;70static char *exec_file;71static int exec_target = 0;72static unsigned long iterations;73static unsigned long iterations_prev;74 75static void run_exec(void)76{77 char *const argv[] = { "./exec_target", NULL };78 79 if (execve("./exec_target", argv, NULL) == -1) {80 perror("execve");81 exit(1);82 }83}84 85static void bench_fork(void)86{87 while (1) {88 pid_t pid = fork();89 if (pid == -1) {90 perror("fork");91 exit(1);92 }93 if (pid == 0) {94 if (do_exec)95 run_exec();96 _exit(0);97 }98 pid = waitpid(pid, NULL, 0);99 if (pid == -1) {100 perror("waitpid");101 exit(1);102 }103 iterations++;104 }105}106 107static void bench_vfork(void)108{109 while (1) {110 pid_t pid = vfork();111 if (pid == -1) {112 perror("fork");113 exit(1);114 }115 if (pid == 0) {116 if (do_exec)117 run_exec();118 _exit(0);119 }120 pid = waitpid(pid, NULL, 0);121 if (pid == -1) {122 perror("waitpid");123 exit(1);124 }125 iterations++;126 }127}128 129static void *null_fn(void *arg)130{131 pthread_exit(NULL);132}133 134static void bench_thread(void)135{136 pthread_t tid;137 cpu_set_t cpuset;138 pthread_attr_t attr;139 int rc;140 141 rc = pthread_attr_init(&attr);142 if (rc) {143 errno = rc;144 perror("pthread_attr_init");145 exit(1);146 }147 148 if (cpu != -1) {149 CPU_ZERO(&cpuset);150 CPU_SET(cpu, &cpuset);151 152 rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);153 if (rc) {154 errno = rc;155 perror("pthread_attr_setaffinity_np");156 exit(1);157 }158 }159 160 while (1) {161 rc = pthread_create(&tid, &attr, null_fn, NULL);162 if (rc) {163 errno = rc;164 perror("pthread_create");165 exit(1);166 }167 rc = pthread_join(tid, NULL);168 if (rc) {169 errno = rc;170 perror("pthread_join");171 exit(1);172 }173 iterations++;174 }175}176 177static void sigalrm_handler(int junk)178{179 unsigned long i = iterations;180 181 printf("%ld\n", i - iterations_prev);182 iterations_prev = i;183 184 if (--timeout == 0)185 kill(0, SIGUSR1);186 187 alarm(1);188}189 190static void sigusr1_handler(int junk)191{192 exit(0);193}194 195static void *bench_proc(void *arg)196{197 signal(SIGALRM, sigalrm_handler);198 alarm(1);199 200 if (do_fork)201 bench_fork();202 else if (do_vfork)203 bench_vfork();204 else205 bench_thread();206 207 return NULL;208}209 210static struct option options[] = {211 { "fork", no_argument, &do_fork, 1 },212 { "vfork", no_argument, &do_vfork, 1 },213 { "exec", no_argument, &do_exec, 1 },214 { "timeout", required_argument, 0, 's' },215 { "exec-target", no_argument, &exec_target, 1 },216 { NULL },217};218 219static void usage(void)220{221 fprintf(stderr, "Usage: fork <options> CPU\n\n");222 fprintf(stderr, "\t\t--fork\tUse fork() (default threads)\n");223 fprintf(stderr, "\t\t--vfork\tUse vfork() (default threads)\n");224 fprintf(stderr, "\t\t--exec\tAlso exec() (default no exec)\n");225 fprintf(stderr, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n");226 fprintf(stderr, "\t\t--exec-target\tInternal option for exec workload\n");227}228 229int main(int argc, char *argv[])230{231 signed char c;232 233 while (1) {234 int option_index = 0;235 236 c = getopt_long(argc, argv, "", options, &option_index);237 238 if (c == -1)239 break;240 241 switch (c) {242 case 0:243 if (options[option_index].flag != 0)244 break;245 246 usage();247 exit(1);248 break;249 250 case 's':251 timeout = atoi(optarg);252 break;253 254 default:255 usage();256 exit(1);257 }258 }259 260 if (do_fork && do_vfork) {261 usage();262 exit(1);263 }264 if (do_exec && !do_fork && !do_vfork) {265 usage();266 exit(1);267 }268 269 if (do_exec) {270 char *dirname = strdup(argv[0]);271 int i;272 i = strlen(dirname) - 1;273 while (i) {274 if (dirname[i] == '/') {275 dirname[i] = '\0';276 if (chdir(dirname) == -1) {277 perror("chdir");278 exit(1);279 }280 break;281 }282 i--;283 }284 }285 286 if (exec_target) {287 exit(0);288 }289 290 if (((argc - optind) != 1)) {291 cpu = -1;292 } else {293 cpu = atoi(argv[optind++]);294 }295 296 if (do_exec)297 exec_file = argv[0];298 299 set_cpu(cpu);300 301 printf("Using ");302 if (do_fork)303 printf("fork");304 else if (do_vfork)305 printf("vfork");306 else307 printf("clone");308 309 if (do_exec)310 printf(" + exec");311 312 printf(" on cpu %d\n", cpu);313 314 /* Create a new process group so we can signal everyone for exit */315 setpgid(getpid(), getpid());316 317 signal(SIGUSR1, sigusr1_handler);318 319 start_process_on(bench_proc, NULL, cpu);320 321 while (1)322 sleep(3600);323 324 return 0;325}326