Revert "Use a variable on the stack to not have a temporary in the call"
[ACE_TAO.git] / ACE / apps / JAWS / clients / WebSTONE / src / webclient.c
blob660ea6a097e31c564aadd43c4106ba7c4c517e80
1 /**************************************************************************
2 * *
3 * Copyright (C) 1995 Silicon Graphics, Inc. *
4 * *
5 * These coded instructions, statements, and computer programs were *
6 * developed by SGI for public use. If any changes are made to this code*
7 * please try to get the changes back to the author. Feel free to make *
8 * modifications and changes to the code and release it. *
9 * *
10 **************************************************************************/
12 /* FUZZ: disable check_for_math_include */
13 /* FUZZ: disable check_for_improper_main_declaration */
15 #include <thread.h>
17 #include <stdio.h>
19 #ifdef WIN32
20 #include <windows.h>
21 #include <winsock.h>
22 #include <time.h>
23 #include <process.h>
24 #include <io.h>
25 #endif /* WIN32 */
27 #include <errno.h>
28 #include <signal.h>
29 #include <stdlib.h>
30 #include <string.h>
32 #ifndef WIN32
33 #include <netdb.h>
34 #include <unistd.h>
35 #endif /* WIN32 */
37 #include <time.h>
38 #include <math.h>
40 #ifndef WIN32
41 #include <sys/param.h>
42 #endif /* WIN32 */
44 #include <sys/types.h>
46 #ifndef WIN32
47 #include <sys/ipc.h>
48 #include <sys/shm.h>
49 #include <sys/errno.h>
50 #include <sys/socket.h>
51 #include <sys/time.h>
52 #include <sys/wait.h>
53 #include <netinet/in.h>
54 #include <arpa/inet.h>
55 #endif /* WIN32 */
57 #include <ctype.h>
59 #include "sysdep.h"
60 #include "bench.h"
62 #define _BSD_SIGNALS
63 #define INFINITY 100000000
64 #define DEFAULTWWWPORT 80
65 #define LOG_FILE "logfile"
66 #ifdef WIN32
67 #define DEBUG_FILE "c:/tmp/webstone-debug"
68 #else
69 #define DEBUG_FILE "/tmp/webstone-debug"
70 #endif /* WIN32 */
71 #define NCCARGS 4096
73 /* global variables */
75 THREAD FILE *debugfile = stderr;
76 page_list_t *load_file_list; /* actually a dynamic array */
78 int amclient = 0;
79 int havewebserver = 0;
80 int haveproxyserver = 0;
81 int savefile = 0;
82 NETPORT portnum = DEFAULTWWWPORT;
83 int timeexpired = 0;
84 int debug = 0;
85 long int number_of_pages = 0;
86 char webmaster[MAXHOSTNAMELEN];
87 char webserver[MAXHOSTNAMELEN];
88 char proxyserver[MAXHOSTNAMELEN];
91 #ifdef WIN32
92 HANDLE hSemaphore;
93 int CounterSemaphore = 0; /* counter semaphore for children */
94 #endif /* WIN32 */
96 static void ClientThread(void *);
98 /* used to bypass DNS/YP name resolution for every page */
99 struct hostent webserv_phe, webmast_phe;
100 struct protoent webserv_ppe, webmast_ppe;
101 unsigned long webserv_addr, webmast_addr;
102 short webserv_type, webmast_type; /* socket type */
104 /* End of globals */
107 static void
108 usage(const char *progname)
110 returnerr("Usage: %s [-d] [-w webserver] [-p port_num]\n",
111 progname);
112 returnerr("\t[-c masterhost:port] [-t run_time | -l loops]\n");
113 returnerr("\t[-n numclients] [-R]\n");
114 returnerr("\t[-f config_file] [-u uilfile | url ...]\n");
115 errexit("\n");
116 } /* END usage() */
118 static void
119 alarmhandler(void)
121 /* RECEIVED AN ALARM SIGNAL */
122 timeexpired = 1;
123 } /* END alarmhandler() */
125 #ifndef WIN32
126 static void
127 childhandler(void)
129 int status;
131 /* RECEIVED A SIGNAL THAT A CHILD PROCESS HAS DIED */
132 D_PRINTF( "A child process has died\n" );
133 while (wait3(&status, WNOHANG, (struct rusage *)0) >= 0)
135 /* do nothing */
138 } /* END childhandler() */
139 #endif /* WIN32 */
142 /* look up the host name and protocol
143 * called once by main() since all threads
144 * use the same protocol and address
147 int resolve_addrs(char *host, char *protocol, struct hostent *host_phe, struct protoent *proto_ppe, unsigned long *addr,
148 short *type) {
149 struct hostent *phe;
150 struct protoent *ppe;
152 /* if IP address given, convert to internal form */
153 if (host[0] >= '0' && host[0] <= '9') {
154 *addr = inet_addr(host);
155 if (*addr == INADDR_NONE)
156 return(returnerr("Invalid IP address %s\n", host));
158 } else {
159 /* look up by name */
160 phe = gethostbyname(host);
161 if (phe == 0)
163 D_PRINTF( "Gethostbyname failed: %s", neterrstr() );
164 return(returnerr("Can't get %s host entry\n", host));
166 memcpy(host_phe, phe, sizeof(struct hostent));
167 memcpy((char *)addr, phe->h_addr, sizeof(*addr));
170 /* Map protocol name to protocol number */
171 ppe = getprotobyname(protocol);
173 if (ppe == 0)
175 D_PRINTF( "protobyname returned %d\n", ppe );
176 return(returnerr("Can't get %s protocol entry\n",protocol));
178 memcpy(proto_ppe, ppe, sizeof(struct protoent));
180 D_PRINTF( "Protocol number %d\n", ppe->p_proto );
182 /* Use protocol to choose a socket type */
183 if (strcmp(protocol,"udp") == 0)
185 *type = SOCK_DGRAM;
187 else
189 *type = SOCK_STREAM;
190 D_PRINTF( "Choosing SOCK_STREAM %d type %d %s\n",
191 SOCK_STREAM, *type, neterrstr() );
194 return 0;
197 /* connect to a socket given the hostname and protocol */
198 SOCKET
199 connectsock(char *host, NETPORT portnum, char *protocol)
201 struct sockaddr_in sin; /* an Internet endpoint address */
202 SOCKET s; /* socket descriptor */
203 int type; /* socket type */
204 short proto;
205 int returnval; /* temporary return value */
207 D_PRINTF( "Beginning connectsock; host=%s port=%d proto=%s\n", host,
208 portnum, protocol );
210 sin.sin_family = AF_INET;
211 memset((char *)&sin, 0, sizeof(sin));
212 D_PRINTF( "Zeroed address structure\n" );
214 sin.sin_port = htons(portnum);
215 D_PRINTF( "Set port number %d\n", portnum );
217 /* get the contact information */
218 if (strcmp(host, webserver) == 0) {
219 sin.sin_addr.S_ADDR = webserv_addr;
220 sin.sin_family = PF_INET;
221 proto = webserv_ppe.p_proto;
222 type = webserv_type;
223 } else if (strcmp(host, webmaster) == 0) {
224 sin.sin_addr.S_ADDR = webmast_addr;
225 sin.sin_family = PF_INET;
226 proto = webmast_ppe.p_proto;
227 type = webmast_type;
228 } else {
229 struct hostent host_phe;
230 struct protoent host_ppe;
231 unsigned long host_addr;
232 short host_type; /* socket type */
234 if (resolve_addrs(host, "tcp", &host_phe, &host_ppe, &host_addr, &host_type))
235 return returnerr("Can't resolve hostname %s in get()\n", host);
236 sin.sin_addr.S_ADDR = host_addr;
237 sin.sin_family = PF_INET;
238 proto = host_ppe.p_proto;
239 type = host_type;
242 /* Allocate a socket */
243 s = socket(PF_INET, type, proto);
244 D_PRINTF( "Socket %d returned %d, %s\n",
245 type, s, neterrstr() );
247 if (BADSOCKET(s))
249 D_PRINTF( "Can't create socket: %s\n",neterrstr() );
250 return BADSOCKET_VALUE;
253 /* Connect the socket */
254 D_PRINTF( "Trying to connect %d with size %d, %s\n",
255 s, sizeof(sin), neterrstr() );
256 D_PRINTF( "Address is family %d, port %d, addr %s\n",
257 sin.sin_family, ntohs(sin.sin_port),
258 inet_ntoa(sin.sin_addr) );
260 returnval = connect(s, (struct sockaddr *)&sin, sizeof(sin));
261 D_PRINTF( "Connect returned %d, %s\n",
262 returnval, neterrstr() );
263 if (returnval < 0)
265 D_PRINTF( "Can't connect: %s\n", neterrstr() );
266 NETCLOSE(s);
267 return BADSOCKET_VALUE;
270 /* all done, returning socket descriptor */
271 D_PRINTF( "Returning %d from connectsock call\n", s );
272 return(s);
274 } /* END connectsock() */
276 SOCKET
277 connecttomaster(char *str)
279 char *tempch;
280 SOCKET sock;
281 char msg[100];
282 char ConnectStr[100]; /* Fix to handle multiple threads */
283 int tries;
285 strcpy(ConnectStr, str);
288 * BREAK UP THE connectstr INTO A HOSTNAME/HOST-IP AND A PORT NUMBER.
290 if((tempch = strpbrk(ConnectStr,":")) == 0)
293 * INCORRECT FORMAT OF ConnectStr. CORRECT FORMAT IS
294 * HOSTNAME:PORT OR HOST-IP:PORT
296 D_PRINTF( "Incorrect format %s: use hostname:port or ip_addr:port\n",
297 ConnectStr );
298 return(returnerr("Incorrect format %s: use host:port or ip_addr:port\n",
299 ConnectStr));
303 * ZERO OUT THE COLON SO WE HAVE TWO STRINGS, THE HOSTNAME AND THE PORT
305 *tempch = '\0';
306 tempch++;
308 /* loop here to connect to webmaster - TCP/IP allows no more than 5
309 * connection requests outstanding at once and thus the webmaster may
310 * reject a connection if there are a lot of client processes
312 #define MAXTRIES 30
313 #define TRYDELAY_SEC 1
314 for (tries = 0; tries < MAXTRIES; tries++) {
316 sock = connectsock(ConnectStr,(NETPORT)atoi(tempch),"tcp");
318 if (!BADSOCKET(sock))
319 break;
321 sleep(TRYDELAY_SEC);
324 if (BADSOCKET(sock))
326 /* ERROR CONNECTING TO MASTER PROCESS */
327 return(returnerr("Could not connect to master process\n"));
331 * SIGNAL THE MASTER THAT WE ARE READY TO PROCEED. WHEN ALL
332 * CHILD PROCESSES HAVE CONNECTED AND SENT THIS SIGNAL,
333 * THE MASTER WILL ISSUE US A GO SIGNAL.
335 if(NETWRITE(sock,READYSTR,READYSTRLEN) != READYSTRLEN)
337 return(returnerr("Error sending READY message to master"));
340 memset(msg,0,GOSTRLEN+1);
341 if(NETREAD(sock,msg,GOSTRLEN) != GOSTRLEN)
343 D_PRINTF( "Error receiving GO message from master: %s\n", neterrstr()
345 return(returnerr("Error receiving GO message from master\n"));
348 if(strncmp(GOSTR,msg,GOSTRLEN))
351 * WE RECEIVED A MESSAGE OTHER THAN GO. PRINT IT OUT AND RETURN ERROR
353 return(returnerr("Received non-GO message %s\n",msg));
356 return(sock);
358 } /* END connecttomaster() */
361 static void
362 accumstats(rqst_timer_t *rqsttimer, page_stats_t *pagestats, stats_t *timestat)
364 rqst_stats_t rqststats;
366 #define TFMT "%10u:%10u"
368 * DUMP THE TIMING INFORMATION HERE, OR COMPUTE WHAT YOU WANT TO
369 * PRINT OUT LATER.
372 D_PRINTF( "Total bytes read: %d \t Body size read: %d\n",
373 rqsttimer->totalbytes,
374 rqsttimer->bodybytes );
376 D_PRINTF( "Enter time: " TFMT " \t Exit Time: " TFMT "\n",
377 rqsttimer->entertime.tv_sec,
378 rqsttimer->entertime.tv_usec,
379 rqsttimer->exittime.tv_sec,
380 rqsttimer->exittime.tv_usec );
381 D_PRINTF( "Before connect: " TFMT " \t After connect: " TFMT "\n",
382 rqsttimer->beforeconnect.tv_sec,
383 rqsttimer->beforeconnect.tv_usec,
384 rqsttimer->afterconnect.tv_sec,
385 rqsttimer->afterconnect.tv_usec );
386 D_PRINTF( "Before header: " TFMT " \t After header: " TFMT "\n",
387 rqsttimer->beforeheader.tv_sec,
388 rqsttimer->beforeheader.tv_usec,
389 rqsttimer->afterheader.tv_sec,
390 rqsttimer->afterheader.tv_usec );
391 D_PRINTF( "After body: " TFMT "\n",
392 rqsttimer->afterbody.tv_sec,
393 rqsttimer->afterbody.tv_usec );
395 rqstat_times(&(rqststats), rqsttimer);
396 rqstat_sum(&(timestat->rs), &(rqststats));
397 rqstat_sum(&(pagestats->rs), &(rqststats));
399 if (rqsttimer->page_number != 999)
401 timestat->page_numbers[rqsttimer->page_number] += 1;
404 #undef TFMT
405 } /* END accumstats */
409 * fetch the set of files that constitute a page
411 * maxcount = the number of files in the WWW page
412 * pageval = the number of the WWW page (offset in load_file_list[])
413 * (if -1, use page # 0 - does this still work?)
415 * returns the number of files retrieved
417 static int
418 makeload(int maxcount, int pageval, THREAD rqst_timer_t *timerarray, THREAD stats_t *timestat, THREAD SOCKET mastersock, THREAD page_stats_t *page_stats)
420 int cnt;
421 int returnval;
422 page_stats_t page_stats_tmp;
423 char server[MAXHOSTNAMELEN];
425 NETPORT loc_portnum;
427 D_PRINTF( "Starting makeload(maxcount %d, pageval %d)\n",
428 maxcount, pageval );
430 strcpy( server, webserver); /* Put in default value */
432 page_stats_init(&page_stats_tmp);
433 D_PRINTF( "Page stats initialized\n" );
435 for (cnt = 0; cnt < maxcount; cnt++)
437 D_PRINTF( "Loop count %d in makeload()\n", cnt );
438 if (pageval == -1)
440 pageval = cnt;
442 if (timeexpired)
444 break;
447 /* check for a filename */
448 if (strlen(load_file_list[pageval].filename[cnt]) < 1)
450 D_PRINTF( "Bad filename at pageval %d, count %d\n",
451 pageval, cnt );
452 return(returnerr("Bad filename at pageval %d, count %d\n",
453 pageval, cnt));
456 /* if (load_file_list[pageval].port_number[cnt] != 0)
458 loc_portnum = load_file_list[pageval].port_number[cnt];
460 else
462 loc_portnum = portnum;
463 } */
464 loc_portnum = portnum;
465 if ((load_file_list[pageval].servername[cnt] != 0)
467 *load_file_list[pageval].servername[cnt])
469 D_PRINTF( "Copying URL server %s to server\n",
470 load_file_list[pageval].servername[cnt] );
471 strcpy(server, load_file_list[pageval].servername[cnt]);
474 if (haveproxyserver)
476 D_PRINTF( "Copying proxy %s to webserver\n", proxyserver );
477 strcpy(server, proxyserver);
481 D_PRINTF( "Calling get(%s, %d, %s, &(timearray[%d]))\n",
482 server, loc_portnum, load_file_list[pageval].filename[cnt],
483 cnt );
485 returnval = get(server, loc_portnum,
486 load_file_list[pageval].filename[cnt],
487 &(timerarray[cnt]));
488 if (returnval < 0)
490 D_PRINTF( "***GET() RETURNED AN ERROR\n" );
494 * DID GET() RETURN A VALID TIME?
496 if ((returnval == 0) && (timerarray[cnt].valid == 2))
498 timerarray[cnt].page_number = pageval;
500 accumstats(&timerarray[cnt], &page_stats_tmp, timestat);
502 else if (!timeexpired) /* INVALID, INCREMENT THE ERROR COUNTER */
504 D_PRINTF( "GET error counter incremented\n" );
505 timestat->rs.totalerrs++;
508 if (amclient) {
509 fd_set readfds;
510 struct timeval timeout;
511 int rv;
513 timeout.tv_sec = 0;
514 timeout.tv_usec = 0;
515 FD_ZERO(&readfds);
516 FD_SET(mastersock, &readfds);
518 /* if the webmaster has aborted, quit */
519 D_PRINTF("Before select() on webmaster socket\n");
520 if (rv = select(FD_SETSIZE, &readfds, 0, 0, &timeout)) {
521 D_PRINTF("select() returned %d\n", rv);
522 D_PRINTF("Client terminating at request of webmaster\n");
523 exit(2);
527 } /* END for cnt */
530 * DO WE HAVE A VALID RETURN FROM GET()?
531 * WHY NOT USE returnval HERE?
533 if ((returnval == 0) &&
534 (cnt == load_file_list[pageval].num_of_files) &&
535 (timerarray[cnt-1].valid == 2))
537 rqst_stats_t *ps_rs;
538 rqst_stats_t *pst_rs;
540 ps_rs = &(page_stats[pageval].rs);
541 pst_rs = &(page_stats_tmp.rs);
543 rqstat_sum(ps_rs, pst_rs);
545 page_stats[pageval].totalpages++;
547 if (page_stats[pageval].page_size == 0)
549 page_stats[pageval].page_size = (unsigned)
550 page_stats_tmp.rs.totalbody;
554 D_PRINTF( "\nMakeload output page %d: %d errors, %d pages\n",
555 pageval, timestat->rs.totalerrs, page_stats[pageval].totalpages );
556 D_PRINTF( "Makeload returning %d\n", cnt );
558 return(cnt);
560 } /* END makeload() */
562 #ifdef WIN32
563 /* close socket library at exit() time */
564 void sock_cleanup(void) {
566 WSACleanup();
568 #endif /* WIN32 */
570 /* globalize variables that were in main() */
571 long int numfiles = 0;
572 int testtime = 0;
573 int numloops = 0;
574 int numclients = 0;
575 int record_all_transactions = 0;
576 int uil_filelist_f = 0; /* filedescriptor of URLs to fetch? */
577 int verbose = 0;
578 int total_weight;
579 char uil_filelist[NCCARGS];
580 char filelist[MAXNUMOFFILES][MAXPATHLEN];
581 char configfile[MAXPATHLEN];
582 char connectstr[MAXHOSTNAMELEN+10];
584 void
585 ACE_TMAIN(int argc, ACE_TCHAR *argv[])
587 int file_count=0;
588 int getoptch;
589 int currarg;
590 extern char *optarg;
591 extern int optind;
592 int i, j;
593 char *tempch;
594 int err;
596 #define SLEEP_USEC 100
597 #ifdef WIN32
598 WSADATA WSAData;
599 #else
601 struct timeval sleeptime;
603 /* set the amount of time that we'll pause before sending a "." to the
604 webmaster */
606 sleeptime.tv_sec = SLEEP_USEC/1000000;
607 sleeptime.tv_usec = SLEEP_USEC % 1000000;
608 #endif /* WIN32 */
610 debugfile = stderr;
612 #ifdef WIN32
613 MessageBeep(~0U); /* announce our existence */
614 MessageBeep(~0U);
615 MessageBeep(~0U);
617 err = WSAStartup(MAKEWORD(1,1), &WSAData);
618 if (err != 0) {
619 errexit("Error in WSAStartup()\n");
622 atexit(sock_cleanup);
624 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
626 /* create semaphore in locked state */
627 hSemaphore = CreateSemaphore(0, 0, 1, 0);
628 if(hSemaphore == 0)
630 errexit("Create semaphore failed: %d", GetLastError());
632 #endif /* WIN32 */
634 memset(webserver, 0, sizeof(webserver));
635 memset(webmaster, 0, sizeof(webmaster));
636 memset(proxyserver, 0, sizeof(proxyserver));
637 memset(connectstr, 0, sizeof(connectstr));
640 * PARSE THE COMMAND LINE OPTIONS
643 while((getoptch = getopt(argc,argv,"P:f:t:l:p:u:R:w:c:n:sdv")) != EOF)
645 switch(getoptch)
647 case 'c':
648 sprintf(connectstr, "%s", optarg);
649 amclient = 1;
650 printf("%s", OKSTR); /* sent back to webmaster */
651 fflush(stdout);
652 break;
653 case 'd':
654 debug = 0; /* sumedh */
655 break;
656 case 'f':
657 sprintf(configfile, "%s", optarg);
658 break;
659 case 'l':
660 numloops = atoi(optarg);
661 break;
662 case 'n':
663 numclients = atoi(optarg);
664 break;
665 case 'u':
666 sprintf(uil_filelist, "%s", optarg);
667 uil_filelist_f = 1;
668 break;
669 case 'p':
670 portnum = atoi(optarg);
671 break;
672 case 's':
673 savefile = 1;
674 break;
675 case 't':
676 testtime = 60 * atoi(optarg);
677 break;
678 case 'v':
679 verbose = 1;
680 break;
681 case 'w':
682 havewebserver = 1;
683 sprintf(webserver,"%s",optarg);
684 break;
685 case 'P':
686 haveproxyserver = 1;
687 sprintf(proxyserver, "%s", optarg);
688 break;
689 case 'R':
690 record_all_transactions = 1;
691 break;
692 default:
693 usage(argv[0]);
697 returnerr("Client begins...\n");
698 D_PRINTF( "Running in debug mode\n\n" );
700 /* print the command line */
701 for (i = 0; i < argc; i++)
702 D_PRINTF( "%s ", argv[i] );
703 D_PRINTF( "\n\n" );
705 if(testtime && numloops)
708 * EITHER numloops OR testtime, BUT NOT BOTH.
710 usage(argv[0]);
713 if(havewebserver != 1)
715 #ifdef WIN32
717 * THE SERVER'S NAME MUST BE SPECIFIED
719 returnerr("No WWW Server specified\n");
720 usage(argv[0]);
721 #else
722 /* IF IT ISN'T, WE ASSUME LOCALHOST */
723 sprintf(webserver, "%s", "localhost");
724 havewebserver = 1;
725 #endif /* WIN32 */
728 currarg = optind;
729 numfiles = 0;
730 while(currarg != argc)
733 * GET THE URLS TO RETRIEVE.
735 if (numfiles == MAXNUMOFFILES) {
736 returnerr("Maximum of %d files on the command line.\n");
737 usage(argv[0]);
739 sscanf(argv[currarg],"%s",filelist[numfiles]);
740 numfiles++;
741 currarg++;
744 if ((numfiles != 0) && uil_filelist_f)
746 returnerr("Both a filelist and UIL specified.\n");
747 usage(argv[0]);
750 if((numfiles == 0) && !(uil_filelist_f))
753 * AT LEAST ONE FILE MUST BE SPECIFIED
755 returnerr("No UIL resources or filelist specified \n");
756 usage(argv[0]);
759 if((numloops == 0) && (testtime == 0))
762 * NO SPECIFIED NUMBER OF LOOPS, AND NO TEST TIME
764 usage(argv[0]);
766 if(numclients > MAXPROCSPERNODE || numclients < 1)
768 returnerr("Number of Clients must be between 1 and %d\n", MAXPROCSPERNODE);
769 exit(1);
772 /* allow use of IP address */
773 if(amclient) {
774 if((tempch = strpbrk(connectstr,":")) == 0)
777 * INCORRECT FORMAT OF ConnectStr. CORRECT FORMAT IS
778 * HOSTNAME:PORT OR HOST-IP:PORT
780 D_PRINTF( "Incorrect format %s: use hostname:port or ip_addr:port\n",
781 connectstr );
782 returnerr("Incorrect format %s: use host:port or ip_addr:port\n", connectstr);
783 exit(1);
784 } else {
785 strncpy(webmaster, connectstr, tempch-connectstr);
787 if(resolve_addrs(webmaster, "tcp", &webmast_phe, &webmast_ppe, &webmast_addr, &webmast_type))
788 exit(1);
791 if (haveproxyserver)
793 D_PRINTF( "Copying proxy %s to webserver\n", proxyserver );
794 strcpy(webserver, proxyserver);
797 if (resolve_addrs(webserver, "tcp", &webserv_phe, &webserv_ppe, &webserv_addr, &webserv_type))
798 exit(1);
801 * INITIALIZE DATA
803 /* allocate space for dynamic arrays */
804 load_file_list =
805 (page_list_t *)mymalloc((MAXNUMOFPAGES)*sizeof(page_list_t));
807 if (uil_filelist_f)
809 /* take a guess at the number of URLs in the file */
810 D_PRINTF( "About to parse filelist %s\n", uil_filelist );
811 number_of_pages = count_file_list(uil_filelist);
812 numfiles = 1;
814 /* IF WE HAVE A FILELIST, PARSE IT */
815 /* allocate memory */
816 D_PRINTF( "Allocating page list: %ld by %d\n",
817 number_of_pages, numfiles );
818 for (i=0; i<number_of_pages; i++)
820 for (j=0; j<MAXNUMOFFILES; j++)
822 load_file_list[i].servername[j] =
823 (char *)mymalloc(URL_SIZE);
824 load_file_list[i].filename[j] =
825 (char *)mymalloc(URL_SIZE);
829 D_PRINTF( "Parsing file list: %s\n", uil_filelist );
830 parse_file_list(uil_filelist, load_file_list,
831 &number_of_pages, &numfiles);
832 /* free memory for pages that won't be used? */
833 D_PRINTF( "Actual page list: %ld by %d\n",
834 number_of_pages, MAXNUMOFFILES );
836 D_PRINTF( "Setting up weighting for %ld pages\n",
837 number_of_pages );
838 total_weight = load_percent(load_file_list, number_of_pages);
839 /* total_weight = load_percent(load_file_list, number_of_pages, pages); */
841 else
843 /* no uil file */
844 number_of_pages = numfiles;
847 if (number_of_pages < 1)
849 /* no pages - exit */
850 D_PRINTF( "No valid URLs found\n" );
851 errexit("No valid URLs found\n");
854 #ifndef WIN32
856 * IF WE ARE TO FORK ADDITIONAL CLIENTS ON THIS MACHINE,
857 * WE MUST DO IT BEFORE WE CONNECT TO THE MASTER.
859 * FIRST, SET UP SIGNAL HANDLING
861 signal(SIGCHLD, childhandler);
862 for(i = 0; i < numclients; i++)
864 thr_create (0, 0, ClientThread, 0, THR_BOUND, 0);
866 /* switch(fork())
868 case 0:
869 numclients = 1;
870 ClientThread(0);
871 exit(0);
872 break;
873 case -1:
874 errexit("Error forking child processes\n");
875 exit(1);
876 default:
877 break;
878 } */
879 select(0,(fd_set *)0,(fd_set *)0, (fd_set *)0, &sleeptime);
883 * Wait for all children to exit.
886 while (thr_join(0, 0, 0) == 0);
888 /* for(;;)
890 int pid = wait((int*)0);
891 if ((pid == -1) && errno == ECHILD) break;
892 } */
893 #else
894 /* start threads on NT */
895 for (i = 0; i < numclients; i++)
897 if (_beginthread(ClientThread, 0, 0) == -1)
899 errexit("_beginthread failed: %d", GetLastError());
902 #endif /* WIN32 */
904 #ifdef WIN32
905 /* wait for children to get to sync point */
906 while (CounterSemaphore < numclients)
907 sleep(1);
908 CounterSemaphore = 0;
910 /* start all children simultaneously */
911 ReleaseSemaphore(hSemaphore, 1, 0);
913 if (testtime) {
914 sleep(testtime);
915 alarmhandler(); /* signal end of test to threads */
919 * Wait for all threads to exit.
921 while (CounterSemaphore < numclients)
922 sleep(1);
924 CloseHandle(hSemaphore);
925 #endif /* WIN32 */
927 return;
928 } /* end main() */
930 void ClientThread(void *dummy)
933 THREAD FILE *logfile;
935 THREAD stats_t timestat;
937 THREAD rqst_timer_t timerarray[MAXNUMOFFILES];
938 THREAD SOCKET mastersock = BADSOCKET_VALUE; /* connection to webmaster */
941 THREAD page_stats_t *page_stats; /* actually a dynamic array */
943 int loopcnt = 0;
944 int filecnt;
945 int loop;
946 int ran_number;
947 int page_index;
948 int page_number;
949 int file_count = 0;
950 char file_name[50];
951 struct timeval runningtime;
952 time_t junk;
953 int i;
954 int returnval;
957 * INITIALIZE DATA
960 page_stats =
961 (page_stats_t *)mymalloc((number_of_pages)*sizeof(page_stats_t));
963 for (i=0; i < number_of_pages; i++) {
964 page_stats_init(&(page_stats[i]));
967 if (debug)
970 * OPEN A DEBUG FILE
972 fflush(stderr);
973 sprintf(file_name, "%s.%d", DEBUG_FILE, (int)getpid());
974 debugfile = fopen(file_name, "w+");
975 if (debugfile == 0)
976 errexit("Can't open debug file\n");
977 D_PRINTF( "Running in debug mode, %d\n",amclient );
980 if (record_all_transactions)
983 * OPEN A LOG FILE.
985 sprintf(file_name, "%s%d", LOG_FILE, (int)getpid());
986 returnerr("Log file is %s\n", file_name);
987 logfile = fopen(file_name, "w+");
990 /* Initialize random number generator */
991 junk = getpid ();
992 rand_r(&junk);
993 D_PRINTF( "Random seed: %d\n", junk );
995 for (i=0; i < MAXNUMOFFILES; i++)
997 rqtimer_init(&(timerarray[i]));
999 stats_init(&timestat);
1001 D_PRINTF( "Number of files %d\n", numfiles );
1003 timestat.total_num_of_files = numfiles;
1005 if (amclient)
1008 * WE ARE A CLIENT PROCESS. (i.e. WE ARE NOT RUN BY A USER, BUT BY
1009 * THE MASTER WWWSTONE PROCESS. WE NEED TO CONNECT TO THE
1010 * MASTER WHO WILL SYNCHRONIZE ALL THE CLIENTS.
1012 D_PRINTF( "Trying to connect with %s\n",connectstr );
1014 mastersock = connecttomaster(connectstr);
1016 D_PRINTF( "connecttomaster returns %d, %s\n",
1017 mastersock, neterrstr() );
1019 if(BADSOCKET(mastersock))
1022 * ERROR CONNECTING TO THE MASTER. ABORT.
1024 errexit("Error connecting to the master: %s\n", neterrstr());
1026 } /* END IF CLIENT */
1028 #ifdef WIN32
1029 /* Tell parent we're ready */
1030 InterlockedIncrement(&CounterSemaphore);
1032 /* Wait for main() thread to release us */
1033 WaitForSingleObject(hSemaphore, INFINITE);
1034 ReleaseSemaphore(hSemaphore, 1, 0);
1035 #endif /* WIN32 */
1036 if (testtime != 0)
1039 * IF RUNNING A TIMED TEST, WE WILL LOOP
1040 * UNTIL THE ALARM GOES OFF.
1041 * WE'LL ALSO NEED TO SET THE SIGNAL HANDLER
1043 #ifndef WIN32
1044 /*signal(SIGALRM, alarmhandler);*/
1046 * SEND SIGALRM IN testtime SECONDS
1048 /*alarm(testtime);*/
1049 #endif /* WIN32 */
1053 * AND THEY'RE OFF...
1056 if (testtime)
1057 numloops = INFINITY;
1058 GETTIMEOFDAY(&(timestat.starttime), &(timestat.starttimezone));
1060 /* LOOP UNTIL WE HIT numloops, OR WE RUN OUT OF TIME */
1061 for(loopcnt = 0; (loopcnt < numloops) && !timeexpired; loopcnt++)
1064 * THIS IS WHERE LOAD TESTING IS DONE.
1065 * GET A RANDOM NUMBER, THEN INDEX INTO THE
1066 * PAGE, AND THEN REQUEST THAT SET OF FILES.
1068 if (uil_filelist_f) /* HAVE FILELIST */
1070 D_PRINTF( "Have filelist\n" );
1071 /* if (testtime != 0) /* RUNNING IN TIMED MODE */
1072 if (1)
1074 D_PRINTF( "Running in timed mode\n" );
1075 /* random number between 0 and totalweight-1 */
1076 junk = getpid ();
1077 ran_number = (rand_r(&junk) % total_weight);
1078 D_PRINTF( "random %ld\n", ran_number );
1080 /* loop through pages, find correct one
1081 * while ran_number is positive, decrement it
1082 * by the load_num of the current page
1083 * example: ran_number is 5, pages have weights of 10 and 10
1084 * first iteration page_index = 0, ran_number = -5
1085 * iteration halted, page_index = 0
1087 page_index = -1;
1088 while (ran_number >= 0)
1090 page_index++;
1091 D_PRINTF( "Current page index %d: %ld - %d\n",
1092 page_index, ran_number,
1093 load_file_list[page_index].load_num
1095 ran_number -= load_file_list[page_index].load_num;
1098 if (page_index >= number_of_pages) { page_index--; }
1100 D_PRINTF( "Final page index %d\n", page_index );
1101 filecnt = makeload(load_file_list[page_index].num_of_files,
1102 page_index, timerarray, &timestat, mastersock, page_stats);
1103 testtime = 1;
1105 else /* NOT RUNNING IN TIMED MODE */
1107 for (page_number = 0;
1108 page_number < number_of_pages;
1109 page_number++)
1111 filecnt = makeload(load_file_list[page_number].num_of_files,
1112 page_number, timerarray, &timestat, mastersock, page_stats);
1114 } /* END for page_number */
1115 } /* END if/else TIMED MODE */
1117 else /* NO FILELIST */
1119 D_PRINTF( "No filelist\n" );
1121 * LOOP THROUGH UNTIL numfiles TIMES OR UNTIL TIMER EXPIRES
1122 * AND ALARM SETS filecnt TO INFINITY.
1125 /* does this still work?? */
1126 /* filecnt = makeload(numfiles, -1, timerarray); */
1127 } /* END if HAVE FILELIST */
1129 if (filecnt > 0)
1130 file_count += filecnt;
1132 } /* END while loopcnt */
1134 GETTIMEOFDAY(&(timestat.endtime), &(timestat.endtimezone));
1135 D_PRINTF( "Test run complete\n" );
1136 signal(SIGALRM, 0);
1138 if (testtime == 0)
1140 numfiles = loopcnt;
1142 if (uil_filelist_f)
1144 numfiles = file_count;
1148 /* This option ( "-R" ) looks broken (e.g. l > 50) -- JEF 2/15/96 */
1149 if (record_all_transactions)
1152 * DUMP THE LOG FILE INFORMATION.
1154 for (loop=0; loop < (loopcnt * file_count); loop++)
1156 fprintf(logfile, " entertime \t%d.%d\n"
1157 " beforeconnect \t%d.%d\n"
1158 " afterconnect \t%d.%d\n"
1159 " beforeheader \t%d.%d\n"
1160 " afterheader \t%d.%d\n"
1161 " afterbody \t%d.%d\n"
1162 " exittime \t%d.%d\n"
1163 " total bytes \t%d\n"
1164 " body bytes\t%d\n",
1165 timerarray[loop].entertime.tv_sec,
1166 timerarray[loop].entertime.tv_usec,
1167 timerarray[loop].beforeconnect.tv_sec,
1168 timerarray[loop].beforeconnect.tv_usec,
1169 timerarray[loop].afterconnect.tv_sec,
1170 timerarray[loop].afterconnect.tv_usec,
1171 timerarray[loop].beforeheader.tv_sec,
1172 timerarray[loop].beforeheader.tv_usec,
1173 timerarray[loop].afterheader.tv_sec,
1174 timerarray[loop].afterheader.tv_usec,
1175 timerarray[loop].afterbody.tv_sec,
1176 timerarray[loop].afterbody.tv_usec,
1177 timerarray[loop].exittime.tv_sec,
1178 timerarray[loop].exittime.tv_usec,
1179 timerarray[loop].totalbytes,
1180 timerarray[loop].bodybytes);
1181 } /* end for loop */
1182 } /* end if recording all transactions */
1184 D_PRINTF( "total errors: %d\n",timestat.rs.totalerrs );
1185 /* gethostname(timestat.hostname,MAXHOSTNAMELEN); */
1186 /* D_PRINTF( "Test for host: %s\n",timestat.hostname ); */
1187 D_PRINTF( "Server is: %s running at port number: %d\n",
1188 webserver,portnum );
1190 /* sprintf(timestat.hostname,"%s:%d",timestat.hostname,getpid()); */
1191 if (amclient) /* CLIENT TO A WEBMASTER */
1193 char *stats_as_text;
1196 * SEND THE TIMING DATA TO THE MASTER
1198 stats_as_text = stats_to_text(&timestat);
1199 D_PRINTF( "stats_to_text returned %s\n", stats_as_text );
1201 returnval = senddata(mastersock, stats_as_text,
1202 SIZEOF_STATSTEXTBASE + number_of_pages*SIZEOF_DOUBLETEXT);
1203 D_PRINTF( "Wrote time stats to master %d\n", returnval );
1205 if (returnval < 1)
1207 D_PRINTF( "Error while writing time stats: %s\n",
1208 neterrstr() );
1209 errexit("Error while writing time stats: %s\n",
1210 neterrstr());
1213 if (uil_filelist_f)
1214 /* write pagestats */
1216 char *page_stats_as_text;
1217 for (i = 0; i < number_of_pages; i++)
1219 D_PRINTF( "On page_stats[%d]\n", i );
1220 page_stats_as_text = page_stats_to_text(&page_stats[i]);
1221 returnval = strlen(page_stats_as_text);
1222 D_PRINTF( "page_stats_to_text[%d] returned %d\n",
1223 i, returnval );
1224 returnval = senddata(mastersock, page_stats_as_text,
1225 SIZEOF_PAGESTATSTEXT);
1226 if (returnval < 1)
1228 D_PRINTF( "Error while writing page_stats[%d]: %s\n",
1229 i, neterrstr() );
1230 errexit("Error while writing page_stats[%d]: %s\n",
1231 i, neterrstr());
1232 } /* end if */
1233 D_PRINTF( "Wrote %d bytes of page_stats[%d] to master\n",
1234 returnval, i );
1235 } /* end for */
1236 } /* end if filelist */
1238 D_PRINTF( "About to close socket\n" );
1239 if (NETCLOSE(mastersock))
1240 D_PRINTF( "Close socket error: %s\n", neterrstr() );
1242 else /* NOT A CLIENT TO A WEBMASTER */
1244 if (testtime)
1246 printf("Test ran for: %d minutes\n",(testtime/60));
1248 else
1250 printf("Test ran for: %d iterations.\n",numloops);
1252 compdifftime(&(timestat.endtime), &(timestat.starttime),
1253 &(runningtime));
1254 printf("Total time of test (sec) %d.%d\n", runningtime.tv_sec,
1255 runningtime.tv_usec);
1256 printf("Files retrieved per iteration: %d\n",numfiles); /* 'per iteration' */
1257 printf("----------------------------------\n");
1258 printf("Totals:\n\n");
1259 rqstat_print(&(timestat.rs));
1261 if (timestat.rs.totalconnects == 0)
1262 goto end;
1263 printf("Thruput = %5.2lf Kbytes/sec\n",
1264 thruputpersec(timestat.rs.totalbytes, &runningtime) / 1000);
1266 if (uil_filelist_f && numloops && verbose)
1268 for (loop = 0; loop < number_of_pages; loop++)
1270 if (timestat.page_numbers[loop] != 0)
1272 printf ("===============================================================================\n");
1273 printf ("Page # %d\n\n", loop);
1274 printf ("Total number of times page was hit %d\n",
1275 page_stats[loop].totalpages);
1276 rqstat_print(&(page_stats[loop].rs));
1277 printf ("Page size %d \n", page_stats[loop].page_size);
1278 printf ("===============================================================================\n\n");
1279 } /* END if timestat */
1280 } /* END for loop */
1281 } /* END if filelist */
1282 } /* END if client */
1284 end:
1285 if(record_all_transactions)
1286 fclose(logfile);
1287 if(debug)
1289 D_PRINTF( "Client exiting.\n" );
1290 fclose(debugfile);
1293 #ifdef WIN32
1294 /* tell parent we're done */
1295 InterlockedIncrement(&CounterSemaphore);
1296 #endif /* WIN32 */
1298 } /* END ClientThread() */