1126049Sgreen/*- 2126049Sgreen * Copyright (c) 2004 Brian Fundakowski Feldman 3126049Sgreen * All rights reserved. 4126049Sgreen * 5126049Sgreen * Redistribution and use in source and binary forms, with or without 6126049Sgreen * modification, are permitted provided that the following conditions 7126049Sgreen * are met: 8126049Sgreen * 1. Redistributions of source code must retain the above copyright 9126049Sgreen * notice, this list of conditions and the following disclaimer. 10126049Sgreen * 2. Redistributions in binary form must reproduce the above copyright 11126049Sgreen * notice, this list of conditions and the following disclaimer in the 12126049Sgreen * documentation and/or other materials provided with the distribution. 13126049Sgreen * 14126049Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15126049Sgreen * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16126049Sgreen * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17126049Sgreen * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18126049Sgreen * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19126049Sgreen * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20126049Sgreen * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21126049Sgreen * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22126049Sgreen * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23126049Sgreen * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24126049Sgreen * SUCH DAMAGE. 25126049Sgreen * 26126049Sgreen * $FreeBSD: releng/10.2/tools/regression/gaithrstress/gaithrstress.c 282837 2015-05-13 10:15:26Z ngie $ 27126049Sgreen */ 28126049Sgreen 29126049Sgreen#include <sys/types.h> 30126049Sgreen#include <sys/socket.h> 31126058Sgreen#include <sys/time.h> 32126049Sgreen 33126049Sgreen#include <netinet/in.h> 34126049Sgreen 35126049Sgreen#include <err.h> 36126049Sgreen#include <netdb.h> 37126049Sgreen#include <pthread.h> 38126049Sgreen#include <resolv.h> 39126049Sgreen#include <stdio.h> 40126049Sgreen#include <stdint.h> 41126049Sgreen#include <stdlib.h> 42126049Sgreen#include <string.h> 43126049Sgreen#include <unistd.h> 44126049Sgreen 45126049Sgreen/* Per-thread struct containing all important data. */ 46126049Sgreenstruct worker { 47126049Sgreen pthread_t w_thread; /* self */ 48126049Sgreen uintmax_t w_lookup_success, w_lookup_failure; /* getaddrinfo stats */ 49126058Sgreen struct timespec w_max_lookup_time; 50126049Sgreen}; 51126049Sgreen 52126049Sgreenstatic volatile int workers_stop = 0; 53126049Sgreenstatic double max_random_sleep = 1.0; 54126049Sgreenstatic char **randwords; 55126049Sgreenstatic size_t nrandwords; 56126085Sgreenstatic const struct addrinfo *hints, hintipv4only = { .ai_family = AF_INET }; 57126049Sgreen 58126049Sgreen/* 59126049Sgreen * We don't have good random(3)-type functions that are thread-safe, 60126049Sgreen * unfortunately. 61126049Sgreen */ 62126049Sgreenstatic u_int32_t 63126049Sgreenmy_arc4random_r(void) 64126049Sgreen{ 65126049Sgreen static pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER; 66126049Sgreen u_int32_t ret; 67126049Sgreen 68126049Sgreen (void)pthread_mutex_lock(&mymutex); 69126049Sgreen ret = arc4random(); 70126049Sgreen (void)pthread_mutex_unlock(&mymutex); 71126049Sgreen return (ret); 72126049Sgreen} 73126049Sgreen 74126049Sgreenstatic void 75126049Sgreenrandomsleep(double max_sleep_sec) 76126049Sgreen{ 77126049Sgreen struct timespec slptime = { 0, 0 }; 78126049Sgreen double rndsleep; 79126049Sgreen 80126049Sgreen rndsleep = (double)my_arc4random_r() / 4294967296.0 * max_sleep_sec; 81126049Sgreen while (rndsleep >= 1.0) { 82126049Sgreen slptime.tv_sec++; 83126049Sgreen rndsleep -= 1.0; 84126049Sgreen } 85126049Sgreen slptime.tv_nsec = rndsleep * 1e9; 86126049Sgreen (void)nanosleep(&slptime, NULL); 87126049Sgreen} 88126049Sgreen 89126049Sgreen/* 90126049Sgreen * Start looking up arbitrary hostnames and record the successes/failures. 91126049Sgreen * Between lookups, sleep a random amount of time to make sure threads 92126049Sgreen * stay well out of synchronization. 93126085Sgreen * 94126085Sgreen * Host name: part probability 95126085Sgreen * ---- ----------- 96126085Sgreen * www. 1/2 97126085Sgreen * random word always, equal 98126085Sgreen * random word 1/3, equal 99126085Sgreen * .(net|com|org) equal 100126049Sgreen */ 101126049Sgreenstatic void * 102126049Sgreenwork(void *arg) 103126049Sgreen{ 104126049Sgreen struct worker *w = arg; 105126049Sgreen 106126085Sgreen /* Turn off domain name list searching as much as possible. */ 107126049Sgreen if (_res.options & RES_INIT || res_init() == 0) 108126049Sgreen _res.options &= ~RES_DNSRCH; 109126049Sgreen do { 110126049Sgreen const char *suffixes[] = { "net", "com", "org" }; 111126049Sgreen const size_t nsuffixes = sizeof(suffixes) / sizeof(suffixes[0]); 112126058Sgreen struct timespec ts_begintime, ts_total; 113126049Sgreen struct addrinfo *res; 114126049Sgreen char *hostname; 115126049Sgreen int error; 116126049Sgreen 117126049Sgreen randomsleep(max_random_sleep); 118126049Sgreen if (asprintf(&hostname, "%s%s%s.%s", 119126049Sgreen (my_arc4random_r() % 2) == 0 ? "www." : "", 120126049Sgreen randwords[my_arc4random_r() % nrandwords], 121126049Sgreen (my_arc4random_r() % 3) == 0 ? 122126049Sgreen randwords[my_arc4random_r() % nrandwords] : "", 123126049Sgreen suffixes[my_arc4random_r() % nsuffixes]) == -1) 124126049Sgreen continue; 125126058Sgreen (void)clock_gettime(CLOCK_REALTIME, &ts_begintime); 126126085Sgreen error = getaddrinfo(hostname, NULL, hints, &res); 127126058Sgreen (void)clock_gettime(CLOCK_REALTIME, &ts_total); 128126058Sgreen ts_total.tv_sec -= ts_begintime.tv_sec; 129126058Sgreen ts_total.tv_nsec -= ts_begintime.tv_nsec; 130126058Sgreen if (ts_total.tv_nsec < 0) { 131126058Sgreen ts_total.tv_sec--; 132126058Sgreen ts_total.tv_nsec += 1000000000; 133126058Sgreen } 134126058Sgreen if (ts_total.tv_sec > w->w_max_lookup_time.tv_sec || 135126058Sgreen (ts_total.tv_sec == w->w_max_lookup_time.tv_sec && 136126058Sgreen ts_total.tv_nsec > w->w_max_lookup_time.tv_sec)) 137126058Sgreen w->w_max_lookup_time = ts_total; 138126049Sgreen free(hostname); 139126049Sgreen if (error == 0) { 140126049Sgreen w->w_lookup_success++; 141126049Sgreen freeaddrinfo(res); 142126049Sgreen } else { 143126049Sgreen w->w_lookup_failure++; 144126049Sgreen } 145126049Sgreen } while (!workers_stop); 146126049Sgreen 147126049Sgreen pthread_exit(NULL); 148126049Sgreen} 149126049Sgreen 150126049Sgreenint 151126049Sgreendowordfile(const char *fname) 152126049Sgreen{ 153126049Sgreen FILE *fp; 154126049Sgreen char newword[64]; 155126049Sgreen size_t n; 156126049Sgreen 157126049Sgreen fp = fopen(fname, "r"); 158126049Sgreen if (fp == NULL) 159126049Sgreen return (-1); 160126049Sgreen nrandwords = 0; 161126049Sgreen while (fgets(newword, sizeof(newword), fp) != NULL) 162126049Sgreen nrandwords++; 163126049Sgreen if (ferror(fp) || fseek(fp, 0, SEEK_SET) != 0) 164126049Sgreen goto fail; 165126049Sgreen randwords = calloc(nrandwords, sizeof(char *)); 166126049Sgreen if (randwords == NULL) 167126049Sgreen goto fail; 168126049Sgreen n = nrandwords; 169126049Sgreen nrandwords = 0; 170126049Sgreen while (fgets(newword, sizeof(newword), fp) != NULL) { 171126049Sgreen newword[strcspn(newword, "\r\n")] = '\0'; 172126049Sgreen randwords[nrandwords] = strdup(newword); 173126049Sgreen if (randwords[nrandwords] == NULL) 174126049Sgreen err(1, "reading words file"); 175126049Sgreen if (++nrandwords == n) 176126049Sgreen break; 177126049Sgreen } 178126049Sgreen nrandwords = n; 179126049Sgreen fclose(fp); 180126049Sgreen return (0); 181126049Sgreenfail: 182126049Sgreen fclose(fp); 183126049Sgreen return (-1); 184126049Sgreen} 185126049Sgreen 186126049Sgreenint 187126049Sgreenmain(int argc, char **argv) { 188126049Sgreen unsigned long nworkers = 1; 189126049Sgreen struct worker *workers; 190126049Sgreen size_t i; 191126049Sgreen char waiting[3], *send, *wordfile = "/usr/share/dict/words"; 192126049Sgreen int ch; 193126049Sgreen 194126049Sgreen if (getprogname() == NULL) 195126049Sgreen setprogname(argv[0]); 196126049Sgreen printf("%s: threaded stress-tester for getaddrinfo(3)\n", 197126049Sgreen getprogname()); 198126049Sgreen printf("(c) 2004 Brian Feldman <green@FreeBSD.org>\n"); 199126085Sgreen while ((ch = getopt(argc, argv, "4s:t:w:")) != -1) { 200126049Sgreen switch (ch) { 201126085Sgreen case '4': 202126085Sgreen hints = &hintipv4only; 203126085Sgreen break; 204126049Sgreen case 's': 205126049Sgreen max_random_sleep = strtod(optarg, &send); 206126049Sgreen if (*send != '\0') 207126049Sgreen goto usage; 208126049Sgreen break; 209126049Sgreen case 't': 210126049Sgreen nworkers = strtoul(optarg, &send, 0); 211126049Sgreen if (*send != '\0') 212126049Sgreen goto usage; 213126049Sgreen break; 214126049Sgreen case 'w': 215126049Sgreen wordfile = optarg; 216126049Sgreen break; 217126049Sgreen default: 218126049Sgreenusage: 219126085Sgreen fprintf(stderr, "usage: %s [-4] [-s sleep] " 220126085Sgreen "[-t threads] [-w wordfile]\n", getprogname()); 221126049Sgreen exit(2); 222126049Sgreen } 223126049Sgreen } 224126049Sgreen argc -= optind; 225126049Sgreen argv += optind; 226126049Sgreen 227126049Sgreen if (nworkers < 1 || nworkers != (size_t)nworkers) 228126049Sgreen goto usage; 229126049Sgreen if (dowordfile(wordfile) == -1) 230126049Sgreen err(1, "reading word file %s", wordfile); 231126049Sgreen if (nrandwords < 1) 232126049Sgreen errx(1, "word file %s did not have >0 words", wordfile); 233282837Sngie printf("Read %zu random words from %s.\n", nrandwords, wordfile); 234126049Sgreen workers = calloc(nworkers, sizeof(*workers)); 235126049Sgreen if (workers == NULL) 236126049Sgreen err(1, "allocating workers"); 237126049Sgreen printf("Intra-query delay time is from 0 to %g seconds (random).\n", 238126049Sgreen max_random_sleep); 239126049Sgreen 240126049Sgreen printf("Starting %lu worker%.*s: ", nworkers, nworkers > 1, "s"); 241126049Sgreen fflush(stdout); 242126049Sgreen for (i = 0; i < nworkers; i++) { 243126049Sgreen if (pthread_create(&workers[i].w_thread, NULL, work, 244203800Sru &workers[i]) != 0) 245282837Sngie err(1, "creating worker %zu", i); 246282837Sngie printf("%zu%s", i, i == nworkers - 1 ? ".\n" : ", "); 247126049Sgreen fflush(stdout); 248126049Sgreen } 249126049Sgreen 250126049Sgreen printf("<Press enter key to end test.>\n"); 251126049Sgreen (void)fgets(waiting, sizeof(waiting), stdin); 252126049Sgreen workers_stop = 1; 253126049Sgreen 254126049Sgreen printf("Stopping %lu worker%.*s: ", nworkers, nworkers > 1, "s"); 255126049Sgreen fflush(stdout); 256126049Sgreen for (i = 0; i < nworkers; i++) { 257126049Sgreen pthread_join(workers[i].w_thread, NULL); 258282837Sngie printf("%zu%s", i, i == nworkers - 1 ? ".\n" : ", "); 259126049Sgreen fflush(stdout); 260126049Sgreen } 261126049Sgreen 262126058Sgreen printf("%-10s%-20s%-20s%-29s\n", "Worker", "Successful GAI", 263126058Sgreen "Failed GAI", "Max resolution time (M:SS*)"); 264126058Sgreen printf("%-10s%-20s%-20s%-29s\n", "------", "--------------", 265126058Sgreen "----------", "---------------------------"); 266126049Sgreen for (i = 0; i < nworkers; i++) { 267282837Sngie printf("%-10zu%-20ju%-20ju%ld:%s%.2f\n", i, 268126058Sgreen workers[i].w_lookup_success, workers[i].w_lookup_failure, 269126058Sgreen workers[i].w_max_lookup_time.tv_sec / 60, 270126058Sgreen workers[i].w_max_lookup_time.tv_sec % 60 < 10 ? "0" : "", 271126058Sgreen (double)(workers[i].w_max_lookup_time.tv_sec % 60) + 272126058Sgreen (double)workers[i].w_max_lookup_time.tv_nsec / 1e9); 273126049Sgreen } 274126049Sgreen 275126049Sgreen exit(0); 276126049Sgreen} 277