diff --git a/tools/regression/README b/tools/regression/README index c7ecdab9fc98..0c98abb606b2 100644 --- a/tools/regression/README +++ b/tools/regression/README @@ -27,3 +27,4 @@ fsx General filesystem exerciser sysvmsg SysV IPC Message Queue Regression Utility sysvsem SysV IPC Semaphore Regression Utility sysvshm SysV IPC Shared Memory Regression Utility +gaithrstress General threaded getaddrinfo(3) exerciser diff --git a/tools/regression/gaithrstress/Makefile b/tools/regression/gaithrstress/Makefile new file mode 100644 index 000000000000..2166aee20fa1 --- /dev/null +++ b/tools/regression/gaithrstress/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +PROG= gaithrstress +LDADD+= -pthread + +NOMAN= yes +.include diff --git a/tools/regression/gaithrstress/gaithrstress.c b/tools/regression/gaithrstress/gaithrstress.c new file mode 100644 index 000000000000..18a7ec806450 --- /dev/null +++ b/tools/regression/gaithrstress/gaithrstress.c @@ -0,0 +1,245 @@ +/*- + * Copyright (c) 2004 Brian Fundakowski Feldman + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * $Id: getaddrinfo-pthreads-stresstest.c,v 1.4 2004/02/20 03:54:17 green Exp green $ + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Per-thread struct containing all important data. */ +struct worker { + pthread_t w_thread; /* self */ + uintmax_t w_lookup_success, w_lookup_failure; /* getaddrinfo stats */ +}; + +static volatile int workers_stop = 0; +static double max_random_sleep = 1.0; +static char **randwords; +static size_t nrandwords; + +/* + * We don't have good random(3)-type functions that are thread-safe, + * unfortunately. + */ +static u_int32_t +my_arc4random_r(void) +{ + static pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER; + u_int32_t ret; + + (void)pthread_mutex_lock(&mymutex); + ret = arc4random(); + (void)pthread_mutex_unlock(&mymutex); + return (ret); +} + +static void +randomsleep(double max_sleep_sec) +{ + struct timespec slptime = { 0, 0 }; + double rndsleep; + + rndsleep = (double)my_arc4random_r() / 4294967296.0 * max_sleep_sec; + while (rndsleep >= 1.0) { + slptime.tv_sec++; + rndsleep -= 1.0; + } + slptime.tv_nsec = rndsleep * 1e9; + (void)nanosleep(&slptime, NULL); +} + +/* + * Start looking up arbitrary hostnames and record the successes/failures. + * Between lookups, sleep a random amount of time to make sure threads + * stay well out of synchronization. + */ +static void * +work(void *arg) +{ + struct worker *w = arg; + + /* Turn off domain name list searching. */ + if (_res.options & RES_INIT || res_init() == 0) + _res.options &= ~RES_DNSRCH; + do { + const char *suffixes[] = { "net", "com", "org" }; + const size_t nsuffixes = sizeof(suffixes) / sizeof(suffixes[0]); + struct addrinfo *res; + char *hostname; + int error; + + randomsleep(max_random_sleep); + if (asprintf(&hostname, "%s%s%s.%s", + (my_arc4random_r() % 2) == 0 ? "www." : "", + randwords[my_arc4random_r() % nrandwords], + (my_arc4random_r() % 3) == 0 ? + randwords[my_arc4random_r() % nrandwords] : "", + suffixes[my_arc4random_r() % nsuffixes]) == -1) + continue; + error = getaddrinfo(hostname, NULL, NULL, &res); + free(hostname); + if (error == 0) { + w->w_lookup_success++; + freeaddrinfo(res); + } else { + w->w_lookup_failure++; + } + } while (!workers_stop); + + pthread_exit(NULL); +} + +int +dowordfile(const char *fname) +{ + FILE *fp; + char newword[64]; + size_t n; + + fp = fopen(fname, "r"); + if (fp == NULL) + return (-1); + nrandwords = 0; + while (fgets(newword, sizeof(newword), fp) != NULL) + nrandwords++; + if (ferror(fp) || fseek(fp, 0, SEEK_SET) != 0) + goto fail; + randwords = calloc(nrandwords, sizeof(char *)); + if (randwords == NULL) + goto fail; + n = nrandwords; + nrandwords = 0; + while (fgets(newword, sizeof(newword), fp) != NULL) { + newword[strcspn(newword, "\r\n")] = '\0'; + randwords[nrandwords] = strdup(newword); + if (randwords[nrandwords] == NULL) + err(1, "reading words file"); + if (++nrandwords == n) + break; + } + nrandwords = n; + fclose(fp); + return (0); +fail: + fclose(fp); + return (-1); +} + +int +main(int argc, char **argv) { + unsigned long nworkers = 1; + struct worker *workers; + size_t i; + char waiting[3], *send, *wordfile = "/usr/share/dict/words"; + int ch; + + if (getprogname() == NULL) + setprogname(argv[0]); + printf("%s: threaded stress-tester for getaddrinfo(3)\n", + getprogname()); + printf("(c) 2004 Brian Feldman \n"); + while ((ch = getopt(argc, argv, "s:t:w:")) != -1) { + switch (ch) { + case 's': + max_random_sleep = strtod(optarg, &send); + if (*send != '\0') + goto usage; + break; + case 't': + nworkers = strtoul(optarg, &send, 0); + if (*send != '\0') + goto usage; + break; + case 'w': + wordfile = optarg; + break; + default: +usage: + fprintf(stderr, "usage: %s [-s sleep] [-t threads] " + "[-w wordfile]\n", getprogname()); + exit(2); + } + } + argc -= optind; + argv += optind; + + if (nworkers < 1 || nworkers != (size_t)nworkers) + goto usage; + if (dowordfile(wordfile) == -1) + err(1, "reading word file %s", wordfile); + if (nrandwords < 1) + errx(1, "word file %s did not have >0 words", wordfile); + printf("Read %u random words from %s.\n", nrandwords, wordfile); + workers = calloc(nworkers, sizeof(*workers)); + if (workers == NULL) + err(1, "allocating workers"); + printf("Intra-query delay time is from 0 to %g seconds (random).\n", + max_random_sleep); + + printf("Starting %lu worker%.*s: ", nworkers, nworkers > 1, "s"); + fflush(stdout); + for (i = 0; i < nworkers; i++) { + if (pthread_create(&workers[i].w_thread, NULL, work, + &workers[i]) == -1) + err(1, "creating worker %u", i); + printf("%u%s", i, i == nworkers - 1 ? ".\n" : ", "); + fflush(stdout); + } + + printf("\n"); + (void)fgets(waiting, sizeof(waiting), stdin); + workers_stop = 1; + + printf("Stopping %lu worker%.*s: ", nworkers, nworkers > 1, "s"); + fflush(stdout); + for (i = 0; i < nworkers; i++) { + pthread_join(workers[i].w_thread, NULL); + printf("%u%s", i, i == nworkers - 1 ? ".\n" : ", "); + fflush(stdout); + } + + printf("%-10s%-20s%-20s\n", "Worker", "Successful GAI", "Failed GAI"); + printf("%-10s%-20s%-20s\n", "------", "--------------", "----------"); + for (i = 0; i < nworkers; i++) { + printf("%-10u%-20ju%-20ju\n", i, workers[i].w_lookup_success, + workers[i].w_lookup_failure); + } + + exit(0); +}