1 /****************************************************************************
2 * *
3 * The contents of this file are subject to the WebStone Public License *
4 * Version 1.0 (the "License"); you may not use this file except in *
5 * compliance with the License. You may obtain a copy of the License *
6 * at http://www.mindcraft.com/webstone/license10.html *
7 * *
8 * Software distributed under the License is distributed on an "AS IS" *
9 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See *
10 * the License for the specific language governing rights and limitations *
11 * under the License. *
12 * *
13 * The Original Code is WebStone 2.5. *
14 * *
15 * The Initial Developer of the Original Code is Silicon Graphics, Inc. *
16 * and Mindcraft, Inc.. Portions created by Silicon Graphics. and *
17 * Mindcraft. are Copyright (C) 1995-1998 Silicon Graphics, Inc. and *
18 * Mindcraft, Inc. All Rights Reserved. *
19 * *
20 * Contributor(s): ______________________________________. *
21 * *
22 * @(#) bench.c 2.4@(#) *
23 ***************************************************************************/
24
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <signal.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <time.h>
32 #include <ctype.h>
33
34 #ifndef WIN32
35 #include <unistd.h>
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/errno.h>
39 #include <sys/socket.h>
40 #include <sys/time.h>
41 #include <netinet/in.h>
42 #include <netdb.h>
43 #include <arpa/inet.h>
44 #else /* WIN32 */
45 #define FD_SETSIZE 1024 /* max size for select() - keep before <winsock.h>
46 * and same size as MAXCLIENTS */
47 #include <windows.h>
48 #include <winsock.h>
49 #include <io.h>
50 #include <process.h>
51 #endif /* WIN32 */
52
53 #include "sysdep.h"
54 #include "bench.h"
55
56 /* command line options/data */
57 int savefile = 0;
58 int debug = DEBUG_OFF;
59 int norexec = 0;
60 int haveproxyserver = 0;
61 char proxyserver[MAXHOSTNAMELEN];
62 char network_mask_str[30] = "255.255.255.0";
63 unsigned network_mask = 0;
64 int servaddrin_config = 0;
65 int dumpall = 0;
66 int testtime = 0;
67 int havewebserver = 0;
68 int numloops = 0;
69 NETPORT portnum = 0;
70 int record_all_transactions = 0;
71 int use_fixed_random_seed = 0;
72 int verbose = 0;
73 char webserver[MAXHOSTNAMELEN];
74 char configfile[MAXPATHLEN];
75 char webclient_path[MAXPATHLEN];
76 char debug_filename[MAXPATHLEN];
77 int uil_filelist_f = 0;
78 char uil_filelist[MAXPATHLEN];
79 char child_uil_filelist[MAXPATHLEN];
80 char filelist[256][MAXPATHLEN];
81 int randomize = 0;
82 fd_set zerofdset;
83
84 /* other key data */
85 long int number_of_pages = 0;
86 long int num_input_lines = 0;
87 int totalnumclients = 0;
88 int num_rexecs = 0;
89 SOCKET socknum[MAXCLIENTS];
90 SOCKET sockIO[MAXTOTALPROCS];
91 SOCKET sockErr[MAXTOTALPROCS];
92 THREAD FILE *debugfile;
93
94 struct hostent *master_phe; /* IP addresses for webmaster */
95
96 static void
usage(const char * progname)97 usage(const char *progname)
98 {
99 fprintf(stderr, "Usage: %s [-a] [-d] -f config_file -C webclient_path\n",
100 progname);
101 fprintf(stderr, " [-t run_time | -l num_loops] [-p port_num]\n");
102 fprintf(stderr, " [-r] [-s] [-v] [-w webserver_URL] \n");
103 fprintf(stderr, " [-u masterfilelist] [-U clientfilelist] [-W]\n");
104 fprintf(stderr, " [-P proxy_server] [-R] [-S]\n");
105 fprintf(stderr, "\n");
106 fprintf(stderr, "-w webserver URL [URL ...]\n\n");
107 fprintf(stderr, "-a print timing information for all clients\n");
108 fprintf(stderr, "-d turn on debug statements\n");
109 fprintf(stderr, "-f config_file\tfile specifying clients\n");
110 fprintf(stderr, "-l number of iterations to retrieve uils\n");
111 fprintf(stderr, "-p port number of web server if not 80\n");
112 fprintf(stderr, "-s save client gets to /tmp/webstone.data.*\n");
113 fprintf(stderr, "-t run_time\tduration of test in minutes\n");
114 fprintf(stderr, "-u URL file\tfilelist of URLs for webmaster\n");
115 fprintf(stderr, "-U URL file\tfilelist of URLs for webclient\n");
116 fprintf(stderr, "-v verbose mode\n");
117 fprintf(stderr, "-D debugfile\tFile to receive debug data on client\n");
118 fprintf(stderr, "-P servername\tuse proxy server for transactions\n");
119 fprintf(stderr, "-W webserver addresses are in the config file\n");
120 fprintf(stderr, "-R record all transactions\n");
121 fprintf(stderr, "-S used fixed seed for random number generator\n");
122 fprintf(stderr, "-T turn on trace statements\n");
123 errexit("\n");
124 }
125
126 static SOCKET
passivesock(const NETPORT portnum,const char * protocol,const int qlen)127 passivesock(const NETPORT portnum, const char *protocol, const int qlen)
128 {
129 struct protoent *ppe; /* pointer to protocol info entry */
130 struct sockaddr_in sin; /* Internet endpoint address */
131 SOCKET s; /* socket descriptor */
132
133 D_PRINTF("Entering passivesock(): portnum=%d, protocol='%s', qlen=%d\n",
134 portnum, protocol, qlen);
135
136 memset((char *)&sin, 0, sizeof(sin));
137
138 sin.sin_family = AF_INET;
139 sin.sin_addr.s_addr = INADDR_ANY;
140
141 errno = 0;
142 sin.sin_port = htons(portnum);
143
144 /* Map protocol name to number */
145 CLEAR_SOCK_ERR;
146 if ((ppe = getprotobyname(protocol)) == NULL)
147 errexit("Can't get \"%s\" protocol entry\n", protocol);
148
149 /* allocate a socket */
150 CLEAR_SOCK_ERR;
151 s = socket(PF_INET, SOCK_STREAM, ppe->p_proto);
152 if (BADSOCKET(s))
153 {
154 D_PRINTF("socket() returned %d. p_proto=%d, errno=%d (%s)",
155 s, ppe->p_proto, s, GET_NET_ERR, neterrstr());
156 errexit("Can't create socket: %s\n", neterrstr());
157 }
158
159 /* Bind the socket */
160 CLEAR_SOCK_ERR;
161 if (SOCK_ERR(bind(s, (struct sockaddr *)&sin, sizeof(sin))))
162 errexit("Can't bind to port %d: %s\n", portnum, neterrstr());
163
164 /*
165 * If it's a stream, listen for connections.
166 * On NT and many UNIX systems, the backlog parameter is silently
167 * limited to 5 connections.
168 */
169 CLEAR_SOCK_ERR;
170 if (SOCK_ERR(listen(s, qlen)))
171 errexit("Can't listen on port %s: %s\n", portnum, neterrstr());
172
173 return s;
174 }
175
176
177 /*
178 * abort_clients()
179 * Called by SIGINT handler as well as other error handlers.
180 * This just does an exit() which will close all socket connections.
181 * This doesn't explicitly kill the clients but they will get an error
182 * and die when they try to send results to the webmaster. If any
183 * children are stuck then they'll have to be killed by hand.
184 */
185 static void
abort_clients(void)186 abort_clients(void)
187 {
188 exit(2);
189 }
190
191
192 /* signal handler for SIGINT */
193 static void
sig_int(int sig)194 sig_int(int sig)
195 {
196 /* its not safe to call fprintf() from a signal handler. */
197 abort_clients();
198 }
199
200
201 #ifdef WIN32
202 /* echo stdout/stderr from clients */
203 void
echo_client(void * arg)204 echo_client(void *arg)
205 {
206 SOCKET *sockarr;
207 char buf[BUFSIZ];
208 int i, len, rv;
209 fd_set readfds;
210 struct timeval timeout;
211 int is_err = (int) arg;
212 int fdout;
213 int anyleft;
214
215 timeout.tv_sec = 5L;
216 timeout.tv_usec = 0L;
217
218 if (is_err)
219 {
220 sockarr = sockErr;
221 fdout = _fileno(stderr);
222 }
223 else
224 {
225 sockarr = sockIO;
226 fdout = _fileno(stdout);
227 }
228
229 while (1)
230 {
231 FD_ZERO(&readfds);
232 for (i = anyleft = 0; i < num_rexecs; i++)
233 {
234 if (sockarr[i] != BADSOCKET_VALUE)
235 {
236 FD_SET(sockarr[i], &readfds);
237 anyleft++;
238 }
239 }
240 if (!anyleft)
241 return;
242 WSASetLastError(0);
243 rv = select(num_rexecs, &readfds, NULL, NULL, &timeout);
244 if (rv == 0)
245 continue;
246 if (rv < 0)
247 {
248 if (WSAGetLastError() == WSANOTINITIALISED)
249 return;
250 errexit("echo_client(): select() returns %d: %s\n",
251 rv, neterrstr());
252 }
253
254 /* loop over the sockets that are ready with data */
255 for (i = 0; i < num_rexecs; i++)
256 {
257 if (sockarr[i] != BADSOCKET_VALUE && FD_ISSET(sockarr[i], &readfds))
258 {
259 len = NETREAD(sockarr[i], buf, sizeof(buf));
260 if (len <= 0) /* mark connection closed */
261 sockarr[i] = BADSOCKET_VALUE;
262 if (len < 0)
263 {
264 if (WSAGetLastError() == WSANOTINITIALISED)
265 return;
266 fprintf(stderr,
267 "Error in echo_client() after NETREAD(): %s\n",
268 neterrstr());
269 continue;
270 }
271 write(fdout, buf, len); /* copy to stdout or stderr */
272 }
273 }
274 }
275 }
276
277 #else
278 static int
echo_client(const int fd,int is_err)279 echo_client(const int fd, int is_err)
280 {
281 /*
282 * WRITE TEXT FROM FILE DESCRIPTOR INTO STDOUT
283 */
284 char buf[BUFSIZ];
285 int cc;
286
287 D_PRINTF("entering echo_client() for %s\n", (is_err ? "stderr" : "stdout"));
288
289 while (getppid() != 1)
290 {
291 cc = NETREAD(fd, buf, sizeof(buf));
292
293 /*
294 * If this is the error stream (stderr of the clients) then only
295 * print it to our stdout if we are in debugging mode.
296 */
297 if ((cc > 0) && is_err && DEBUGGING_ON)
298 write(STDOUT_FILENO, buf, cc);
299 }
300 D_PRINTF("Exiting echo_client() for %s\n", (is_err ? "stderr" : "stdout"));
301 NETCLOSE(fd);
302 }
303 #endif /* WIN32 */
304
305
306 /* Picks the appropriate webmaster IP address based on the address of the
307 * client. This is significant only for hosts with multiple interfaces.
308 * Return value is a string with the IP address or hostname (or NULL)
309 */
310 char *
pick_webmaster_IP_address(char * client_hostname,struct hostent * master_phe,unsigned netmask)311 pick_webmaster_IP_address(char *client_hostname, struct hostent *master_phe,
312 unsigned netmask)
313 {
314 static char buf[20];
315 unsigned char addr[4];
316 int client_addr;
317 int i;
318
319 /* Uncomment the following line to let clients do their own
320 * hostname lookup to find the webmaster system.
321 */
322 /* return master_phe->h_name; */
323
324 if (isdigit(client_hostname[0]))
325 {
326 /* we have an IP address */
327 client_addr = inet_addr(client_hostname);
328 if (client_addr == INADDR_NONE)
329 return NULL;
330 }
331 else
332 {
333 /* we have a hostname, use the webmaster hostname */
334 return master_phe->h_name;
335 }
336
337 for (i = 0; master_phe->h_addr_list[i] != NULL; i++)
338 {
339 if ((*(int *)(master_phe->h_addr_list[i]) & netmask) ==
340 (client_addr & netmask))
341 {
342 memcpy((char *)addr, master_phe->h_addr_list[i], sizeof(addr));
343 sprintf(buf, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);
344 return buf;
345 }
346 }
347 /* No common net - can't give a verified hostname to webclient
348 */
349 return NULL;
350 }
351
352
353 /*
354 * Command line parsing
355 */
356 static void
ParseCmdLine(int argc,char ** argv)357 ParseCmdLine(int argc, char **argv )
358 {
359 int getoptch;
360 int currarg;
361 extern char *optarg;
362 extern int optind;
363 FILE *filelist_fp;
364
365 /*
366 * PARSE THE COMMAND LINE OPTIONS
367 */
368 while((getoptch = getopt(argc,argv,"aC:dD:f:l:M:n:p:P:R:sSt:Tu:U:vw:WX"))
369 != EOF)
370 {
371 switch(getoptch)
372 {
373 case 'C':
374 strcpy(webclient_path, optarg);
375 break;
376 case 'D':
377 strcpy(debug_filename, optarg);
378 break;
379 case 'M':
380 strcpy(network_mask_str, optarg);
381 break;
382 case 'P':
383 haveproxyserver = 1;
384 strcpy(proxyserver, optarg);
385 break;
386 case 'R':
387 record_all_transactions = 1;
388 break;
389 case 'S':
390 use_fixed_random_seed = 1;
391 break;
392 case 'T':
393 debug |= DEBUG_TRACE;
394 break;
395 case 'U':
396 strcpy(child_uil_filelist, optarg);
397 break;
398 case 'X':
399 norexec = 1;
400 break;
401 case 'W':
402 servaddrin_config = 1;
403 break;
404 case 'a':
405 dumpall = 1;
406 break;
407 case 'd':
408 debug = DEBUG_ALL;
409 break;
410 case 'f':
411 strcpy(configfile, optarg);
412 break;
413 case 'l':
414 numloops = atoi(optarg);
415 break;
416 case 'p':
417 portnum = atoi(optarg);
418 break;
419 case 's':
420 savefile = 1;
421 break;
422 case 't':
423 testtime = atoi(optarg);
424 break;
425 case 'u':
426 uil_filelist_f = 1;
427 strcpy(uil_filelist, optarg);
428 break;
429 case 'v':
430 verbose = 1;
431 break;
432 case 'w':
433 havewebserver = 1;
434 strcpy(webserver, optarg);
435 break;
436 default:
437 fprintf(stderr,"-%c: Flag not recognized\n", (char)getoptch);
438 usage(argv[0]);
439 } /* end switch */
440 } /* end while */
441
442 if (numloops && testtime)
443 errexit("Can't have both -l and -t\n");
444
445 if (!havewebserver && !servaddrin_config)
446 {
447 /*
448 * THE SERVERS NAME MUST BE SPECIFIED
449 */
450 fprintf(stderr,"No WWW Server specified\n");
451 usage(argv[0]);
452 }
453
454 if (havewebserver && servaddrin_config)
455 {
456 /*
457 * CAN'T HAVE BOTH -w and -W
458 */
459 fprintf(stderr, "Can't have both -w and -W options\n");
460 usage(argv[0]);
461 }
462
463 network_mask = inet_addr(network_mask_str);
464 if (network_mask == INADDR_NONE)
465 {
466 fprintf(stderr, "Invalid network mask (-M %s)\n", network_mask_str);
467 usage(argv[0]);
468 }
469
470 if (strlen(webclient_path) == 0)
471 {
472 fprintf(stderr,"No path specified for the webclient program\n");
473 usage(argv[0]);
474 }
475
476 if (strlen(configfile) == 0)
477 {
478 /*
479 * THE MASTER MUST HAVE A CONFIGURATION FILE TO READ.
480 */
481 fprintf(stderr,"No Configuration file specified\n");
482 usage(argv[0]);
483 }
484
485 /*
486 * Get the list of UILs to retrieve from the server. The
487 * page count here may include comment lines. We'll get the
488 * actual count from the webclient program later.
489 */
490 if (uil_filelist_f == 1)
491 {
492 D_PRINTF( "webmaster(): About to read filelist %s\n",
493 uil_filelist );
494
495 filelist_fp = fopen(uil_filelist, "r");
496 if (filelist_fp == NULL)
497 {
498 D_PRINTF( "Error %d opening filelist %s: %s\n",
499 errno, uil_filelist, strerror(errno) );;
500 errexit("Error %d opening filelist %s: %s\n",
501 errno, uil_filelist, strerror(errno));
502 }
503 while (fgets(filelist[num_input_lines], MAXPATHLEN, filelist_fp)
504 != (char *)NULL)
505 num_input_lines++;
506 fclose(filelist_fp);
507 }
508 else if (optind < argc) /* UILs are on the command line */
509 {
510 currarg = optind;
511 while(currarg != argc)
512 {
513
514 /*
515 * GET THE UILS TO RETRIEVE.
516 */
517 sscanf(argv[currarg],"%s",filelist[num_input_lines]);
518 num_input_lines++;
519 currarg++;
520 }
521 }
522 else if (*child_uil_filelist)
523 {
524 num_input_lines=1; /* Bogus value; trust that webclient has a file */
525 }
526 else /* Get UILs from stdin */
527 {
528 while (fgets(filelist[num_input_lines], MAXPATHLEN, stdin)
529 != (char *)NULL)
530 num_input_lines++;
531 }
532
533
534 if (num_input_lines == 0)
535 {
536 /*
537 * AT LEAST ONE FILE MUST BE SPECIFIED
538 */
539 fprintf(stderr,"No URL resources specified\n");
540 usage(argv[0]);
541 }
542 }
543
544 /*
545 * This function sets up the socket we will use to synchronize with the
546 * clients.
547 * Returns the socket number if successful, doesn't return if it fails
548 */
549 SOCKET
SetupSyncSocket(struct sockaddr_in * serveraddr)550 SetupSyncSocket(struct sockaddr_in *serveraddr)
551 {
552 int sock, len;
553
554 D_PRINTF("entering SetupSyncSocket()\n");
555
556 /*
557 * passivesock() will exit if there is any error so the returned
558 * socket is always valid.
559 */
560 sock = passivesock(0, "tcp", MAXCLIENTS);
561
562 len = sizeof(struct sockaddr);
563 CLEAR_SOCK_ERR;
564 if (SOCK_ERR(getsockname(sock, (struct sockaddr *)serveraddr, &len)))
565 errexit("Could not get socket informaton\n");
566
567 return sock;
568 }
569
570 /* Added by Rajesh Shah 5/18/96 */
571 int
HostEntCpy(struct hostent * dest,struct hostent * src)572 HostEntCpy(struct hostent *dest, struct hostent *src)
573 {
574 size_t count;
575 char **sptr, **dptr;
576 struct in_addr *iptr;
577
578 dest->h_name = (char *)malloc(strlen(src->h_name)+1);
579 strcpy(dest->h_name, src->h_name);
580 printf("WebMaster name = %s\n", dest->h_name);
581 dest->h_aliases = src->h_aliases;
582 dest->h_addrtype = src->h_addrtype;
583 dest->h_length = src->h_length;
584
585 /*
586 * ADDED: by Greg Burrell of Mindcraft Inc. 10/22/96
587 * PROBLEM: we can't just do the assignment:
588 *
589 * dest->h_addr_list = src->h_addr_list
590 *
591 * because those are just pointers and the memory pointed to
592 * may get overwritten during the next gethostbyname() call.
593 *
594 * FIX: Make a copy of the h_addr_list of a hostent structure.
595 */
596 for(count = 0, sptr = src->h_addr_list; *sptr != NULL; sptr++, count++);
597 if ((dest->h_addr_list = malloc(count + 1)) == NULL)
598 return 0;
599 if ((iptr = malloc(count * sizeof(struct in_addr))) == NULL)
600 return 0;
601 for (sptr = src->h_addr_list, dptr = dest->h_addr_list;
602 *sptr != NULL; sptr++, dptr++, iptr++)
603 {
604 *iptr = *((struct in_addr *) *sptr);
605 *dptr = (char *) iptr;
606 }
607 *dptr = NULL;
608 return 1;
609 }
610
611
612 /*
613 * Function which generates a commandline for the webclients
614 */
615 void
MakeCmdLine(char * commandline)616 MakeCmdLine(char *commandline)
617 {
618 char tmpcommandline[NCCARGS];
619 char hostname[MAXHOSTNAMELEN];
620 struct hostent *master_phe_tmp;
621
622 /*
623 * BUILD THE PORTIONS OF THE cmdline FOR EACH CLIENT THAT WE CAN BUILD NOW.
624 * WE WILL FILL IN THE NUMBER OF CLIENTS LATER WITH AN sprintf.
625 */
626 D_PRINTF( "Calling gethostname\n" );
627
628 CLEAR_SOCK_ERR;
629 if(SOCK_ERR(gethostname(hostname,MAXHOSTNAMELEN)))
630 errexit("Could not retrieve local host name");
631
632 /* Convert hostname to address, to avoid DNS problems for webclients. */
633 /* Copy the output of gethostbyname() to our own storage,
634 * so it isn't overwritten by a later call to gethostbyname(). */
635
636 master_phe_tmp = gethostbyname(hostname);
637 if (master_phe_tmp == NULL)
638 errexit("Unable to resolve webmaster hostname '%s'\n", hostname);
639 master_phe = (struct hostent *)malloc(sizeof(struct hostent));
640 HostEntCpy(master_phe, master_phe_tmp);
641
642 sprintf(commandline,"%s", webclient_path);
643
644 if(haveproxyserver)
645 {
646 sprintf(tmpcommandline, " -P %s", proxyserver);
647 strcat(commandline, tmpcommandline);
648 }
649
650 if (DEBUGGING_ON)
651 strcat(commandline, " -d");
652 else if (TRACING_ON)
653 strcat(commandline, " -T");
654
655 if (*debug_filename)
656 {
657 strcat(commandline," -D ");
658 strcat(commandline,debug_filename);
659 }
660
661 if (numloops != 0)
662 {
663 sprintf(tmpcommandline," -l %d", numloops);
664 strcat(commandline,tmpcommandline);
665 }
666
667 if (portnum)
668 {
669 sprintf(tmpcommandline," -p %d", portnum);
670 strcat(commandline,tmpcommandline);
671 }
672
673 if (savefile)
674 strcat(commandline," -s");
675
676 if (record_all_transactions)
677 strcat(commandline, " -R");
678
679 if (testtime != 0)
680 {
681 sprintf(tmpcommandline," -t %d", testtime);
682 strcat(commandline,tmpcommandline);
683 }
684
685 if (*child_uil_filelist)
686 {
687 sprintf(tmpcommandline," -u %s", child_uil_filelist);
688 strcat(commandline,tmpcommandline);
689 }
690
691 /*
692 * SET UP A SPACE FOR THE NUMBER OF CLIENTS ON THE commandline.
693 */
694 sprintf(tmpcommandline,
695 (use_fixed_random_seed ? "%s -n %%d -w %%s -c %%s:%%d -S %%d" :
696 "%s -n %%d -w %%s -c %%s:%%d"),
697 commandline);
698 strcpy(commandline,tmpcommandline);
699 }
700
701
702 /*
703 * rexec to the client hosts and start the webclients
704 */
705 int
RexecClients(char * commandline,char clienthostname[MAXCLIENTS][MAXHOSTNAMELEN],struct sockaddr_in * serveraddr)706 RexecClients(char *commandline,
707 char clienthostname[MAXCLIENTS][MAXHOSTNAMELEN],
708 struct sockaddr_in *serveraddr)
709 {
710 int tmpfd;
711 int numclients = 0;
712 char tmpcommandline[NCCARGS];
713 struct servent *inetport;
714 int cnt;
715 char buffer[NCCARGS];
716 char login[MAXUSERNAME];
717 char password[MAXPASSWD];
718 FILE *fp;
719 int returnval;
720 char *tmphostname;
721 int client_random_seed_base = 0;
722 int i;
723
724 D_PRINTF( "Entering RexecClients(\"%s\", \"%s\", %lu)\n",
725 commandline, clienthostname[0], (long)serveraddr);
726 fflush(debugfile);
727
728 /*
729 * OPEN UP THE CONFIG FILE. FOR EACH LINE IN THE CONFIG FILE, CHECK
730 * ITS VALIDITY AND THEN rexec A COMMAND ON THE CLIENT.
731 */
732 errno = 0;
733 D_PRINTF("Opening configfile: %s\n", configfile);
734 if ((fp = fopen(configfile,"r")) == NULL)
735 errexit("Could not open config file %s\n", configfile);
736
737 CLEAR_SOCK_ERR;
738 if ((inetport = getservbyname("exec","tcp")) == NULL)
739 errexit("Could not get service name for exec/tcp\n");
740
741 D_PRINTF( "getservbyname returned %d\n", ntohs(inetport->s_port) );
742
743 cnt = 0;
744
745 while(1)
746 {
747 char webserver2[MAXHOSTNAMELEN];
748 char linebuf[150];
749 int num;
750 char *primename;
751
752 if (NULL == fgets(linebuf, sizeof(linebuf), fp))
753 break;
754 num = sscanf(linebuf,"%s %s %s %d %s", clienthostname[cnt], login,
755 password, &numclients, webserver2);
756 D_PRINTF(" %s %s %s %d %s\n", clienthostname[cnt], login,
757 password, numclients, webserver2);
758 if (num < 4)
759 break;
760 if (servaddrin_config)
761 {
762 if (num == 4)
763 errexit("No webserver specified in config file for %s\n",
764 clienthostname[cnt]);
765 strcpy(webserver, webserver2);
766 }
767
768 if (numclients == 0)
769 continue; /* no work for this client system */
770 if (numclients < 0)
771 errexit("Number of clients must be >= 0\n");
772 if (numclients > MAXPROCSPERNODE)
773 errexit("Number of clients per node can't exceed %d\n",
774 MAXPROCSPERNODE);
775 totalnumclients += numclients;
776
777 primename = pick_webmaster_IP_address(clienthostname[cnt], master_phe,
778 network_mask);
779 if (primename == NULL)
780 errexit("Bad client address %s for Client %d\n",
781 clienthostname[cnt], cnt);
782
783 fprintf(stdout, "Client %d: %s \t# Processes: %d\n Webserver: %s\tWebmaster: %s:%d\n", cnt, clienthostname[cnt], numclients, webserver, primename,
784 ntohs(serveraddr->sin_port));
785 fflush(stdout);
786
787 if (use_fixed_random_seed) {
788 sprintf(tmpcommandline, commandline, numclients, webserver,
789 primename, ntohs(serveraddr->sin_port),
790 client_random_seed_base);
791 client_random_seed_base += numclients;
792 }
793 else
794 sprintf(tmpcommandline, commandline, numclients, webserver,
795 primename, ntohs(serveraddr->sin_port));
796
797 D_PRINTF( "%s rexec %s\n",clienthostname[cnt],tmpcommandline );
798 if (norexec)
799 sleep(30); /* gives some time to start clients for debugging */
800 else
801 {
802 tmphostname = &(clienthostname[cnt][0]);
803 D_PRINTF("rexec( %s, %d, %s, %s, \"%s,\" %d)\n",
804 tmphostname, inetport->s_port, login, password,
805 tmpcommandline, sockErr[cnt]);
806 tmpfd = rexec(&tmphostname, inetport->s_port, login, password,
807 tmpcommandline, &sockErr[cnt]);
808 if(BADSOCKET(sockIO[cnt] = tmpfd))
809 {
810 errexit("Could not rexec: rexec to client %s, cmdline %s failed\n", clienthostname[cnt],tmpcommandline);
811 }
812
813 /*
814 * Send the list of UILs to the webclient process, before it
815 * forks
816 */
817 if (!(*child_uil_filelist))
818 {
819 for (i = 0; i < num_input_lines; i++)
820 {
821 D_PRINTF("webmaster: sending <%s> to client, socket %d.\n",
822 filelist[i], sockIO[cnt]);
823 NETWRITE(tmpfd, filelist[i], strlen(filelist[i]));
824 }
825 }
826 D_PRINTF("webmaster: sending <%s> to client, socket %d.\n",
827 ENDTOKEN, tmpfd);
828 /*
829 * write an extra newline in case the last line of the filelist
830 * didn't end in a newline.
831 */
832 NETWRITE(tmpfd, "\n", 1);
833 NETWRITE(tmpfd, ENDTOKEN, ENDTOKENSTRLEN);
834
835 memset(buffer, 0, sizeof(buffer));
836
837 if (NETREAD(tmpfd, buffer, 10) <= 0)
838 {
839 D_PRINTF( "Error reading number of pages: %s\n", neterrstr());
840 fprintf(stderr,
841 "Error reading number of pages: %s\nSocket number %d\n",
842 neterrstr(),tmpfd);
843 abort_clients();
844 errexit("");
845 }
846 sscanf(buffer, "%d", &number_of_pages);
847
848 memset(buffer, 0, sizeof(buffer));
849
850 if (NETREAD(tmpfd, buffer, 10) <= 0)
851 {
852 D_PRINTF( "Error reading randomize flag: %s\n", neterrstr());
853 fprintf(stderr,
854 "Error reading randomize flag: %s\nSocket number %d\n",
855 neterrstr(),tmpfd);
856 abort_clients();
857 errexit("");
858 }
859 sscanf(buffer, "%d", &randomize);
860 D_PRINTF( "randomize set to %d.\n", randomize);
861
862 memset(buffer, 0, sizeof(buffer));
863
864 returnval = NETREAD(tmpfd, buffer, OKSTRLEN);
865 if (returnval <= 0 || memcmp(buffer, OKSTR, OKSTRLEN) != 0)
866 {
867 errexit("rexec to client %s, cmdline %s received error %s\n",
868 clienthostname[cnt],tmpcommandline, buffer);
869 }
870 D_PRINTF("Received OK from %s.\n", clienthostname[cnt] );
871 }
872
873 #ifndef WIN32
874 /*
875 * Set up processes to read the stdout and stderr of the
876 * rexec().
877 */
878 D_PRINTF( "Forking webclient stdout process\n" );
879 switch (fork())
880 {
881 case -1: /* ERROR */
882 errexit("fork: %s\n", strerror(errno));
883 case 0: /* CHILD */
884 exit(echo_client(sockIO[cnt], 0));
885 default: /* PARENT */
886 break;
887 }
888 D_PRINTF( "Forking webclient stderr process\n" );
889 switch (fork())
890 {
891 case -1: /* ERROR */
892 errexit("fork: %s\n", strerror(errno));
893 case 0: /* CHILD */
894 exit(echo_client(sockErr[cnt], 1));
895 default: /* PARENT */
896 break;
897 }
898 #endif /* ! WIN32 */
899
900 cnt++;
901 if (cnt > MAXCLIENTS || cnt > FD_SETSIZE)
902 errexit("Number of Clients can't exceed %d\n", MAXCLIENTS);
903 }
904
905 num_rexecs = cnt;
906 if (totalnumclients > MAXTOTALPROCS)
907 errexit("Total number of processes can't exceed %d\n", MAXTOTALPROCS);
908
909 #ifdef WIN32
910 /* start threads to echo stdout/stderr from clients */
911 _beginthread(echo_client, 0, (void *)0);
912 _beginthread(echo_client, 0, (void *)1);
913 #endif /* WIN32 */
914
915 fprintf(stdout,"\n");
916 fprintf(stdout,"\n");
917 fclose(fp);
918
919 return totalnumclients;
920 }
921
922
923 /*
924 * GetReady()
925 * When each client thread or process starts up it makes a connection
926 * back to the web master.
927 */
928 void
GetReady(fd_set * fdset,int totalnumclients,int sock)929 GetReady(fd_set *fdset, int totalnumclients, int sock)
930 {
931 int cnt, len;
932 char buffer[NCCARGS];
933
934 D_PRINTF( "Beginning accept loop\n" );
935 for (cnt = 0; cnt < totalnumclients; cnt++)
936 {
937 fd_set readfds;
938 struct timeval timeout;
939 int rv;
940
941 timeout.tv_sec = MAX_ACCEPT_SECS;
942 timeout.tv_usec = 0;
943 FD_ZERO(&readfds);
944 FD_SET(sock, &readfds);
945
946 /* Time out if any of the clients are unable to connect to us. */
947 if (!(rv = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout)))
948 {
949 fprintf(stdout,
950 "Listen timeout after %d seconds (%d clients so far)\n",
951 MAX_ACCEPT_SECS, cnt);
952 D_PRINTF("select() timed out after %d seconds\n", MAX_ACCEPT_SECS);
953 errexit("Webmaster terminating\n");
954 }
955
956 CLEAR_SOCK_ERR;
957 if (BADSOCKET(socknum[cnt] = accept(sock, NULL, 0)))
958 {
959 abort_clients();
960 errexit("accept() error: %s", neterrstr());
961 }
962
963 FD_SET(socknum[cnt], fdset);
964 D_PRINTF( "GetReady(): cnt=%d, socket=%d\n", cnt, socknum[cnt] );
965 }
966
967 /*
968 * WAIT FOR A READY.
969 */
970 fprintf(stdout,"Waiting for READY from %d clients\n",totalnumclients);
971 fflush(stdout);
972
973 for (cnt = 0; cnt < totalnumclients; cnt++)
974 {
975 len = NETREAD(socknum[cnt], buffer, READYSTRLEN);
976 if (len != READYSTRLEN)
977 {
978 abort_clients();
979 errexit("Error reading from client #%d\n", cnt);
980 }
981 if (memcmp(buffer, READYSTR, READYSTRLEN))
982 {
983 abort_clients();
984 errexit("Received bad READY string: len %d, value %s\n",
985 len,buffer);
986 }
987 D_PRINTF("Got ready from client #%d\n", cnt);
988 }
989 fprintf(stdout,"All READYs received\n");
990 fflush(stdout);
991 }
992
993 /*
994 * Start all the clients by sending them a GO message.
995 * totalnumclients is the total number of clients
996 * socknum is an array with the socket descriptors for all the client
997 * connections. We know they are all valid descriptors because we wouldn't
998 * have gotten this far if they weren't.
999 */
1000 void
SendGo(int totalnumclients,int * socknum)1001 SendGo(int totalnumclients, int *socknum)
1002 {
1003 int cnt;
1004
1005 fprintf(stdout, "Sending GO to all clients\n");
1006 for(cnt = 0; cnt < totalnumclients; cnt++)
1007 {
1008 if (NETWRITE(socknum[cnt], GOSTR, GOSTRLEN) != GOSTRLEN)
1009 {
1010 abort_clients();
1011 errexit("Error sending GO to client %d: %s\n", cnt,
1012 neterrstr());
1013 }
1014 }
1015 }
1016
1017
1018 /*
1019 * This function gathers statistics from all the clients
1020 */
1021 void
GetResults(fd_set * fdset,page_stats_t ** page_stats,time_t * endtime,char * timestr,int totalnumclients,stats_t statarray[])1022 GetResults(fd_set *fdset, page_stats_t **page_stats, time_t *endtime,
1023 char *timestr, int totalnumclients, stats_t statarray[])
1024 {
1025 fd_set leftfdset;
1026 char *stats_as_text;
1027 char *page_stats_as_text;
1028 int returnval;
1029 int cnt,i;
1030
1031
1032 /* DOESN'T ACTUALLY PRINT UNTIL THE FIRST CLIENT REPORTS */
1033 fprintf(stdout,"Reading results ");
1034
1035 /*
1036 * COPY THE FILE DESCRIPTORS TO A TMP LIST,
1037 * ALLOCATE MEMORY FOR STATS, PAGESTATS IN TEXT FORM
1038 */
1039 leftfdset = *fdset;
1040 stats_as_text = (char *)mymalloc(SIZEOF_STATSTEXT+1);
1041 page_stats_as_text = (char *)mymalloc(SIZEOF_PAGESTATSTEXT+1);
1042
1043
1044 /*
1045 * LOOP UNTIL ALL CLIENTS HAVE REPORTED
1046 */
1047 for(cnt = 0; cnt < totalnumclients; cnt++)
1048 {
1049 /*
1050 * Send "GO" to each socket and receive its data.
1051 */
1052 if(!BADSOCKET(socknum[cnt]) && (FD_ISSET(socknum[cnt],&leftfdset)))
1053 {
1054 int len;
1055
1056 D_PRINTF( "Sending GO to receive data from client %d\n", cnt );
1057 if (NETWRITE(socknum[cnt], GOSTR, GOSTRLEN) != GOSTRLEN)
1058 {
1059 abort_clients();
1060 errexit("Error sending GO to client %d: %s\n", cnt,
1061 neterrstr());
1062 }
1063
1064 len = SIZEOF_STATSTEXTBASE + number_of_pages*SIZEOF_DOUBLETEXT;
1065 returnval = recvdata(socknum[cnt], stats_as_text, len);
1066 if (returnval < 0)
1067 {
1068 D_PRINTF( "Error reading timing stats: %s\n", neterrstr() );
1069 fprintf(stderr,
1070 "Error reading timing stats: %s\nSocket number %d\n",
1071 neterrstr(),socknum[cnt]);
1072 fprintf(stderr, "recvdata() returned %d\n", returnval);
1073 abort_clients();
1074 errexit("");
1075 }
1076
1077 /* convert text to stats */
1078 stats_as_text[len] = 0; /* add an end marker */
1079 statarray[cnt] = *text_to_stats(stats_as_text);
1080
1081 fputc('.', stdout); /* PROGRESS MARKER */
1082 fflush(stdout);
1083
1084 if (randomize)
1085 {
1086 for (i = 0; i < number_of_pages; i++)
1087 {
1088 D_PRINTF( "On page_stats[%d][%d]\n", cnt, i );
1089 returnval = recvdata(socknum[cnt], page_stats_as_text,
1090 SIZEOF_PAGESTATSTEXT);
1091 if (returnval < 0)
1092 {
1093 D_PRINTF( "Error reading page_stats[%d][%d]: %s\n",
1094 cnt, i, neterrstr() );
1095 fprintf(stderr,
1096 "Error reading page_stats[%d][%d]: %s\n",
1097 cnt, i, neterrstr());
1098 fprintf(stderr, "recvdata() returned %d\n", returnval);
1099 abort_clients();
1100 errexit("");
1101 }
1102 page_stats_as_text[returnval] = 0; /* add end marker */
1103 page_stats[cnt][i] =
1104 *text_to_page_stats(page_stats_as_text);
1105 } /* end for */
1106 }
1107
1108 FD_CLR(socknum[cnt],&leftfdset);
1109 NETCLOSE(socknum[cnt]);
1110 D_PRINTF("Finished with socket %d\n", socknum[cnt]);
1111 socknum[cnt] = BADSOCKET_VALUE;
1112 } /* end if socknum */
1113 } /* end for cnt */
1114
1115 /*
1116 * DONE READING RESULTS FROM CLIENTS
1117 */
1118 *endtime = time(NULL);
1119 timestr = asctime(localtime(endtime));
1120 fprintf(stdout,"\nAll clients ended at %s\n",timestr);
1121 fflush(stdout);
1122
1123 /* FREE MEMORY ALLOCATED FOR CLIENT STATS, PAGESTATS AS TEXT */
1124 free(stats_as_text);
1125 free(page_stats_as_text);
1126 }
1127
1128 /*
1129 * Prints out all the results
1130 */
1131 void
PrintResults(page_stats_t ** page_stats,time_t endtime,char * timestr,int totalnumclients,stats_t statarray[],page_stats_t * page_stats_total)1132 PrintResults(page_stats_t **page_stats, time_t endtime, char *timestr,
1133 int totalnumclients, stats_t statarray[],
1134 page_stats_t *page_stats_total)
1135 {
1136 stats_t masterstat;
1137 int cnt, i, j;
1138 double thruput;
1139 struct timeval dtime;
1140
1141 /*
1142 * Print everything out.
1143 */
1144 stats_init(&masterstat);
1145 masterstat.rs.totalconnects = 0;
1146 for(cnt = 0; cnt < totalnumclients; cnt++)
1147 {
1148
1149 if(statarray[cnt].rs.totalconnects == 0)
1150 {
1151 fprintf(stdout,
1152 "No connects from client %d. Is the Web server running?\n", cnt);
1153 }
1154 else if(statarray[cnt].rs.totalconnects > 0)
1155 {
1156 if(dumpall)
1157 {
1158 fprintf(stdout,"----------------------------------\n");
1159 fprintf(stdout,
1160 "Total number of pages retrieved from server: %u\n",
1161 statarray[cnt].totalpages);
1162
1163 rqstat_fprint(stdout, &(statarray[cnt].rs));
1164
1165 thruput = thruputpersec((double)(statarray[cnt].rs.totalbytes),
1166 &(statarray[cnt].rs.totalresponsetime));
1167
1168 fprintf(stdout,
1169 "Thruput average per connection: %.0lf bytes/sec\n",
1170 thruput);
1171 }
1172
1173 D_PRINTF( "Summing stats for %d, with %ld total connections\n",
1174 cnt, statarray[cnt].rs.totalconnects );
1175 rqstat_sum(&masterstat.rs, &(statarray[cnt].rs));
1176 }
1177 else
1178 masterstat.rs.totalerrs += statarray[cnt].rs.totalerrs;
1179 fflush(stdout);
1180 }
1181
1182 if(masterstat.rs.totalconnects == 0)
1183 {
1184 fprintf(stdout, "No connects at all. Is the Web server running?\n");
1185 return;
1186 }
1187
1188 for (i=0; i < totalnumclients; i++)
1189 {
1190 for (j=0; j < number_of_pages; j++)
1191 {
1192 D_PRINTF( "Summing page stats for %d, page %d, with %d connects\n",
1193 i, j, statarray[i].page_numbers[j] );
1194
1195 if (statarray[i].page_numbers[j] != 0)
1196 {
1197 rqst_stats_t *pst_rs;
1198 rqst_stats_t *ps_rs;
1199
1200 pst_rs = &(page_stats_total[j].rs);
1201 ps_rs = &(page_stats[i][j].rs);
1202
1203 rqstat_sum(pst_rs, ps_rs);
1204
1205 page_stats_total[j].totalpages += page_stats[i][j].totalpages;
1206 masterstat.totalpages += page_stats[i][j].totalpages;
1207
1208 /* yes, this is assignment, not sum */
1209 page_stats_total[j].page_size = page_stats[i][j].page_size;
1210 page_stats_total[j].page_valid = 1;
1211 }
1212 }
1213 }
1214
1215 /*
1216 * If the -v (verbose) option was used then print even more. Note that
1217 * using the -v option can cause a LOT of data to be output and may fill
1218 * up disks if you're saving all stdout and stderr to a log file. It may
1219 * also greatly increase the run time while all the data is dumped to
1220 * screen.
1221 */
1222 if (verbose)
1223 {
1224 for (i = 0; i < number_of_pages; i++)
1225 {
1226 if (page_stats_total[i].page_valid == 1)
1227 {
1228 page_stats_t *pst;
1229
1230 pst = &(page_stats_total[i]);
1231
1232 printf ("===============================================================================\n");
1233 printf ("Page # %lu\n\n", i);
1234 printf ("Total number of times page was hit %u\n",
1235 pst->totalpages);
1236
1237 rqstat_print(&(pst->rs));
1238
1239 printf ("Page size %u \n", pst->page_size);
1240 printf ("===============================================================================\n\n");
1241 }
1242 }
1243 }
1244
1245 fprintf(stdout,"===============================================================================\n");
1246
1247 /*
1248 * Validate run.
1249 */
1250 masterstat.total_num_of_files = statarray[0].total_num_of_files;
1251 for (i=1; i < totalnumclients; i++)
1252 {
1253 if ((statarray[i].rs.totalconnects > 0) &&
1254 (statarray[i].total_num_of_files != masterstat.total_num_of_files))
1255 {
1256 fprintf(stdout,"**********************************************************************\n");
1257 fprintf(stdout,"**** ERROR: number of files in each test configuration is not the same\n");
1258 fprintf(stdout,"**** ERROR: Check configuration file %s on each client\n", configfile);
1259 fprintf(stdout,"**********************************************************************\n");
1260 break;
1261 }
1262 }
1263
1264 /*
1265 * Print summary statistics
1266 */
1267 fprintf(stdout, "WEBSTONE %s results:\n", VERSION);
1268
1269 fprintf(stdout,
1270 "Total number of clients: %5d\n", totalnumclients);
1271 if (testtime)
1272 {
1273 fprintf(stdout,
1274 "Test time: %5d minutes\n", testtime);
1275
1276 fprintf(stdout,
1277 "Server connection rate: %10.2lf connections/sec\n",
1278 (double)(masterstat.rs.totalconnects)/(60*testtime));
1279
1280 fprintf(stdout,
1281 "Server error rate: %10.2lf err/sec\n",
1282 (double)(masterstat.rs.totalerrs)/(60*testtime));
1283
1284 fprintf(stdout,
1285 "Server thruput: %10.2lf Mbit/sec\n",
1286 (double)(8*masterstat.rs.totalbytes)/(60*testtime*1000000));
1287
1288 fprintf(stdout,
1289 "Little's Load Factor: %10.2lf \n",
1290 (double)(masterstat.rs.totalresponsetime.tv_sec)/
1291 (1000*60*testtime));
1292 }
1293
1294 avgtime(&masterstat.rs.totalresponsetime, masterstat.rs.totalconnects,
1295 &dtime);
1296
1297 fprintf(stdout,
1298 "Average response time: %11.3lf sec\n",
1299 timevaldouble(&dtime) / 1000);
1300
1301 if (masterstat.rs.totalconnects)
1302 {
1303 fprintf(stdout,
1304 "Error Level: %10.2lf %%\n",
1305 (double)(100 * masterstat.rs.totalerrs)/(masterstat.rs.totalconnects));
1306 }
1307 else
1308 {
1309 fprintf(stdout,
1310 "Error Level: UNDEFINED\n");
1311 fprintf(stderr,
1312 "No connections during run; is the Web server running?\n");
1313 }
1314
1315 /* so much for the key metrics */
1316
1317 thruput = 8000 * thruputpersec((double)(masterstat.rs.totalbytes),
1318 &(masterstat.rs.totalresponsetime));
1319
1320 fprintf(stdout,
1321 "Average client thruput: %10.2lf Mbit/sec\n",
1322 thruput/1000000);
1323
1324 fprintf(stdout,
1325 "Sum of client response times: %10.2lf sec\n",
1326 timevaldouble(&(masterstat.rs.totalresponsetime)) / 1000);
1327
1328 fprintf(stdout,
1329 "Total number of pages read: %5ld\n\n",
1330 masterstat.totalpages);
1331
1332 /* Remaining stats are the same as usual */
1333 rqstat_fprint(stdout, &(masterstat.rs));
1334 fflush(stdout);
1335 }
1336
1337
1338 #ifdef WIN32
1339 /* close socket library */
1340 void
sock_cleanup(void)1341 sock_cleanup(void)
1342 {
1343 WSACleanup();
1344 }
1345 #endif /* WIN32 */
1346
1347 int
main(const int argc,char * argv[])1348 main(const int argc, char *argv[])
1349 {
1350
1351 /*
1352 * sync_sock is the socket that clients will use to let us know that
1353 * they've started up and are ready to go.
1354 */
1355 int sync_sock;
1356
1357 int i, j;
1358 char buffer[NCCARGS];
1359 char commandline[NCCARGS];
1360 char *timestr;
1361 time_t starttime;
1362 time_t endtime;
1363 fd_set fdset;
1364
1365 /* make the big arrays static to avoid stack overflow */
1366 static char clienthostname[MAXCLIENTS][MAXHOSTNAMELEN];
1367 static stats_t statarray[MAXCLIENTS];
1368 page_stats_t **page_stats;
1369 page_stats_t *page_stats_total;
1370 struct sockaddr_in serveraddr;
1371
1372 #ifdef WIN32
1373 WSADATA WSAData;
1374 COORD dwSize;
1375
1376 if ((WSAStartup(MAKEWORD(1,1), &WSAData)) != 0)
1377 errexit("Error in WSAStartup()\n");
1378 atexit(sock_cleanup);
1379
1380 /* increase size of output window */
1381 dwSize.X = 80;
1382 dwSize.Y = 500;
1383 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), dwSize);
1384 #endif /* WIN32 */
1385
1386
1387 /* Initalization of variables. */
1388 /* debugfile = stdout; */
1389 debugfile = stderr;
1390 memset(buffer, 0, NCCARGS);
1391 memset(webserver, 0, MAXHOSTNAMELEN);
1392 memset(configfile, 0, MAXPATHLEN);
1393 FD_ZERO(&zerofdset);
1394 FD_ZERO(&fdset);
1395
1396 for(i = 0; i < MAXCLIENTS; i++)
1397 {
1398 socknum[i] = BADSOCKET_VALUE;
1399 statarray[i].rs.totalconnects = 0;
1400 }
1401
1402 signal(SIGINT, sig_int);
1403
1404 ParseCmdLine(argc, argv);
1405 sync_sock = SetupSyncSocket(&serveraddr);
1406 MakeCmdLine(commandline);
1407 totalnumclients = RexecClients(commandline, clienthostname, &serveraddr);
1408 if(totalnumclients == 0)
1409 {
1410 errexit("No work to do!\n");
1411 }
1412
1413 /* Initalization of variables. */
1414 page_stats = (page_stats_t **)
1415 mymalloc(totalnumclients*sizeof(page_stats_t *));
1416 for (i=0; i < totalnumclients; i++)
1417 {
1418 page_stats[i] = (page_stats_t *)
1419 mymalloc(number_of_pages*sizeof(page_stats_t));
1420 }
1421 page_stats_total = (page_stats_t *)
1422 mymalloc(number_of_pages*sizeof(page_stats_t));
1423
1424 for (i=0; i < totalnumclients; i++)
1425 stats_init(&(statarray[i]));
1426
1427 for (i=0; i < totalnumclients; i++)
1428 {
1429 for (j=0; j < number_of_pages; j++)
1430 page_stats_init(&(page_stats[i][j]));
1431 }
1432 for (i=0; i < number_of_pages; i++)
1433 page_stats_init(&(page_stats_total[i]));
1434
1435 GetReady(&fdset, totalnumclients, sync_sock );
1436 NETCLOSE(sync_sock);
1437
1438 /*
1439 * Send a "GO" message to all webclients so they will start their tests.
1440 */
1441 SendGo(totalnumclients, socknum);
1442
1443 /*
1444 * Wait for all of the webclients to complete. We have a socket
1445 * connection to each webclient process or thread so we should get
1446 * a reply from each one. The reply will be the timing information.
1447 */
1448 starttime = time(NULL);
1449 timestr = asctime(localtime(&starttime));
1450 fprintf(stdout, "All clients started at %s\n", timestr);
1451 fprintf(stdout, "Waiting for clients completion...\n");
1452 fflush(stdout);
1453
1454 /*
1455 * If this is a timed test, we might as well snooze. The clients have
1456 * their own timers and will contact us when they are finished with
1457 * their tests and ready to report their data.
1458 */
1459 if (testtime)
1460 sleep(testtime * 60);
1461
1462
1463 GetResults(&fdset, page_stats, &endtime, timestr, totalnumclients,
1464 statarray);
1465
1466 PrintResults(page_stats, endtime, timestr, totalnumclients, statarray,
1467 page_stats_total);
1468
1469 exit(0);
1470 }
1471
1472