/* * Copyright (c) 2014 Justus Winter <4winter@informatik.uni-hamburg.de> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include "machometer.h" volatile struct mapped_time_value *mtime; struct producer_args { mach_port_t port; size_t count; struct timeval runtime; }; void * producer (void *args_) { error_t err = 0; struct producer_args *args = args_; struct timeval start_time; maptime_read (mtime, &start_time); for (size_t i = 0; i < args->count; i++) { err = send_empty (args->port); if (err) goto out; } struct timeval end_time; maptime_read (mtime, &end_time); timersub (&end_time, &start_time, &args->runtime); out: return (void *) err; } struct consumer_args { mach_port_t port; size_t count; struct timeval runtime; }; error_t consumer (void *args_) { error_t err; struct consumer_args *args = args_; struct timeval start_time; maptime_read (mtime, &start_time); for (size_t i = 0; i < args->count; i++) { err = receive_empty (args->port); if (err) return err; } struct timeval end_time; maptime_read (mtime, &end_time); timersub (&end_time, &start_time, &args->runtime); out: return 0; } void print_stats (const char *id, size_t n, struct timeval *tv) { double duration = tv->tv_sec + (tv->tv_usec / 1000000.); printf ("% 10s: % 3is%06ius\t%fns\t%9.3f (1/s)\n", id, (int) tv->tv_sec, (int) tv->tv_usec, 1000000000. * duration / n, n / duration); } /* Options. */ mach_port_msgcount_t qlimit = MACH_PORT_QLIMIT_DEFAULT; size_t iterations = 1l<<25; const char *argp_program_version = "machometer-0.0"; static const struct argp_option options[] = { {"iterations", 'n', "N", 0, "Repeat tests N times"}, {"qlimit", 'Q', "LIMIT", NULL, "Set qlimit"}, {NULL} }; /* Parse a command line option. */ error_t parse_opt (int key, char *arg, struct argp_state *state) { char *arg_end; unsigned long *ptr = NULL; switch (key) { case 'n': ptr = ptr ?: &iterations; case 'Q': ptr = ptr ?: &qlimit; *ptr = strtoul (arg, &arg_end, 10); if (*arg == '\0' || *arg_end != '\0') argp_error (state, "invalid integer: %s", arg); break; case ARGP_KEY_ARG: argp_usage (state); return EINVAL; case ARGP_KEY_NO_ARGS: break; default: return ARGP_ERR_UNKNOWN; } return 0; } static struct argp argp = { options, parse_opt, "YYY", "XXX", }; /* mach_print performance test. */ void mach_print(const char *); #ifndef EXTERNAL_MACH_PRINT asm (".global mach_print;" " mach_print:;" " mov $0xffffffe2, %eax;" " lcall $0x7, $0x0;" " ret;"); #endif /* EXTERNAL_MACH_PRINT */ error_t test_mach_print (const size_t n) { error_t err = 0; struct timeval start_time; maptime_read (mtime, &start_time); for (size_t i = 0; i < n; i++) mach_print (""); struct timeval end_time; maptime_read (mtime, &end_time); struct timeval runtime; timersub (&end_time, &start_time, &runtime); print_stats ("mach_print", n, &runtime); return err; } /* Measure the trap overhead of mach_msg. */ error_t test_null_msg (const size_t n) { error_t err = 0; struct timeval start_time; maptime_read (mtime, &start_time); for (size_t i = 0; i < n; i++) { err = mach_msg (NULL, MACH_MSG_OPTION_NONE, 0, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (err) goto out; } struct timeval end_time; maptime_read (mtime, &end_time); struct timeval runtime; timersub (&end_time, &start_time, &runtime); print_stats ("nullmsg", n, &runtime); out: return err; } int main (int argc, char **argv) { error_t err; mach_port_t port; /* Parse our arguments. */ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0); fprintf (stderr, "N: %u (1<<%d), qlimit: %d\n", iterations, __builtin_ffs (iterations) - 1, qlimit); err = maptime_map (0, NULL, &mtime); if (err) error (1, err, "maptime_map"); err = test_mach_print (iterations); if (err) error (1, err, "test_mach_print"); err = test_null_msg (iterations); if (err) error (1, err, "test_null_msg"); err = mach_port_allocate (mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); if (err) error (1, err, "mach_port_allocate"); err = mach_port_set_qlimit (mach_task_self(), port, qlimit); if (err) error (1, err, "mach_port_set_qlimit"); struct producer_args pargs = { .port=port, .count=iterations }; pthread_t producer_thread; err = pthread_create (&producer_thread, NULL, &producer, &pargs); if (err) error (2, err, "pthread_create"); struct consumer_args cargs = { .port=port, .count=iterations }; err = consumer (&cargs); error_t producer_err; err = pthread_join (producer_thread, (void **)&producer_err); if (err) error (3, err, "pthread_join"); if (producer_err) error (4, producer_err, "producer"); print_stats ("producer", iterations, &pargs.runtime); print_stats ("consumer", iterations, &cargs.runtime); return EXIT_SUCCESS; }