1/* 2 * Copyright (c) 2009 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28#include <unistd.h> 29#include <stdio.h> 30#include <math.h> 31#include <sys/wait.h> 32#include <sys/syscall.h> 33#include <sys/types.h> 34#include <sys/ptrace.h> 35#include <semaphore.h> 36#include <stdlib.h> 37#include <pthread.h> 38#include <fcntl.h> 39#include <errno.h> 40#include <err.h> 41#include <string.h> 42 43#include <libkern/OSAtomic.h> 44 45#include <mach/mach_time.h> 46#include <mach/mach.h> 47#include <mach/task.h> 48#include <mach/semaphore.h> 49 50typedef enum my_policy_type { MY_POLICY_REALTIME, MY_POLICY_TIMESHARE, MY_POLICY_FIXEDPRI } my_policy_type_t; 51 52#define DEFAULT_MAX_SLEEP_NS 2000000000ll /* Two seconds */ 53#define CONSTRAINT_NANOS (20000000ll) /* 20 ms */ 54#define COMPUTATION_NANOS (10000000ll) /* 10 ms */ 55 56struct mach_timebase_info g_mti; 57 58#define assert(truth, label) do { if(!(truth)) { printf("Thread %p: failure on line %d\n", pthread_self(), __LINE__); goto label; } } while (0) 59 60struct second_thread_args { 61 semaphore_t wakeup_semaphore; 62 semaphore_t return_semaphore; 63 uint64_t iterations; 64 my_policy_type_t pol; 65 double *wakeup_second_jitter_arr; 66 uint64_t woke_on_same_cpu; 67 uint64_t too_much; 68 volatile uint64_t last_poke_time; 69 volatile int cpuno; 70}; 71 72extern int cpu_number(void); 73 74void * 75second_thread(void *args); 76 77void 78print_usage() 79{ 80 printf("Usage: jitter [-w] [-s <random seed>] [-n <min sleep, ns>] [-m <max sleep, ns>] <realtime | timeshare | fixed> <num iterations> <traceworthy jitter, ns>\n"); 81} 82 83my_policy_type_t 84parse_thread_policy(const char *str) 85{ 86 if (strcmp(str, "timeshare") == 0) { 87 return MY_POLICY_TIMESHARE; 88 } else if (strcmp(str, "realtime") == 0) { 89 return MY_POLICY_REALTIME; 90 } else if (strcmp(str, "fixed") == 0) { 91 return MY_POLICY_FIXEDPRI; 92 } else { 93 printf("Invalid thread policy %s\n", str); 94 exit(1); 95 } 96} 97 98int 99thread_setup(my_policy_type_t pol) 100{ 101 int res; 102 103 switch (pol) { 104 case MY_POLICY_TIMESHARE: 105 { 106 return 0; 107 } 108 case MY_POLICY_REALTIME: 109 { 110 thread_time_constraint_policy_data_t pol; 111 112 /* Hard-coded realtime parameters (similar to what Digi uses) */ 113 pol.period = 100000; 114 pol.constraint = CONSTRAINT_NANOS * g_mti.denom / g_mti.numer; 115 pol.computation = COMPUTATION_NANOS * g_mti.denom / g_mti.numer; 116 pol.preemptible = 0; /* Ignored by OS */ 117 118 res = thread_policy_set(mach_thread_self(), THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t) &pol, THREAD_TIME_CONSTRAINT_POLICY_COUNT); 119 assert(res == 0, fail); 120 break; 121 } 122 case MY_POLICY_FIXEDPRI: 123 { 124 thread_extended_policy_data_t pol; 125 pol.timeshare = 0; 126 127 res = thread_policy_set(mach_thread_self(), THREAD_EXTENDED_POLICY, (thread_policy_t) &pol, THREAD_EXTENDED_POLICY_COUNT); 128 assert(res == 0, fail); 129 break; 130 } 131 default: 132 { 133 printf("invalid policy type\n"); 134 return 1; 135 } 136 } 137 138 return 0; 139fail: 140 return 1; 141} 142 143uint64_t 144get_random_sleep_length_abs_ns(uint64_t min_sleep_ns, uint64_t max_sleep_ns) 145{ 146 uint64_t tmp; 147 148 tmp = (uint32_t)random(); 149 tmp <<= 32; 150 tmp |= (uint32_t)random(); 151 152 /* Now use the random number to sleep amount within the window */ 153 tmp %= (max_sleep_ns - min_sleep_ns); 154 155 return min_sleep_ns + tmp; 156} 157 158void 159compute_stats(double *values, uint64_t count, double *average_magnitudep, double *maxp, double *minp, double *stddevp) 160{ 161 uint64_t i; 162 double _sum = 0; 163 double _max = 0; 164 double _min = (double)INT64_MAX; 165 double _avg = 0; 166 double _dev = 0; 167 168 for (i = 0; i < count; i++) { 169 _sum += fabs(values[i]); 170 _max = values[i] > _max ? values[i] : _max; 171 _min = values[i] < _min ? values[i] : _min; 172 } 173 174 _avg = _sum / (double)count; 175 176 _dev = 0; 177 for (i = 0; i < count; i++) { 178 _dev += pow((values[i] - _avg), 2); 179 } 180 181 _dev /= count; 182 _dev = sqrt(_dev); 183 184 *average_magnitudep = _avg; 185 *maxp = _max; 186 *minp = _min; 187 *stddevp = _dev; 188} 189 190void 191print_stats_us(const char *label, double avg, double max, double min, double stddev) 192{ 193 printf("Max %s: %.1lfus\n", label, max / 1000.0 * (((double)g_mti.numer) / ((double)g_mti.denom))); 194 printf("Min %s: %.1lfus\n", label, min / 1000.0 * (((double)g_mti.numer) / ((double)g_mti.denom))); 195 printf("Avg magnitude of %s: %.1lfus\n", label, avg / 1000.0 * (((double)g_mti.numer) / ((double)g_mti.denom))); 196 printf("Stddev: %.1lfus\n", stddev / 1000.0 * (((double)g_mti.numer) / ((double)g_mti.denom))); 197 putchar('\n'); 198} 199 200void 201print_stats_fract(const char *label, double avg, double max, double min, double stddev) 202{ 203 printf("Max %s jitter: %.1lf%%\n", label, max * 100); 204 printf("Min %s jitter: %.1lf%%\n", label, min * 100); 205 printf("Avg %s jitter: %.1lf%%\n", label, avg * 100); 206 printf("Stddev: %.1lf%%\n", stddev * 100); 207 putchar('\n'); 208} 209 210int 211main(int argc, char **argv) 212{ 213 uint64_t iterations, i; 214 double *jitter_arr, *fraction_arr; 215 double *wakeup_second_jitter_arr; 216 uint64_t target_time; 217 uint64_t sleep_length_abs; 218 uint64_t min_sleep_ns = 0; 219 uint64_t max_sleep_ns = DEFAULT_MAX_SLEEP_NS; 220 uint64_t wake_time; 221 unsigned random_seed; 222 boolean_t need_seed = TRUE; 223 char ch; 224 int res; 225 kern_return_t kret; 226 my_policy_type_t pol; 227 boolean_t wakeup_second_thread = FALSE; 228 semaphore_t wakeup_semaphore, return_semaphore; 229 230 double avg, stddev, max, min; 231 double avg_fract, stddev_fract, max_fract, min_fract; 232 uint64_t too_much; 233 234 struct second_thread_args secargs; 235 pthread_t secthread; 236 237 mach_timebase_info(&g_mti); 238 239 /* Seed random */ 240 opterr = 0; 241 while ((ch = getopt(argc, argv, "m:n:hs:w")) != -1 && ch != '?') { 242 switch (ch) { 243 case 's': 244 /* Specified seed for random)() */ 245 random_seed = (unsigned)atoi(optarg); 246 srandom(random_seed); 247 need_seed = FALSE; 248 break; 249 case 'm': 250 /* How long per timer? */ 251 max_sleep_ns = strtoull(optarg, NULL, 10); 252 break; 253 case 'n': 254 /* How long per timer? */ 255 min_sleep_ns = strtoull(optarg, NULL, 10); 256 break; 257 case 'w': 258 /* After each timed wait, wakeup another thread */ 259 wakeup_second_thread = TRUE; 260 break; 261 case 'h': 262 print_usage(); 263 exit(0); 264 break; 265 default: 266 fprintf(stderr, "Got unexpected result from getopt().\n"); 267 exit(1); 268 break; 269 } 270 } 271 272 argc -= optind; 273 argv += optind; 274 275 if (argc != 3) { 276 print_usage(); 277 exit(1); 278 } 279 280 if (min_sleep_ns >= max_sleep_ns) { 281 print_usage(); 282 exit(1); 283 } 284 285 if (need_seed) { 286 srandom(time(NULL)); 287 } 288 289 /* What scheduling policy? */ 290 pol = parse_thread_policy(argv[0]); 291 292 /* How many timers? */ 293 iterations = strtoull(argv[1], NULL, 10); 294 295 /* How much jitter is so extreme that we should cut a trace point */ 296 too_much = strtoull(argv[2], NULL, 10); 297 298 /* Array for data */ 299 jitter_arr = (double*)malloc(sizeof(*jitter_arr) * iterations); 300 if (jitter_arr == NULL) { 301 printf("Couldn't allocate array to store results.\n"); 302 exit(1); 303 } 304 305 fraction_arr = (double*)malloc(sizeof(*fraction_arr) * iterations); 306 if (fraction_arr == NULL) { 307 printf("Couldn't allocate array to store results.\n"); 308 exit(1); 309 } 310 311 if (wakeup_second_thread) { 312 /* Array for data */ 313 wakeup_second_jitter_arr = (double*)malloc(sizeof(*jitter_arr) * iterations); 314 if (wakeup_second_jitter_arr == NULL) { 315 printf("Couldn't allocate array to store results.\n"); 316 exit(1); 317 } 318 319 kret = semaphore_create(mach_task_self(), &wakeup_semaphore, SYNC_POLICY_FIFO, 0); 320 if (kret != KERN_SUCCESS) { 321 printf("Couldn't allocate semaphore %d\n", kret); 322 exit(1); 323 } 324 325 kret = semaphore_create(mach_task_self(), &return_semaphore, SYNC_POLICY_FIFO, 0); 326 if (kret != KERN_SUCCESS) { 327 printf("Couldn't allocate semaphore %d\n", kret); 328 exit(1); 329 } 330 331 332 secargs.wakeup_semaphore = wakeup_semaphore; 333 secargs.return_semaphore = return_semaphore; 334 secargs.iterations = iterations; 335 secargs.pol = pol; 336 secargs.wakeup_second_jitter_arr = wakeup_second_jitter_arr; 337 secargs.woke_on_same_cpu = 0; 338 secargs.too_much = too_much; 339 secargs.last_poke_time = 0ULL; 340 secargs.cpuno = 0; 341 342 res = pthread_create(§hread, NULL, second_thread, &secargs); 343 if (res) { 344 err(1, "pthread_create"); 345 } 346 347 sleep(1); /* Time for other thread to start up */ 348 } 349 350 /* Set scheduling policy */ 351 res = thread_setup(pol); 352 if (res != 0) { 353 printf("Couldn't set thread policy.\n"); 354 exit(1); 355 } 356 357 /* 358 * Repeatedly pick a random timer length and 359 * try to sleep exactly that long 360 */ 361 for (i = 0; i < iterations; i++) { 362 sleep_length_abs = (uint64_t) (get_random_sleep_length_abs_ns(min_sleep_ns, max_sleep_ns) * (((double)g_mti.denom) / ((double)g_mti.numer))); 363 target_time = mach_absolute_time() + sleep_length_abs; 364 365 /* Sleep */ 366 kret = mach_wait_until(target_time); 367 wake_time = mach_absolute_time(); 368 369 jitter_arr[i] = (double)(wake_time - target_time); 370 fraction_arr[i] = jitter_arr[i] / ((double)sleep_length_abs); 371 372 /* Too much: cut a tracepoint for a debugger */ 373 if (jitter_arr[i] >= too_much) { 374 syscall(SYS_kdebug_trace, 0xeeeeeeee, 0, 0, 0, 0); 375 } 376 377 if (wakeup_second_thread) { 378 secargs.last_poke_time = mach_absolute_time(); 379 secargs.cpuno = cpu_number(); 380 OSMemoryBarrier(); 381 kret = semaphore_signal(wakeup_semaphore); 382 if (kret != KERN_SUCCESS) { 383 errx(1, "semaphore_signal"); 384 } 385 386 kret = semaphore_wait(return_semaphore); 387 if (kret != KERN_SUCCESS) { 388 errx(1, "semaphore_wait"); 389 } 390 391 } 392 } 393 394 /* 395 * Compute statistics and output results. 396 */ 397 compute_stats(jitter_arr, iterations, &avg, &max, &min, &stddev); 398 compute_stats(fraction_arr, iterations, &avg_fract, &max_fract, &min_fract, &stddev_fract); 399 400 putchar('\n'); 401 print_stats_us("jitter", avg, max, min, stddev); 402 print_stats_fract("%", avg_fract, max_fract, min_fract, stddev_fract); 403 404 if (wakeup_second_thread) { 405 406 res = pthread_join(secthread, NULL); 407 if (res) { 408 err(1, "pthread_join"); 409 } 410 411 compute_stats(wakeup_second_jitter_arr, iterations, &avg, &max, &min, &stddev); 412 413 putchar('\n'); 414 print_stats_us("second jitter", avg, max, min, stddev); 415 416 putchar('\n'); 417 printf("%llu/%llu (%.1f%%) wakeups on same CPU\n", secargs.woke_on_same_cpu, iterations, 418 100.0*((double)secargs.woke_on_same_cpu)/iterations); 419 } 420 421 return 0; 422} 423 424void * 425second_thread(void *args) 426{ 427 struct second_thread_args *secargs = (struct second_thread_args *)args; 428 int res; 429 uint64_t i; 430 kern_return_t kret; 431 uint64_t wake_time; 432 int cpuno; 433 434 /* Set scheduling policy */ 435 res = thread_setup(secargs->pol); 436 if (res != 0) { 437 printf("Couldn't set thread policy.\n"); 438 exit(1); 439 } 440 441 /* 442 * Repeatedly pick a random timer length and 443 * try to sleep exactly that long 444 */ 445 for (i = 0; i < secargs->iterations; i++) { 446 447 /* Wake up when poked by main thread */ 448 kret = semaphore_wait(secargs->wakeup_semaphore); 449 if (kret != KERN_SUCCESS) { 450 errx(1, "semaphore_wait %d", kret); 451 } 452 453 wake_time = mach_absolute_time(); 454 cpuno = cpu_number(); 455 if (wake_time < secargs->last_poke_time) { 456 /* Woke in past, unsynchronized mach_absolute_time()? */ 457 458 errx(1, "woke in past %llu (%d) < %llu (%d)", wake_time, cpuno, secargs->last_poke_time, secargs->cpuno); 459 } 460 461 if (cpuno == secargs->cpuno) { 462 secargs->woke_on_same_cpu++; 463 } 464 465 secargs->wakeup_second_jitter_arr[i] = (double)(wake_time - secargs->last_poke_time); 466 467 /* Too much: cut a tracepoint for a debugger */ 468 if (secargs->wakeup_second_jitter_arr[i] >= secargs->too_much) { 469 syscall(SYS_kdebug_trace, 0xeeeeeeef, 0, 0, 0, 0); 470 } 471 472 kret = semaphore_signal(secargs->return_semaphore); 473 if (kret != KERN_SUCCESS) { 474 errx(1, "semaphore_signal %d", kret); 475 } 476 477 } 478 479 return NULL; 480} 481