migrate.c final version
/* * rt-migrate-test.c * * Copyright (C) 2007-2009 Steven Rostedt <srostedt@redhat.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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; version 2 of the License (not later!) * * 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 <stdio.h> #ifndef __USE_XOPEN2K # define __USE_XOPEN2K #endif #include <stdlib.h> #include <string.h> #include <getopt.h> #include <stdarg.h> #include <unistd.h> #include <ctype.h> #include <time.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> #include <sys/time.h> #include <linux/unistd.h> #include <sys/syscall.h> #include <errno.h> #include <sched.h> #include <pthread.h> #define gettid() syscall(__NR_gettid) int nr_tasks; int nr_locks; int sleep_time = 5; int diffprio = 1; int nopi; int numprio; int can_migrate = 1; int cpus; static int read_ctx_switches(int pid, int *vol, int *nonvol, int *migrate); static int mark_fd = -1; static __thread char buff[BUFSIZ+1]; static void setup_ftrace_marker(void) { struct stat st; char *files[] = { "/sys/kernel/debug/tracing/trace_marker", "/debug/tracing/trace_marker", "/debugfs/tracing/trace_marker", }; int ret; int i; for (i = 0; i < (sizeof(files) / sizeof(char *)); i++) { ret = stat(files[i], &st); if (ret >= 0) goto found; } /* todo, check mounts system */ return; found: mark_fd = open(files[i], O_WRONLY); } static void ftrace_write(const char *fmt, ...) { va_list ap; int n; if (mark_fd < 0) return; va_start(ap, fmt); n = vsnprintf(buff, BUFSIZ, fmt, ap); va_end(ap); write(mark_fd, buff, n); } #define nano2sec(nan) (nan / 1000000000ULL) #define nano2ms(nan) (nan / 1000000ULL) #define nano2usec(nan) (nan / 1000ULL) #define usec2nano(sec) (sec * 1000ULL) #define ms2nano(ms) (ms * 1000000ULL) #define sec2nano(sec) (sec * 1000000000ULL) #define RUN_INTERVAL ms2nano(1ULL) #define NR_RUNS 50 #define PRIO_START 2 #define PROGRESS_CHARS 70 static int nr_runs = NR_RUNS; static int prio_start = PRIO_START; static unsigned long long now; static unsigned long long end; static pthread_barrier_t start_barrier; static pthread_barrier_t end_barrier; static pthread_mutex_t *locks; static unsigned long long *lock_time; static unsigned long long **lock_wait_time; static int *iterations; static int *vol_switches; static int *nonvol_switches; static int *migrated; static long *thread_pids; static int done; static char buffer[BUFSIZ]; static void perr(char *fmt, ...) { va_list ap; va_start(ap, fmt); vsnprintf(buffer, BUFSIZ, fmt, ap); va_end(ap); perror(buffer); fflush(stderr); exit(-1); } static void usage(char **argv) { char *arg = argv[0]; char *p = arg+strlen(arg); while (p >= arg && *p != '/') p--; p++; printf("Usage:\n" "%s <options> nr_tasks\n\n" "-p prio --prio prio base priority to start RT tasks with (2) \n" "-l locks --locks locks number of locks to have\n" "-r time --run-time time Run time (secs)\n" "-o # --limitprio # Limit RT priority tasks to #\n" "-s --same Keep threads at same prio\n" "-n --nopi Do not use prio inheritance mutexes\n" "-m --nomigrate CPU/2 low prio tasks are pinned to CPU\n" " () above are defaults \n", p); exit(0); } static void parse_options (int argc, char *argv[]) { for (;;) { int option_index = 0; /** Options for getopt */ static struct option long_options[] = { {"prio", required_argument, NULL, 'p'}, {"time", required_argument, NULL, 'r'}, {"locks", required_argument, NULL, 'l'}, {"same", no_argument, NULL, 's'}, {"nopi", no_argument, NULL, 'n'}, {"limitprio", no_argument, NULL, 'o'}, {"nomigrate", no_argument, NULL, 'm'}, {"help", no_argument, NULL, '?'}, {NULL, 0, NULL, 0} }; int c = getopt_long (argc, argv, "p:r:l:o:snmh", long_options, &option_index); if (c == -1) break; switch (c) { case 'p': prio_start = atoi(optarg); break; case 'l': nr_locks = atoi(optarg); break; case 'r': sleep_time = atoi(optarg); break; case 'o': numprio = atoi(optarg); break; case 's': diffprio = 0; break; case 'n': nopi = 1; break; case 'm': can_migrate = 0; break; case '?': case 'h': usage(argv); break; } } } static unsigned long long get_time(void) { struct timeval tv; unsigned long long time; gettimeofday(&tv, NULL); time = sec2nano(tv.tv_sec); time += usec2nano(tv.tv_usec); return time; } static unsigned long busy_loop(int x) { unsigned long long start_time; unsigned long long time; unsigned long l = 0; if (x <= 0) return 0; start_time = get_time(); do { l++; time = get_time(); } while ((time - start_time) < RUN_INTERVAL * (x + 1)); return l; } static void ms_sleep(int ms) { struct timespec ts; unsigned long sec = 0; unsigned long nsec; if (ms <= 0) return; if (ms > 1000) { sec = ms / 1000; ms -= sec * 100; } nsec = ms * 1000000; ts.tv_sec = sec; ts.tv_nsec = nsec; nanosleep(&ts, NULL); } static void print_results(void) { long total_vol = 0; long total_nonvol = 0; long total_migrate = 0; long total_iterations = 0; int i; printf("Task vol nonvol migrated iterations\n" "---- --- ------ -------- ----------\n"); for (i = 0; i < nr_tasks; i++) { printf(" %d: %6d %8d %8d %8d\n", i, vol_switches[i], nonvol_switches[i], migrated[i], iterations[i]); total_vol += vol_switches[i]; total_nonvol += nonvol_switches[i]; total_migrate += migrated[i]; total_iterations += iterations[i]; } printf("\ntotal: %6ld %8ld %8ld %8ld\n", total_vol, total_nonvol, total_migrate, total_iterations); } static void grab_lock(long id, int iter, int l) { unsigned long long try_time; unsigned long long time; unsigned long long delta; ftrace_write("thread %ld iter %d, taking lock %d\n", id, iter, l); try_time = get_time(); pthread_mutex_lock(&locks[l]); time = get_time(); delta = time - try_time; if (iter < nr_runs) lock_wait_time[l][iter] = time - try_time; ftrace_write("thread %ld iter %d, took lock %d in %llu us\n", id, iter, delta / 1000); busy_loop(nr_tasks - id); ftrace_write("thread %ld iter %d, unlock lock %d\n", id, iter, l); pthread_mutex_unlock(&locks[l]); } void *start_task(void *data) { long id = (long)data; unsigned long long start_time; struct sched_param param = { .sched_priority = id * diffprio + prio_start, }; int ret; long pid; int i, l; pid = gettid(); if (!numprio || id >= (nr_tasks - numprio)) { ret = sched_setscheduler(0, SCHED_FIFO, ¶m); if (ret < 0 && !id) fprintf(stderr, "Warning, can't set priorities\n"); } /* some processes may need to be pinned */ if (!can_migrate && id < cpus) { cpu_set_t cpumask; int cpu; /* if possible, do not pin the lowest task */ if (!id && nr_tasks > 2) goto skip; if (nr_tasks < 3) cpu = id; else cpu = id - 1; if (cpu >= cpus/2) goto skip; CPU_ZERO(&cpumask); CPU_SET(id, &cpumask); /* bind to CPU */ sched_setaffinity(0, sizeof(cpumask), &cpumask); } skip: pthread_barrier_wait(&start_barrier); start_time = get_time(); ftrace_write("Thread %d: started %lld diff %lld\n", pid, start_time, start_time - now); i = 0; while (!done) { for (l = 0; l < nr_locks; l++) { grab_lock(id, i, l); ftrace_write("thread %ld iter %d sleeping\n", id, i); ms_sleep(id); } i++; } iterations[id] = i; read_ctx_switches(gettid(), &vol_switches[id], &nonvol_switches[id], &migrated[id]); pthread_barrier_wait(&end_barrier); return (void*)pid; } static void stop_log(int sig) { done = 1; } static int get_value(const char *line) { const char *p; for (p = line; isspace(*p); p++) ; if (*p != ':') return -1; p++; for (; isspace(*p); p++) ; return atoi(p); } static int update_value(const char *line, int *val, const char *name) { int ret; if (strncmp(line, name, strlen(name)) == 0) { ret = get_value(line + strlen(name)); if (ret < 0) return 0; *val = ret; return 1; } return 0; } static int read_ctx_switches(int pid, int *vol, int *nonvol, int *migrate) { static int vol_once, nonvol_once; const char *vol_name = "nr_voluntary_switches"; const char *nonvol_name = "nr_involuntary_switches"; const char *migrate_name = "se.nr_migrations"; char file[1024]; char buf[1024]; char *pbuf; size_t *pn; size_t n; FILE *fp; int r; snprintf(file, 1024, "/proc/%d/sched", pid); fp = fopen(file, "r"); if (!fp) { snprintf(file, 1024, "/proc/%d/status", pid); fp = fopen(file, "r"); if (!fp) perr("could not open %s", file); vol_name = "voluntary_ctxt_switches"; nonvol_name = "nonvoluntary_ctxt_switches"; } *vol = *nonvol = *migrate = -1; n = 1024; pn = &n; pbuf = buf; while ((r = getline(&pbuf, pn, fp)) >= 0) { if (update_value(buf, vol, vol_name)) continue; if (update_value(buf, nonvol, nonvol_name)) continue; if (update_value(buf, migrate, migrate_name)) continue; } fclose(fp); if (!vol_once && *vol == -1) { vol_once++; fprintf(stderr, "Warning, could not find voluntary ctx switch count\n"); } if (!nonvol_once && *nonvol == -1) { nonvol_once++; fprintf(stderr, "Warning, could not find nonvoluntary ctx switch count\n"); } return 0; } static int count_cpus(void) { return sysconf(_SC_NPROCESSORS_ONLN); } static void *__do_malloc(int size, const char *str) { void *ret; ret = malloc(size); if (!ret) perr("malloc %s", str); memset(ret, 0, size); return ret; } #define do_malloc(obj, size) \ __do_malloc(size * sizeof(*obj), #obj) int main (int argc, char **argv) { pthread_t *threads; long i; int ret; struct sched_param param; pthread_mutexattr_t attr; pthread_mutexattr_t *pattr; parse_options(argc, argv); signal(SIGINT, stop_log); cpus = count_cpus(); if (cpus < 2) perr("Test must be run on SMP box (more than 1 CPU)"); if (argc >= (optind + 1)) nr_tasks = atoi(argv[optind]); else nr_tasks = cpus * 2; if (!nr_locks) nr_locks = cpus; if (nr_tasks < 1) perr("Need at least 1 task to run"); threads = do_malloc(threads, nr_tasks); locks = do_malloc(locks, nr_locks); iterations = do_malloc(iterations, nr_tasks); if (nopi) pattr = NULL; else { if (pthread_mutexattr_init(&attr)) perr("pthread_mutexattr_init"); if (pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT)) perr("pthread_mutexattr_setprotocol"); pattr = &attr; } for (i = 0; i < nr_locks; i++) { ret = pthread_mutex_init(&locks[i], pattr); if (ret < 0) perr("pthread_mutex_init"); } lock_time = do_malloc(lock_time, nr_locks); lock_wait_time = do_malloc(lock_wait_time, nr_locks); for (i = 0; i < nr_locks; i++) { lock_wait_time[i] = malloc(sizeof(**lock_wait_time) * nr_runs); if (!lock_wait_time[i]) perr("malloc"); memset(lock_wait_time[i], 0, sizeof(**lock_wait_time) * nr_runs); } vol_switches = do_malloc(vol_switches, nr_tasks); nonvol_switches = do_malloc(nonvol_switches, nr_tasks); migrated = do_malloc(migrated, nr_tasks); ret = pthread_barrier_init(&start_barrier, NULL, nr_tasks + 1); if (ret < 0) perr("pthread_barrier_init"); ret = pthread_barrier_init(&end_barrier, NULL, nr_tasks + 1); if (ret < 0) perr("pthread_barrier_init"); thread_pids = malloc(sizeof(long) * nr_tasks); if (!thread_pids) perr("malloc thread_pids"); for (i=0; i < nr_tasks; i++) { if (pthread_create(&threads[i], NULL, start_task, (void *)i)) perr("pthread_create"); } /* up our prio above all tasks */ memset(¶m, 0, sizeof(param)); param.sched_priority = nr_tasks + prio_start; if (sched_setscheduler(0, SCHED_FIFO, ¶m)) fprintf(stderr, "Warning, can't set priority of main thread!\n"); setup_ftrace_marker(); pthread_barrier_wait(&start_barrier); ftrace_write("All running!!!\n"); sleep(sleep_time); end = get_time(); ftrace_write("End=%lld now=%lld diff=%lld\n", end, end - now); done = 1; pthread_barrier_wait(&end_barrier); putc('\n', stderr); for (i=0; i < nr_tasks; i++) pthread_join(threads[i], (void*)&thread_pids[i]); print_results(); return 0; }
Posted Apr 11, 2014 19:01 UTC (Fri)
by blitzkrieg3 (guest, #57873)
[Link]
ftrace_write("thread %ld iter %d, took lock %d in %llu us\n",
migrate.c final version
The timing put into the ftrace buffer is wrong, there are too few variables there. It should be:
id, iter, l, delta / 1000);