1 /****************************************************************************
2  *                                                                          *
3  * The contents of this file are subject to the WebStone Public License     *
4  * Version 1.0 (the "License"); you may not use this file except in         *
5  * compliance with the License. You may obtain a copy of the License        *
6  * at http://www.mindcraft.com/webstone/license10.html                      *
7  *                                                                          *
8  * Software distributed under the License is distributed on an "AS IS"      *
9  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See      *
10  * the License for the specific language governing rights and limitations   *
11  * under the License.                                                       *
12  *                                                                          *
13  * The Original Code is WebStone 2.5.                                       *
14  *                                                                          *
15  * The Initial Developer of the Original Code is Silicon Graphics, Inc.     *
16  * and Mindcraft, Inc.. Portions created by Silicon Graphics. and           *
17  * Mindcraft. are Copyright (C) 1995-1998 Silicon Graphics, Inc. and        *
18  * Mindcraft, Inc. All Rights Reserved.                                     *
19  *                                                                          *
20  * Contributor(s): ______________________________________.                  *
21  *                                                                          *
22  * @(#) bench.c 2.4@(#)                                                     *
23  ***************************************************************************/
24 
25 #include <stdio.h>
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <math.h>
31 #include <limits.h>
32 #include <float.h>
33 
34 #ifndef WIN32
35 #include <unistd.h>
36 #include <sys/time.h>
37 #include <sys/uio.h>
38 #include <sys/param.h>
39 #include <netdb.h>
40 #else
41 #include <windows.h>
42 #include <winsock.h>
43 #endif /* WIN32 */
44 
45 #include "sysdep.h"
46 #include "bench.h"
47 
48 
49 /* allocate memory and exit if out of memory */
50 void *
mymalloc(size_t size)51 mymalloc(size_t size)
52 {
53 	void *ptr;
54 
55 	ptr = malloc(size);
56 	if (ptr == NULL)
57 		errexit("Call to malloc() failed\n");
58 	return ptr;
59 }
60 
61 
62 /*
63  * Receive n bytes from a socket
64  * Returns 0 on success, less than 0 on error.
65  */
66 int
recvdata(SOCKET sock,char * ptr,int nbytes)67 recvdata(SOCKET sock, char *ptr, int nbytes)
68 {
69  	int nleft, nread, rv;
70  	fd_set sockfds;
71  	struct timeval timeout;
72 
73 D_PRINTF("recvdata(): attempting to read %d bytes from socket %d, ptr %x\n", nbytes, sock, (int) ptr);
74  	timeout.tv_sec = MAX_ACCEPT_SECS;
75  	timeout.tv_usec = 0;
76  	FD_ZERO(&sockfds);
77  	FD_SET(sock, &sockfds);
78 
79  	/* Time out if the webmaster is too late with our GO
80      * by MAX_ACCEPT_SECS
81 	 */
82  	if ((rv = select(FD_SETSIZE, &sockfds, NULL, NULL, &timeout)) == 0)
83  	{
84  		fprintf(stdout,
85  			"Timeout waiting to write data after %d seconds\n",
86  					MAX_ACCEPT_SECS);
87  		D_PRINTF("select() timed out after %d seconds\n", MAX_ACCEPT_SECS);
88  		errexit("Webclient terminating\n");
89  	}
90 
91 	CLEAR_SOCK_ERR;
92 	nleft = nbytes;
93 	while (nleft > 0)
94 	{
95 		nread = NETREAD(sock, ptr, nleft);
96 		if (SOCK_ERR(nread) || nread == 0)
97 		{
98  			fprintf(stderr, "Error in recvdata(): %s\n", neterrstr());
99  			fprintf(stderr, "NETREAD() data: \"%.*s\"\n", nread, ptr);
100 			D_PRINTF( "Error in recvdata(): %s\n", neterrstr());
101 			return -1;
102 		}
103 
104 		D_PRINTF( "NETREAD() data: %d bytes,\"%s\"\n", nread, ptr);
105 		nleft -= nread;
106 		ptr   += nread;
107 	}
108 
109 	/*
110 	 * if we somehow read too much then this return value will be
111 	 * less than zero, signifying an error.
112 	 */
113 	return (nbytes - nleft);
114 }
115 
116 
117 /*
118  * Send n bytes to a socket
119  * Returns 0 on success, less than 0 on error.
120  */
121 int
senddata(SOCKET sock,char * ptr,int nbytes)122 senddata(SOCKET sock, char *ptr, int nbytes)
123 {
124 	int nleft, nwritten;
125 
126 	TRACE("senddata(): entering, nbytes=%d.\n", nbytes);
127 	CLEAR_SOCK_ERR;
128 	nleft = nbytes;
129 	while (nleft > 0)
130 	{
131 		nwritten = NETWRITE(sock, ptr, nleft);
132 		if (SOCK_ERR(nwritten))
133 		{
134 			TRACE( "senddata(): returning error: %s\n", neterrstr() );
135 			return -1;
136 		}
137 		nleft -= nwritten;
138 		ptr += nwritten;
139 	}
140 	/*
141 	 * if we somehow wrote too much then this return value will be
142 	 * less than zero, signifying an error.
143 	 */
144 	TRACE("senddata(): returning %d.\n", nbytes - nleft);
145 	return (nbytes - nleft);
146 }
147 
148 /* GENERAL NOTE: the conversion routines that follow pass their results
149  * back in a static arrays.  A second call to the same routine overwrites
150  * the previous buffer value for that routine.  If you want to save the
151  * value in the buffer copy it to another variable.
152  */
153 char *
timeval_to_text(const struct timeval * the_timeval)154 timeval_to_text(const struct timeval *the_timeval)
155 {
156 	/*
157 	 * given a timeval (seconds and microseconds), put the text
158 	 * "seconds.microseconds" into timeval_as_text
159 	 * Note that the %10 printf() specifier is a MINIMUM field width.
160 	 */
161 	THREAD static char timeval_as_text[SIZEOF_TIMEVALTEXT+1];
162 	int seconds, microseconds;
163 
164 	seconds = the_timeval->tv_sec;
165 	microseconds = the_timeval->tv_usec;
166 	sprintf(timeval_as_text, "%10d.%6.6d\t", seconds, microseconds);
167 D_PRINTF("timeval_to_text(): returning '%s'\n", timeval_as_text);
168 	return timeval_as_text;
169 }
170 
171 
172 char *
double_to_text(const double the_double)173 double_to_text(const double the_double)
174 {
175 	/*
176 	 * given a double, return text
177 	 * Note that the %17 printf() specifier is a MINIMUM field width.
178 	 */
179 	THREAD static char double_as_text[SIZEOF_DOUBLETEXT + 1];
180 
181 	sprintf(double_as_text, "%17.01f\t", the_double);
182 D_PRINTF("double_to_text(): returning '%s'\n", double_as_text);
183 	return(double_as_text);
184 }
185 
186 struct timeval
text_to_timeval(char * timeval_as_text)187 text_to_timeval(char *timeval_as_text)
188 {
189 	long int seconds, microseconds;
190 	struct timeval the_timeval;
191 
192 	D_PRINTF("T/%d %s\n", (int)timeval_as_text, timeval_as_text);
193 	sscanf(timeval_as_text, "%ld.%ld", &seconds, &microseconds);
194 	the_timeval.tv_sec = seconds;
195 	the_timeval.tv_usec = microseconds;
196 	return the_timeval;
197 }
198 
199 double
text_to_double(char * double_as_text)200 text_to_double(char *double_as_text)
201 {
202 	double the_double = 0;
203 
204 	D_PRINTF("D/%d %s\n", (int)double_as_text, double_as_text);
205 	sscanf(double_as_text, "%lf", &the_double);
206 	return(the_double);
207 }
208 
209 
210 rqst_stats_t *
text_to_rqst_stats(char * rqst_stats_as_text)211 text_to_rqst_stats(char *rqst_stats_as_text)
212 {
213 	THREAD static rqst_stats_t rqst_stats;
214 	rqst_stats_t *the_rqst_stats = &rqst_stats;
215 
216 	the_rqst_stats->totalresponsetime =
217 							text_to_timeval(strtok(rqst_stats_as_text, "\t"));
218 
219 	the_rqst_stats->totalresponsetimesq =
220 							text_to_double(strtok((char *)NULL, "\t"));
221 
222 	the_rqst_stats->minresponsetime =
223 							text_to_timeval(strtok((char *)NULL, "\t"));
224 
225 	the_rqst_stats->maxresponsetime =
226 							text_to_timeval(strtok((char *)NULL, "\t"));
227 
228 	the_rqst_stats->totalconnecttime =
229 							text_to_timeval(strtok((char *)NULL, "\t"));
230 
231 	the_rqst_stats->totalconnecttimesq =
232 							text_to_double(strtok((char *)NULL, "\t"));
233 
234 	the_rqst_stats->minconnecttime =
235 							text_to_timeval(strtok((char *)NULL, "\t"));
236 
237 	the_rqst_stats->maxconnecttime =
238 							text_to_timeval(strtok((char *)NULL, "\t"));
239 
240 	the_rqst_stats->totalconnects = (unsigned long)
241 							text_to_double(strtok((char *)NULL, "\t"));
242 
243 	the_rqst_stats->totalerrs = (unsigned long)
244 							text_to_double(strtok((char *)NULL, "\t"));
245 
246 	the_rqst_stats->totalerrortime =
247 							text_to_timeval(strtok((char *)NULL, "\t"));
248 
249 	the_rqst_stats->totalbytes =
250 							text_to_double(strtok((char *)NULL, "\t"));
251 
252 	the_rqst_stats->totalbytessq =
253 							text_to_double(strtok((char *)NULL, "\t"));
254 
255 	the_rqst_stats->minbytes =
256 							text_to_double(strtok((char *)NULL, "\t"));
257 
258 	the_rqst_stats->maxbytes =
259 							text_to_double(strtok((char *)NULL, "\t"));
260 
261 	the_rqst_stats->totalbody =
262 							text_to_double(strtok((char *)NULL, "\t"));
263 
264 	the_rqst_stats->totalbodysq =
265 							text_to_double(strtok((char *)NULL, "\t"));
266 
267 	the_rqst_stats->minbody =
268 							text_to_double(strtok((char *)NULL, "\t"));
269 
270 	the_rqst_stats->maxbody =
271 							text_to_double(strtok((char *)NULL, "\t"));
272 
273 	return(the_rqst_stats);
274 }
275 
276 
277 char *
rqst_stats_to_text(rqst_stats_t * the_rqst_stats)278 rqst_stats_to_text(rqst_stats_t *the_rqst_stats)
279 {
280 	THREAD static char rqst_stats_as_text[SIZEOF_RQSTSTATSTEXT];
281 	char *tmpbuf;
282 
283 	*rqst_stats_as_text = 0;
284 
285 	tmpbuf = timeval_to_text(&(the_rqst_stats->totalresponsetime));
286 	strcat(rqst_stats_as_text, tmpbuf);
287 
288 	tmpbuf = double_to_text((the_rqst_stats->totalresponsetimesq));
289 	strcat(rqst_stats_as_text, tmpbuf);
290 
291 	tmpbuf = timeval_to_text(&(the_rqst_stats->minresponsetime));
292 	strcat(rqst_stats_as_text, tmpbuf);
293 
294 	tmpbuf = timeval_to_text(&(the_rqst_stats->maxresponsetime));
295 	strcat(rqst_stats_as_text, tmpbuf);
296 
297 	tmpbuf = timeval_to_text(&(the_rqst_stats->totalconnecttime));
298 	strcat(rqst_stats_as_text, tmpbuf);
299 
300 	tmpbuf = double_to_text((the_rqst_stats->totalconnecttimesq));
301 	strcat(rqst_stats_as_text, tmpbuf);
302 
303 	tmpbuf = timeval_to_text(&(the_rqst_stats->minconnecttime));
304 	strcat(rqst_stats_as_text, tmpbuf);
305 
306 	tmpbuf = timeval_to_text(&(the_rqst_stats->maxconnecttime));
307 	strcat(rqst_stats_as_text, tmpbuf);
308 
309 	tmpbuf = double_to_text((the_rqst_stats->totalconnects));
310 	strcat(rqst_stats_as_text, tmpbuf);
311 
312 	tmpbuf = double_to_text((the_rqst_stats->totalerrs));
313 	strcat(rqst_stats_as_text, tmpbuf);
314 
315 	tmpbuf = timeval_to_text(&(the_rqst_stats->totalerrortime));
316 	strcat(rqst_stats_as_text, tmpbuf);
317 
318 	tmpbuf = double_to_text((the_rqst_stats->totalbytes));
319 	strcat(rqst_stats_as_text, tmpbuf);
320 
321 	tmpbuf = double_to_text((the_rqst_stats->totalbytessq));
322 	strcat(rqst_stats_as_text, tmpbuf);
323 
324 	tmpbuf = double_to_text((the_rqst_stats->minbytes));
325 	strcat(rqst_stats_as_text, tmpbuf);
326 
327 	tmpbuf = double_to_text((the_rqst_stats->maxbytes));
328 	strcat(rqst_stats_as_text, tmpbuf);
329 
330 	tmpbuf = double_to_text((the_rqst_stats->totalbody));
331 	strcat(rqst_stats_as_text, tmpbuf);
332 
333 	tmpbuf = double_to_text((the_rqst_stats->totalbodysq));
334 	strcat(rqst_stats_as_text, tmpbuf);
335 
336 	tmpbuf = double_to_text((the_rqst_stats->minbody));
337 	strcat(rqst_stats_as_text, tmpbuf);
338 
339 	tmpbuf = double_to_text((the_rqst_stats->maxbody));
340 	strcat(rqst_stats_as_text, tmpbuf);
341 
342 	D_PRINTF( "rqst_stats_to_text returning %d: %s\n",
343 			strlen(rqst_stats_as_text), rqst_stats_as_text );
344 
345 	return(rqst_stats_as_text);
346 }
347 
348 
349 stats_t *
text_to_stats(char * stats_as_text)350 text_to_stats(char *stats_as_text)
351 {
352 	int i;
353 	rqst_stats_t 			*the_rqst_stats;
354 	THREAD static stats_t	stats;
355 	stats_t					*the_stats = &stats;
356 
357 	D_PRINTF( "Parsing stats: %s\n", stats_as_text );
358 	/* grab stats.rs */
359 	the_rqst_stats = text_to_rqst_stats(stats_as_text);
360 	the_stats->rs = *the_rqst_stats;
361 
362 	/* grab main structure */
363 	the_stats->starttime = text_to_timeval(strtok((char *)NULL, "\t"));
364 	the_stats->endtime = text_to_timeval(strtok((char *)NULL, "\t"));
365 	the_stats->datatime = text_to_timeval(strtok((char *)NULL, "\t"));
366 	the_stats->totalpages =
367 				(unsigned long) text_to_double(strtok((char *)NULL, "\t"));
368 	the_stats->total_num_of_files =
369 				(unsigned int) text_to_double(strtok((char *)NULL, "\t"));
370 	for (i = 0; i < number_of_pages; i++)
371 	{
372 		the_stats->page_numbers[i] =
373 				(unsigned int) text_to_double(strtok((char *)NULL, "\t"));
374 	}
375 	/* return bytes read */
376 	D_PRINTF( "Returning stats\n");
377 	return(the_stats);
378 }
379 
380 
381 char *
stats_to_text(const stats_t * the_stats)382 stats_to_text(const stats_t *the_stats)
383 {
384 	int    i;
385 	THREAD static char  stats_as_text[SIZEOF_STATSTEXT];
386 	char  *tmpbuf;
387 	rqst_stats_t the_rqst_stats;
388 
389 	TRACE("stats_to_text(): entering.\n");
390 	*stats_as_text = 0;
391 
392 	/* stats.rs */
393 	the_rqst_stats = the_stats->rs;
394 	tmpbuf = rqst_stats_to_text(&the_rqst_stats);
395 	strcat(stats_as_text, tmpbuf);
396 
397 	/* main structure */
398 	tmpbuf = timeval_to_text(&(the_stats->starttime));
399 	strcat(stats_as_text, tmpbuf);
400 
401 	tmpbuf = timeval_to_text(&(the_stats->endtime));
402 	strcat(stats_as_text, tmpbuf);
403 
404 	tmpbuf = timeval_to_text(&(the_stats->datatime));
405 	strcat(stats_as_text, tmpbuf);
406 
407 	tmpbuf = double_to_text((the_stats->totalpages));
408 	strcat(stats_as_text, tmpbuf);
409 
410 	tmpbuf = double_to_text((the_stats->total_num_of_files));
411 	strcat(stats_as_text, tmpbuf);
412 
413 	for (i = 0; i < number_of_pages; i++)
414 	{
415 		tmpbuf = double_to_text((the_stats->page_numbers[i]));
416 		strcat(stats_as_text, tmpbuf);
417 	}
418 
419 	strcat(stats_as_text, "\n");
420 
421 	TRACE("stats_as_text(): leaving.\n");
422 	return stats_as_text;
423 }
424 
425 
426 page_stats_t *
text_to_page_stats(char * page_stats_as_text)427 text_to_page_stats(char *page_stats_as_text)
428 {
429 	rqst_stats_t *the_rqst_stats;
430 	THREAD static page_stats_t pagestat;
431 	page_stats_t *pagestats = &pagestat;
432 
433 	/* grab stats.rs */
434 	the_rqst_stats = text_to_rqst_stats(page_stats_as_text);
435 
436 	/* grab main structure */
437 	pagestats->totalpages =
438 				(unsigned long) text_to_double(strtok((char *)NULL, "\t"));
439 
440 	pagestats->page_size =
441 				(unsigned int) text_to_double(strtok((char *)NULL, "\t"));
442 
443 	pagestats->page_valid = (int) text_to_double(strtok((char *)NULL, "\t"));
444 
445 	pagestats->rs = *the_rqst_stats;
446 
447 	return(pagestats);
448 }
449 
450 
451 char *
page_stats_to_text(const page_stats_t * pagestats)452 page_stats_to_text(const page_stats_t *pagestats)
453 {
454 	THREAD static char page_stats_as_text[SIZEOF_PAGESTATSTEXT];
455 	char  *tmpbuf;
456 	rqst_stats_t the_rqst_stats;
457 
458 	TRACE("page_stats_to_text(): entering.\n");
459 	*page_stats_as_text = 0;
460 
461 	/* stats.rs */
462 	the_rqst_stats = pagestats->rs;
463 	tmpbuf = rqst_stats_to_text(&the_rqst_stats);
464 	strcat(page_stats_as_text, tmpbuf);
465 
466 	/* main structure */
467 	tmpbuf = double_to_text(pagestats->totalpages);
468 	strcat(page_stats_as_text, tmpbuf);
469 
470 	tmpbuf = double_to_text(pagestats->page_size);
471 	strcat(page_stats_as_text, tmpbuf);
472 
473 	tmpbuf = double_to_text(pagestats->page_valid);
474 	strcat(page_stats_as_text, tmpbuf);
475 
476 	strcat(page_stats_as_text, "\n");
477 
478 	TRACE("page_stats_to_text(): exiting.\n");
479 	return(page_stats_as_text);
480 }
481 
482 void
rqtimer_init(rqst_timer_t * p)483 rqtimer_init(rqst_timer_t *p)
484 {
485 	memset(p, 0, sizeof(*p));
486 }
487 
488 void
rqstat_init(rqst_stats_t * p)489 rqstat_init(rqst_stats_t *p)
490 {
491 	memset(p, 0, sizeof(*p));
492 
493 	p->minbytes = DBL_MAX;
494 	p->minbody  = DBL_MAX;
495 	p->minconnecttime.tv_sec = LONG_MAX;
496 	p->minconnecttime.tv_usec = LONG_MAX;
497 	p->minresponsetime.tv_sec = LONG_MAX;
498 	p->minresponsetime.tv_usec = LONG_MAX;
499 }
500 
501 
502 void
stats_init(stats_t * p)503 stats_init(stats_t *p)
504 {
505 	memset(p, 0, sizeof(*p));
506 
507 	p->rs.minbytes = DBL_MAX;
508 	p->rs.minbody  = DBL_MAX;
509 	p->rs.minconnecttime.tv_sec = LONG_MAX;
510 	p->rs.minconnecttime.tv_usec = LONG_MAX;
511 	p->rs.minresponsetime.tv_sec = LONG_MAX;
512 	p->rs.minresponsetime.tv_usec = LONG_MAX;
513 }
514 
515 
516 void
page_stats_init(page_stats_t * p)517 page_stats_init(page_stats_t *p)
518 {
519     memset(p, 0, sizeof(*p));
520 
521     p->rs.minbytes = DBL_MAX;
522     p->rs.minbody  = DBL_MAX;
523     p->rs.minconnecttime.tv_sec = LONG_MAX;
524     p->rs.minconnecttime.tv_usec = LONG_MAX;
525     p->rs.minresponsetime.tv_sec = LONG_MAX;
526     p->rs.minresponsetime.tv_usec = LONG_MAX;
527 }
528 
529 
530 void
rqstat_times(rqst_stats_t * rs,rqst_timer_t * rt)531 rqstat_times(rqst_stats_t *rs, rqst_timer_t *rt)
532 {
533 	double    t;
534 
535 	/* calculate in milliseconds to avoid underflow */
536 
537 	compdifftime(&(rt->exittime), &(rt->entertime), &(rs->totalresponsetime));
538 	t = 1000 * timevaldouble(&(rs->totalresponsetime));
539 	rs->totalresponsetimesq = t * t;
540 	doubletimeval(t, &(rs->totalresponsetime));
541 
542 	rs->minresponsetime = rs->totalresponsetime;
543 	rs->maxresponsetime = rs->totalresponsetime;
544 
545 	compdifftime(&(rt->afterconnect), &(rt->beforeconnect),
546 					&(rs->totalconnecttime));
547 	t = 1000 * timevaldouble(&(rs->totalconnecttime));
548 	rs->totalconnecttimesq = t * t;
549 	doubletimeval(t, &(rs->totalconnecttime));
550 
551 	rs->minconnecttime = rs->totalconnecttime;
552 	rs->maxconnecttime = rs->totalconnecttime;
553 
554 	rs->totalbody =   rt->bodybytes;
555 	rs->totalbodysq = ((double)(rt->bodybytes)) * ((double)(rt->bodybytes));
556 	rs->minbody =     rt->bodybytes;
557 	rs->maxbody =     rt->bodybytes;
558 
559 	rs->totalbytes =   rt->totalbytes;
560 	rs->totalbytessq = ((double)(rt->totalbytes)) * ((double)(rt->totalbytes));
561 	rs->minbytes =     rt->totalbytes;
562 	rs->maxbytes =     rt->totalbytes;
563 
564 	rs->totalconnects = 1;
565 	rs->totalerrs = 0;
566 	rs->totalerrortime.tv_sec = 0;
567 	rs->totalerrortime.tv_usec = 0;
568 }
569 
570 void
rqstat_sum(rqst_stats_t * sum,rqst_stats_t * incr)571 rqstat_sum(rqst_stats_t *sum, rqst_stats_t *incr)
572 {
573 	addtime( &(sum->totalresponsetime),   &(incr->totalresponsetime));
574 	mintime( &(sum->minresponsetime),     &(incr->minresponsetime));
575 	maxtime( &(sum->maxresponsetime),     &(incr->maxresponsetime));
576 	sum->totalresponsetimesq += incr->totalresponsetimesq;
577 
578 	addtime( &(sum->totalconnecttime),    &(incr->totalconnecttime));
579 	mintime( &(sum->minconnecttime),      &(incr->minconnecttime));
580 	maxtime( &(sum->maxconnecttime),      &(incr->maxconnecttime));
581 	sum->totalconnecttimesq += incr->totalconnecttimesq;
582 
583 	sum->totalconnects += incr->totalconnects;
584 	sum->totalerrs     += incr->totalerrs;
585 	addtime( &(sum->totalerrortime), &(incr->totalerrortime));
586 
587 	sum->totalbytes    += incr->totalbytes;
588 
589 	sum->totalbytessq  += incr->totalbytessq;
590 	sum->minbytes      =  min(sum->minbytes, incr->minbytes);
591 	sum->maxbytes      =  max(sum->maxbytes, incr->maxbytes);
592 
593 	sum->totalbody     += incr->totalbody;
594 
595 	sum->totalbodysq   += incr->totalbodysq;
596 	sum->minbody       =  min(sum->minbody, incr->minbody);
597 	sum->maxbody       =  max(sum->maxbody, incr->maxbody);
598 }
599 
600 
601 void
rqstat_print(rqst_stats_t * stats)602 rqstat_print(rqst_stats_t *stats)
603 {
604 	rqstat_fprint(stdout, stats);
605 }
606 
607 
608 void
rqstat_fprint(FILE * f,rqst_stats_t * stats)609 rqstat_fprint(FILE *f, rqst_stats_t *stats)
610 {
611 	struct timeval meantime, /*vartime,*/ stdtime;
612 
613 	fprintf(f, "%d connection(s) to server, %d errors\n",
614 	stats->totalconnects, stats->totalerrs);
615 
616 	if (stats->totalconnects == 0) {
617 		fprintf(f,"NO CONNECTIONS, THEREFORE NO STATISTICS\n"
618 				"IS YOUR WEBSERVER RUNNING?\n"
619 				"DO THE PAGES EXIST ON THE SERVER?\n");
620 		return;
621 	}
622 
623 	/* title */
624 	fprintf(f, "\n                            Average      Std Dev      Minimum      Maximum\n\n");
625 
626 	/* first line (connect time) */
627 	avgtime(&(stats->totalconnecttime),
628 	stats->totalconnects, &meantime);
629 
630 	/* variancetime(&(stats->totalconnecttime),
631 	stats->totalconnecttimesq,
632 	stats->totalconnects, &vartime); */
633 
634 	stddevtime(&(stats->totalconnecttime),
635 	stats->totalconnecttimesq,
636 	stats->totalconnects, &stdtime);
637 
638 	/* convert from milliseconds to seconds - see rqstat_times() */
639 	fprintf(f, "Connect time (sec)        %10.6lf   %10.6lf   %10.6lf   %10.6lf\n",
640 		timevaldouble(&meantime) / 1000, timevaldouble(&stdtime) / 1000,
641 		timevaldouble(&(stats->minconnecttime)) / 1000,
642 		timevaldouble(&(stats->maxconnecttime)) / 1000);
643 
644 TRACE("total connect time: %lf   sum of squares: %lf   count: %d\n",
645 	timevaldouble(&(stats->totalconnecttime)),
646 	stats->totalconnecttimesq,
647 	stats->totalconnects);
648 
649 	/* second line (response time) */
650 	avgtime(&(stats->totalresponsetime), stats->totalconnects, &meantime);
651 
652 	/* variancetime(&(stats->totalresponsetime), stats->totalresponsetimesq,
653 			stats->totalconnects, &vartime); */
654 
655 	stddevtime(&(stats->totalresponsetime), stats->totalresponsetimesq,
656 			stats->totalconnects, &stdtime);
657 
658 	/* convert from milliseconds to seconds - see rqstat_times() */
659 	fprintf(f,
660 	  "Response time (sec)       %10.6lf   %10.6lf   %10.6lf   %10.6lf\n",
661 		timevaldouble(&meantime) / 1000, timevaldouble(&stdtime) / 1000,
662 		timevaldouble(&(stats->minresponsetime)) / 1000,
663 		timevaldouble(&(stats->maxresponsetime)) / 1000);
664 
665 TRACE("total response time: %lf   sum of squares: %lf   count: %d\n",
666 	timevaldouble(&(stats->totalresponsetime)),
667 	stats->totalresponsetimesq,
668 	stats->totalconnects);
669 
670 	/* 3rd-5th lines (response size, body size, # bytes moved */
671 	fprintf(f,
672 		"Response size (bytes)     %10.0lf   %10.0lf   %10.0lf   %10.0lf\n",
673 		mean(stats->totalbytes, stats->totalconnects),
674 		stddev(stats->totalbytes, stats->totalbytessq,
675 		stats->totalconnects),
676 		stats->minbytes,
677 		stats->maxbytes);
678 
679 	fprintf(f, "Body size (bytes)         %10.0lf   %10.0lf   %10.0lf   %10.0lf\n\n",
680 		mean(stats->totalbody, stats->totalconnects),
681 		stddev(stats->totalbody, stats->totalbodysq, stats->totalconnects),
682 		stats->minbody,
683 		stats->maxbody);
684 
685 	fprintf(f,
686 		"%.0lf body bytes moved + %.0lf header bytes moved = %.0lf total\n",
687 		stats->totalbody,
688 		stats->totalbytes - stats->totalbody,
689 		stats->totalbytes);
690 }
691