+
+/*
+ * WARNING: This is SPI specifically intended for use by launchd to start UI
+ * apps. We use it here for a test tool only to opt into QoS using the same
+ * policies. Do not use this outside xnu or libxpc/launchd.
+ */
+static void
+selfexec_with_apptype(int argc, char *argv[])
+{
+ int ret;
+ posix_spawnattr_t attr;
+ extern char **environ;
+ char *new_argv[argc + 1 + 1 /* NULL */];
+ int i;
+ char prog[PATH_MAX];
+ uint32_t prog_size = PATH_MAX;
+
+ ret = _NSGetExecutablePath(prog, &prog_size);
+ if (ret) err(EX_OSERR, "_NSGetExecutablePath");
+
+ for (i=0; i < argc; i++) {
+ new_argv[i] = argv[i];
+ }
+
+ new_argv[i] = "--switched_apptype";
+ new_argv[i+1] = NULL;
+
+ ret = posix_spawnattr_init(&attr);
+ if (ret) errc(EX_OSERR, ret, "posix_spawnattr_init");
+
+ ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC);
+ if (ret) errc(EX_OSERR, ret, "posix_spawnattr_setflags");
+
+ ret = posix_spawnattr_setprocesstype_np(&attr, POSIX_SPAWN_PROC_TYPE_APP_DEFAULT);
+ if (ret) errc(EX_OSERR, ret, "posix_spawnattr_setprocesstype_np");
+
+ ret = posix_spawn(NULL, prog, NULL, &attr, new_argv, environ);
+ if (ret) errc(EX_OSERR, ret, "posix_spawn");
+}
+
+/*
+ * Admittedly not very attractive.
+ */
+static void __attribute__((noreturn))
+usage()
+{
+ errx(EX_USAGE, "Usage: %s <threads> <chain | hop | broadcast-single-sem | broadcast-per-thread> "
+ "<realtime | timeshare | fixed> <iterations>\n\t\t"
+ "[--trace <traceworthy latency in ns>] "
+ "[--verbose] [--spin-one] [--spin-all] [--spin-time <nanos>] [--affinity]\n\t\t"
+ "[--no-sleep] [--drop-priority] [--churn-pri <pri>] [--churn-count <n>]",
+ getprogname());
+}
+
+static struct option* g_longopts;
+static int option_index;
+
+static uint32_t
+read_dec_arg()
+{
+ char *cp;
+ /* char* optarg is a magic global */
+
+ uint32_t arg_val = (uint32_t)strtoull(optarg, &cp, 10);
+
+ if (cp == optarg || *cp)
+ errx(EX_USAGE, "arg --%s requires a decimal number, found \"%s\"",
+ g_longopts[option_index].name, optarg);
+
+ return arg_val;
+}
+
+static void
+parse_args(int argc, char *argv[])
+{
+ enum {
+ OPT_GETOPT = 0,
+ OPT_SPIN_TIME,
+ OPT_TRACE,
+ OPT_PRIORITY,
+ OPT_CHURN_PRI,
+ OPT_CHURN_COUNT,
+ };
+
+ static struct option longopts[] = {
+ { "spin-time", required_argument, NULL, OPT_SPIN_TIME },
+ { "trace", required_argument, NULL, OPT_TRACE },
+ { "priority", required_argument, NULL, OPT_PRIORITY },
+ { "churn-pri", required_argument, NULL, OPT_CHURN_PRI },
+ { "churn-count", required_argument, NULL, OPT_CHURN_COUNT },
+ { "switched_apptype", no_argument, (int*)&g_seen_apptype, TRUE },
+ { "spin-one", no_argument, (int*)&g_do_one_long_spin, TRUE },
+ { "spin-all", no_argument, (int*)&g_do_all_spin, TRUE },
+ { "affinity", no_argument, (int*)&g_do_affinity, TRUE },
+ { "no-sleep", no_argument, (int*)&g_do_sleep, FALSE },
+ { "drop-priority", no_argument, (int*)&g_drop_priority, TRUE },
+ { "verbose", no_argument, (int*)&g_verbose, TRUE },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ g_longopts = longopts;
+ int ch = 0;
+
+ while ((ch = getopt_long(argc, argv, "h", longopts, &option_index)) != -1) {
+ switch (ch) {
+ case OPT_GETOPT:
+ /* getopt_long set a variable */
+ break;
+ case OPT_SPIN_TIME:
+ g_do_each_spin = TRUE;
+ g_each_spin_duration_ns = read_dec_arg();
+ break;
+ case OPT_TRACE:
+ g_traceworthy_latency_ns = read_dec_arg();
+ break;
+ case OPT_PRIORITY:
+ g_priority = read_dec_arg();
+ break;
+ case OPT_CHURN_PRI:
+ g_churn_pri = read_dec_arg();
+ break;
+ case OPT_CHURN_COUNT:
+ g_churn_count = read_dec_arg();
+ break;
+ case '?':
+ case 'h':
+ default:
+ usage();
+ /* NORETURN */
+ }
+ }
+
+ /*
+ * getopt_long reorders all the options to the beginning of the argv array.
+ * Jump past them to the non-option arguments.
+ */
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 4) {
+ warnx("Too many non-option arguments passed");
+ usage();
+ }
+
+ if (argc != 4) {
+ warnx("Missing required <threads> <waketype> <policy> <iterations> arguments");
+ usage();
+ }
+
+ char *cp;
+
+ /* How many threads? */
+ g_numthreads = (uint32_t)strtoull(argv[0], &cp, 10);
+
+ if (cp == argv[0] || *cp)
+ errx(EX_USAGE, "numthreads requires a decimal number, found \"%s\"", argv[0]);
+
+ if (g_numthreads < 1)
+ errx(EX_USAGE, "Must use at least one thread");
+
+ /* What wakeup pattern? */
+ g_waketype = parse_wakeup_pattern(argv[1]);
+
+ /* Policy */
+ g_policy = parse_thread_policy(argv[2]);
+
+ /* Iterations */
+ g_iterations = (uint32_t)strtoull(argv[3], &cp, 10);
+
+ if (cp == argv[3] || *cp)
+ errx(EX_USAGE, "numthreads requires a decimal number, found \"%s\"", argv[3]);
+
+ if (g_iterations < 1)
+ errx(EX_USAGE, "Must have at least one iteration");
+
+ if (g_numthreads == 1 && g_waketype == WAKE_CHAIN)
+ errx(EX_USAGE, "chain mode requires more than one thread");
+
+ if (g_numthreads == 1 && g_waketype == WAKE_HOP)
+ errx(EX_USAGE, "hop mode requires more than one thread");
+}
+
+