/**************************************************************************** * * * The contents of this file are subject to the WebStone Public License * * Version 1.0 (the "License"); you may not use this file except in * * compliance with the License. You may obtain a copy of the License * * at http://www.mindcraft.com/webstone/license10.html * * * * Software distributed under the License is distributed on an "AS IS" * * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * * the License for the specific language governing rights and limitations * * under the License. * * * * The Original Code is WebStone 2.5. * * * * The Initial Developer of the Original Code is Silicon Graphics, Inc. * * and Mindcraft, Inc.. Portions created by Silicon Graphics. and * * Mindcraft. are Copyright (C) 1995-1998 Silicon Graphics, Inc. and * * Mindcraft, Inc. All Rights Reserved. * * * * Contributor(s): ______________________________________. * * * * @(#) bench.c 2.4@(#) * ***************************************************************************/ #include #include #include #include #include #include #include #include #ifndef WIN32 #include #include #include #include #include #else #include #include #endif /* WIN32 */ #include "sysdep.h" #include "bench.h" /* allocate memory and exit if out of memory */ void * mymalloc(size_t size) { void *ptr; ptr = malloc(size); if (ptr == NULL) errexit("Call to malloc() failed\n"); return ptr; } /* * Receive n bytes from a socket * Returns 0 on success, less than 0 on error. */ int recvdata(SOCKET sock, char *ptr, int nbytes) { int nleft, nread, rv; fd_set sockfds; struct timeval timeout; D_PRINTF("recvdata(): attempting to read %d bytes from socket %d, ptr %x\n", nbytes, sock, (int) ptr); timeout.tv_sec = MAX_ACCEPT_SECS; timeout.tv_usec = 0; FD_ZERO(&sockfds); FD_SET(sock, &sockfds); /* Time out if the webmaster is too late with our GO * by MAX_ACCEPT_SECS */ if ((rv = select(FD_SETSIZE, &sockfds, NULL, NULL, &timeout)) == 0) { fprintf(stdout, "Timeout waiting to write data after %d seconds\n", MAX_ACCEPT_SECS); D_PRINTF("select() timed out after %d seconds\n", MAX_ACCEPT_SECS); errexit("Webclient terminating\n"); } CLEAR_SOCK_ERR; nleft = nbytes; while (nleft > 0) { nread = NETREAD(sock, ptr, nleft); if (SOCK_ERR(nread) || nread == 0) { fprintf(stderr, "Error in recvdata(): %s\n", neterrstr()); fprintf(stderr, "NETREAD() data: \"%.*s\"\n", nread, ptr); D_PRINTF( "Error in recvdata(): %s\n", neterrstr()); return -1; } D_PRINTF( "NETREAD() data: %d bytes,\"%s\"\n", nread, ptr); nleft -= nread; ptr += nread; } /* * if we somehow read too much then this return value will be * less than zero, signifying an error. */ return (nbytes - nleft); } /* * Send n bytes to a socket * Returns 0 on success, less than 0 on error. */ int senddata(SOCKET sock, char *ptr, int nbytes) { int nleft, nwritten; TRACE("senddata(): entering, nbytes=%d.\n", nbytes); CLEAR_SOCK_ERR; nleft = nbytes; while (nleft > 0) { nwritten = NETWRITE(sock, ptr, nleft); if (SOCK_ERR(nwritten)) { TRACE( "senddata(): returning error: %s\n", neterrstr() ); return -1; } nleft -= nwritten; ptr += nwritten; } /* * if we somehow wrote too much then this return value will be * less than zero, signifying an error. */ TRACE("senddata(): returning %d.\n", nbytes - nleft); return (nbytes - nleft); } /* GENERAL NOTE: the conversion routines that follow pass their results * back in a static arrays. A second call to the same routine overwrites * the previous buffer value for that routine. If you want to save the * value in the buffer copy it to another variable. */ char * timeval_to_text(const struct timeval *the_timeval) { /* * given a timeval (seconds and microseconds), put the text * "seconds.microseconds" into timeval_as_text * Note that the %10 printf() specifier is a MINIMUM field width. */ THREAD static char timeval_as_text[SIZEOF_TIMEVALTEXT+1]; int seconds, microseconds; seconds = the_timeval->tv_sec; microseconds = the_timeval->tv_usec; sprintf(timeval_as_text, "%10d.%6.6d\t", seconds, microseconds); D_PRINTF("timeval_to_text(): returning '%s'\n", timeval_as_text); return timeval_as_text; } char * double_to_text(const double the_double) { /* * given a double, return text * Note that the %17 printf() specifier is a MINIMUM field width. */ THREAD static char double_as_text[SIZEOF_DOUBLETEXT + 1]; sprintf(double_as_text, "%17.01f\t", the_double); D_PRINTF("double_to_text(): returning '%s'\n", double_as_text); return(double_as_text); } struct timeval text_to_timeval(char *timeval_as_text) { long int seconds, microseconds; struct timeval the_timeval; D_PRINTF("T/%d %s\n", (int)timeval_as_text, timeval_as_text); sscanf(timeval_as_text, "%ld.%ld", &seconds, µseconds); the_timeval.tv_sec = seconds; the_timeval.tv_usec = microseconds; return the_timeval; } double text_to_double(char *double_as_text) { double the_double = 0; D_PRINTF("D/%d %s\n", (int)double_as_text, double_as_text); sscanf(double_as_text, "%lf", &the_double); return(the_double); } rqst_stats_t * text_to_rqst_stats(char *rqst_stats_as_text) { THREAD static rqst_stats_t rqst_stats; rqst_stats_t *the_rqst_stats = &rqst_stats; the_rqst_stats->totalresponsetime = text_to_timeval(strtok(rqst_stats_as_text, "\t")); the_rqst_stats->totalresponsetimesq = text_to_double(strtok((char *)NULL, "\t")); the_rqst_stats->minresponsetime = text_to_timeval(strtok((char *)NULL, "\t")); the_rqst_stats->maxresponsetime = text_to_timeval(strtok((char *)NULL, "\t")); the_rqst_stats->totalconnecttime = text_to_timeval(strtok((char *)NULL, "\t")); the_rqst_stats->totalconnecttimesq = text_to_double(strtok((char *)NULL, "\t")); the_rqst_stats->minconnecttime = text_to_timeval(strtok((char *)NULL, "\t")); the_rqst_stats->maxconnecttime = text_to_timeval(strtok((char *)NULL, "\t")); the_rqst_stats->totalconnects = (unsigned long) text_to_double(strtok((char *)NULL, "\t")); the_rqst_stats->totalerrs = (unsigned long) text_to_double(strtok((char *)NULL, "\t")); the_rqst_stats->totalerrortime = text_to_timeval(strtok((char *)NULL, "\t")); the_rqst_stats->totalbytes = text_to_double(strtok((char *)NULL, "\t")); the_rqst_stats->totalbytessq = text_to_double(strtok((char *)NULL, "\t")); the_rqst_stats->minbytes = text_to_double(strtok((char *)NULL, "\t")); the_rqst_stats->maxbytes = text_to_double(strtok((char *)NULL, "\t")); the_rqst_stats->totalbody = text_to_double(strtok((char *)NULL, "\t")); the_rqst_stats->totalbodysq = text_to_double(strtok((char *)NULL, "\t")); the_rqst_stats->minbody = text_to_double(strtok((char *)NULL, "\t")); the_rqst_stats->maxbody = text_to_double(strtok((char *)NULL, "\t")); return(the_rqst_stats); } char * rqst_stats_to_text(rqst_stats_t *the_rqst_stats) { THREAD static char rqst_stats_as_text[SIZEOF_RQSTSTATSTEXT]; char *tmpbuf; *rqst_stats_as_text = 0; tmpbuf = timeval_to_text(&(the_rqst_stats->totalresponsetime)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = double_to_text((the_rqst_stats->totalresponsetimesq)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = timeval_to_text(&(the_rqst_stats->minresponsetime)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = timeval_to_text(&(the_rqst_stats->maxresponsetime)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = timeval_to_text(&(the_rqst_stats->totalconnecttime)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = double_to_text((the_rqst_stats->totalconnecttimesq)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = timeval_to_text(&(the_rqst_stats->minconnecttime)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = timeval_to_text(&(the_rqst_stats->maxconnecttime)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = double_to_text((the_rqst_stats->totalconnects)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = double_to_text((the_rqst_stats->totalerrs)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = timeval_to_text(&(the_rqst_stats->totalerrortime)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = double_to_text((the_rqst_stats->totalbytes)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = double_to_text((the_rqst_stats->totalbytessq)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = double_to_text((the_rqst_stats->minbytes)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = double_to_text((the_rqst_stats->maxbytes)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = double_to_text((the_rqst_stats->totalbody)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = double_to_text((the_rqst_stats->totalbodysq)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = double_to_text((the_rqst_stats->minbody)); strcat(rqst_stats_as_text, tmpbuf); tmpbuf = double_to_text((the_rqst_stats->maxbody)); strcat(rqst_stats_as_text, tmpbuf); D_PRINTF( "rqst_stats_to_text returning %d: %s\n", strlen(rqst_stats_as_text), rqst_stats_as_text ); return(rqst_stats_as_text); } stats_t * text_to_stats(char *stats_as_text) { int i; rqst_stats_t *the_rqst_stats; THREAD static stats_t stats; stats_t *the_stats = &stats; D_PRINTF( "Parsing stats: %s\n", stats_as_text ); /* grab stats.rs */ the_rqst_stats = text_to_rqst_stats(stats_as_text); the_stats->rs = *the_rqst_stats; /* grab main structure */ the_stats->starttime = text_to_timeval(strtok((char *)NULL, "\t")); the_stats->endtime = text_to_timeval(strtok((char *)NULL, "\t")); the_stats->datatime = text_to_timeval(strtok((char *)NULL, "\t")); the_stats->totalpages = (unsigned long) text_to_double(strtok((char *)NULL, "\t")); the_stats->total_num_of_files = (unsigned int) text_to_double(strtok((char *)NULL, "\t")); for (i = 0; i < number_of_pages; i++) { the_stats->page_numbers[i] = (unsigned int) text_to_double(strtok((char *)NULL, "\t")); } /* return bytes read */ D_PRINTF( "Returning stats\n"); return(the_stats); } char * stats_to_text(const stats_t *the_stats) { int i; THREAD static char stats_as_text[SIZEOF_STATSTEXT]; char *tmpbuf; rqst_stats_t the_rqst_stats; TRACE("stats_to_text(): entering.\n"); *stats_as_text = 0; /* stats.rs */ the_rqst_stats = the_stats->rs; tmpbuf = rqst_stats_to_text(&the_rqst_stats); strcat(stats_as_text, tmpbuf); /* main structure */ tmpbuf = timeval_to_text(&(the_stats->starttime)); strcat(stats_as_text, tmpbuf); tmpbuf = timeval_to_text(&(the_stats->endtime)); strcat(stats_as_text, tmpbuf); tmpbuf = timeval_to_text(&(the_stats->datatime)); strcat(stats_as_text, tmpbuf); tmpbuf = double_to_text((the_stats->totalpages)); strcat(stats_as_text, tmpbuf); tmpbuf = double_to_text((the_stats->total_num_of_files)); strcat(stats_as_text, tmpbuf); for (i = 0; i < number_of_pages; i++) { tmpbuf = double_to_text((the_stats->page_numbers[i])); strcat(stats_as_text, tmpbuf); } strcat(stats_as_text, "\n"); TRACE("stats_as_text(): leaving.\n"); return stats_as_text; } page_stats_t * text_to_page_stats(char *page_stats_as_text) { rqst_stats_t *the_rqst_stats; THREAD static page_stats_t pagestat; page_stats_t *pagestats = &pagestat; /* grab stats.rs */ the_rqst_stats = text_to_rqst_stats(page_stats_as_text); /* grab main structure */ pagestats->totalpages = (unsigned long) text_to_double(strtok((char *)NULL, "\t")); pagestats->page_size = (unsigned int) text_to_double(strtok((char *)NULL, "\t")); pagestats->page_valid = (int) text_to_double(strtok((char *)NULL, "\t")); pagestats->rs = *the_rqst_stats; return(pagestats); } char * page_stats_to_text(const page_stats_t *pagestats) { THREAD static char page_stats_as_text[SIZEOF_PAGESTATSTEXT]; char *tmpbuf; rqst_stats_t the_rqst_stats; TRACE("page_stats_to_text(): entering.\n"); *page_stats_as_text = 0; /* stats.rs */ the_rqst_stats = pagestats->rs; tmpbuf = rqst_stats_to_text(&the_rqst_stats); strcat(page_stats_as_text, tmpbuf); /* main structure */ tmpbuf = double_to_text(pagestats->totalpages); strcat(page_stats_as_text, tmpbuf); tmpbuf = double_to_text(pagestats->page_size); strcat(page_stats_as_text, tmpbuf); tmpbuf = double_to_text(pagestats->page_valid); strcat(page_stats_as_text, tmpbuf); strcat(page_stats_as_text, "\n"); TRACE("page_stats_to_text(): exiting.\n"); return(page_stats_as_text); } void rqtimer_init(rqst_timer_t *p) { memset(p, 0, sizeof(*p)); } void rqstat_init(rqst_stats_t *p) { memset(p, 0, sizeof(*p)); p->minbytes = DBL_MAX; p->minbody = DBL_MAX; p->minconnecttime.tv_sec = LONG_MAX; p->minconnecttime.tv_usec = LONG_MAX; p->minresponsetime.tv_sec = LONG_MAX; p->minresponsetime.tv_usec = LONG_MAX; } void stats_init(stats_t *p) { memset(p, 0, sizeof(*p)); p->rs.minbytes = DBL_MAX; p->rs.minbody = DBL_MAX; p->rs.minconnecttime.tv_sec = LONG_MAX; p->rs.minconnecttime.tv_usec = LONG_MAX; p->rs.minresponsetime.tv_sec = LONG_MAX; p->rs.minresponsetime.tv_usec = LONG_MAX; } void page_stats_init(page_stats_t *p) { memset(p, 0, sizeof(*p)); p->rs.minbytes = DBL_MAX; p->rs.minbody = DBL_MAX; p->rs.minconnecttime.tv_sec = LONG_MAX; p->rs.minconnecttime.tv_usec = LONG_MAX; p->rs.minresponsetime.tv_sec = LONG_MAX; p->rs.minresponsetime.tv_usec = LONG_MAX; } void rqstat_times(rqst_stats_t *rs, rqst_timer_t *rt) { double t; /* calculate in milliseconds to avoid underflow */ compdifftime(&(rt->exittime), &(rt->entertime), &(rs->totalresponsetime)); t = 1000 * timevaldouble(&(rs->totalresponsetime)); rs->totalresponsetimesq = t * t; doubletimeval(t, &(rs->totalresponsetime)); rs->minresponsetime = rs->totalresponsetime; rs->maxresponsetime = rs->totalresponsetime; compdifftime(&(rt->afterconnect), &(rt->beforeconnect), &(rs->totalconnecttime)); t = 1000 * timevaldouble(&(rs->totalconnecttime)); rs->totalconnecttimesq = t * t; doubletimeval(t, &(rs->totalconnecttime)); rs->minconnecttime = rs->totalconnecttime; rs->maxconnecttime = rs->totalconnecttime; rs->totalbody = rt->bodybytes; rs->totalbodysq = ((double)(rt->bodybytes)) * ((double)(rt->bodybytes)); rs->minbody = rt->bodybytes; rs->maxbody = rt->bodybytes; rs->totalbytes = rt->totalbytes; rs->totalbytessq = ((double)(rt->totalbytes)) * ((double)(rt->totalbytes)); rs->minbytes = rt->totalbytes; rs->maxbytes = rt->totalbytes; rs->totalconnects = 1; rs->totalerrs = 0; rs->totalerrortime.tv_sec = 0; rs->totalerrortime.tv_usec = 0; } void rqstat_sum(rqst_stats_t *sum, rqst_stats_t *incr) { addtime( &(sum->totalresponsetime), &(incr->totalresponsetime)); mintime( &(sum->minresponsetime), &(incr->minresponsetime)); maxtime( &(sum->maxresponsetime), &(incr->maxresponsetime)); sum->totalresponsetimesq += incr->totalresponsetimesq; addtime( &(sum->totalconnecttime), &(incr->totalconnecttime)); mintime( &(sum->minconnecttime), &(incr->minconnecttime)); maxtime( &(sum->maxconnecttime), &(incr->maxconnecttime)); sum->totalconnecttimesq += incr->totalconnecttimesq; sum->totalconnects += incr->totalconnects; sum->totalerrs += incr->totalerrs; addtime( &(sum->totalerrortime), &(incr->totalerrortime)); sum->totalbytes += incr->totalbytes; sum->totalbytessq += incr->totalbytessq; sum->minbytes = min(sum->minbytes, incr->minbytes); sum->maxbytes = max(sum->maxbytes, incr->maxbytes); sum->totalbody += incr->totalbody; sum->totalbodysq += incr->totalbodysq; sum->minbody = min(sum->minbody, incr->minbody); sum->maxbody = max(sum->maxbody, incr->maxbody); } void rqstat_print(rqst_stats_t *stats) { rqstat_fprint(stdout, stats); } void rqstat_fprint(FILE *f, rqst_stats_t *stats) { struct timeval meantime, /*vartime,*/ stdtime; fprintf(f, "%d connection(s) to server, %d errors\n", stats->totalconnects, stats->totalerrs); if (stats->totalconnects == 0) { fprintf(f,"NO CONNECTIONS, THEREFORE NO STATISTICS\n" "IS YOUR WEBSERVER RUNNING?\n" "DO THE PAGES EXIST ON THE SERVER?\n"); return; } /* title */ fprintf(f, "\n Average Std Dev Minimum Maximum\n\n"); /* first line (connect time) */ avgtime(&(stats->totalconnecttime), stats->totalconnects, &meantime); /* variancetime(&(stats->totalconnecttime), stats->totalconnecttimesq, stats->totalconnects, &vartime); */ stddevtime(&(stats->totalconnecttime), stats->totalconnecttimesq, stats->totalconnects, &stdtime); /* convert from milliseconds to seconds - see rqstat_times() */ fprintf(f, "Connect time (sec) %10.6lf %10.6lf %10.6lf %10.6lf\n", timevaldouble(&meantime) / 1000, timevaldouble(&stdtime) / 1000, timevaldouble(&(stats->minconnecttime)) / 1000, timevaldouble(&(stats->maxconnecttime)) / 1000); TRACE("total connect time: %lf sum of squares: %lf count: %d\n", timevaldouble(&(stats->totalconnecttime)), stats->totalconnecttimesq, stats->totalconnects); /* second line (response time) */ avgtime(&(stats->totalresponsetime), stats->totalconnects, &meantime); /* variancetime(&(stats->totalresponsetime), stats->totalresponsetimesq, stats->totalconnects, &vartime); */ stddevtime(&(stats->totalresponsetime), stats->totalresponsetimesq, stats->totalconnects, &stdtime); /* convert from milliseconds to seconds - see rqstat_times() */ fprintf(f, "Response time (sec) %10.6lf %10.6lf %10.6lf %10.6lf\n", timevaldouble(&meantime) / 1000, timevaldouble(&stdtime) / 1000, timevaldouble(&(stats->minresponsetime)) / 1000, timevaldouble(&(stats->maxresponsetime)) / 1000); TRACE("total response time: %lf sum of squares: %lf count: %d\n", timevaldouble(&(stats->totalresponsetime)), stats->totalresponsetimesq, stats->totalconnects); /* 3rd-5th lines (response size, body size, # bytes moved */ fprintf(f, "Response size (bytes) %10.0lf %10.0lf %10.0lf %10.0lf\n", mean(stats->totalbytes, stats->totalconnects), stddev(stats->totalbytes, stats->totalbytessq, stats->totalconnects), stats->minbytes, stats->maxbytes); fprintf(f, "Body size (bytes) %10.0lf %10.0lf %10.0lf %10.0lf\n\n", mean(stats->totalbody, stats->totalconnects), stddev(stats->totalbody, stats->totalbodysq, stats->totalconnects), stats->minbody, stats->maxbody); fprintf(f, "%.0lf body bytes moved + %.0lf header bytes moved = %.0lf total\n", stats->totalbody, stats->totalbytes - stats->totalbody, stats->totalbytes); }