diff options
Diffstat (limited to 'support/ab.c')
| -rw-r--r-- | support/ab.c | 582 |
1 files changed, 321 insertions, 261 deletions
diff --git a/support/ab.c b/support/ab.c index 5adf5a5b..e39536d1 100644 --- a/support/ab.c +++ b/support/ab.c @@ -80,6 +80,10 @@ ** Switched to the new abstract pollset API, allowing ab to ** take advantage of future apr_pollset_t scalability improvements. ** Contributed by Brian Pane, August 31, 2002 + ** + ** Version 2.3 + ** SIGINT now triggers output_results(). + ** Contributed by colm, March 30, 2006 **/ /* Note: this version string should start with \d+[\d\.]* and be a valid @@ -91,7 +95,7 @@ * ab - or to due to a change in the distribution it is compiled with * (such as an APR change in for example blocking). */ -#define AP_AB_BASEREVISION "2.0.40-dev" +#define AP_AB_BASEREVISION "2.3" /* * BUGS: @@ -189,6 +193,9 @@ typedef STACK_OF(X509) X509_STACK_TYPE; #if APR_HAVE_CTYPE_H #include <ctype.h> #endif +#if APR_HAVE_LIMITS_H +#include <limits.h> +#endif /* ------------------- DEFINITIONS -------------------------- */ @@ -199,7 +206,7 @@ typedef STACK_OF(X509) X509_STACK_TYPE; #endif /* maximum number of requests on a time limited test */ -#define MAX_REQUESTS 50000 +#define MAX_REQUESTS (INT_MAX > 50000 ? 50000 : INT_MAX) /* good old state hostname */ #define STATE_UNCONNECTED 0 @@ -238,22 +245,22 @@ struct connection { }; struct data { - int read; /* number of bytes read */ - apr_time_t starttime; /* start time of connection in seconds since - * Jan. 1, 1970 */ - apr_interval_time_t waittime; /* Between writing request and reading - * response */ - apr_interval_time_t ctime; /* time in ms to connect */ - apr_interval_time_t time; /* time in ms for connection */ + apr_time_t starttime; /* start time of connection */ + apr_interval_time_t waittime; /* between request and reading response */ + apr_interval_time_t ctime; /* time to connect */ + apr_interval_time_t time; /* time for connection */ }; #define ap_min(a,b) ((a)<(b))?(a):(b) #define ap_max(a,b) ((a)>(b))?(a):(b) +#define ap_round_ms(a) ((apr_time_t)((a) + 500)/1000) +#define ap_double_ms(a) ((double)(a)/1000.0) #define MAX_CONCURRENCY 20000 /* --------------------- GLOBALS ---------------------------- */ int verbosity = 0; /* no verbosity by default */ +int recverrok = 0; /* ok to proceed after socket receive errors */ int posting = 0; /* GET by default */ int requests = 1; /* Number of requests to make */ int heartbeatres = 100; /* How often do we say we're alive */ @@ -262,6 +269,7 @@ int percentile = 1; /* Show percentile served */ int confidence = 1; /* Show confidence estimator and warnings */ int tlimit = 0; /* time limit in secs */ int keepalive = 0; /* try and do keepalive connections */ +int windowsize = 0; /* we use the OS default window size */ char servername[1024]; /* name that server reports */ char *hostname; /* host name from URL */ char *host_field; /* value of "Host:" header field */ @@ -298,15 +306,20 @@ const char *tablestring; const char *trstring; const char *tdstring; -apr_size_t doclen = 0; /* the length the document should be */ -long started = 0; /* number of requests started, so no excess */ -long totalread = 0; /* total number of bytes read */ -long totalbread = 0; /* totoal amount of entity body read */ -long totalposted = 0; /* total number of bytes posted, inc. headers */ -long done = 0; /* number of requests we have done */ -long doneka = 0; /* number of keep alive connections done */ -long good = 0, bad = 0; /* number of good and bad requests */ -long epipe = 0; /* number of broken pipe writes */ +apr_size_t doclen = 0; /* the length the document should be */ +apr_int64_t totalread = 0; /* total number of bytes read */ +apr_int64_t totalbread = 0; /* totoal amount of entity body read */ +apr_int64_t totalposted = 0; /* total number of bytes posted, inc. headers */ +int started = 0; /* number of requests started, so no excess */ +int done = 0; /* number of requests we have done */ +int doneka = 0; /* number of keep alive connections done */ +int good = 0, bad = 0; /* number of good and bad requests */ +int epipe = 0; /* number of broken pipe writes */ +int err_length = 0; /* requests failed due to response length */ +int err_conn = 0; /* requests failed due to connection drop */ +int err_recv = 0; /* requests failed due to broken read */ +int err_except = 0; /* requests failed due to exception */ +int err_response = 0; /* requests with invalid or non-200 response */ #ifdef USE_SSL int is_ssl; @@ -316,11 +329,7 @@ char *ssl_info = NULL; BIO *bio_out,*bio_err; #endif -/* store error cases */ -int err_length = 0, err_conn = 0, err_except = 0; -int err_response = 0; - -apr_time_t start, endtime; +apr_time_t start, lasttime, stoptime; /* global request (and its length) */ char _request[2048]; @@ -334,7 +343,7 @@ char buffer[8192]; int percs[] = {50, 66, 75, 80, 90, 95, 98, 99, 100}; struct connection *con; /* connection array */ -struct data *stats; /* date for each request */ +struct data *stats; /* data for each request */ apr_pool_t *cntxt; apr_pollset_t *readbits; @@ -356,7 +365,7 @@ static void err(char *s) { fprintf(stderr, "%s\n", s); if (done) - printf("Total of %ld requests completed\n" , done); + printf("Total of %d requests completed\n" , done); exit(1); } @@ -370,7 +379,7 @@ static void apr_err(char *s, apr_status_t rv) "%s: %s (%d)\n", s, apr_strerror(rv, buf, sizeof buf), rv); if (done) - printf("Total of %ld requests completed\n" , done); + printf("Total of %d requests completed\n" , done); exit(rv); } @@ -420,7 +429,6 @@ static void ssl_state_cb(const SSL *s, int w, int r) } #ifndef RAND_MAX -#include <limits.h> #define RAND_MAX INT_MAX #endif @@ -609,18 +617,20 @@ static void ssl_proceed_handshake(struct connection *c) static void write_request(struct connection * c) { do { - apr_time_t tnow = apr_time_now(); + apr_time_t tnow; apr_size_t l = c->rwrite; apr_status_t e = APR_SUCCESS; /* prevent gcc warning */ + tnow = lasttime = apr_time_now(); + /* * First time round ? */ if (c->rwrite == 0) { apr_socket_timeout_set(c->aprsock, 0); c->connect = tnow; - c->rwrite = reqlen; c->rwrote = 0; + c->rwrite = reqlen; if (posting) c->rwrite += postlen; } @@ -647,30 +657,19 @@ static void write_request(struct connection * c) #endif e = apr_socket_send(c->aprsock, request + c->rwrote, &l); - /* - * Bail early on the most common case - */ - if (l == c->rwrite) - break; - - if (e != APR_SUCCESS) { - /* - * Let's hope this traps EWOULDBLOCK too ! - */ - if (!APR_STATUS_IS_EAGAIN(e)) { - epipe++; - printf("Send request failed!\n"); - close_connection(c); - } + if (e != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(e)) { + epipe++; + printf("Send request failed!\n"); + close_connection(c); return; } + totalposted += l; c->rwrote += l; c->rwrite -= l; - } while (1); + } while (c->rwrite); - totalposted += c->rwrite; c->state = STATE_READ; - c->endwrite = apr_time_now(); + c->endwrite = lasttime = apr_time_now(); { apr_pollfd_t new_pollfd; new_pollfd.desc_type = APR_POLL_SOCKET; @@ -723,15 +722,14 @@ static int compwait(struct data * a, struct data * b) return 0; } -static void output_results(void) +static void output_results(int sig) { - apr_interval_time_t timetakenusec; - float timetaken; + double timetaken; - endtime = apr_time_now(); - timetakenusec = endtime - start; - timetaken = ((float)apr_time_sec(timetakenusec)) + - ((float)apr_time_usec(timetakenusec)) / 1000000.0F; + if (sig) { + lasttime = apr_time_now(); /* record final time if interrupted */ + } + timetaken = (double) (lasttime - start) / APR_USEC_PER_SEC; printf("\n\n"); printf("Server Software: %s\n", servername); @@ -747,45 +745,43 @@ static void output_results(void) printf("Document Length: %" APR_SIZE_T_FMT " bytes\n", doclen); printf("\n"); printf("Concurrency Level: %d\n", concurrency); - printf("Time taken for tests: %ld.%03ld seconds\n", - (long) apr_time_sec(timetakenusec), - (long) apr_time_usec(timetakenusec)); - printf("Complete requests: %ld\n", done); - printf("Failed requests: %ld\n", bad); + printf("Time taken for tests: %.3f seconds\n", timetaken); + printf("Complete requests: %d\n", done); + printf("Failed requests: %d\n", bad); if (bad) - printf(" (Connect: %d, Length: %d, Exceptions: %d)\n", - err_conn, err_length, err_except); - printf("Write errors: %ld\n", epipe); + printf(" (Connect: %d, Receive: %d, Length: %d, Exceptions: %d)\n", + err_conn, err_recv, err_length, err_except); + printf("Write errors: %d\n", epipe); if (err_response) printf("Non-2xx responses: %d\n", err_response); if (keepalive) - printf("Keep-Alive requests: %ld\n", doneka); - printf("Total transferred: %ld bytes\n", totalread); + printf("Keep-Alive requests: %d\n", doneka); + printf("Total transferred: %" APR_INT64_T_FMT " bytes\n", totalread); if (posting > 0) - printf("Total POSTed: %ld\n", totalposted); - printf("HTML transferred: %ld bytes\n", totalbread); + printf("Total POSTed: %" APR_INT64_T_FMT "\n", totalposted); + printf("HTML transferred: %" APR_INT64_T_FMT " bytes\n", totalbread); /* avoid divide by zero */ - if (timetaken) { + if (timetaken && done) { printf("Requests per second: %.2f [#/sec] (mean)\n", - (float) (done / timetaken)); + (double) done / timetaken); printf("Time per request: %.3f [ms] (mean)\n", - (float) (1000 * concurrency * timetaken / done)); + (double) concurrency * timetaken * 1000 / done); printf("Time per request: %.3f [ms] (mean, across all concurrent requests)\n", - (float) (1000 * timetaken / done)); + (double) timetaken * 1000 / done); printf("Transfer rate: %.2f [Kbytes/sec] received\n", - (float) (totalread / 1024 / timetaken)); + (double) totalread / 1024 / timetaken); if (posting > 0) { printf(" %.2f kb/s sent\n", - (float) (totalposted / timetaken / 1024)); + (double) totalposted / timetaken / 1024); printf(" %.2f kb/s total\n", - (float) ((totalread + totalposted) / timetaken / 1024)); + (double) (totalread + totalposted) / timetaken / 1024); } } - if (requests) { + if (done > 0) { /* work out connection times */ - long i; + int i; apr_time_t totalcon = 0, total = 0, totald = 0, totalwait = 0; apr_time_t meancon, meantot, meand, meanwait; apr_interval_time_t mincon = AB_MAX, mintot = AB_MAX, mind = AB_MAX, @@ -794,119 +790,117 @@ static void output_results(void) apr_interval_time_t mediancon = 0, mediantot = 0, mediand = 0, medianwait = 0; double sdtot = 0, sdcon = 0, sdd = 0, sdwait = 0; - for (i = 0; i < requests; i++) { - struct data s = stats[i]; - mincon = ap_min(mincon, s.ctime); - mintot = ap_min(mintot, s.time); - mind = ap_min(mind, s.time - s.ctime); - minwait = ap_min(minwait, s.waittime); - - maxcon = ap_max(maxcon, s.ctime); - maxtot = ap_max(maxtot, s.time); - maxd = ap_max(maxd, s.time - s.ctime); - maxwait = ap_max(maxwait, s.waittime); - - totalcon += s.ctime; - total += s.time; - totald += s.time - s.ctime; - totalwait += s.waittime; + for (i = 0; i < done; i++) { + struct data *s = &stats[i]; + mincon = ap_min(mincon, s->ctime); + mintot = ap_min(mintot, s->time); + mind = ap_min(mind, s->time - s->ctime); + minwait = ap_min(minwait, s->waittime); + + maxcon = ap_max(maxcon, s->ctime); + maxtot = ap_max(maxtot, s->time); + maxd = ap_max(maxd, s->time - s->ctime); + maxwait = ap_max(maxwait, s->waittime); + + totalcon += s->ctime; + total += s->time; + totald += s->time - s->ctime; + totalwait += s->waittime; } - meancon = totalcon / requests; - meantot = total / requests; - meand = totald / requests; - meanwait = totalwait / requests; + meancon = totalcon / done; + meantot = total / done; + meand = totald / done; + meanwait = totalwait / done; /* calculating the sample variance: the sum of the squared deviations, divided by n-1 */ - for (i = 0; i < requests; i++) { - struct data s = stats[i]; + for (i = 0; i < done; i++) { + struct data *s = &stats[i]; double a; - a = ((double)s.time - meantot); + a = ((double)s->time - meantot); sdtot += a * a; - a = ((double)s.ctime - meancon); + a = ((double)s->ctime - meancon); sdcon += a * a; - a = ((double)s.time - (double)s.ctime - meand); + a = ((double)s->time - (double)s->ctime - meand); sdd += a * a; - a = ((double)s.waittime - meanwait); + a = ((double)s->waittime - meanwait); sdwait += a * a; } - sdtot = (requests > 1) ? sqrt(sdtot / (requests - 1)) : 0; - sdcon = (requests > 1) ? sqrt(sdcon / (requests - 1)) : 0; - sdd = (requests > 1) ? sqrt(sdd / (requests - 1)) : 0; - sdwait = (requests > 1) ? sqrt(sdwait / (requests - 1)) : 0; + sdtot = (done > 1) ? sqrt(sdtot / (done - 1)) : 0; + sdcon = (done > 1) ? sqrt(sdcon / (done - 1)) : 0; + sdd = (done > 1) ? sqrt(sdd / (done - 1)) : 0; + sdwait = (done > 1) ? sqrt(sdwait / (done - 1)) : 0; - if (gnuplot) { - FILE *out = fopen(gnuplot, "w"); - long i; - apr_time_t sttime; - char tmstring[1024];/* XXXX */ - if (!out) { - perror("Cannot open gnuplot output file"); - exit(1); - } - fprintf(out, "starttime\tseconds\tctime\tdtime\tttime\twait\n"); - for (i = 0; i < requests; i++) { - apr_time_t diff = stats[i].time - stats[i].ctime; - - sttime = stats[i].starttime; - (void) apr_ctime(tmstring, sttime); - fprintf(out, "%s\t%" APR_TIME_T_FMT "\t%" APR_TIME_T_FMT "\t%" APR_TIME_T_FMT "\t%" APR_TIME_T_FMT "\t%" APR_TIME_T_FMT "\n", - tmstring, - sttime, - stats[i].ctime, - diff, - stats[i].time, - stats[i].waittime); - } - fclose(out); - } /* * XXX: what is better; this hideous cast of the compradre function; or * the four warnings during compile ? dirkx just does not know and * hates both/ */ - qsort(stats, requests, sizeof(struct data), + qsort(stats, done, sizeof(struct data), (int (*) (const void *, const void *)) compradre); - if ((requests > 1) && (requests % 2)) - mediancon = (stats[requests / 2].ctime + stats[requests / 2 + 1].ctime) / 2; + if ((done > 1) && (done % 2)) + mediancon = (stats[done / 2].ctime + stats[done / 2 + 1].ctime) / 2; else - mediancon = stats[requests / 2].ctime; + mediancon = stats[done / 2].ctime; - qsort(stats, requests, sizeof(struct data), + qsort(stats, done, sizeof(struct data), (int (*) (const void *, const void *)) compri); - if ((requests > 1) && (requests % 2)) - mediand = (stats[requests / 2].time + stats[requests / 2 + 1].time \ - -stats[requests / 2].ctime - stats[requests / 2 + 1].ctime) / 2; + if ((done > 1) && (done % 2)) + mediand = (stats[done / 2].time + stats[done / 2 + 1].time \ + -stats[done / 2].ctime - stats[done / 2 + 1].ctime) / 2; else - mediand = stats[requests / 2].time - stats[requests / 2].ctime; + mediand = stats[done / 2].time - stats[done / 2].ctime; - qsort(stats, requests, sizeof(struct data), + qsort(stats, done, sizeof(struct data), (int (*) (const void *, const void *)) compwait); - if ((requests > 1) && (requests % 2)) - medianwait = (stats[requests / 2].waittime + stats[requests / 2 + 1].waittime) / 2; + if ((done > 1) && (done % 2)) + medianwait = (stats[done / 2].waittime + stats[done / 2 + 1].waittime) / 2; else - medianwait = stats[requests / 2].waittime; + medianwait = stats[done / 2].waittime; - qsort(stats, requests, sizeof(struct data), + qsort(stats, done, sizeof(struct data), (int (*) (const void *, const void *)) comprando); - if ((requests > 1) && (requests % 2)) - mediantot = (stats[requests / 2].time + stats[requests / 2 + 1].time) / 2; + if ((done > 1) && (done % 2)) + mediantot = (stats[done / 2].time + stats[done / 2 + 1].time) / 2; else - mediantot = stats[requests / 2].time; + mediantot = stats[done / 2].time; printf("\nConnection Times (ms)\n"); + /* + * Reduce stats from apr time to milliseconds + */ + mincon = ap_round_ms(mincon); + mind = ap_round_ms(mind); + minwait = ap_round_ms(minwait); + mintot = ap_round_ms(mintot); + meancon = ap_round_ms(meancon); + meand = ap_round_ms(meand); + meanwait = ap_round_ms(meanwait); + meantot = ap_round_ms(meantot); + mediancon = ap_round_ms(mediancon); + mediand = ap_round_ms(mediand); + medianwait = ap_round_ms(medianwait); + mediantot = ap_round_ms(mediantot); + maxcon = ap_round_ms(maxcon); + maxd = ap_round_ms(maxd); + maxwait = ap_round_ms(maxwait); + maxtot = ap_round_ms(maxtot); + sdcon = ap_double_ms(sdcon); + sdd = ap_double_ms(sdd); + sdwait = ap_double_ms(sdwait); + sdtot = ap_double_ms(sdtot); if (confidence) { -#define CONF_FMT_STRING "%5" APR_TIME_T_FMT " %4d %5.1f %6" APR_TIME_T_FMT " %7" APR_TIME_T_FMT "\n" +#define CONF_FMT_STRING "%5" APR_TIME_T_FMT " %4" APR_TIME_T_FMT " %5.1f %6" APR_TIME_T_FMT " %7" APR_TIME_T_FMT "\n" printf(" min mean[+/-sd] median max\n"); printf("Connect: " CONF_FMT_STRING, - mincon, (int) (meancon + 0.5), sdcon, mediancon, maxcon); + mincon, meancon, sdcon, mediancon, maxcon); printf("Processing: " CONF_FMT_STRING, - mind, (int) (meand + 0.5), sdd, mediand, maxd); + mind, meand, sdd, mediand, maxd); printf("Waiting: " CONF_FMT_STRING, - minwait, (int) (meanwait + 0.5), sdwait, medianwait, maxwait); + minwait, meanwait, sdwait, medianwait, maxwait); printf("Total: " CONF_FMT_STRING, - mintot, (int) (meantot + 0.5), sdtot, mediantot, maxtot); + mintot, meantot, sdtot, mediantot, maxtot); #undef CONF_FMT_STRING #define SANE(what,mean,median,sd) \ @@ -928,51 +922,73 @@ static void output_results(void) else { printf(" min avg max\n"); #define CONF_FMT_STRING "%5" APR_TIME_T_FMT " %5" APR_TIME_T_FMT "%5" APR_TIME_T_FMT "\n" - printf("Connect: " CONF_FMT_STRING, - mincon, meancon, maxcon); - printf("Processing: " CONF_FMT_STRING, - mintot - mincon, meantot - meancon, maxtot - maxcon); - printf("Total: " CONF_FMT_STRING, - mintot, meantot, maxtot); + printf("Connect: " CONF_FMT_STRING, mincon, meancon, maxcon); + printf("Processing: " CONF_FMT_STRING, mintot - mincon, + meantot - meancon, + maxtot - maxcon); + printf("Total: " CONF_FMT_STRING, mintot, meantot, maxtot); #undef CONF_FMT_STRING } /* Sorted on total connect times */ - if (percentile && (requests > 1)) { + if (percentile && (done > 1)) { printf("\nPercentage of the requests served within a certain time (ms)\n"); for (i = 0; i < sizeof(percs) / sizeof(int); i++) { if (percs[i] <= 0) printf(" 0%% <0> (never)\n"); else if (percs[i] >= 100) printf(" 100%% %5" APR_TIME_T_FMT " (longest request)\n", - stats[requests - 1].time); + ap_round_ms(stats[done - 1].time)); else printf(" %d%% %5" APR_TIME_T_FMT "\n", percs[i], - stats[(int) (requests * percs[i] / 100)].time); + ap_round_ms(stats[(int) (done * percs[i] / 100)].time)); } } if (csvperc) { FILE *out = fopen(csvperc, "w"); - int i; if (!out) { perror("Cannot open CSV output file"); exit(1); } fprintf(out, "" "Percentage served" "," "Time in ms" "\n"); for (i = 0; i < 100; i++) { - apr_time_t t; + double t; if (i == 0) - t = stats[0].time; + t = ap_double_ms(stats[0].time); else if (i == 100) - t = stats[requests - 1].time; + t = ap_double_ms(stats[done - 1].time); else - t = stats[(int) (0.5 + requests * i / 100.0)].time; - fprintf(out, "%d,%e\n", i, (double)t); + t = ap_double_ms(stats[(int) (0.5 + done * i / 100.0)].time); + fprintf(out, "%d,%.3f\n", i, t); + } + fclose(out); + } + if (gnuplot) { + FILE *out = fopen(gnuplot, "w"); + char tmstring[APR_CTIME_LEN]; + if (!out) { + perror("Cannot open gnuplot output file"); + exit(1); + } + fprintf(out, "starttime\tseconds\tctime\tdtime\tttime\twait\n"); + for (i = 0; i < done; i++) { + (void) apr_ctime(tmstring, stats[i].starttime); + fprintf(out, "%s\t%" APR_TIME_T_FMT "\t%" APR_TIME_T_FMT + "\t%" APR_TIME_T_FMT "\t%" APR_TIME_T_FMT + "\t%" APR_TIME_T_FMT "\n", tmstring, + apr_time_sec(stats[i].starttime), + ap_round_ms(stats[i].ctime), + ap_round_ms(stats[i].time - stats[i].ctime), + ap_round_ms(stats[i].time), + ap_round_ms(stats[i].waittime)); } fclose(out); } + } + if (sig) { + exit(1); } } @@ -982,10 +998,7 @@ static void output_results(void) static void output_html_results(void) { - long timetaken; - - endtime = apr_time_now(); - timetaken = (long)((endtime - start) / 1000); + double timetaken = (double) (lasttime - start) / APR_USEC_PER_SEC; printf("\n\n<table %s>\n", tablestring); printf("<tr %s><th colspan=2 %s>Server Software:</th>" @@ -1007,14 +1020,13 @@ static void output_html_results(void) "<td colspan=2 %s>%d</td></tr>\n", trstring, tdstring, tdstring, concurrency); printf("<tr %s><th colspan=2 %s>Time taken for tests:</th>" - "<td colspan=2 %s>%" APR_INT64_T_FMT ".%03ld seconds</td></tr>\n", - trstring, tdstring, tdstring, apr_time_sec(timetaken), - (long)apr_time_usec(timetaken)); + "<td colspan=2 %s>%.3f seconds</td></tr>\n", + trstring, tdstring, tdstring, timetaken); printf("<tr %s><th colspan=2 %s>Complete requests:</th>" - "<td colspan=2 %s>%ld</td></tr>\n", + "<td colspan=2 %s>%d</td></tr>\n", trstring, tdstring, tdstring, done); printf("<tr %s><th colspan=2 %s>Failed requests:</th>" - "<td colspan=2 %s>%ld</td></tr>\n", + "<td colspan=2 %s>%d</td></tr>\n", trstring, tdstring, tdstring, bad); if (bad) printf("<tr %s><td colspan=4 %s > (Connect: %d, Length: %d, Exceptions: %d)</td></tr>\n", @@ -1025,56 +1037,65 @@ static void output_html_results(void) trstring, tdstring, tdstring, err_response); if (keepalive) printf("<tr %s><th colspan=2 %s>Keep-Alive requests:</th>" - "<td colspan=2 %s>%ld</td></tr>\n", + "<td colspan=2 %s>%d</td></tr>\n", trstring, tdstring, tdstring, doneka); printf("<tr %s><th colspan=2 %s>Total transferred:</th>" - "<td colspan=2 %s>%ld bytes</td></tr>\n", + "<td colspan=2 %s>%" APR_INT64_T_FMT " bytes</td></tr>\n", trstring, tdstring, tdstring, totalread); if (posting > 0) printf("<tr %s><th colspan=2 %s>Total POSTed:</th>" - "<td colspan=2 %s>%ld</td></tr>\n", + "<td colspan=2 %s>%" APR_INT64_T_FMT "</td></tr>\n", trstring, tdstring, tdstring, totalposted); printf("<tr %s><th colspan=2 %s>HTML transferred:</th>" - "<td colspan=2 %s>%ld bytes</td></tr>\n", + "<td colspan=2 %s>%" APR_INT64_T_FMT " bytes</td></tr>\n", trstring, tdstring, tdstring, totalbread); /* avoid divide by zero */ if (timetaken) { printf("<tr %s><th colspan=2 %s>Requests per second:</th>" "<td colspan=2 %s>%.2f</td></tr>\n", - trstring, tdstring, tdstring, 1000 * (float) (done) / timetaken); + trstring, tdstring, tdstring, (double) done * 1000 / timetaken); printf("<tr %s><th colspan=2 %s>Transfer rate:</th>" "<td colspan=2 %s>%.2f kb/s received</td></tr>\n", - trstring, tdstring, tdstring, (float) (totalread) / timetaken); + trstring, tdstring, tdstring, (double) totalread / timetaken); if (posting > 0) { printf("<tr %s><td colspan=2 %s> </td>" "<td colspan=2 %s>%.2f kb/s sent</td></tr>\n", trstring, tdstring, tdstring, - (float) (totalposted) / timetaken); + (double) totalposted / timetaken); printf("<tr %s><td colspan=2 %s> </td>" "<td colspan=2 %s>%.2f kb/s total</td></tr>\n", trstring, tdstring, tdstring, - (float) (totalread + totalposted) / timetaken); + (double) (totalread + totalposted) / timetaken); } } { /* work out connection times */ - long i; + int i; apr_interval_time_t totalcon = 0, total = 0; apr_interval_time_t mincon = AB_MAX, mintot = AB_MAX; apr_interval_time_t maxcon = 0, maxtot = 0; - for (i = 0; i < requests; i++) { - struct data s = stats[i]; - mincon = ap_min(mincon, s.ctime); - mintot = ap_min(mintot, s.time); - maxcon = ap_max(maxcon, s.ctime); - maxtot = ap_max(maxtot, s.time); - totalcon += s.ctime; - total += s.time; + for (i = 0; i < done; i++) { + struct data *s = &stats[i]; + mincon = ap_min(mincon, s->ctime); + mintot = ap_min(mintot, s->time); + maxcon = ap_max(maxcon, s->ctime); + maxtot = ap_max(maxtot, s->time); + totalcon += s->ctime; + total += s->time; } - - if (requests > 0) { /* avoid division by zero (if 0 requests) */ + /* + * Reduce stats from apr time to milliseconds + */ + mincon = ap_round_ms(mincon); + mintot = ap_round_ms(mintot); + maxcon = ap_round_ms(maxcon); + maxtot = ap_round_ms(maxtot); + totalcon = ap_round_ms(totalcon); + total = ap_round_ms(total); + + if (done > 0) { /* avoid division by zero (if 0 done) */ printf("<tr %s><th %s colspan=4>Connnection Times (ms)</th></tr>\n", trstring, tdstring); printf("<tr %s><th %s> </th> <th %s>min</th> <th %s>avg</th> <th %s>max</th></tr>\n", @@ -1083,18 +1104,18 @@ static void output_html_results(void) "<td %s>%5" APR_TIME_T_FMT "</td>" "<td %s>%5" APR_TIME_T_FMT "</td>" "<td %s>%5" APR_TIME_T_FMT "</td></tr>\n", - trstring, tdstring, tdstring, mincon, tdstring, totalcon / requests, tdstring, maxcon); + trstring, tdstring, tdstring, mincon, tdstring, totalcon / done, tdstring, maxcon); printf("<tr %s><th %s>Processing:</th>" "<td %s>%5" APR_TIME_T_FMT "</td>" "<td %s>%5" APR_TIME_T_FMT "</td>" "<td %s>%5" APR_TIME_T_FMT "</td></tr>\n", trstring, tdstring, tdstring, mintot - mincon, tdstring, - (total / requests) - (totalcon / requests), tdstring, maxtot - maxcon); + (total / done) - (totalcon / done), tdstring, maxtot - maxcon); printf("<tr %s><th %s>Total:</th>" "<td %s>%5" APR_TIME_T_FMT "</td>" "<td %s>%5" APR_TIME_T_FMT "</td>" "<td %s>%5" APR_TIME_T_FMT "</td></tr>\n", - trstring, tdstring, tdstring, mintot, tdstring, total / requests, tdstring, maxtot); + trstring, tdstring, tdstring, mintot, tdstring, total / done, tdstring, maxtot); } printf("</table>\n"); } @@ -1118,8 +1139,9 @@ static void start_connect(struct connection * c) c->gotheader = 0; c->rwrite = 0; if (c->ctx) - apr_pool_destroy(c->ctx); - apr_pool_create(&c->ctx, cntxt); + apr_pool_clear(c->ctx); + else + apr_pool_create(&c->ctx, cntxt); if ((rv = apr_socket_create(&c->aprsock, destsa->family, SOCK_STREAM, 0, c->ctx)) != APR_SUCCESS) { @@ -1129,7 +1151,21 @@ static void start_connect(struct connection * c) != APR_SUCCESS) { apr_err("socket nonblock", rv); } - c->start = apr_time_now(); + + if (windowsize != 0) { + rv = apr_socket_opt_set(c->aprsock, APR_SO_SNDBUF, + windowsize); + if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { + apr_err("socket send buffer", rv); + } + rv = apr_socket_opt_set(c->aprsock, APR_SO_RCVBUF, + windowsize); + if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { + apr_err("socket receive buffer", rv); + } + } + + c->start = lasttime = apr_time_now(); #ifdef USE_SSL if (is_ssl) { BIO *bio; @@ -1220,18 +1256,16 @@ static void close_connection(struct connection * c) } /* save out time */ if (done < requests) { - struct data s; - if ((done) && heartbeatres && !(done % heartbeatres)) { - fprintf(stderr, "Completed %ld requests\n", done); + struct data *s = &stats[done++]; + c->done = lasttime = apr_time_now(); + s->starttime = c->start; + s->ctime = ap_max(0, c->connect - c->start); + s->time = ap_max(0, c->done - c->start); + s->waittime = ap_max(0, c->beginread - c->endwrite); + if (heartbeatres && !(done % heartbeatres)) { + fprintf(stderr, "Completed %d requests\n", done); fflush(stderr); } - c->done = apr_time_now(); - s.read = c->read; - s.starttime = c->start; - s.ctime = ap_max(0, (c->connect - c->start) / 1000); - s.time = ap_max(0, (c->done - c->start) / 1000); - s.waittime = ap_max(0, (c->beginread - c->endwrite) / 1000); - stats[done++] = s; } } @@ -1304,10 +1338,18 @@ static void read_connection(struct connection * c) } /* catch legitimate fatal apr_socket_recv errors */ else if (status != APR_SUCCESS) { - err_except++; /* XXX: is this the right error counter? */ - /* XXX: Should errors here be fatal, or should we allow a - * certain number of them before completely failing? -aaron */ - apr_err("apr_socket_recv", status); + err_recv++; + if (recverrok) { + bad++; + close_connection(c); + if (verbosity >= 1) { + char buf[120]; + fprintf(stderr,"%s: %s (%d)\n", "apr_socket_recv", apr_strerror(status, buf, sizeof buf), status); + } + return; + } else { + apr_err("apr_socket_recv", status); + } } } @@ -1329,8 +1371,8 @@ static void read_connection(struct connection * c) status = apr_xlate_conv_buffer(from_ascii, buffer, &inbytes_left, c->cbuff + c->cbx, &outbytes_left); if (status || inbytes_left || outbytes_left) { - fprintf(stderr, "only simple translation is supported (%d/%u/%u)\n", - status, inbytes_left, outbytes_left); + fprintf(stderr, "only simple translation is supported (%d/%" APR_SIZE_T_FMT + "/%" APR_SIZE_T_FMT ")\n", status, inbytes_left, outbytes_left); exit(1); } #else @@ -1424,8 +1466,14 @@ static void read_connection(struct connection * c) cl = strstr(c->cbuff, "Content-length:"); if (cl) { c->keepalive = 1; - c->length = atoi(cl + 16); + /* response to HEAD doesn't have entity body */ + c->length = posting >= 0 ? atoi(cl + 16) : 0; } + /* The response may not have a Content-Length header */ + if (!cl) { + c->keepalive = 1; + c->length = 0; + } } c->bread += c->cbx - (s + l - c->cbuff) + r - tocopy; totalbread += c->bread; @@ -1450,26 +1498,25 @@ static void read_connection(struct connection * c) err_length++; } if (done < requests) { - struct data s; + struct data *s = &stats[done++]; doneka++; - if (done && heartbeatres && !(done % heartbeatres)) { - fprintf(stderr, "Completed %ld requests\n", done); + c->done = apr_time_now(); + s->starttime = c->start; + s->ctime = ap_max(0, c->connect - c->start); + s->time = ap_max(0, c->done - c->start); + s->waittime = ap_max(0, c->beginread - c->endwrite); + if (heartbeatres && !(done % heartbeatres)) { + fprintf(stderr, "Completed %d requests\n", done); fflush(stderr); } - c->done = apr_time_now(); - s.read = c->read; - s.starttime = c->start; - s.ctime = ap_max(0, (c->connect - c->start) / 1000); - s.waittime = ap_max(0, (c->beginread - c->endwrite) / 1000); - s.time = ap_max(0, (c->done - c->start) / 1000); - stats[done++] = s; } c->keepalive = 0; c->length = 0; c->gotheader = 0; c->cbx = 0; c->read = c->bread = 0; - c->start = c->connect = apr_time_now(); /* zero connect time with keep-alive */ + /* zero connect time with keep-alive */ + c->start = c->connect = lasttime = apr_time_now(); write_request(c); } } @@ -1480,9 +1527,9 @@ static void read_connection(struct connection * c) static void test(void) { - apr_time_t now; + apr_time_t stoptime; apr_int16_t rv; - long i; + int i; apr_status_t status; int snprintf_res = 0; #ifdef NOT_ASCII @@ -1507,8 +1554,6 @@ static void test(void) fflush(stdout); } - now = apr_time_now(); - con = calloc(concurrency, sizeof(struct connection)); stats = calloc(requests, sizeof(struct data)); @@ -1595,8 +1640,9 @@ static void test(void) status = apr_xlate_conv_buffer(to_ascii, request, &inbytes_left, request, &outbytes_left); if (status || inbytes_left || outbytes_left) { - fprintf(stderr, "only simple translation is supported (%d/%u/%u)\n", - status, inbytes_left, outbytes_left); + fprintf(stderr, "only simple translation is supported (%d/%" + APR_SIZE_T_FMT "/%" APR_SIZE_T_FMT ")\n", + status, inbytes_left, outbytes_left); exit(1); } #endif /* NOT_ASCII */ @@ -1611,7 +1657,13 @@ static void test(void) } /* ok - lets start */ - start = apr_time_now(); + start = lasttime = apr_time_now(); + stoptime = tlimit ? (start + apr_time_from_sec(tlimit)) : AB_MAX; + +#ifdef SIGINT + /* Output the results if the user terminates the run early. */ + apr_signal(SIGINT, output_results); +#endif /* initialise lots of requests */ for (i = 0; i < concurrency; i++) { @@ -1619,18 +1671,9 @@ static void test(void) start_connect(&con[i]); } - while (done < requests) { + do { apr_int32_t n; - apr_int32_t timed; - const apr_pollfd_t *pollresults; - - /* check for time limit expiry */ - now = apr_time_now(); - timed = (apr_int32_t)apr_time_sec(now - start); - if (tlimit && timed >= tlimit) { - requests = done; /* so stats are correct */ - break; /* no need to do another round */ - } + const apr_pollfd_t *pollresults; n = concurrency; status = apr_pollset_poll(readbits, aprtimeout, &n, &pollresults); @@ -1645,7 +1688,7 @@ static void test(void) const apr_pollfd_t *next_fd = &(pollresults[i]); struct connection *c; - c = next_fd->client_data; + c = next_fd->client_data; /* * If the connection isn't connected how can we check it? @@ -1733,17 +1776,17 @@ static void test(void) apr_pollset_add(readbits, &new_pollfd); } } - } - + } while (lasttime < stoptime && done < requests); + if (heartbeatres) - fprintf(stderr, "Finished %ld requests\n", done); + fprintf(stderr, "Finished %d requests\n", done); else printf("..done\n"); if (use_html) output_html_results(); else - output_results(); + output_results(0); } /* ------------------------------------------------------- */ @@ -1752,16 +1795,16 @@ static void test(void) static void copyright(void) { if (!use_html) { - printf("This is ApacheBench, Version %s\n", AP_AB_BASEREVISION " <$Revision: 1.146 $> apache-2.0"); + printf("This is ApacheBench, Version %s\n", AP_AB_BASEREVISION " <$Revision: 655654 $>"); printf("Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/\n"); - printf("Copyright 2006 The Apache Software Foundation, http://www.apache.org/\n"); + printf("Licensed to The Apache Software Foundation, http://www.apache.org/\n"); printf("\n"); } else { printf("<p>\n"); - printf(" This is ApacheBench, Version %s <i><%s></i> apache-2.0<br>\n", AP_AB_BASEREVISION, "$Revision: 1.146 $"); + printf(" This is ApacheBench, Version %s <i><%s></i><br>\n", AP_AB_BASEREVISION, "$Revision: 655654 $"); printf(" Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/<br>\n"); - printf(" Copyright 2006 The Apache Software Foundation, http://www.apache.org/<br>\n"); + printf(" Licensed to The Apache Software Foundation, http://www.apache.org/<br>\n"); printf("</p>\n<p>\n"); } } @@ -1774,12 +1817,17 @@ static void usage(const char *progname) "[s]" #endif "://]hostname[:port]/path\n", progname); +/* 80 column ruler: ******************************************************************************** + */ fprintf(stderr, "Options are:\n"); fprintf(stderr, " -n requests Number of requests to perform\n"); fprintf(stderr, " -c concurrency Number of multiple requests to make\n"); fprintf(stderr, " -t timelimit Seconds to max. wait for responses\n"); - fprintf(stderr, " -p postfile File containing data to POST\n"); - fprintf(stderr, " -T content-type Content-type header for POSTing\n"); + fprintf(stderr, " -b windowsize Size of TCP send/receive buffer, in bytes\n"); + fprintf(stderr, " -p postfile File containing data to POST. Remember also to set -T\n"); + fprintf(stderr, " -T content-type Content-type header for POSTing, eg.\n"); + fprintf(stderr, " 'application/x-www-form-urlencoded'\n"); + fprintf(stderr, " Default is 'text/plain'\n"); fprintf(stderr, " -v verbosity How much troubleshooting info to print\n"); fprintf(stderr, " -w Print out results in HTML tables\n"); fprintf(stderr, " -i Use HEAD instead of GET\n"); @@ -1800,6 +1848,7 @@ static void usage(const char *progname) fprintf(stderr, " -S Do not show confidence estimators and warnings.\n"); fprintf(stderr, " -g filename Output collected data to gnuplot format file.\n"); fprintf(stderr, " -e filename Output CSV file with percentages served\n"); + fprintf(stderr, " -r Don't exit on socket receive errors.\n"); fprintf(stderr, " -h Display usage information (this message)\n"); #ifdef USE_SSL fprintf(stderr, " -Z ciphersuite Specify SSL/TLS cipher suite (See openssl ciphers)\n"); @@ -1898,7 +1947,12 @@ static int open_postfile(const char *pfile) return rv; } - apr_file_info_get(&finfo, APR_FINFO_NORM, postfd); + rv = apr_file_info_get(&finfo, APR_FINFO_NORM, postfd); + if (rv != APR_SUCCESS) { + fprintf(stderr, "ab: Could not stat POST data file (%s): %s\n", pfile, + apr_strerror(rv, errmsg, sizeof errmsg)); + return rv; + } postlen = (apr_size_t)finfo.size; postdata = malloc(postlen); if (!postdata) { @@ -1962,7 +2016,7 @@ int main(int argc, const char * const argv[]) #endif apr_getopt_init(&opt, cntxt, argc, argv); - while ((status = apr_getopt(opt, "n:c:t:T:p:v:kVhwix:y:z:C:H:P:A:g:X:de:Sq" + while ((status = apr_getopt(opt, "n:c:t:b:T:p:v:rkVhwix:y:z:C:H:P:A:g:X:de:Sq" #ifdef USE_SSL "Z:f:" #endif @@ -1970,7 +2024,7 @@ int main(int argc, const char * const argv[]) switch (c) { case 'n': requests = atoi(optarg); - if (!requests) { + if (requests <= 0) { err("Invalid number of requests\n"); } break; @@ -1983,6 +2037,9 @@ int main(int argc, const char * const argv[]) case 'c': concurrency = atoi(optarg); break; + case 'b': + windowsize = atoi(optarg); + break; case 'i': if (posting == 1) err("Cannot mix POST and HEAD\n"); @@ -2010,6 +2067,9 @@ int main(int argc, const char * const argv[]) exit(r); } break; + case 'r': + recverrok = 1; + break; case 'v': verbosity = atoi(optarg); break; |
