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