diff options
Diffstat (limited to 'ACE/apps/JAWS/clients/WebSTONE/src/webclient.c')
-rw-r--r-- | ACE/apps/JAWS/clients/WebSTONE/src/webclient.c | 1299 |
1 files changed, 1299 insertions, 0 deletions
diff --git a/ACE/apps/JAWS/clients/WebSTONE/src/webclient.c b/ACE/apps/JAWS/clients/WebSTONE/src/webclient.c new file mode 100644 index 00000000000..cd8f3b48391 --- /dev/null +++ b/ACE/apps/JAWS/clients/WebSTONE/src/webclient.c @@ -0,0 +1,1299 @@ +/* $Id$ */ +/************************************************************************** + * * + * Copyright (C) 1995 Silicon Graphics, Inc. * + * * + * These coded instructions, statements, and computer programs were * + * developed by SGI for public use. If any changes are made to this code* + * please try to get the changes back to the author. Feel free to make * + * modifications and changes to the code and release it. * + * * + **************************************************************************/ + +/* FUZZ: disable check_for_math_include */ +/* FUZZ: disable check_for_improper_main_declaration */ + +#include <thread.h> + +#include <stdio.h> + +#ifdef WIN32 +#include <windows.h> +#include <winsock.h> +#include <time.h> +#include <process.h> +#include <io.h> +#endif /* WIN32 */ + +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> + +#ifndef WIN32 +#include <netdb.h> +#include <unistd.h> +#endif /* WIN32 */ + +#include <time.h> +#include <math.h> + +#ifndef WIN32 +#include <sys/param.h> +#endif /* WIN32 */ + +#include <sys/types.h> + +#ifndef WIN32 +#include <sys/ipc.h> +#include <sys/shm.h> +#include <sys/errno.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif /* WIN32 */ + +#include <ctype.h> + +#include "sysdep.h" +#include "bench.h" + +#define _BSD_SIGNALS +#define INFINITY 100000000 +#define DEFAULTWWWPORT 80 +#define LOG_FILE "logfile" +#ifdef WIN32 +#define DEBUG_FILE "c:/tmp/webstone-debug" +#else +#define DEBUG_FILE "/tmp/webstone-debug" +#endif /* WIN32 */ +#define NCCARGS 4096 + +/* global variables */ + + THREAD FILE *debugfile = stderr; + page_list_t *load_file_list; /* actually a dynamic array */ + +int amclient = 0; +int havewebserver = 0; +int haveproxyserver = 0; +int savefile = 0; +NETPORT portnum = DEFAULTWWWPORT; +int timeexpired = 0; +int debug = 0; +long int number_of_pages = 0; +char webmaster[MAXHOSTNAMELEN]; +char webserver[MAXHOSTNAMELEN]; +char proxyserver[MAXHOSTNAMELEN]; + + +#ifdef WIN32 +HANDLE hSemaphore; +int CounterSemaphore = 0; /* counter semaphore for children */ +#endif /* WIN32 */ + +static void ClientThread(void *); + +/* used to bypass DNS/YP name resolution for every page */ +struct hostent webserv_phe, webmast_phe; +struct protoent webserv_ppe, webmast_ppe; +unsigned long webserv_addr, webmast_addr; +short webserv_type, webmast_type; /* socket type */ + +/* End of globals */ + + +static void +usage(const char *progname) +{ + returnerr("Usage: %s [-d] [-w webserver] [-p port_num]\n", + progname); + returnerr("\t[-c masterhost:port] [-t run_time | -l loops]\n"); + returnerr("\t[-n numclients] [-R]\n"); + returnerr("\t[-f config_file] [-u uilfile | url ...]\n"); + errexit("\n"); +} /* END usage() */ + +static void +alarmhandler(void) +{ + /* RECEIVED AN ALARM SIGNAL */ + timeexpired = 1; +} /* END alarmhandler() */ + +#ifndef WIN32 +static void +childhandler(void) +{ + int status; + + /* RECEIVED A SIGNAL THAT A CHILD PROCESS HAS DIED */ + D_PRINTF( "A child process has died\n" ); + while (wait3(&status, WNOHANG, (struct rusage *)0) >= 0) + { + /* do nothing */ + ; + } +} /* END childhandler() */ +#endif /* WIN32 */ + + +/* look up the host name and protocol + * called once by main() since all threads + * use the same protocol and address + */ + +int resolve_addrs(char *host, char *protocol, struct hostent *host_phe, struct protoent *proto_ppe, unsigned long *addr, + short *type) { +struct hostent *phe; +struct protoent *ppe; + + /* if IP address given, convert to internal form */ + if (host[0] >= '0' && host[0] <= '9') { + *addr = inet_addr(host); + if (*addr == INADDR_NONE) + return(returnerr("Invalid IP address %s\n", host)); + + } else { + /* look up by name */ + phe = gethostbyname(host); + if (phe == 0) + { + D_PRINTF( "Gethostbyname failed: %s", neterrstr() ); + return(returnerr("Can't get %s host entry\n", host)); + } + memcpy(host_phe, phe, sizeof(struct hostent)); + memcpy((char *)addr, phe->h_addr, sizeof(*addr)); + } + + /* Map protocol name to protocol number */ + ppe = getprotobyname(protocol); + + if (ppe == 0) + { + D_PRINTF( "protobyname returned %d\n", ppe ); + return(returnerr("Can't get %s protocol entry\n",protocol)); + } + memcpy(proto_ppe, ppe, sizeof(struct protoent)); + + D_PRINTF( "Protocol number %d\n", ppe->p_proto ); + + /* Use protocol to choose a socket type */ + if (strcmp(protocol,"udp") == 0) + { + *type = SOCK_DGRAM; + } + else + { + *type = SOCK_STREAM; + D_PRINTF( "Choosing SOCK_STREAM %d type %d %s\n", + SOCK_STREAM, *type, neterrstr() ); + } + + return 0; +} + +/* connect to a socket given the hostname and protocol */ +SOCKET +connectsock(char *host, NETPORT portnum, char *protocol) + { + struct sockaddr_in sin; /* an Internet endpoint address */ + SOCKET s; /* socket descriptor */ + int type; /* socket type */ + short proto; + int returnval; /* temporary return value */ + + D_PRINTF( "Beginning connectsock; host=%s port=%d proto=%s\n", host, + portnum, protocol ); + + sin.sin_family = AF_INET; + memset((char *)&sin, 0, sizeof(sin)); + D_PRINTF( "Zeroed address structure\n" ); + + sin.sin_port = htons(portnum); + D_PRINTF( "Set port number %d\n", portnum ); + + /* get the contact information */ + if (strcmp(host, webserver) == 0) { + sin.sin_addr.S_ADDR = webserv_addr; + sin.sin_family = PF_INET; + proto = webserv_ppe.p_proto; + type = webserv_type; + } else if (strcmp(host, webmaster) == 0) { + sin.sin_addr.S_ADDR = webmast_addr; + sin.sin_family = PF_INET; + proto = webmast_ppe.p_proto; + type = webmast_type; + } else { + struct hostent host_phe; + struct protoent host_ppe; + unsigned long host_addr; + short host_type; /* socket type */ + + if (resolve_addrs(host, "tcp", &host_phe, &host_ppe, &host_addr, &host_type)) + return returnerr("Can't resolve hostname %s in get()\n", host); + sin.sin_addr.S_ADDR = host_addr; + sin.sin_family = PF_INET; + proto = host_ppe.p_proto; + type = host_type; + } + + /* Allocate a socket */ + s = socket(PF_INET, type, proto); + D_PRINTF( "Socket %d returned %d, %s\n", + type, s, neterrstr() ); + + if (BADSOCKET(s)) + { + D_PRINTF( "Can't create socket: %s\n",neterrstr() ); + return BADSOCKET_VALUE; + } + + /* Connect the socket */ + D_PRINTF( "Trying to connect %d with size %d, %s\n", + s, sizeof(sin), neterrstr() ); + D_PRINTF( "Address is family %d, port %d, addr %s\n", + sin.sin_family, ntohs(sin.sin_port), + inet_ntoa(sin.sin_addr) ); + + returnval = connect(s, (struct sockaddr *)&sin, sizeof(sin)); + D_PRINTF( "Connect returned %d, %s\n", + returnval, neterrstr() ); + if (returnval < 0) + { + D_PRINTF( "Can't connect: %s\n", neterrstr() ); + NETCLOSE(s); + return BADSOCKET_VALUE; + } + + /* all done, returning socket descriptor */ + D_PRINTF( "Returning %d from connectsock call\n", s ); + return(s); + +} /* END connectsock() */ + +SOCKET +connecttomaster(char *str) +{ + char *tempch; + SOCKET sock; + char msg[100]; + char ConnectStr[100]; /* Fix to handle multiple threads */ + int tries; + + strcpy(ConnectStr, str); + + /* + * BREAK UP THE connectstr INTO A HOSTNAME/HOST-IP AND A PORT NUMBER. + */ + if((tempch = strpbrk(ConnectStr,":")) == 0) + { + /* + * INCORRECT FORMAT OF ConnectStr. CORRECT FORMAT IS + * HOSTNAME:PORT OR HOST-IP:PORT + */ + D_PRINTF( "Incorrect format %s: use hostname:port or ip_addr:port\n", + ConnectStr ); + return(returnerr("Incorrect format %s: use host:port or ip_addr:port\n", + ConnectStr)); + } + + /* + * ZERO OUT THE COLON SO WE HAVE TWO STRINGS, THE HOSTNAME AND THE PORT + */ + *tempch = '\0'; + tempch++; + + /* loop here to connect to webmaster - TCP/IP allows no more than 5 + * connection requests outstanding at once and thus the webmaster may + * reject a connection if there are a lot of client processes + */ +#define MAXTRIES 30 +#define TRYDELAY_SEC 1 + for (tries = 0; tries < MAXTRIES; tries++) { + + sock = connectsock(ConnectStr,(NETPORT)atoi(tempch),"tcp"); + + if (!BADSOCKET(sock)) + break; + + sleep(TRYDELAY_SEC); + } + + if (BADSOCKET(sock)) + { + /* ERROR CONNECTING TO MASTER PROCESS */ + return(returnerr("Could not connect to master process\n")); + } + + /* + * SIGNAL THE MASTER THAT WE ARE READY TO PROCEED. WHEN ALL + * CHILD PROCESSES HAVE CONNECTED AND SENT THIS SIGNAL, + * THE MASTER WILL ISSUE US A GO SIGNAL. + */ + if(NETWRITE(sock,READYSTR,READYSTRLEN) != READYSTRLEN) + { + return(returnerr("Error sending READY message to master")); + } + + memset(msg,0,GOSTRLEN+1); + if(NETREAD(sock,msg,GOSTRLEN) != GOSTRLEN) + { + D_PRINTF( "Error receiving GO message from master: %s\n", neterrstr() + ); + return(returnerr("Error receiving GO message from master\n")); + } + + if(strncmp(GOSTR,msg,GOSTRLEN)) + { + /* + * WE RECEIVED A MESSAGE OTHER THAN GO. PRINT IT OUT AND RETURN ERROR + */ + return(returnerr("Received non-GO message %s\n",msg)); + } + + return(sock); + +} /* END connecttomaster() */ + + +static void +accumstats(rqst_timer_t *rqsttimer, page_stats_t *pagestats, stats_t *timestat) +{ + rqst_stats_t rqststats; + +#define TFMT "%10u:%10u" + /* + * DUMP THE TIMING INFORMATION HERE, OR COMPUTE WHAT YOU WANT TO + * PRINT OUT LATER. + */ + + D_PRINTF( "Total bytes read: %d \t Body size read: %d\n", + rqsttimer->totalbytes, + rqsttimer->bodybytes ); + + D_PRINTF( "Enter time: " TFMT " \t Exit Time: " TFMT "\n", + rqsttimer->entertime.tv_sec, + rqsttimer->entertime.tv_usec, + rqsttimer->exittime.tv_sec, + rqsttimer->exittime.tv_usec ); + D_PRINTF( "Before connect: " TFMT " \t After connect: " TFMT "\n", + rqsttimer->beforeconnect.tv_sec, + rqsttimer->beforeconnect.tv_usec, + rqsttimer->afterconnect.tv_sec, + rqsttimer->afterconnect.tv_usec ); + D_PRINTF( "Before header: " TFMT " \t After header: " TFMT "\n", + rqsttimer->beforeheader.tv_sec, + rqsttimer->beforeheader.tv_usec, + rqsttimer->afterheader.tv_sec, + rqsttimer->afterheader.tv_usec ); + D_PRINTF( "After body: " TFMT "\n", + rqsttimer->afterbody.tv_sec, + rqsttimer->afterbody.tv_usec ); + + rqstat_times(&(rqststats), rqsttimer); + rqstat_sum(&(timestat->rs), &(rqststats)); + rqstat_sum(&(pagestats->rs), &(rqststats)); + + if (rqsttimer->page_number != 999) + { + timestat->page_numbers[rqsttimer->page_number] += 1; + } + +#undef TFMT +} /* END accumstats */ + + +/* + * fetch the set of files that constitute a page + * + * maxcount = the number of files in the WWW page + * pageval = the number of the WWW page (offset in load_file_list[]) + * (if -1, use page # 0 - does this still work?) + * + * returns the number of files retrieved + */ +static int +makeload(int maxcount, int pageval, THREAD rqst_timer_t *timerarray, THREAD stats_t *timestat, THREAD SOCKET mastersock, THREAD page_stats_t *page_stats) +{ + int cnt; + int returnval; + page_stats_t page_stats_tmp; + char server[MAXHOSTNAMELEN]; + + NETPORT loc_portnum; + + D_PRINTF( "Starting makeload(maxcount %d, pageval %d)\n", + maxcount, pageval ); + + strcpy( server, webserver); /* Put in default value */ + + page_stats_init(&page_stats_tmp); + D_PRINTF( "Page stats initialized\n" ); + + for (cnt = 0; cnt < maxcount; cnt++) + { + D_PRINTF( "Loop count %d in makeload()\n", cnt ); + if (pageval == -1) + { + pageval = cnt; + } + if (timeexpired) + { + break; + } + + /* check for a filename */ + if (strlen(load_file_list[pageval].filename[cnt]) < 1) + { + D_PRINTF( "Bad filename at pageval %d, count %d\n", + pageval, cnt ); + return(returnerr("Bad filename at pageval %d, count %d\n", + pageval, cnt)); + } + + /* if (load_file_list[pageval].port_number[cnt] != 0) + { + loc_portnum = load_file_list[pageval].port_number[cnt]; + } + else + { + loc_portnum = portnum; + } */ + loc_portnum = portnum; + if ((load_file_list[pageval].servername[cnt] != 0) + && + *load_file_list[pageval].servername[cnt]) + { + D_PRINTF( "Copying URL server %s to server\n", + load_file_list[pageval].servername[cnt] ); + strcpy(server, load_file_list[pageval].servername[cnt]); + } + + if (haveproxyserver) + { + D_PRINTF( "Copying proxy %s to webserver\n", proxyserver ); + strcpy(server, proxyserver); + } + + + D_PRINTF( "Calling get(%s, %d, %s, &(timearray[%d]))\n", + server, loc_portnum, load_file_list[pageval].filename[cnt], + cnt ); + + returnval = get(server, loc_portnum, + load_file_list[pageval].filename[cnt], + &(timerarray[cnt])); + if (returnval < 0) + { + D_PRINTF( "***GET() RETURNED AN ERROR\n" ); + } + + /* + * DID GET() RETURN A VALID TIME? + */ + if ((returnval == 0) && (timerarray[cnt].valid == 2)) + { + timerarray[cnt].page_number = pageval; + + accumstats(&timerarray[cnt], &page_stats_tmp, timestat); + } + else if (!timeexpired) /* INVALID, INCREMENT THE ERROR COUNTER */ + { + D_PRINTF( "GET error counter incremented\n" ); + timestat->rs.totalerrs++; + } + + if (amclient) { + fd_set readfds; + struct timeval timeout; + int rv; + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + FD_ZERO(&readfds); + FD_SET(mastersock, &readfds); + + /* if the webmaster has aborted, quit */ + D_PRINTF("Before select() on webmaster socket\n"); + if (rv = select(FD_SETSIZE, &readfds, 0, 0, &timeout)) { + D_PRINTF("select() returned %d\n", rv); + D_PRINTF("Client terminating at request of webmaster\n"); + exit(2); + } + } + + } /* END for cnt */ + + /* + * DO WE HAVE A VALID RETURN FROM GET()? + * WHY NOT USE returnval HERE? + */ + if ((returnval == 0) && + (cnt == load_file_list[pageval].num_of_files) && + (timerarray[cnt-1].valid == 2)) + { + rqst_stats_t *ps_rs; + rqst_stats_t *pst_rs; + + ps_rs = &(page_stats[pageval].rs); + pst_rs = &(page_stats_tmp.rs); + + rqstat_sum(ps_rs, pst_rs); + + page_stats[pageval].totalpages++; + + if (page_stats[pageval].page_size == 0) + { + page_stats[pageval].page_size = (unsigned) + page_stats_tmp.rs.totalbody; + } + } + + D_PRINTF( "\nMakeload output page %d: %d errors, %d pages\n", + pageval, timestat->rs.totalerrs, page_stats[pageval].totalpages ); + D_PRINTF( "Makeload returning %d\n", cnt ); + + return(cnt); + +} /* END makeload() */ + +#ifdef WIN32 +/* close socket library at exit() time */ +void sock_cleanup(void) { + + WSACleanup(); +} +#endif /* WIN32 */ + +/* globalize variables that were in main() */ +long int numfiles = 0; +int testtime = 0; +int numloops = 0; +int numclients = 0; +int record_all_transactions = 0; +int uil_filelist_f = 0; /* filedescriptor of URLs to fetch? */ +int verbose = 0; +int total_weight; +char uil_filelist[NCCARGS]; +char filelist[MAXNUMOFFILES][MAXPATHLEN]; +char configfile[MAXPATHLEN]; +char connectstr[MAXHOSTNAMELEN+10]; + +void +ACE_TMAIN(int argc, ACE_TCHAR *argv[]) +{ + int file_count=0; + int getoptch; + int currarg; + extern char *optarg; + extern int optind; + int i, j; + char *tempch; + int err; + +#define SLEEP_USEC 100 +#ifdef WIN32 + WSADATA WSAData; +#else + + struct timeval sleeptime; + + /* set the amount of time that we'll pause before sending a "." to the + webmaster */ + + sleeptime.tv_sec = SLEEP_USEC/1000000; + sleeptime.tv_usec = SLEEP_USEC % 1000000; +#endif /* WIN32 */ + + debugfile = stderr; + +#ifdef WIN32 + MessageBeep(~0U); /* announce our existence */ + MessageBeep(~0U); + MessageBeep(~0U); + + err = WSAStartup(MAKEWORD(1,1), &WSAData); + if (err != 0) { + errexit("Error in WSAStartup()\n"); + } + + atexit(sock_cleanup); + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); + + /* create semaphore in locked state */ + hSemaphore = CreateSemaphore(0, 0, 1, 0); + if(hSemaphore == 0) + { + errexit("Create semaphore failed: %d", GetLastError()); + } +#endif /* WIN32 */ + + memset(webserver, 0, sizeof(webserver)); + memset(webmaster, 0, sizeof(webmaster)); + memset(proxyserver, 0, sizeof(proxyserver)); + memset(connectstr, 0, sizeof(connectstr)); + + /* + * PARSE THE COMMAND LINE OPTIONS + */ + + while((getoptch = getopt(argc,argv,"P:f:t:l:p:u:R:w:c:n:sdv")) != EOF) + { + switch(getoptch) + { + case 'c': + sprintf(connectstr, "%s", optarg); + amclient = 1; + printf("%s", OKSTR); /* sent back to webmaster */ + fflush(stdout); + break; + case 'd': + debug = 0; /* sumedh */ + break; + case 'f': + sprintf(configfile, "%s", optarg); + break; + case 'l': + numloops = atoi(optarg); + break; + case 'n': + numclients = atoi(optarg); + break; + case 'u': + sprintf(uil_filelist, "%s", optarg); + uil_filelist_f = 1; + break; + case 'p': + portnum = atoi(optarg); + break; + case 's': + savefile = 1; + break; + case 't': + testtime = 60 * atoi(optarg); + break; + case 'v': + verbose = 1; + break; + case 'w': + havewebserver = 1; + sprintf(webserver,"%s",optarg); + break; + case 'P': + haveproxyserver = 1; + sprintf(proxyserver, "%s", optarg); + break; + case 'R': + record_all_transactions = 1; + break; + default: + usage(argv[0]); + } + } + + returnerr("Client begins...\n"); + D_PRINTF( "Running in debug mode\n\n" ); + + /* print the command line */ + for (i = 0; i < argc; i++) + D_PRINTF( "%s ", argv[i] ); + D_PRINTF( "\n\n" ); + + if(testtime && numloops) + { + /* + * EITHER numloops OR testtime, BUT NOT BOTH. + */ + usage(argv[0]); + } + + if(havewebserver != 1) + { +#ifdef WIN32 + /* + * THE SERVER'S NAME MUST BE SPECIFIED + */ + returnerr("No WWW Server specified\n"); + usage(argv[0]); +#else + /* IF IT ISN'T, WE ASSUME LOCALHOST */ + sprintf(webserver, "%s", "localhost"); + havewebserver = 1; +#endif /* WIN32 */ + } + + currarg = optind; + numfiles = 0; + while(currarg != argc) + { + /* + * GET THE URLS TO RETRIEVE. + */ + if (numfiles == MAXNUMOFFILES) { + returnerr("Maximum of %d files on the command line.\n"); + usage(argv[0]); + } + sscanf(argv[currarg],"%s",filelist[numfiles]); + numfiles++; + currarg++; + } + + if ((numfiles != 0) && uil_filelist_f) + { + returnerr("Both a filelist and UIL specified.\n"); + usage(argv[0]); + } + + if((numfiles == 0) && !(uil_filelist_f)) + { + /* + * AT LEAST ONE FILE MUST BE SPECIFIED + */ + returnerr("No UIL resources or filelist specified \n"); + usage(argv[0]); + } + + if((numloops == 0) && (testtime == 0)) + { + /* + * NO SPECIFIED NUMBER OF LOOPS, AND NO TEST TIME + */ + usage(argv[0]); + } + if(numclients > MAXPROCSPERNODE || numclients < 1) + { + returnerr("Number of Clients must be between 1 and %d\n", MAXPROCSPERNODE); + exit(1); + } + + /* allow use of IP address */ + if(amclient) { + if((tempch = strpbrk(connectstr,":")) == 0) + { + /* + * INCORRECT FORMAT OF ConnectStr. CORRECT FORMAT IS + * HOSTNAME:PORT OR HOST-IP:PORT + */ + D_PRINTF( "Incorrect format %s: use hostname:port or ip_addr:port\n", + connectstr ); + returnerr("Incorrect format %s: use host:port or ip_addr:port\n", connectstr); + exit(1); + } else { + strncpy(webmaster, connectstr, tempch-connectstr); + } + if(resolve_addrs(webmaster, "tcp", &webmast_phe, &webmast_ppe, &webmast_addr, &webmast_type)) + exit(1); + } + + if (haveproxyserver) + { + D_PRINTF( "Copying proxy %s to webserver\n", proxyserver ); + strcpy(webserver, proxyserver); + } + + if (resolve_addrs(webserver, "tcp", &webserv_phe, &webserv_ppe, &webserv_addr, &webserv_type)) + exit(1); + + /* + * INITIALIZE DATA + */ + /* allocate space for dynamic arrays */ + load_file_list = + (page_list_t *)mymalloc((MAXNUMOFPAGES)*sizeof(page_list_t)); + + if (uil_filelist_f) + { + /* take a guess at the number of URLs in the file */ + D_PRINTF( "About to parse filelist %s\n", uil_filelist ); + number_of_pages = count_file_list(uil_filelist); + numfiles = 1; + + /* IF WE HAVE A FILELIST, PARSE IT */ + /* allocate memory */ + D_PRINTF( "Allocating page list: %ld by %d\n", + number_of_pages, numfiles ); + for (i=0; i<number_of_pages; i++) + { + for (j=0; j<MAXNUMOFFILES; j++) + { + load_file_list[i].servername[j] = + (char *)mymalloc(URL_SIZE); + load_file_list[i].filename[j] = + (char *)mymalloc(URL_SIZE); + } + } + + D_PRINTF( "Parsing file list: %s\n", uil_filelist ); + parse_file_list(uil_filelist, load_file_list, + &number_of_pages, &numfiles); + /* free memory for pages that won't be used? */ + D_PRINTF( "Actual page list: %ld by %d\n", + number_of_pages, MAXNUMOFFILES ); + + D_PRINTF( "Setting up weighting for %ld pages\n", + number_of_pages ); + total_weight = load_percent(load_file_list, number_of_pages); + /* total_weight = load_percent(load_file_list, number_of_pages, pages); */ + } + else + { + /* no uil file */ + number_of_pages = numfiles; + } + + if (number_of_pages < 1) + { + /* no pages - exit */ + D_PRINTF( "No valid URLs found\n" ); + errexit("No valid URLs found\n"); + } + +#ifndef WIN32 + /* + * IF WE ARE TO FORK ADDITIONAL CLIENTS ON THIS MACHINE, + * WE MUST DO IT BEFORE WE CONNECT TO THE MASTER. + * + * FIRST, SET UP SIGNAL HANDLING + */ + signal(SIGCHLD, childhandler); + for(i = 0; i < numclients; i++) + { + thr_create (0, 0, ClientThread, 0, THR_BOUND, 0); + + /* switch(fork()) + { + case 0: + numclients = 1; + ClientThread(0); + exit(0); + break; + case -1: + errexit("Error forking child processes\n"); + exit(1); + default: + break; + } */ + select(0,(fd_set *)0,(fd_set *)0, (fd_set *)0, &sleeptime); + } + + /* + * Wait for all children to exit. + */ + + while (thr_join(0, 0, 0) == 0); + + /* for(;;) + { + int pid = wait((int*)0); + if ((pid == -1) && errno == ECHILD) break; + } */ +#else + /* start threads on NT */ + for (i = 0; i < numclients; i++) + { + if (_beginthread(ClientThread, 0, 0) == -1) + { + errexit("_beginthread failed: %d", GetLastError()); + } + } +#endif /* WIN32 */ + +#ifdef WIN32 + /* wait for children to get to sync point */ + while (CounterSemaphore < numclients) + sleep(1); + CounterSemaphore = 0; + + /* start all children simultaneously */ + ReleaseSemaphore(hSemaphore, 1, 0); + + if (testtime) { + sleep(testtime); + alarmhandler(); /* signal end of test to threads */ + } + + /* + * Wait for all threads to exit. + */ + while (CounterSemaphore < numclients) + sleep(1); + + CloseHandle(hSemaphore); +#endif /* WIN32 */ + + return; +} /* end main() */ + +void ClientThread(void *dummy) +{ + + THREAD FILE *logfile; + + THREAD stats_t timestat; + + THREAD rqst_timer_t timerarray[MAXNUMOFFILES]; + THREAD SOCKET mastersock = BADSOCKET_VALUE; /* connection to webmaster */ + + + THREAD page_stats_t *page_stats; /* actually a dynamic array */ + + int loopcnt = 0; + int filecnt; + int loop; + int ran_number; + int page_index; + int page_number; + int file_count = 0; + char file_name[50]; + struct timeval runningtime; + time_t junk; + int i; + int returnval; + + /* + * INITIALIZE DATA + */ + + page_stats = + (page_stats_t *)mymalloc((number_of_pages)*sizeof(page_stats_t)); + + for (i=0; i < number_of_pages; i++) { + page_stats_init(&(page_stats[i])); + } + + if (debug) + { + /* + * OPEN A DEBUG FILE + */ + fflush(stderr); + sprintf(file_name, "%s.%d", DEBUG_FILE, (int)getpid()); + debugfile = fopen(file_name, "w+"); + if (debugfile == 0) + errexit("Can't open debug file\n"); + D_PRINTF( "Running in debug mode, %d\n",amclient ); + } + + if (record_all_transactions) + { + /* + * OPEN A LOG FILE. + */ + sprintf(file_name, "%s%d", LOG_FILE, (int)getpid()); + returnerr("Log file is %s\n", file_name); + logfile = fopen(file_name, "w+"); + } + + /* Initialize random number generator */ + junk = getpid (); + rand_r(&junk); + D_PRINTF( "Random seed: %d\n", junk ); + + for (i=0; i < MAXNUMOFFILES; i++) + { + rqtimer_init(&(timerarray[i])); + } + stats_init(×tat); + + D_PRINTF( "Number of files %d\n", numfiles ); + + timestat.total_num_of_files = numfiles; + + if (amclient) + { + /* + * WE ARE A CLIENT PROCESS. (i.e. WE ARE NOT RUN BY A USER, BUT BY + * THE MASTER WWWSTONE PROCESS. WE NEED TO CONNECT TO THE + * MASTER WHO WILL SYNCHRONIZE ALL THE CLIENTS. + */ + D_PRINTF( "Trying to connect with %s\n",connectstr ); + + mastersock = connecttomaster(connectstr); + + D_PRINTF( "connecttomaster returns %d, %s\n", + mastersock, neterrstr() ); + + if(BADSOCKET(mastersock)) + { + /* + * ERROR CONNECTING TO THE MASTER. ABORT. + */ + errexit("Error connecting to the master: %s\n", neterrstr()); + } + } /* END IF CLIENT */ + +#ifdef WIN32 + /* Tell parent we're ready */ + InterlockedIncrement(&CounterSemaphore); + + /* Wait for main() thread to release us */ + WaitForSingleObject(hSemaphore, INFINITE); + ReleaseSemaphore(hSemaphore, 1, 0); +#endif /* WIN32 */ + if (testtime != 0) + { + /* + * IF RUNNING A TIMED TEST, WE WILL LOOP + * UNTIL THE ALARM GOES OFF. + * WE'LL ALSO NEED TO SET THE SIGNAL HANDLER + */ +#ifndef WIN32 + /*signal(SIGALRM, alarmhandler);*/ + /* + * SEND SIGALRM IN testtime SECONDS + */ + /*alarm(testtime);*/ +#endif /* WIN32 */ + } + + /* + * AND THEY'RE OFF... + */ + + if (testtime) + numloops = INFINITY; + GETTIMEOFDAY(&(timestat.starttime), &(timestat.starttimezone)); + + /* LOOP UNTIL WE HIT numloops, OR WE RUN OUT OF TIME */ + for(loopcnt = 0; (loopcnt < numloops) && !timeexpired; loopcnt++) + { + /* + * THIS IS WHERE LOAD TESTING IS DONE. + * GET A RANDOM NUMBER, THEN INDEX INTO THE + * PAGE, AND THEN REQUEST THAT SET OF FILES. + */ + if (uil_filelist_f) /* HAVE FILELIST */ + { + D_PRINTF( "Have filelist\n" ); + /* if (testtime != 0) /* RUNNING IN TIMED MODE */ + if (1) + { + D_PRINTF( "Running in timed mode\n" ); + /* random number between 0 and totalweight-1 */ + junk = getpid (); + ran_number = (rand_r(&junk) % total_weight); + D_PRINTF( "random %ld\n", ran_number ); + + /* loop through pages, find correct one + * while ran_number is positive, decrement it + * by the load_num of the current page + * example: ran_number is 5, pages have weights of 10 and 10 + * first iteration page_index = 0, ran_number = -5 + * iteration halted, page_index = 0 + */ + page_index = -1; + while (ran_number >= 0) + { + page_index++; + D_PRINTF( "Current page index %d: %ld - %d\n", + page_index, ran_number, + load_file_list[page_index].load_num + ); + ran_number -= load_file_list[page_index].load_num; + } + + if (page_index >= number_of_pages) { page_index--; } + + D_PRINTF( "Final page index %d\n", page_index ); + filecnt = makeload(load_file_list[page_index].num_of_files, + page_index, timerarray, ×tat, mastersock, page_stats); + testtime = 1; + } + else /* NOT RUNNING IN TIMED MODE */ + { + for (page_number = 0; + page_number < number_of_pages; + page_number++) + { + filecnt = makeload(load_file_list[page_number].num_of_files, + page_number, timerarray, ×tat, mastersock, page_stats); + + } /* END for page_number */ + } /* END if/else TIMED MODE */ + } + else /* NO FILELIST */ + { + D_PRINTF( "No filelist\n" ); + /* + * LOOP THROUGH UNTIL numfiles TIMES OR UNTIL TIMER EXPIRES + * AND ALARM SETS filecnt TO INFINITY. + */ + + /* does this still work?? */ + /* filecnt = makeload(numfiles, -1, timerarray); */ + } /* END if HAVE FILELIST */ + + if (filecnt > 0) + file_count += filecnt; + + } /* END while loopcnt */ + + GETTIMEOFDAY(&(timestat.endtime), &(timestat.endtimezone)); + D_PRINTF( "Test run complete\n" ); + signal(SIGALRM, 0); + + if (testtime == 0) + { + numfiles = loopcnt; + + if (uil_filelist_f) + { + numfiles = file_count; + } + } + + /* This option ( "-R" ) looks broken (e.g. l > 50) -- JEF 2/15/96 */ + if (record_all_transactions) + { + /* + * DUMP THE LOG FILE INFORMATION. + */ + for (loop=0; loop < (loopcnt * file_count); loop++) + { + fprintf(logfile, " entertime \t%d.%d\n" + " beforeconnect \t%d.%d\n" + " afterconnect \t%d.%d\n" + " beforeheader \t%d.%d\n" + " afterheader \t%d.%d\n" + " afterbody \t%d.%d\n" + " exittime \t%d.%d\n" + " total bytes \t%d\n" + " body bytes\t%d\n", + timerarray[loop].entertime.tv_sec, + timerarray[loop].entertime.tv_usec, + timerarray[loop].beforeconnect.tv_sec, + timerarray[loop].beforeconnect.tv_usec, + timerarray[loop].afterconnect.tv_sec, + timerarray[loop].afterconnect.tv_usec, + timerarray[loop].beforeheader.tv_sec, + timerarray[loop].beforeheader.tv_usec, + timerarray[loop].afterheader.tv_sec, + timerarray[loop].afterheader.tv_usec, + timerarray[loop].afterbody.tv_sec, + timerarray[loop].afterbody.tv_usec, + timerarray[loop].exittime.tv_sec, + timerarray[loop].exittime.tv_usec, + timerarray[loop].totalbytes, + timerarray[loop].bodybytes); + } /* end for loop */ + } /* end if recording all transactions */ + + D_PRINTF( "total errors: %d\n",timestat.rs.totalerrs ); + /* gethostname(timestat.hostname,MAXHOSTNAMELEN); */ + /* D_PRINTF( "Test for host: %s\n",timestat.hostname ); */ + D_PRINTF( "Server is: %s running at port number: %d\n", + webserver,portnum ); + + /* sprintf(timestat.hostname,"%s:%d",timestat.hostname,getpid()); */ + if (amclient) /* CLIENT TO A WEBMASTER */ + { + char *stats_as_text; + + /* + * SEND THE TIMING DATA TO THE MASTER + */ + stats_as_text = stats_to_text(×tat); + D_PRINTF( "stats_to_text returned %s\n", stats_as_text ); + + returnval = senddata(mastersock, stats_as_text, + SIZEOF_STATSTEXTBASE + number_of_pages*SIZEOF_DOUBLETEXT); + D_PRINTF( "Wrote time stats to master %d\n", returnval ); + + if (returnval < 1) + { + D_PRINTF( "Error while writing time stats: %s\n", + neterrstr() ); + errexit("Error while writing time stats: %s\n", + neterrstr()); + } + + if (uil_filelist_f) + /* write pagestats */ + { + char *page_stats_as_text; + for (i = 0; i < number_of_pages; i++) + { + D_PRINTF( "On page_stats[%d]\n", i ); + page_stats_as_text = page_stats_to_text(&page_stats[i]); + returnval = strlen(page_stats_as_text); + D_PRINTF( "page_stats_to_text[%d] returned %d\n", + i, returnval ); + returnval = senddata(mastersock, page_stats_as_text, + SIZEOF_PAGESTATSTEXT); + if (returnval < 1) + { + D_PRINTF( "Error while writing page_stats[%d]: %s\n", + i, neterrstr() ); + errexit("Error while writing page_stats[%d]: %s\n", + i, neterrstr()); + } /* end if */ + D_PRINTF( "Wrote %d bytes of page_stats[%d] to master\n", + returnval, i ); + } /* end for */ + } /* end if filelist */ + + D_PRINTF( "About to close socket\n" ); + if (NETCLOSE(mastersock)) + D_PRINTF( "Close socket error: %s\n", neterrstr() ); + } + else /* NOT A CLIENT TO A WEBMASTER */ + { + if (testtime) + { + printf("Test ran for: %d minutes\n",(testtime/60)); + } + else + { + printf("Test ran for: %d iterations.\n",numloops); + } + compdifftime(&(timestat.endtime), &(timestat.starttime), + &(runningtime)); + printf("Total time of test (sec) %d.%d\n", runningtime.tv_sec, + runningtime.tv_usec); + printf("Files retrieved per iteration: %d\n",numfiles); /* 'per iteration' */ + printf("----------------------------------\n"); + printf("Totals:\n\n"); + rqstat_print(&(timestat.rs)); + + if (timestat.rs.totalconnects == 0) + goto end; + printf("Thruput = %5.2lf Kbytes/sec\n", + thruputpersec(timestat.rs.totalbytes, &runningtime) / 1000); + + if (uil_filelist_f && numloops && verbose) + { + for (loop = 0; loop < number_of_pages; loop++) + { + if (timestat.page_numbers[loop] != 0) + { + printf ("===============================================================================\n"); + printf ("Page # %d\n\n", loop); + printf ("Total number of times page was hit %d\n", + page_stats[loop].totalpages); + rqstat_print(&(page_stats[loop].rs)); + printf ("Page size %d \n", page_stats[loop].page_size); + printf ("===============================================================================\n\n"); + } /* END if timestat */ + } /* END for loop */ + } /* END if filelist */ + } /* END if client */ + +end: + if(record_all_transactions) + fclose(logfile); + if(debug) + { + D_PRINTF( "Client exiting.\n" ); + fclose(debugfile); + } + +#ifdef WIN32 + /* tell parent we're done */ + InterlockedIncrement(&CounterSemaphore); +#endif /* WIN32 */ + +} /* END ClientThread() */ |