]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - tools/regression/gaithrstress/gaithrstress.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / tools / regression / gaithrstress / gaithrstress.c
1 /*-
2  * Copyright (c) 2004 Brian Fundakowski Feldman
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/time.h>
32
33 #include <netinet/in.h>
34
35 #include <err.h>
36 #include <netdb.h>
37 #include <pthread.h>
38 #include <resolv.h>
39 #include <stdio.h>
40 #include <stdint.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 /* Per-thread struct containing all important data. */
46 struct worker {
47         pthread_t w_thread;                          /* self */
48         uintmax_t w_lookup_success, w_lookup_failure;   /* getaddrinfo stats */
49         struct timespec w_max_lookup_time;
50 };
51
52 static volatile int workers_stop = 0;
53 static double max_random_sleep = 1.0;
54 static char **randwords;
55 static size_t nrandwords;
56 static const struct addrinfo *hints, hintipv4only = { .ai_family = AF_INET };
57
58 /*
59  * We don't have good random(3)-type functions that are thread-safe,
60  * unfortunately.
61  */
62 static u_int32_t
63 my_arc4random_r(void)
64 {
65         static pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
66         u_int32_t ret;
67
68         (void)pthread_mutex_lock(&mymutex);
69         ret = arc4random();
70         (void)pthread_mutex_unlock(&mymutex);
71         return (ret);
72 }
73
74 static void
75 randomsleep(double max_sleep_sec)
76 {
77         struct timespec slptime = { 0, 0 };
78         double rndsleep;
79
80         rndsleep = (double)my_arc4random_r() / 4294967296.0 * max_sleep_sec;
81         while (rndsleep >= 1.0) {
82                 slptime.tv_sec++;
83                 rndsleep -= 1.0;
84         }
85         slptime.tv_nsec = rndsleep * 1e9;
86         (void)nanosleep(&slptime, NULL);
87 }
88
89 /*
90  * Start looking up arbitrary hostnames and record the successes/failures.
91  * Between lookups, sleep a random amount of time to make sure threads
92  * stay well out of synchronization.
93  *
94  * Host name:   part            probability
95  *              ----            -----------
96  *              www.            1/2
97  *              random word     always, equal
98  *              random word     1/3, equal
99  *              .(net|com|org)  equal
100  */
101 static void *
102 work(void *arg)
103 {
104         struct worker *w = arg;
105
106         /* Turn off domain name list searching as much as possible. */
107         if (_res.options & RES_INIT || res_init() == 0)
108                 _res.options &= ~RES_DNSRCH;
109         do {
110                 const char *suffixes[] = { "net", "com", "org" };
111                 const size_t nsuffixes = sizeof(suffixes) / sizeof(suffixes[0]);
112                 struct timespec ts_begintime, ts_total;
113                 struct addrinfo *res;
114                 char *hostname;
115                 int error;
116
117                 randomsleep(max_random_sleep);
118                 if (asprintf(&hostname, "%s%s%s.%s",
119                     (my_arc4random_r() % 2) == 0 ? "www." : "",
120                     randwords[my_arc4random_r() % nrandwords],
121                     (my_arc4random_r() % 3) == 0 ?
122                     randwords[my_arc4random_r() % nrandwords] : "",
123                     suffixes[my_arc4random_r() % nsuffixes]) == -1)
124                         continue;
125                 (void)clock_gettime(CLOCK_REALTIME, &ts_begintime);
126                 error = getaddrinfo(hostname, NULL, hints, &res);
127                 (void)clock_gettime(CLOCK_REALTIME, &ts_total);
128                 ts_total.tv_sec -= ts_begintime.tv_sec;
129                 ts_total.tv_nsec -= ts_begintime.tv_nsec;
130                 if (ts_total.tv_nsec < 0) {
131                         ts_total.tv_sec--;
132                         ts_total.tv_nsec += 1000000000;
133                 }
134                 if (ts_total.tv_sec > w->w_max_lookup_time.tv_sec ||
135                     (ts_total.tv_sec == w->w_max_lookup_time.tv_sec &&
136                     ts_total.tv_nsec > w->w_max_lookup_time.tv_sec))
137                         w->w_max_lookup_time = ts_total;
138                 free(hostname);
139                 if (error == 0) {
140                         w->w_lookup_success++;
141                         freeaddrinfo(res);
142                 } else {
143                         w->w_lookup_failure++;
144                 }
145         } while (!workers_stop);
146
147         pthread_exit(NULL);
148 }
149
150 int
151 dowordfile(const char *fname)
152 {
153         FILE *fp;
154         char newword[64];
155         size_t n;
156
157         fp = fopen(fname, "r");
158         if (fp == NULL)
159                 return (-1);
160         nrandwords = 0;
161         while (fgets(newword, sizeof(newword), fp) != NULL)
162                 nrandwords++;
163         if (ferror(fp) || fseek(fp, 0, SEEK_SET) != 0)
164                 goto fail;
165         randwords = calloc(nrandwords, sizeof(char *));
166         if (randwords == NULL)
167                 goto fail;
168         n = nrandwords;
169         nrandwords = 0;
170         while (fgets(newword, sizeof(newword), fp) != NULL) {
171                 newword[strcspn(newword, "\r\n")] = '\0';
172                 randwords[nrandwords] = strdup(newword);
173                 if (randwords[nrandwords] == NULL)
174                         err(1, "reading words file");
175                 if (++nrandwords == n)
176                         break;
177         }
178         nrandwords = n;
179         fclose(fp);
180         return (0);
181 fail:
182         fclose(fp);
183         return (-1);
184 }
185
186 int
187 main(int argc, char **argv) {
188         unsigned long nworkers = 1;
189         struct worker *workers;
190         size_t i;
191         char waiting[3], *send, *wordfile = "/usr/share/dict/words";
192         int ch;
193
194         if (getprogname() == NULL)
195                 setprogname(argv[0]);
196         printf("%s: threaded stress-tester for getaddrinfo(3)\n",
197             getprogname());
198         printf("(c) 2004 Brian Feldman <green@FreeBSD.org>\n");
199         while ((ch = getopt(argc, argv, "4s:t:w:")) != -1) {
200                 switch (ch) {
201                 case '4':
202                         hints = &hintipv4only;
203                         break;
204                 case 's':
205                         max_random_sleep = strtod(optarg, &send);
206                         if (*send != '\0')
207                                 goto usage;
208                         break;
209                 case 't':
210                         nworkers = strtoul(optarg, &send, 0);
211                         if (*send != '\0')
212                                 goto usage;
213                         break;
214                 case 'w':
215                         wordfile = optarg;
216                         break;
217                 default:
218 usage:
219                         fprintf(stderr, "usage: %s [-4] [-s sleep] "
220                             "[-t threads] [-w wordfile]\n", getprogname());
221                         exit(2);
222                 }
223         }
224         argc -= optind;
225         argv += optind;
226
227         if (nworkers < 1 || nworkers != (size_t)nworkers)
228                 goto usage;
229         if (dowordfile(wordfile) == -1)
230                 err(1, "reading word file %s", wordfile);
231         if (nrandwords < 1)
232                 errx(1, "word file %s did not have >0 words", wordfile);
233         printf("Read %u random words from %s.\n", nrandwords, wordfile);
234         workers = calloc(nworkers, sizeof(*workers));
235         if (workers == NULL)
236                 err(1, "allocating workers");
237         printf("Intra-query delay time is from 0 to %g seconds (random).\n",
238             max_random_sleep);
239
240         printf("Starting %lu worker%.*s: ", nworkers, nworkers > 1, "s");
241         fflush(stdout);
242         for (i = 0; i < nworkers; i++) {
243                 if (pthread_create(&workers[i].w_thread, NULL, work,
244                     &workers[i]) != 0)
245                         err(1, "creating worker %u", i);
246                 printf("%u%s", i, i == nworkers - 1 ? ".\n" : ", ");
247                 fflush(stdout);
248         }
249
250         printf("<Press enter key to end test.>\n");
251         (void)fgets(waiting, sizeof(waiting), stdin);
252         workers_stop = 1;
253
254         printf("Stopping %lu worker%.*s: ", nworkers, nworkers > 1, "s");
255         fflush(stdout);
256         for (i = 0; i < nworkers; i++) {
257                 pthread_join(workers[i].w_thread, NULL);
258                 printf("%u%s", i, i == nworkers - 1 ? ".\n" : ", ");
259                 fflush(stdout);
260         }
261
262         printf("%-10s%-20s%-20s%-29s\n", "Worker", "Successful GAI",
263             "Failed GAI", "Max resolution time (M:SS*)");
264         printf("%-10s%-20s%-20s%-29s\n", "------", "--------------",
265             "----------", "---------------------------");
266         for (i = 0; i < nworkers; i++) {
267                 printf("%-10u%-20ju%-20ju%u:%s%.2f\n", i,
268                     workers[i].w_lookup_success, workers[i].w_lookup_failure,
269                     workers[i].w_max_lookup_time.tv_sec / 60,
270                     workers[i].w_max_lookup_time.tv_sec % 60 < 10 ? "0" : "",
271                     (double)(workers[i].w_max_lookup_time.tv_sec % 60) +
272                     (double)workers[i].w_max_lookup_time.tv_nsec / 1e9);
273         }
274
275         exit(0);
276 }