1 /**************************************************************************
3 * Copyright (C) 1995 Silicon Graphics, Inc. *
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. *
10 **************************************************************************/
12 /* FUZZ: disable check_for_math_include */
13 /* FUZZ: disable check_for_improper_main_declaration */
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/errno.h>
32 #include <sys/socket.h>
34 #include <netinet/in.h>
37 #define FD_SETSIZE 1024 /* max size for select() - keep before <winsock.h>
38 * and same size as MAXCLIENTS */
48 /* command line options/data */
52 int haveproxyserver
= 0;
53 char proxyserver
[MAXHOSTNAMELEN
];
54 char network_mask_str
[30] = "255.255.255.0";
55 unsigned network_mask
= 0;
56 int servaddrin_config
= 0;
59 int havewebserver
= 0;
63 int record_all_transactions
= 0;
64 int uil_filelist_f
= 0;
66 char webserver
[MAXHOSTNAMELEN
];
67 char configfile
[MAXPATHLEN
];
68 char uil_filelist
[NCCARGS
];
70 char filelist
[256][MAXPATHLEN
];
74 long int number_of_pages
= 0;
75 int totalnumclients
= 0;
77 SOCKET socknum
[MAXCLIENTS
];
78 SOCKET sockIO
[MAXTOTALPROCS
];
79 SOCKET sockErr
[MAXTOTALPROCS
];
80 THREAD
FILE *debugfile
= stderr
;
81 struct hostent
*master_phe
; /* IP addresses for webmaster */
82 struct timeval sumedh_start
, sumedh_end
;
84 void HostEntCpy(struct hostent
*dest
, struct hostent
*src
);
87 usage(const char *progname
)
90 fprintf(stderr
, "Usage: %s [-a] [-d] -f config_file [-l numloops]\n",
92 fprintf(stderr
, " [-p port_num] [-r] [-s] [-t run_time] \n");
93 fprintf(stderr
, "\n");
94 fprintf(stderr
, "-w webserver URL [URL ...]\n\n");
95 fprintf(stderr
, "-a print timing information for all clients\n");
96 fprintf(stderr
, "-d turn on debug statements\n");
97 fprintf(stderr
, "-f config_file\tfile specifying clients\n");
98 fprintf(stderr
, "-l number of iterations to retrieve uils\n");
99 fprintf(stderr
, "-p port number of web server if not 80\n");
100 fprintf(stderr
, "-r redirect stdout of clients to /tmp/webstone.xxx\n");
101 fprintf(stderr
, "-s save client gets to /tmp/webstone.data.*\n");
102 fprintf(stderr
, "-t run_time\tduration of test in minutes\n");
103 fprintf(stderr
, "-w webserver\tname of webserver host to contact\n");
104 fprintf(stderr
, "-u URL file\tfilelist of URLs\n");
105 fprintf(stderr
, "-v verbose mode\n");
106 fprintf(stderr
, "-P servername\tuse proxy server for transactions\n");
107 fprintf(stderr
, "-W webserver addresses are in the config file\n");
108 fprintf(stderr
, "-R record all transactions\n");
113 passivesock(const NETPORT portnum
, const char *protocol
, const int qlen
)
115 struct protoent
*ppe
; /* pointer to protocol info entry */
116 struct sockaddr_in sin
; /* Internet endpoint address */
117 SOCKET s
; /* socket descriptor */
118 int type
; /* socket type */
120 D_PRINTF( "Beginning passivesock with errno %d\n",errno
);
122 D_PRINTF( "Zeroing address structure\n" );
123 memset((char *)&sin
, 0, sizeof(sin
));
125 sin
.sin_family
= AF_INET
;
126 sin
.sin_addr
.s_addr
= INADDR_ANY
;
128 /* NOT USED: Map service name to portnumber */
129 D_PRINTF( "Mapping portnum errno %d\n",errno
);
130 sin
.sin_port
= htons(portnum
);
132 /* Map protocol name to number */
133 D_PRINTF( "Mapping protocol name errno %d\n",errno
);
134 if ((ppe
= getprotobyname(protocol
)) == 0)
136 errexit("Can't get \"%s\" protocol entry\n", protocol
);
140 /* use protocol to choose socket type */
141 D_PRINTF( "Changing socket type, errno %d\n",errno
);
142 if (strcmp(protocol
, "udp") == 0)
145 D_PRINTF( "Choosing SOCK_DGRAM\n" );
150 D_PRINTF( "Choosing SOCK_STREAM, errno %d\n",errno
);
153 /* allocate a socket */
154 s
= socket(PF_INET
, type
, ppe
->p_proto
);
157 D_PRINTF( "Socket PF_INET %d %d returned %d with %s\n",
158 type
, ppe
->p_proto
, s
, neterrstr() );
159 errexit("Can't create socket: %s\n", neterrstr());
161 D_PRINTF( "Socket %d created with errno %d\n",s
,errno
);
163 /* Bind the socket */
164 if (bind(s
, (struct sockaddr
*)&sin
, sizeof(sin
)) < 0)
166 errexit("Can't bind to port %d: %s\n", portnum
, neterrstr());
168 D_PRINTF( "Bind succeeded\n" );
170 /* If it's a stream, listen for connections */
171 /* NOTE: ON NT, the listen() backlog parm is silently limited to 5 conns */
172 if ((type
== SOCK_STREAM
) && BADSOCKET(listen(s
, qlen
)))
174 errexit("Can't listen on port %s: %s\n", portnum
, neterrstr());
176 D_PRINTF( "Listen succeeded\n" );
178 /* all done, return socket descriptor */
183 /* abort clients -- called by SIGINT handler */
184 static void abort_clients(void)
186 /* Not supposed to have fprintf in a signal handler, but... */
187 fprintf(stdout
, "Webmaster received SIGINT. Terminating.\n");
188 /* exit will close all open connections */
192 /* signal handler for SIGINT */
193 static void sig_int(int sig
) {
200 /* echo stdout/stderr from clients */
201 void echo_client(void *stream
)
205 int which_stream
= (int) stream
;
210 /* This code which handles the timeout may need
211 to be ifdef'ed for WIN32 */
212 struct timeval timeout
;
214 timeout
.tv_sec
= (long)5;
215 timeout
.tv_usec
= (long)0;
225 D_PRINTF( "echo_client running\n" );
226 signal( SIGINT
, SIG_DFL
); /* restore default behavior
231 for (i
= 0; i
< num_rexecs
; i
++)
232 if (sockarr
[i
] != BADSOCKET_VALUE
)
233 FD_SET(sockarr
[i
], &readfds
);
234 rv
= select(num_rexecs
, &readfds
, 0, 0, &timeout
);
237 if (rv
< 0 && WSAGetLastError() == WSANOTINITIALISED
)
240 errexit("Error in echo_client(): select() returns %d: %s\n", rv
, neterrstr());
242 /* loop over the sockets that are ready with data */
243 for (i
= 0; i
< num_rexecs
; i
++) {
244 if (sockarr
[i
] != BADSOCKET_VALUE
&& FD_ISSET(sockarr
[i
], &readfds
)) {
245 len
= NETREAD(sockarr
[i
], buf
, sizeof(buf
));
247 /* mark connection closed */
248 sockarr
[i
] = BADSOCKET_VALUE
;
249 if (len
< 0 && WSAGetLastError() == WSANOTINITIALISED
)
252 fprintf(stderr
, "Error in echo_client() after NETREAD(): %s\n", neterrstr());
256 /* copy to stdout or stderr */
257 fwrite(buf
, sizeof(char), len
, outfile
);
261 D_PRINTF( "Exiting echo_client\n" );
266 echo_client(char *hostname
, const int fd
)
269 * WRITE TEXT FROM FILE DESCRIPTOR INTO STDOUT
273 D_PRINTF( "echo_client running\n" );
275 while (getppid() != 1)
277 cc
= NETREAD(fd
, buf
, sizeof(buf
));
280 write(STDOUT_FILENO
, buf
, cc
);
283 D_PRINTF( "Exiting echo_client\n" );
288 /* Picks the appropriate webmaster IP address based on the address of the client.
289 * This is significant only for hosts with multiple interfaces
291 * return value is a string with the IP address or hostname (or NULL)
293 char *pick_webmaster_IP_address(char *client_hostname
, struct hostent
*master_phe
,
296 unsigned char addr
[4];
300 if (client_hostname
[0] >= '0' && client_hostname
[0] <= '9') {
301 /* we have an IP address */
302 client_addr
= inet_addr(client_hostname
);
303 if (client_addr
== INADDR_NONE
)
306 /* we have a hostname, use the webserver hostname */
307 return master_phe
->h_name
;
310 for (i
= 0; master_phe
->h_addr_list
[i
] != 0; i
++) {
311 if ((*(int *)(master_phe
->h_addr_list
[i
]) & netmask
) ==
312 (client_addr
& netmask
))
318 memcpy((char *)addr
, master_phe
->h_addr_list
[i
], sizeof(addr
)); /* Internet specific */
319 sprintf(buf
, "%d.%d.%d.%d", addr
[0], addr
[1], addr
[2], addr
[3]);
327 void ParseCmdLine(int argc
, char **argv
)
335 * PARSE THE COMMAND LINE OPTIONS
337 while((getoptch
= getopt(argc
,argv
,"P:f:t:l:p:u:R:w:n:M:adrsvWX")) != (const char)EOF
)
342 strcpy(network_mask_str
, optarg
);
346 strcpy(proxyserver
, optarg
);
349 record_all_transactions
= 1;
355 servaddrin_config
= 1;
364 strcpy(configfile
, optarg
);
367 numloops
= atoi(optarg
);
370 portnum
= atoi(optarg
);
379 testtime
= atoi(optarg
);
383 strcpy(uil_filelist
, optarg
);
390 strcpy(webserver
, optarg
);
397 if (numloops
&& testtime
)
398 errexit("Can't have both -l and -t\n");
400 if(!havewebserver
&& !servaddrin_config
)
403 * THE SERVERS NAME MUST BE SPECIFIED
406 fprintf(stderr
,"No WWW Server specified\n");
410 if (havewebserver
&& servaddrin_config
)
413 * CAN'T HAVE BOTH -w and -W
415 fprintf(stderr
, "Can't have both -w and -W options\n");
419 network_mask
= inet_addr(network_mask_str
);
420 if (network_mask
== INADDR_NONE
) {
421 fprintf(stderr
, "Invalid network mask (-M %s)\n", network_mask_str
);
425 if(strlen(configfile
) == 0)
428 * THE MASTER MUST HAVE A CONFIGURATION FILE TO READ.
430 fprintf(stderr
,"No Configuration file specified\n");
433 /* IF WE DO NOT HAVE A FILE LIST THEN THERE ARE UIL'S AT THE END OF THE
434 * COMMAND LINE SO GRAB THEM.
436 if (uil_filelist_f
== 0)
440 while(currarg
!= argc
)
443 * GET THE UILS TO RETRIEVE.
446 sscanf(argv
[currarg
],"%s",filelist
[number_of_pages
]);
453 /* have filelist; take a stab at the number of valid URLs */
454 D_PRINTF( "About to parse filelist %s\n", uil_filelist
);
455 number_of_pages
= count_file_list(uil_filelist
);
457 if (number_of_pages
== 0)
460 * AT LEAST ONE FILE MUST BE SPECIFIED
462 fprintf(stderr
,"No URL resources specified\n");
468 This function sets up the socket we will use to synchronize with the
470 Returns the socket number if successful, doesn't return if it fails
473 SOCKET
SetupSyncSocket( serveraddr
)
474 struct sockaddr_in
*serveraddr
;
479 * SET UP THE SOCKET WE ARE GOING TO USE TO SYNCHRONIZE WITH THE CLIENTS.
481 D_PRINTF( "About to call sock %d %d\n", portnum
, MAXCLIENTS
);
483 sock
= passivesock(0, "tcp", MAXCLIENTS
);
487 errexit("Couldn't open socket %d: %s\n", sock
, neterrstr());
489 D_PRINTF( "The passivesock call succeeded\n" );
491 D_PRINTF( "calling getsockname\n" );
493 len
= sizeof(struct sockaddr
);
494 if(getsockname(sock
, (struct sockaddr
*)serveraddr
, &len
) < 0)
496 errexit("Could not get socket informaton\n");
503 Function which generates a commandline for the webclients
505 void MakeCmdLine( commandline
)
508 char tmpcommandline
[NCCARGS
];
509 char hostname
[MAXHOSTNAMELEN
];
510 char *webclient_path
, *temp
;
512 struct hostent
*master_phe_tmp
; /* temp. variable added by - Rajesh Shah*/
515 * BUILD THE PORTIONS OF THE cmdline FOR EACH CLIENT THAT WE CAN BUILD NOW.
516 * WE WILL FILL IN THE NUMBER OF CLIENTS LATER WITH AN sprintf.
518 D_PRINTF( "Calling gethostname\n" );
520 if(gethostname(hostname
,MAXHOSTNAMELEN
) != 0)
522 errexit("Could not retrieve local host name");
524 /* convert hostname to address (to avoid DNS problems for webclients) */
525 /* The following lines are add to copy the system
526 buffer (output of gethostbyname()) into user area.
527 This is because, there are very good chances that later
528 on system buffer might be overwritten by some calls and
529 still if your pointer is pointing to same addr. nothing
530 but only trouble and trouble! Infact this is what
531 happening when I tried to run webstone benchmark for more
532 then one clients. It used to over write the webmaster name
533 with the first client name and so remaining on client(s)
534 the webclient process(es) were invoked with wrong webmaster
535 name! This behaviour is observed Solaris 2.4 this bug
536 can be hit in any OS. - Rajesh Shah 5/18/96 */
538 /* master_phe = gethostbyname(hostname); */
539 master_phe_tmp
= gethostbyname(hostname
);
540 master_phe
= (struct hostent
*)malloc(sizeof(struct hostent
));
541 HostEntCpy(master_phe
, master_phe_tmp
);
544 /* set up executable pathname */
546 temp
= getenv("TMPDIR");
548 if ( temp
&& *temp
) {
549 webclient_path
= (char *)mymalloc( strlen(temp
) + strlen("/webclient")
551 strcpy(webclient_path
, temp
);
552 strcat(webclient_path
, "/webclient");
558 webclient_path
= PROGPATH
;
561 D_PRINTF( "Path to webclient is: %s\n", webclient_path
);
563 sprintf(commandline
,"%s", webclient_path
);
567 sprintf(tmpcommandline
, " -P %s", proxyserver
);
568 strcat(commandline
, tmpcommandline
);
572 strcat(commandline
," -d");
576 sprintf(tmpcommandline
," -l %d", numloops
);
577 strcat(commandline
,tmpcommandline
);
581 sprintf(tmpcommandline
," -p %d", portnum
);
582 strcat(commandline
,tmpcommandline
);
586 strcat(commandline
," -r");
590 strcat(commandline
," -s");
594 strcat(commandline
," -u ");
595 strcat(commandline
,uil_filelist
);
597 if (record_all_transactions
)
599 strcat(commandline
," -R");
603 sprintf(tmpcommandline
," -t %d", testtime
);
604 strcat(commandline
,tmpcommandline
);
608 * SET UP A SPACE FOR THE NUMBER OF CLIENTS ON THE commandline.
610 sprintf(tmpcommandline
,"%s -n %%d -w %%s -c %%s:%%d", commandline
);
611 strcpy(commandline
,tmpcommandline
);
613 if (uil_filelist_f
== 0)
616 while(cnt
< number_of_pages
)
619 * PUT THE FILES AT THE END OF THE LIST.
621 strcat(commandline
," ");
622 strcat(commandline
,filelist
[cnt
]);
630 rexec to the client hosts and start the webclients
632 int RexecClients( commandline
, clienthostname
, serveraddr
)
634 char clienthostname
[MAXCLIENTS
][MAXHOSTNAMELEN
];
635 struct sockaddr_in
*serveraddr
;
640 char tmpcommandline
[NCCARGS
];
641 struct servent
*inetport
;
643 char buffer
[NCCARGS
];
644 char login
[MAXUSERNAME
];
645 char password
[MAXPASSWD
];
650 memset(buffer
, 0, sizeof(buffer
));
653 * OPEN UP THE CONFIG FILE. FOR EACH LINE IN THE CONFIG FILE, CHECK
654 * ITS VALIDITY AND THEN rexec A COMMAND ON THE CLIENT.
657 if ((fp
= fopen(configfile
,"r")) == 0)
659 errexit("Could not open config file %s\n", configfile
);
662 if ((inetport
= getservbyname("exec","tcp")) == 0)
664 errexit("Could not get service name for exec/tcp\n");
666 D_PRINTF( "getservbyname returned %d\n", ntohs(inetport
->s_port
) );
670 D_PRINTF( "rexec loop\n" );
673 char webserver2
[MAXHOSTNAMELEN
];
678 if (0 == fgets(linebuf
, sizeof(linebuf
), fp
))
680 num
= sscanf(linebuf
,"%s %s %s %d %s",clienthostname
[cnt
],login
,password
,
681 &numclients
, webserver2
);
684 if (servaddrin_config
) {
686 errexit("No webserver specified in config file for %s\n", clienthostname
[cnt
]);
688 strcpy(webserver
, webserver2
);
692 errexit("Number of clients must be >= 0\n");
693 if (numclients
> MAXPROCSPERNODE
)
695 errexit("Number of clients per node can't exceed %d\n", MAXPROCSPERNODE
);
697 totalnumclients
+= numclients
;
699 primename
= pick_webmaster_IP_address(clienthostname
[cnt
], master_phe
, network_mask
);
700 if (primename
== 0) {
701 errexit("Bad client address %s for Client %d\n", clienthostname
[cnt
], cnt
);
704 fprintf(stdout
,"Client %d: %s \t# Processes: %d\n Webserver: %s\tWebmaster: %s:%d\n",
705 cnt
, clienthostname
[cnt
], numclients
, webserver
, primename
,
706 ntohs(serveraddr
->sin_port
));
708 sprintf(tmpcommandline
, commandline
, numclients
, webserver
, primename
,
709 ntohs(serveraddr
->sin_port
));
711 fprintf(stderr
, "tmpcommandline: %s\n", tmpcommandline
);
713 D_PRINTF( "%s rexec %s\n",&clienthostname
[cnt
],tmpcommandline
);
715 sleep(30); /* gives some time to start clients for debugging */
718 tmphostname
= &(clienthostname
[cnt
][0]);
719 tmpfd
= rexec(&tmphostname
, inetport
->s_port
, login
, password
,
720 tmpcommandline
, &sockErr
[cnt
]);
721 if((sockIO
[cnt
] = tmpfd
) < 0)
723 errexit("Could not rexec: rexec to client %s, cmdline %s failed\n",
724 clienthostname
[cnt
],tmpcommandline
);
729 returnval
= NETREAD(tmpfd
, buffer
, OKSTRLEN
);
730 D_PRINTF( "read returns %d, %s\n", returnval
, buffer
);
732 if (returnval
<= 0 || memcmp(buffer
, OKSTR
, OKSTRLEN
) != 0)
734 errexit("rexec to client %s, cmdline %s received error %s\n",
735 clienthostname
[cnt
],tmpcommandline
, buffer
);
740 if (cnt
> MAXCLIENTS
|| cnt
> FD_SETSIZE
)
742 errexit("Number of Clients can't exceed %d\n", MAXCLIENTS
);
747 if (totalnumclients
> MAXTOTALPROCS
)
749 errexit("Total number of processes can't exceed %d\n",
754 /* NOW WE NEED TO HANDLE THE OUTPUT FROM THE REXEC.
755 * TO DO THIS, WE FORK, THEN HAVE ONE PROCESS READ FROM TMPFD.
756 * THE OTHER PROCESS CONTINUES WITH THE PROGRAM
758 D_PRINTF( "Forking webclient stderr/stdout processes\n" );
762 errexit("fork: %s\n", strerror(errno
));
764 exit(echo_client(clienthostname
[cnt
], tmpfd
));
765 default: /* PARENT */
769 /* start threads to echo stdout/stderr from clients */
770 _beginthread(echo_client
, 0, (void *)0);
771 _beginthread(echo_client
, 0, (void *)1);
774 fprintf(stdout
,"\n");
775 fprintf(stdout
,"\n");
778 return totalnumclients
;
781 void GetReady( fdset
, totalnumclients
, sock
)
787 fd_set tmpfdset
, leftfdset
;
788 char buffer
[NCCARGS
];
791 * NOW WE NEED TO ACCEPT ALL THE CONNECTIONS FROM THE CLIENTS,
792 * ACCEPT ALL THE READY MESSAGES
795 D_PRINTF( "Beginning accept loop\n" );
796 for (cnt
= 0; cnt
< totalnumclients
; cnt
++)
798 D_PRINTF( "Client %d:\t", cnt
);
802 struct timeval timeout
;
805 timeout
.tv_sec
= MAX_ACCEPT_SECS
;
808 FD_SET(sock
, &readfds
);
810 /* if we're hung, quit */
811 D_PRINTF("Before select() on listen() socket\n");
812 if (!(rv
= select(FD_SETSIZE
, &readfds
, 0, 0, &timeout
))) {
814 "Listen timeout after %d seconds (%d clients so far)\n",
815 MAX_ACCEPT_SECS
, cnt
);
816 D_PRINTF("select() timed out after %d seconds\n", MAX_ACCEPT_SECS
);
817 errexit("Webmaster terminating\n");
821 if(BADSOCKET(socknum
[cnt
] = accept(sock
, 0, 0)))
824 * ERROR accepting FROM THE CLIENTS. WE NEED TO ISSUE AN
828 errexit("Error accepting from one of the clients: %s", neterrstr());
832 * SET THE FD IN THE MASK
834 FD_SET(socknum
[cnt
],fdset
);
836 D_PRINTF( "on socket %d\n",socknum
[cnt
] );
844 fprintf(stdout
,"Waiting for READY from %d clients\n",totalnumclients
);
848 while(memcmp(&leftfdset
,&zerofdset
,sizeof(fd_set
)))
850 tmpfdset
= leftfdset
;
852 if(select(FD_SETSIZE
,&tmpfdset
,NULL
,NULL
,NULL
) < 0)
855 * ERROR SELECTING. ABORT ALL.
858 errexit("Error accepting from one of the clients: %s\n",
863 /* I don't see why a select is needed at all--all clients must respond
864 * and there is no synchronization/timing issue.
866 tmpfdset
= leftfdset
;
870 for (cnt
= 0; cnt
< totalnumclients
; cnt
++)
873 * SEE WHICH SOCKETS HAVE A INPUT ON THEM PENDING
876 if(!BADSOCKET(socknum
[cnt
]) && (FD_ISSET(socknum
[cnt
],&tmpfdset
)))
879 * GET THE READY FROM THIS GUY.
880 * DON'T FORGET TO CLEAR HIS BIT IN THE tmpfdset
882 len
= NETREAD(socknum
[cnt
],buffer
,READYSTRLEN
);
883 if(len
!= READYSTRLEN
)
886 errexit("Error reading from client #%d\n", cnt
);
888 if(memcmp(buffer
, READYSTR
, READYSTRLEN
))
891 fprintf(stdout
,"Received bad READY string: len %d, value %s\n",
894 FD_CLR(socknum
[cnt
],&leftfdset
);
899 fprintf(stdout
,"All READYs received\n");
904 Start all the clients by sending them a GO signal
905 totalnumclients is the total number of clients
906 socknum is an int array with the filedescriptors for all the
909 void SendGo( totalnumclients
, socknum
)
914 fprintf(stdout
,"Sending GO to all clients\n");
915 for(cnt
= 0; cnt
< totalnumclients
; cnt
++)
922 if(NETWRITE(socknum
[cnt
], GOSTR
, GOSTRLEN
) != GOSTRLEN
)
925 errexit("Error sending GO to client %d: %s\n", cnt
, neterrstr());
932 This function gathers statistics from all the clients
935 void GetResults(fdset
, page_stats
, endtime
, timestr
, totalnumclients
,
938 page_stats_t
**page_stats
;
942 stats_t statarray
[MAXCLIENTS
];
944 fd_set leftfdset
,tmpfdset
;
946 char *page_stats_as_text
;
951 /* DOESN'T ACTUALLY PRINT UNTIL THE FIRST CLIENT REPORTS */
952 fprintf(stdout
,"Reading results ");
955 * COPY THE FILE DESCRIPTORS TO A TMP LIST,
956 * ALLOCATE MEMORY FOR STATS, PAGESTATS IN TEXT FORM
959 stats_as_text
= (char *)mymalloc(SIZEOF_STATSTEXT
+1);
960 page_stats_as_text
= (char *)mymalloc(SIZEOF_PAGESTATSTEXT
+1);
963 * COPY THE FILE DESCRIPTORS TO A TMP LIST,
964 * PLUS A LIST OF REMAINING FDs
968 * LOOP UNTIL ALL CLIENTS HAVE REPORTED
969 * AND tmpfdset IS EMPTY
972 while(memcmp(&leftfdset
,&zerofdset
,sizeof(fd_set
)))
974 tmpfdset
= leftfdset
;
976 returnval
= select(FD_SETSIZE
,&tmpfdset
,NULL
,NULL
,NULL
);
977 D_PRINTF( "Call to select returned %d, errno %d\n",
982 * ERROR SELECTING. ABORT ALL.
984 D_PRINTF( "select() error %s\n", neterrstr() );
986 errexit("Error selecting from one of the clients\n");
989 /* I don't see why a select is needed at all */
990 tmpfdset
= leftfdset
;
993 for(cnt
= 0; cnt
< totalnumclients
; cnt
++)
996 * SEE WHICH SOCKETS HAVE A INPUT ON THEM PENDING AND
1000 /* IS THIS A VALID SOCKET? IS IT READY TO READ? */
1001 if(!BADSOCKET(socknum
[cnt
]) && (FD_ISSET(socknum
[cnt
],&tmpfdset
)))
1006 * GET THE TIMING DATA FROM THIS GUY
1007 * THEN REMOVE HIM FROM THE tmpfdset
1011 * DOES READ() RETURN THE CORRECT LENGTH?
1013 D_PRINTF( "About to read timestats, count %d, errno %d\n",
1015 len
= SIZEOF_STATSTEXTBASE
+ number_of_pages
*SIZEOF_DOUBLETEXT
;
1016 returnval
= recvdata(socknum
[cnt
], stats_as_text
,
1018 D_PRINTF( "Read time stats %d\n", returnval
);
1019 if (returnval
!= len
) /* <= 0) */
1021 D_PRINTF( "Error reading timing stats: %s\n",
1023 fprintf(stderr
, "Error reading timing stats: %s\nSocket number %d\n",
1024 neterrstr(),socknum
[cnt
]);
1029 /* convert text to stats */
1030 stats_as_text
[returnval
] = 0; /* add an end marker */
1031 statarray
[cnt
] = *text_to_stats(stats_as_text
);
1033 fputc('.', stdout
); /* PROGRESS MARKER */
1036 if(uil_filelist_f
) /* READ PAGE STATS */
1038 for (i
= 0; i
< number_of_pages
; i
++)
1040 D_PRINTF( "On page_stats[%d][%d]\n", cnt
, i
);
1041 returnval
= recvdata(socknum
[cnt
], page_stats_as_text
,
1042 SIZEOF_PAGESTATSTEXT
);
1043 D_PRINTF( "Read page stats %d\n", returnval
);
1045 if (returnval
!= SIZEOF_PAGESTATSTEXT
) /* <= 0) */
1047 D_PRINTF( "Error reading page_stats[%d][%d]: %s\n",
1048 cnt
, i
, neterrstr() );
1049 fprintf(stderr
, "Error reading page_stats[%d][%d]: %s\n",
1050 cnt
, i
, neterrstr());
1054 D_PRINTF( "Page stats: read %d bytes\n",
1057 page_stats_as_text
[returnval
] = 0; /* add an end marker */
1058 D_PRINTF("strlen(page_stats_as_text) = %d\n",
1059 strlen(page_stats_as_text
));
1060 page_stats
[cnt
][i
] =
1061 *text_to_page_stats(page_stats_as_text
);
1064 } /* end if filelist */
1066 FD_CLR(socknum
[cnt
],&leftfdset
);
1067 NETCLOSE(socknum
[cnt
]);
1068 socknum
[cnt
] = BADSOCKET_VALUE
;
1069 } /* end if socknum */
1071 } /* end while memcmp fd */
1074 * DONE READING RESULTS FROM CLIENTS
1078 timestr
= asctime(localtime(endtime
));
1079 fprintf(stdout
,"\nAll clients ended at %s\n",timestr
);
1082 /* FREE MEMORY ALLOCATED FOR CLIENT STATS, PAGESTATS AS TEXT */
1083 free(stats_as_text
);
1084 free(page_stats_as_text
);
1089 Prints out all the results
1091 void PrintResults( page_stats
, endtime
, timestr
, totalnumclients
, statarray
,
1093 page_stats_t
**page_stats
;
1096 int totalnumclients
;
1097 stats_t statarray
[MAXCLIENTS
];
1098 page_stats_t
*page_stats_total
;
1103 struct timeval dtime
;
1106 * PRINT EVERYTHING OUT
1108 stats_init(&masterstat
);
1109 for(cnt
= 0; cnt
< totalnumclients
; cnt
++)
1111 if((statarray
[cnt
].rs
.totalconnects
> 0) && (dumpall
))
1113 fprintf(stdout
,"----------------------------------\n");
1114 /* fprintf(stdout,"Test for host: %s\n",statarray[cnt].hostname); */
1115 fprintf(stdout
,"Total number of pages retrieved from server: %u\n",
1116 statarray
[cnt
].totalpages
);
1118 rqstat_fprint(stdout
, &(statarray
[cnt
].rs
));
1120 thruput
= thruputpersec((double)(statarray
[cnt
].rs
.totalbytes
),
1121 &(statarray
[cnt
].rs
.totalresponsetime
));
1123 fprintf(stdout
, "Thruput average per connection: %.0f bytes/sec\n",
1126 if(statarray
[cnt
].rs
.totalconnects
> 0)
1128 D_PRINTF( "Summing stats for %d, with %ld total connections\n",
1129 cnt
, statarray
[cnt
].rs
.totalconnects
);
1130 rqstat_sum(&masterstat
.rs
, &(statarray
[cnt
].rs
));
1134 masterstat
.rs
.totalerrs
+= statarray
[cnt
].rs
.totalerrs
;
1138 for (i
=0; i
< totalnumclients
; i
++)
1140 for (j
=0; j
< number_of_pages
; j
++)
1142 D_PRINTF( "Summing page stats for %d, page %d, with %d connects\n",
1143 i
, j
, statarray
[i
].page_numbers
[j
] );
1145 if (statarray
[i
].page_numbers
[j
] != 0)
1147 rqst_stats_t
*pst_rs
;
1148 rqst_stats_t
*ps_rs
;
1150 pst_rs
= &(page_stats_total
[j
].rs
);
1151 ps_rs
= &(page_stats
[i
][j
].rs
);
1153 rqstat_sum(pst_rs
, ps_rs
);
1155 page_stats_total
[j
].totalpages
+= page_stats
[i
][j
].totalpages
;
1156 masterstat
.totalpages
+= page_stats
[i
][j
].totalpages
;
1158 /* yes, this is assignment, not sum */
1159 page_stats_total
[j
].page_size
= page_stats
[i
][j
].page_size
;
1161 page_stats_total
[j
].page_valid
= 1;
1166 /* print page statistics */
1168 for (i
= 0; i
< number_of_pages
; i
++)
1170 if (page_stats_total
[i
].page_valid
== 1)
1174 pst
= &(page_stats_total
[i
]);
1176 printf ("===============================================================================\n");
1177 printf ("Page # %d\n\n", i
);
1178 printf ("Total number of times page was hit %u\n",
1181 rqstat_print(&(pst
->rs
));
1183 printf ("Page size %u \n", pst
->page_size
);
1184 printf ("===============================================================================\n\n");
1189 fprintf(stdout
,"===============================================================================\n");
1194 masterstat
.total_num_of_files
= statarray
[0].total_num_of_files
;
1195 for (i
=1; i
< totalnumclients
; i
++)
1197 if ((statarray
[i
].rs
.totalconnects
> 0) &&
1198 (statarray
[i
].total_num_of_files
!= masterstat
.total_num_of_files
))
1200 fprintf(stdout
,"**********************************************************************\n");
1201 fprintf(stdout
,"**** ERROR: number of files in each test configuration is not the same\n");
1202 fprintf(stdout
,"**** ERROR: Check configuration file %s on each client\n", configfile
);
1203 fprintf(stdout
,"**********************************************************************\n");
1210 * Print summary statistics
1212 fprintf(stdout
, "WEBSTONE 2.0 results:\n");
1214 fprintf(stdout
, "Total number of clients: \t%d\n", totalnumclients
);
1215 testtime
= sumedh_end
.tv_sec
- sumedh_start
.tv_sec
;
1216 fprintf(stdout
,"Test time: \t\t\t%d seconds\n", testtime
);
1218 fprintf(stdout
, "Server connection rate: \t%3.2f connections/sec\n",
1219 (double)(masterstat
.rs
.totalconnects
)/(testtime
));
1221 fprintf(stdout
, "Server error rate: \t\t%4.4f err/sec\n",
1222 (double)(masterstat
.rs
.totalerrs
)/(testtime
));
1224 fprintf(stdout
, "Server thruput: \t\t%2.2f Mbit/sec\n",
1225 (double)(8*masterstat
.rs
.totalbytes
)/(testtime
*1024*1024));
1227 fprintf(stdout
, "Little's Load Factor: \t\t%3.2f \n",
1228 (double)(masterstat
.rs
.totalresponsetime
.tv_sec
)
1230 avgtime(&masterstat
.rs
.totalresponsetime
,
1231 masterstat
.rs
.totalconnects
, &dtime
);
1233 fprintf(stdout
, "Average response time: \t\t%4.4f millisec\n",
1234 (double)1000*(dtime
.tv_sec
+ (double)dtime
.tv_usec
/ 1000000));
1236 fprintf(stdout
, "Error Level:\t\t\t%4.4f %%\n",
1237 (double)(100 * masterstat
.rs
.totalerrs
)/(masterstat
.rs
.totalconnects
));
1239 /* so much for the key metrics */
1241 thruput
= 8 * thruputpersec((double)(masterstat
.rs
.totalbytes
),
1242 &(masterstat
.rs
.totalresponsetime
));
1244 fprintf(stdout
, "Average client thruput: \t%4.4f Mbit/sec\n",
1245 thruput
/(1024*1024));
1247 fprintf(stdout
,"Sum of client response times:\t%u.%u sec\n",
1248 masterstat
.rs
.totalresponsetime
.tv_sec
,
1249 masterstat
.rs
.totalresponsetime
.tv_usec
);
1251 fprintf(stdout
,"Total number of pages read:\t%u\n\n",
1252 masterstat
.totalpages
);
1254 /* Remaining stats are the same as usual */
1256 rqstat_fprint(stdout
, &(masterstat
.rs
));
1262 /* close socket library */
1263 void sock_cleanup(void) {
1270 main(const int argc
, char *argv
[])
1276 char buffer
[NCCARGS
];
1277 char commandline
[NCCARGS
];
1282 /* make the big arrays static to avoid stack overflow */
1283 static char clienthostname
[MAXCLIENTS
][MAXHOSTNAMELEN
];
1284 static stats_t statarray
[MAXCLIENTS
];
1285 page_stats_t
**page_stats
;
1286 page_stats_t
*page_stats_total
;
1287 struct sockaddr_in serveraddr
;
1294 if ((WSAStartup(MAKEWORD(1,1), &WSAData
)) != 0) {
1295 errexit("Error in WSAStartup()\n");
1297 atexit(sock_cleanup
);
1299 /* increase size of output window */
1302 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE
), dwSize
);
1306 /* Initalization of variables. */
1308 memset(buffer
, 0, NCCARGS
);
1309 memset(webserver
, 0, MAXHOSTNAMELEN
);
1310 memset(configfile
, 0, MAXPATHLEN
);
1311 FD_ZERO(&zerofdset
);
1314 for(i
= 0; i
< MAXCLIENTS
; i
++)
1316 socknum
[i
] = BADSOCKET_VALUE
;
1317 statarray
[i
].rs
.totalconnects
= 0;
1320 signal(SIGINT
, sig_int
);
1322 ParseCmdLine( argc
, argv
);
1324 sync_sock
= SetupSyncSocket( &serveraddr
);
1326 MakeCmdLine( commandline
);
1328 totalnumclients
= RexecClients( commandline
, clienthostname
, &serveraddr
);
1330 /* Initalization of variables. */
1333 mymalloc(totalnumclients
*sizeof(page_stats_t
*));
1334 for (i
=0; i
< totalnumclients
; i
++)
1336 page_stats
[i
] = (page_stats_t
*)
1337 mymalloc(number_of_pages
*sizeof(page_stats_t
));
1340 (page_stats_t
*)mymalloc(number_of_pages
*sizeof(page_stats_t
));
1342 for (i
=0; i
< totalnumclients
; i
++) {
1343 stats_init(&(statarray
[i
]));
1345 for (i
=0; i
< totalnumclients
; i
++) {
1346 for (j
=0; j
< number_of_pages
; j
++) {
1347 page_stats_init(&(page_stats
[i
][j
]));
1350 for (i
=0; i
< number_of_pages
; i
++) {
1351 page_stats_init(&(page_stats_total
[i
]));
1354 for(i
= 0; i
< totalnumclients
; i
++)
1356 socknum
[i
] = BADSOCKET_VALUE
;
1357 statarray
[i
].rs
.totalconnects
= 0;
1360 GetReady( &fdset
, totalnumclients
, sync_sock
);
1361 NETCLOSE(sync_sock
);
1364 * START ALL OF THE CLIENTS BY SENDING THEM A GO SIGNAL.
1368 gettimeofday (&sumedh_start
, 0);
1369 SendGo( totalnumclients
, socknum
);
1372 * WAIT FOR ALL OF THE CLIENTS TO COMPLETE. WE SHOULD GET A REPLY
1373 * FOR EACH SOCKET WE HAVE OPEN. THE REPLY WILL BE THE TIMING
1374 * INFORMATION WE USE.
1377 starttime
= time(0);
1378 timestr
= asctime(localtime(&starttime
));
1379 fprintf(stdout
,"All clients started at %s\n",timestr
);
1380 fprintf(stdout
,"Waiting for clients completion\n");
1383 /* IF THIS IS A TIMED TEST, WE MIGHT AS WELL SNOOZE */
1385 sleep(testtime
* 60);
1388 GetResults( &fdset
, page_stats
, &endtime
, timestr
, totalnumclients
,
1391 gettimeofday (&sumedh_end
, 0);
1392 PrintResults( page_stats
, endtime
, timestr
, totalnumclients
, statarray
,
1396 for (i
= 0; i
< totalnumclients
; i
++)
1398 free(page_stats
[i
]);
1401 free(page_stats_total
);
1406 /* Added by Rajesh Shah 5/18/96 */
1408 HostEntCpy(struct hostent
*dest
, struct hostent
*src
)
1411 dest
->h_name
= (char *)malloc(strlen(src
->h_name
)+1);
1412 strcpy(dest
->h_name
, src
->h_name
);
1413 printf("WebMaster name = %s\n", dest
->h_name
);
1414 dest
->h_aliases
= src
->h_aliases
;
1415 dest
->h_addrtype
= src
->h_addrtype
;
1416 dest
->h_length
= src
->h_length
;
1417 dest
->h_addr_list
= src
->h_addr_list
;