Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 
0003 /*
0004  * Context switch microbenchmark.
0005  *
0006  * Copyright 2018, Anton Blanchard, IBM Corp.
0007  */
0008 
0009 #define _GNU_SOURCE
0010 #include <assert.h>
0011 #include <errno.h>
0012 #include <getopt.h>
0013 #include <limits.h>
0014 #include <linux/futex.h>
0015 #include <pthread.h>
0016 #include <sched.h>
0017 #include <signal.h>
0018 #include <stdio.h>
0019 #include <stdlib.h>
0020 #include <string.h>
0021 #include <sys/shm.h>
0022 #include <sys/syscall.h>
0023 #include <sys/time.h>
0024 #include <sys/types.h>
0025 #include <sys/wait.h>
0026 #include <unistd.h>
0027 
0028 static unsigned int timeout = 30;
0029 
0030 static void set_cpu(int cpu)
0031 {
0032     cpu_set_t cpuset;
0033 
0034     if (cpu == -1)
0035         return;
0036 
0037     CPU_ZERO(&cpuset);
0038     CPU_SET(cpu, &cpuset);
0039 
0040     if (sched_setaffinity(0, sizeof(cpuset), &cpuset)) {
0041         perror("sched_setaffinity");
0042         exit(1);
0043     }
0044 }
0045 
0046 static void start_process_on(void *(*fn)(void *), void *arg, int cpu)
0047 {
0048     int pid;
0049 
0050     pid = fork();
0051     if (pid == -1) {
0052         perror("fork");
0053         exit(1);
0054     }
0055 
0056     if (pid)
0057         return;
0058 
0059     set_cpu(cpu);
0060 
0061     fn(arg);
0062 
0063     exit(0);
0064 }
0065 
0066 static int cpu;
0067 static int do_fork = 0;
0068 static int do_vfork = 0;
0069 static int do_exec = 0;
0070 static char *exec_file;
0071 static int exec_target = 0;
0072 static unsigned long iterations;
0073 static unsigned long iterations_prev;
0074 
0075 static void run_exec(void)
0076 {
0077     char *const argv[] = { "./exec_target", NULL };
0078 
0079     if (execve("./exec_target", argv, NULL) == -1) {
0080         perror("execve");
0081         exit(1);
0082     }
0083 }
0084 
0085 static void bench_fork(void)
0086 {
0087     while (1) {
0088         pid_t pid = fork();
0089         if (pid == -1) {
0090             perror("fork");
0091             exit(1);
0092         }
0093         if (pid == 0) {
0094             if (do_exec)
0095                 run_exec();
0096             _exit(0);
0097         }
0098         pid = waitpid(pid, NULL, 0);
0099         if (pid == -1) {
0100             perror("waitpid");
0101             exit(1);
0102         }
0103         iterations++;
0104     }
0105 }
0106 
0107 static void bench_vfork(void)
0108 {
0109     while (1) {
0110         pid_t pid = vfork();
0111         if (pid == -1) {
0112             perror("fork");
0113             exit(1);
0114         }
0115         if (pid == 0) {
0116             if (do_exec)
0117                 run_exec();
0118             _exit(0);
0119         }
0120         pid = waitpid(pid, NULL, 0);
0121         if (pid == -1) {
0122             perror("waitpid");
0123             exit(1);
0124         }
0125         iterations++;
0126     }
0127 }
0128 
0129 static void *null_fn(void *arg)
0130 {
0131     pthread_exit(NULL);
0132 }
0133 
0134 static void bench_thread(void)
0135 {
0136     pthread_t tid;
0137     cpu_set_t cpuset;
0138     pthread_attr_t attr;
0139     int rc;
0140 
0141     rc = pthread_attr_init(&attr);
0142     if (rc) {
0143         errno = rc;
0144         perror("pthread_attr_init");
0145         exit(1);
0146     }
0147 
0148     if (cpu != -1) {
0149         CPU_ZERO(&cpuset);
0150         CPU_SET(cpu, &cpuset);
0151 
0152         rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
0153         if (rc) {
0154             errno = rc;
0155             perror("pthread_attr_setaffinity_np");
0156             exit(1);
0157         }
0158     }
0159 
0160     while (1) {
0161         rc = pthread_create(&tid, &attr, null_fn, NULL);
0162         if (rc) {
0163             errno = rc;
0164             perror("pthread_create");
0165             exit(1);
0166         }
0167         rc = pthread_join(tid, NULL);
0168         if (rc) {
0169             errno = rc;
0170             perror("pthread_join");
0171             exit(1);
0172         }
0173         iterations++;
0174     }
0175 }
0176 
0177 static void sigalrm_handler(int junk)
0178 {
0179     unsigned long i = iterations;
0180 
0181     printf("%ld\n", i - iterations_prev);
0182     iterations_prev = i;
0183 
0184     if (--timeout == 0)
0185         kill(0, SIGUSR1);
0186 
0187     alarm(1);
0188 }
0189 
0190 static void sigusr1_handler(int junk)
0191 {
0192     exit(0);
0193 }
0194 
0195 static void *bench_proc(void *arg)
0196 {
0197     signal(SIGALRM, sigalrm_handler);
0198     alarm(1);
0199 
0200     if (do_fork)
0201         bench_fork();
0202     else if (do_vfork)
0203         bench_vfork();
0204     else
0205         bench_thread();
0206 
0207     return NULL;
0208 }
0209 
0210 static struct option options[] = {
0211     { "fork", no_argument, &do_fork, 1 },
0212     { "vfork", no_argument, &do_vfork, 1 },
0213     { "exec", no_argument, &do_exec, 1 },
0214     { "timeout", required_argument, 0, 's' },
0215     { "exec-target", no_argument, &exec_target, 1 },
0216     { NULL },
0217 };
0218 
0219 static void usage(void)
0220 {
0221     fprintf(stderr, "Usage: fork <options> CPU\n\n");
0222     fprintf(stderr, "\t\t--fork\tUse fork() (default threads)\n");
0223     fprintf(stderr, "\t\t--vfork\tUse vfork() (default threads)\n");
0224     fprintf(stderr, "\t\t--exec\tAlso exec() (default no exec)\n");
0225     fprintf(stderr, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n");
0226     fprintf(stderr, "\t\t--exec-target\tInternal option for exec workload\n");
0227 }
0228 
0229 int main(int argc, char *argv[])
0230 {
0231     signed char c;
0232 
0233     while (1) {
0234         int option_index = 0;
0235 
0236         c = getopt_long(argc, argv, "", options, &option_index);
0237 
0238         if (c == -1)
0239             break;
0240 
0241         switch (c) {
0242         case 0:
0243             if (options[option_index].flag != 0)
0244                 break;
0245 
0246             usage();
0247             exit(1);
0248             break;
0249 
0250         case 's':
0251             timeout = atoi(optarg);
0252             break;
0253 
0254         default:
0255             usage();
0256             exit(1);
0257         }
0258     }
0259 
0260     if (do_fork && do_vfork) {
0261         usage();
0262         exit(1);
0263     }
0264     if (do_exec && !do_fork && !do_vfork) {
0265         usage();
0266         exit(1);
0267     }
0268 
0269     if (do_exec) {
0270         char *dirname = strdup(argv[0]);
0271         int i;
0272         i = strlen(dirname) - 1;
0273         while (i) {
0274             if (dirname[i] == '/') {
0275                 dirname[i] = '\0';
0276                 if (chdir(dirname) == -1) {
0277                     perror("chdir");
0278                     exit(1);
0279                 }
0280                 break;
0281             }
0282             i--;
0283         }
0284     }
0285 
0286     if (exec_target) {
0287         exit(0);
0288     }
0289 
0290     if (((argc - optind) != 1)) {
0291         cpu = -1;
0292     } else {
0293         cpu = atoi(argv[optind++]);
0294     }
0295 
0296     if (do_exec)
0297         exec_file = argv[0];
0298 
0299     set_cpu(cpu);
0300 
0301     printf("Using ");
0302     if (do_fork)
0303         printf("fork");
0304     else if (do_vfork)
0305         printf("vfork");
0306     else
0307         printf("clone");
0308 
0309     if (do_exec)
0310         printf(" + exec");
0311 
0312     printf(" on cpu %d\n", cpu);
0313 
0314     /* Create a new process group so we can signal everyone for exit */
0315     setpgid(getpid(), getpid());
0316 
0317     signal(SIGUSR1, sigusr1_handler);
0318 
0319     start_process_on(bench_proc, NULL, cpu);
0320 
0321     while (1)
0322         sleep(3600);
0323 
0324     return 0;
0325 }