/* * Copyright (C) 2000-2007 Hewlett-Packard Company * Copyright (C) 2007 Ted Bullock * * This file is part of httperf, a web server performance measurment tool. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * In addition, as a special exception, the copyright holders give permission * to link the code of this work with the OpenSSL project's "OpenSSL" library * (or with modified versions of it that use the same license as the "OpenSSL" * library), and distribute linked combinations including the two. You must * obey the GNU General Public License in all respects for all of the code * used other than "OpenSSL". If you modify this file, you may extend this * exception to your version of the file, but you are not obligated to do so. * If you do not wish to do so, delete this exception statement from your * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ /* * Fundamentals: * * There are three subsystems to httperf: * * 1) The load generator which determines what URI is fetched next. * * 2) The core engine that handles the mechanics of issuing a request. * * 3) The instrumentation infrastructure that measures various aspects of the * transaction(s). * * Since there is considerable potential variation in all three, it seems like * an event-based approach might be ideal in tying the three together. * Ideally, it should be possible to write a new load generator without * modifications to the other subsystems. Similarly, it should be possible to * add instrumentation without requiring changes to the load generator or http * engine. * * Axioms: - The only point at which the client will fall back is if the * client itself is overloaded. There is no point trying to fix up this * case---simply declare defeat and abort the test. */ #include "config.h" #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ /* Required for fpsetmask() under FreeBSD */ #include #endif #include #include #include /* after sys/types.h for BSD (in generic_types.h) */ #include #include #include #include #include #include #include #ifdef HAVE_SSL # include #endif #define RATE_INTERVAL 5.0 const char *prog_name; int verbose; int periodic_stats; Cmdline_Params param; Time test_time_start; Time test_time_stop; struct rusage test_rusage_start; struct rusage test_rusage_stop; size_t object_type_size[OBJ_NUM_TYPES]; #ifdef HAVE_SSL SSL_CTX *ssl_ctx; #endif #ifdef DEBUG int debug_level; #endif static Time perf_sample_start; static struct option longopts[] = { {"add-header", required_argument, (int *) ¶m.additional_header, 0}, {"add-header-file", required_argument, (int *) ¶m.additional_header_file, 0 }, {"burst-length", required_argument, (int *) ¶m.burst_len, 0}, {"client", required_argument, (int *) ¶m.client, 0}, {"close-with-reset", no_argument, ¶m.close_with_reset, 1}, {"debug", required_argument, 0, 'd'}, {"failure-status", required_argument, ¶m.failure_status, 0}, {"help", no_argument, 0, 'h'}, {"hog", no_argument, ¶m.hog, 1}, {"http-version", required_argument, (int *) ¶m.http_version, 0}, {"max-connections", required_argument, (int *) ¶m.max_conns, 0}, {"max-piped-calls", required_argument, (int *) ¶m.max_piped, 0}, {"method", required_argument, (int *) ¶m.method, 0}, {"myaddr", required_argument, (int *) ¶m.myaddr, 0}, {"no-host-hdr", no_argument, ¶m.no_host_hdr, 1}, {"num-calls", required_argument, (int *) ¶m.num_calls, 0}, {"num-conns", required_argument, (int *) ¶m.num_conns, 0}, {"period", required_argument, (int *) ¶m.rate.mean_iat, 0}, {"port", required_argument, (int *) ¶m.port, 0}, {"print-reply", optional_argument, ¶m.print_reply, 0}, {"print-request", optional_argument, ¶m.print_request, 0}, {"rate", required_argument, (int *) ¶m.rate, 0}, {"recv-buffer", required_argument, (int *) ¶m.recv_buffer_size, 0}, {"retry-on-failure", no_argument, ¶m.retry_on_failure, 1}, {"runtime", required_argument, (int *) ¶m.runtime, 0}, {"send-buffer", required_argument, (int *) ¶m.send_buffer_size, 0}, {"server", required_argument, (int *) ¶m.server, 0}, {"server-name", required_argument, (int *) ¶m.server_name, 0}, {"servers", required_argument, (int *) ¶m.servers, 0}, {"uri", required_argument, (int *) ¶m.uri, 0}, {"session-cookies", no_argument, (int *) ¶m.session_cookies, 1}, #ifdef HAVE_SSL {"ssl", no_argument, ¶m.use_ssl, 1}, {"ssl-ciphers", required_argument, (int *) ¶m.ssl_cipher_list, 0}, {"ssl-no-reuse", no_argument, ¶m.ssl_reuse, 0}, {"ssl-certificate", required_argument, (int *) ¶m.ssl_cert, 0}, {"ssl-key", required_argument, (int *) ¶m.ssl_key, 0}, {"ssl-verify", optional_argument, (int *) ¶m.ssl_verify, 0}, {"ssl-ca-file", required_argument, (int *) ¶m.ssl_ca_file, 0}, {"ssl-ca-path", required_argument, (int *) ¶m.ssl_ca_path, 0}, {"ssl-protocol", required_argument, ¶m.ssl_protocol, 0}, #endif {"think-timeout", required_argument, (int *) ¶m.think_timeout, 0}, {"timeout", required_argument, (int *) ¶m.timeout, 0}, {"use-timer-cache", no_argument, ¶m.use_timer_cache, 1}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {"periodic-stats", no_argument, 0, 'n'}, {"wlog", required_argument, (int *) ¶m.wlog, 0}, {"wsess", required_argument, (int *) ¶m.wsess, 0}, {"wsesslog", required_argument, (int *) ¶m.wsesslog, 0}, {"wsesspage", required_argument, (int *) ¶m.wsesspage, 0}, {"wset", required_argument, (int *) ¶m.wset, 0}, {0, 0, 0, 0} }; static void usage(void) { printf("Usage: %s " "[-hdvV] [--add-header S] [--burst-length N] [--client N/N]\n" "\t[--close-with-reset] [--debug N] [--failure-status N]\n" "\t[--help] [--hog] [--http-version S] [--max-connections N]\n" "\t[--max-piped-calls N] [--method S] [--no-host-hdr]\n" "\t[--num-calls N] [--num-conns N] [--session-cookies]\n" "\t[--period [d|u|e]T1[,T2]|[v]T1,D1[,T2,D2]...[,Tn,Dn]\n" "\t[--print-reply [header|body]] [--print-request [header|body]]\n" "\t[--rate X] [--recv-buffer N] [--retry-on-failure] [--send-buffer N]\n" "\t[--server S|--servers file] [--server-name S] [--port N] [--uri S] " "[--myaddr S]\n" #ifdef HAVE_SSL "\t[--ssl] [--ssl-ciphers L] [--ssl-no-reuse]\n" "\t[--ssl-certificate file] [--ssl-key file]\n" "\t[--ssl-ca-file file] [--ssl-ca-path path]\n" "\t[--ssl-verify [yes|no]] [--ssl-protocol S]\n" #endif "\t[--think-timeout X] [--timeout X] [--verbose] [--version]\n" "\t[--wlog y|n,file] [--wsess N,N,X] [--wsesslog N,X,file]\n" "\t[--wset N,X]\n" "\t[--runtime X]\n" "\t[--use-timer-cache]\n" "\t[--periodic-stats]\n", prog_name); } void panic(const char *msg, ...) { va_list va; va_start(va, msg); vfprintf(stderr, msg, va); va_end(va); exit(1); } void no_op(void) { } static void perf_sample(struct Timer *t, Any_Type regarg) { Any_Type callarg; callarg.d = 1.0 / (timer_now() - perf_sample_start); event_signal(EV_PERF_SAMPLE, 0, callarg); /* * prepare for next sample interval: */ perf_sample_start = timer_now(); if (timer_schedule(perf_sample, regarg, RATE_INTERVAL) == NULL) panic("%s(%d): Received NULL from timer_schedule\n", __func__, __LINE__); } int main(int argc, char **argv) { extern Load_Generator uri_fixed, uri_wlog, uri_wset, conn_rate, call_seq; extern Load_Generator wsess, wsesslog, wsesspage, sess_cookie, misc; extern Stat_Collector stats_basic, session_stat; extern Stat_Collector stats_print_reply; extern char *optarg; int session_workload = 0; int num_gen = 3; Load_Generator *gen[5] = { &call_seq, &uri_fixed, &conn_rate, }; int num_stats = 1; Stat_Collector *stat[3] = { &stats_basic }; int i, ch, longindex; u_int minor, major; char *end, *name; Any_Type arg; void *flag; Time t; int numRates = 0; #ifdef __FreeBSD__ /* * This works around a bug in earlier versions of FreeBSD that cause * non-finite IEEE arithmetic to cause SIGFPE instead of the * non-finite arithmetic as defined by IEEE. */ fpsetmask(0); #endif object_type_size[OBJ_CONN] = sizeof(Conn); object_type_size[OBJ_CALL] = sizeof(Call); param.http_version = 0x10001; /* default to HTTP/1.1 */ param.client.id = 0; param.client.num_clients = 1; param.port = -1; param.uri = "/"; param.num_calls = 1; param.burst_len = 1; param.num_conns = 1; /* * These should be set to the minimum of 2*bandwidth*delay and the * maximum request/reply size for single-call connections. */ param.send_buffer_size = 4096; param.recv_buffer_size = 16384; param.rate.dist = DETERMINISTIC; #ifdef HAVE_SSL param.ssl_reuse = 1; param.ssl_verify = 0; param.ssl_protocol = 0; #endif /* * get program name: */ prog_name = strrchr(argv[0], '/'); if (prog_name) ++prog_name; else prog_name = argv[0]; /* * process command line options: */ while ((ch = getopt_long(argc, argv, "d:hvVn", longopts, &longindex)) >= 0) { switch (ch) { case 0: flag = longopts[longindex].flag; if (flag == ¶m.method) param.method = optarg; else if (flag == ¶m.additional_header) param.additional_header = optarg; else if (flag == ¶m.additional_header_file) param.additional_header_file = optarg; else if (flag == ¶m.num_calls) { errno = 0; param.num_calls = strtoul(optarg, &end, 10); if (errno == ERANGE || end == optarg || *end) { fprintf(stderr, "%s: illegal number of calls %s\n", prog_name, optarg); exit(1); } } else if (flag == ¶m.http_version) { if (sscanf(optarg, "%u.%u", &major, &minor) != 2) { fprintf(stderr, "%s: illegal version number %s\n", prog_name, optarg); exit(1); } param.http_version = (major << 16) | (minor & 0xffff); } else if (flag == ¶m.myaddr) { core_add_addresses(optarg); } else if (flag == ¶m.burst_len) { errno = 0; param.burst_len = strtoul(optarg, &end, 10); if (errno == ERANGE || end == optarg || *end || param.burst_len < 1) { fprintf(stderr, "%s: illegal burst-length %s\n", prog_name, optarg); exit(1); } } else if (flag == ¶m.failure_status) { errno = 0; param.failure_status = strtoul(optarg, &end, 10); if (errno == ERANGE || end == optarg || *end || param.failure_status <= 0) { fprintf(stderr, "%s: illegal failure status %s\n", prog_name, optarg); exit(1); } } else if (flag == ¶m.num_conns) { errno = 0; param.num_conns = strtoul(optarg, &end, 10); if (errno == ERANGE || end == optarg || *end) { fprintf(stderr, "%s: illegal number of connections %s\n", prog_name, optarg); exit(1); } } else if (flag == ¶m.max_conns) { errno = 0; param.max_conns = strtoul(optarg, &end, 10); if (errno == ERANGE || end == optarg || *end) { fprintf(stderr, "%s: illegal max. # of connection %s\n", prog_name, optarg); exit(1); } } else if (flag == ¶m.max_piped) { errno = 0; param.max_piped = strtoul(optarg, &end, 10); if (errno == ERANGE || end == optarg || *end) { fprintf(stderr, "%s: illegal max. # of piped calls %s\n", prog_name, optarg); exit(1); } } else if (flag == ¶m.port) { errno = 0; param.port = strtoul(optarg, &end, 10); if (errno == ERANGE || end == optarg || *end || (unsigned) param.port > 0xffff) { fprintf(stderr, "%s: illegal port number %s\n", prog_name, optarg); exit(1); } } else if (flag == ¶m.print_request || flag == ¶m.print_reply) { int val; if (!optarg) val = PRINT_HEADER | PRINT_BODY; else switch (tolower(optarg[0])) { case 'h': val = PRINT_HEADER; break; case 'b': val = PRINT_BODY; break; default: val = PRINT_HEADER | PRINT_BODY; break; } *(int *) flag = val; } else if (flag == ¶m.rate) { errno = 0; param.rate.rate_param = strtod(optarg, &end); if (errno == ERANGE || end == optarg || *end) { fprintf(stderr, "%s: illegal request rate %s\n", prog_name, optarg); exit(1); } if (param.rate.rate_param <= 0.0) param.rate.mean_iat = 0.0; else param.rate.mean_iat = 1 / param.rate.rate_param; param.rate.dist = DETERMINISTIC; } else if (flag == ¶m.rate.mean_iat) { /* --period */ param.rate.dist = DETERMINISTIC; if (!isdigit(*optarg)) switch (tolower(*optarg++)) { case 'd': param.rate.dist = DETERMINISTIC; break; case 'u': param.rate.dist = UNIFORM; break; case 'e': param.rate.dist = EXPONENTIAL; break; case 'v': param.rate.dist = VARIABLE; break; default: fprintf(stderr, "%s: illegal interarrival distribution " "'%c' in %s\n", prog_name, optarg[-1], optarg - 1); exit(1); } /* * remaining params depend on selected * distribution: */ errno = 0; switch (param.rate.dist) { case DETERMINISTIC: case EXPONENTIAL: param.rate.mean_iat = strtod(optarg, &end); if (errno == ERANGE || end == optarg || *end || param.rate.mean_iat < 0) { fprintf(stderr, "%s: illegal mean interarrival " "time %s\n", prog_name, optarg); exit(1); } break; case UNIFORM: param.rate.min_iat = strtod(optarg, &end); if (errno == ERANGE || end == optarg || param.rate.min_iat < 0) { fprintf(stderr, "%s: illegal minimum interarrival " "time %s\n", prog_name, optarg); exit(1); } if (*end != ',') { fprintf(stderr, "%s: minimum interarrival time not " "followed by `,MAX_IAT' (rest: `%s')\n", prog_name, end); exit(1); } optarg = end + 1; param.rate.max_iat = strtod(optarg, &end); if (errno == ERANGE || end == optarg || *end || param.rate.max_iat < 0) { fprintf(stderr, "%s: illegal request period %s\n", prog_name, optarg); exit(1); } param.rate.mean_iat = 0.5 * (param.rate.min_iat + param.rate.max_iat); break; case VARIABLE: while (1) { if (numRates >= NUM_RATES) { fprintf(stderr, "%s: too many rates\n", prog_name); exit(1); } param.rate.iat[numRates] = strtod(optarg, &end); if (errno == ERANGE || end == optarg || param.rate. iat[numRates] < 0) { fprintf(stderr, "%s: illegal minimum interarrival" " time %s\n", prog_name, optarg); exit(1); } if (*end != ',') { fprintf(stderr, "%s: interarrival time not " "followed by `,duration' (rest: `%s')\n", prog_name, end); exit(1); } optarg = end + 1; param.rate.duration[numRates] = strtod(optarg, &end); if (errno == ERANGE || end == optarg || param.rate. duration[numRates] < 0) { fprintf(stderr, "%s: illegal duration %s\n", prog_name, optarg); exit(1); } if (numRates == 0) param.rate.mean_iat = param.rate. iat[numRates]; else param.rate.mean_iat += param.rate. iat[numRates]; numRates++; if (*end != ',') { param.rate.numRates = numRates; break; } else optarg = end + 1; } param.rate.mean_iat /= numRates; break; default: fprintf(stderr, "%s: internal error parsing %s\n", prog_name, optarg); exit(1); break; } param.rate.rate_param = ((param.rate.mean_iat <= 0.0) ? 0.0 : (1.0 / param.rate.mean_iat)); } else if (flag == ¶m.recv_buffer_size) { errno = 0; param.recv_buffer_size = strtoul(optarg, &end, 10); if (errno == ERANGE || end == optarg || *end || param.port > 0xffff) { fprintf(stderr, "%s: illegal receive buffer size %s\n", prog_name, optarg); exit(1); } } else if (flag == ¶m.send_buffer_size) { errno = 0; param.send_buffer_size = strtoul(optarg, &end, 10); if (errno == ERANGE || end == optarg || *end || param.port > 0xffff) { fprintf(stderr, "%s: illegal send buffer size %s\n", prog_name, optarg); exit(1); } } else if (flag == ¶m.client) { errno = 0; param.client.id = strtoul(optarg, &end, 0); if (end == optarg || errno == ERANGE) { fprintf(stderr, "%s: bad client id (rest: `%s')\n", prog_name, optarg); exit(1); } if (*end != '/') { fprintf(stderr, "%s: client id not followed by `/' (rest: `%s')\n", prog_name, end); exit(1); } optarg = end + 1; param.client.num_clients = strtoul(optarg, &end, 0); if (end == optarg || errno == ERANGE || param.client.id >= param.client.num_clients) { fprintf(stderr, "%s: bad number of clients (rest: `%s')\n", prog_name, optarg); exit(1); } } else if (flag == ¶m.server) param.server = optarg; else if (flag == ¶m.server_name) param.server_name = optarg; else if (flag == ¶m.servers) param.servers = optarg; #ifdef HAVE_SSL else if (flag == ¶m.ssl_cipher_list) param.ssl_cipher_list = optarg; else if (flag == ¶m.ssl_cert) param.ssl_cert = optarg; else if (flag == ¶m.ssl_key) param.ssl_key = optarg; else if (flag == ¶m.ssl_verify) { if (!optarg) param.ssl_verify = 1; else switch (tolower (optarg[0])) { case 'y': param.ssl_verify = 1; break; case 'n': param.ssl_verify = 0; break; default: param.ssl_verify = 0; break; } } else if (flag == ¶m.ssl_ca_file) param.ssl_ca_file = optarg; else if (flag == ¶m.ssl_ca_path) param.ssl_ca_path = optarg; else if (flag == ¶m.ssl_protocol) { param.use_ssl = 1; if (strcasecmp (optarg, "auto") == 0) param.ssl_protocol = 0; #ifndef OPENSSL_NO_SSL2 else if (strcasecmp (optarg, "SSLv2") == 0) param.ssl_protocol = 2; #endif #ifndef OPENSSL_NO_SSL3 else if (strcasecmp (optarg, "SSLv3") == 0) param.ssl_protocol = 3; #endif else if (strcasecmp (optarg, "TLSv1.0") == 0 || strcasecmp (optarg, "TLSv1_0") == 0 || strcasecmp (optarg, "TLSv1") == 0) param.ssl_protocol = 4; else if (strcasecmp (optarg, "TLSv1.1") == 0 || strcasecmp (optarg, "TLSv1_1") == 0) param.ssl_protocol = 5; else if (strcasecmp (optarg, "TLSv1.2") == 0 || strcasecmp (optarg, "TLSv1_2") == 0) param.ssl_protocol = 6; #if (OPENSSL_VERSION_NUMBER >= 0x10101000L) else if (strcasecmp (optarg, "TLSv1.3") == 0 || strcasecmp (optarg, "TLSv1_3") == 0) param.ssl_protocol = 7; #endif else { fprintf (stderr, "%s: illegal SSL protocol %s\n", prog_name, optarg); exit (1); } } #endif else if (flag == ¶m.uri) param.uri = optarg; else if (flag == ¶m.think_timeout) { errno = 0; param.think_timeout = strtod(optarg, &end); if (errno == ERANGE || end == optarg || *end) { fprintf(stderr, "%s: illegal think timeout value %s\n", prog_name, optarg); exit(1); } } else if (flag == ¶m.timeout) { errno = 0; param.timeout = strtod(optarg, &end); if (errno == ERANGE || end == optarg || *end) { fprintf(stderr, "%s: illegal connect timeout %s\n", prog_name, optarg); exit(1); } } else if (flag == ¶m.runtime) { errno = 0; param.runtime = strtod(optarg, &end); if (errno == ERANGE || end == optarg || *end || param.runtime <= 0.0) { fprintf(stderr, "%s: illegal runtime value %s\n", prog_name, optarg); exit(1); } } else if (flag == ¶m.wlog) { gen[1] = &uri_wlog; /* XXX fix * me---somehow */ param.wlog.do_loop = (*optarg == 'y') || (*optarg == 'Y'); param.wlog.file = optarg + 2; } else if (flag == ¶m.wsess) { num_gen = 2; /* XXX fix me---somehow */ gen[0] = &wsess; stat[num_stats++] = &session_stat; errno = 0; name = "bad number of sessions (1st param)"; param.wsess.num_sessions = strtoul(optarg, &end, 0); if (end == optarg || errno == ERANGE) goto bad_wsess_param; optarg = end + 1; name = "bad number of calls per session (2nd param)"; if (*end != ',') goto bad_wsess_param; optarg = end + 1; param.wsess.num_calls = strtoul(optarg, &end, 0); if (end == optarg || errno == ERANGE) goto bad_wsess_param; name = "bad user think time (3rd param)"; if (*end != ',') goto bad_wsess_param; optarg = end + 1; param.wsess.think_time = strtod(optarg, &end); if (end == optarg || errno == ERANGE || param.wsess.think_time < 0.0) goto bad_wsess_param; name = "extraneous parameter"; if (*end) { bad_wsess_param: fprintf(stderr, "%s: %s in --wsess arg (rest: `%s')", prog_name, name, end); if (errno) fprintf(stderr, ": %s", strerror(errno)); fputc('\n', stderr); exit(1); } session_workload = 1; } else if (flag == ¶m.wsesspage) { num_gen = 2; /* XXX fix me---somehow */ gen[0] = &wsesspage; stat[num_stats++] = &session_stat; errno = 0; name = "bad number of sessions (1st param)"; param.wsesspage.num_sessions = strtoul(optarg, &end, 0); if (end == optarg || errno == ERANGE) goto bad_wsesspage_param; optarg = end + 1; name = "bad number of user requests per session (2nd param)"; if (*end != ',') goto bad_wsesspage_param; optarg = end + 1; param.wsesspage.num_reqs = strtoul(optarg, &end, 0); if (end == optarg || errno == ERANGE) goto bad_wsesspage_param; name = "bad user think time (3rd param)"; if (*end != ',') goto bad_wsesspage_param; optarg = end + 1; param.wsesspage.think_time = strtod(optarg, &end); if (end == optarg || errno == ERANGE || param.wsesspage.think_time < 0.0) goto bad_wsesspage_param; name = "extraneous parameter"; if (*end) { bad_wsesspage_param: fprintf(stderr, "%s: %s in --wsesspage arg (rest: `%s')", prog_name, name, end); if (errno) fprintf(stderr, ": %s", strerror(errno)); fputc('\n', stderr); exit(1); } session_workload = 1; } else if (flag == ¶m.wsesslog) { num_gen = 1; /* XXX fix me---somehow */ gen[0] = &wsesslog; stat[num_stats++] = &session_stat; errno = 0; name = "bad number of sessions (1st param)"; param.wsesslog.num_sessions = strtoul(optarg, &end, 0); if (end == optarg || errno == ERANGE) goto bad_wsesslog_param; optarg = end + 1; name = "bad user think time (2nd param)"; if (*end != ',') goto bad_wsesslog_param; optarg = end + 1; param.wsesslog.think_time = strtod(optarg, &end); if (end == optarg || errno == ERANGE || param.wsesslog.think_time < 0.0) goto bad_wsesslog_param; name = "bad session filename (3rd param)"; if (*end != ',') goto bad_wsesslog_param; optarg = end + 1; /* * simulate parsing of string */ param.wsesslog.file = optarg; if ((end = strchr(optarg, ',')) == NULL) /* * must be last param, position end at * final \0 */ end = optarg + strlen(optarg); else /* * terminate end of string */ *end++ = '\0'; optarg = end; name = "extraneous parameter"; if (*end) { bad_wsesslog_param: fprintf(stderr, "%s: %s in --wsesslog arg (rest: `%s')", prog_name, name, end); if (errno) fprintf(stderr, ": %s", strerror(errno)); fputc('\n', stderr); exit(1); } session_workload = 1; } else if (flag == ¶m.wset) { gen[1] = &uri_wset; /* XXX fix * me---somehow */ errno = 0; name = "bad working set size (1st parameter)"; param.wset.num_files = strtoul(optarg, &end, 0); if (end == optarg || errno == ERANGE) goto bad_wset_param; name = "bad target miss rate (2nd parameter)"; if (*end != ',') goto bad_wset_param; optarg = end + 1; param.wset.target_miss_rate = strtod(optarg, &end); if (end == optarg || errno == ERANGE || param.wset.target_miss_rate < 0.0 || param.wset.target_miss_rate > 1.0) goto bad_wset_param; name = "extraneous parameter"; if (*end) { bad_wset_param: fprintf(stderr, "%s: %s in --wset arg (rest: `%s')", prog_name, name, optarg); if (errno) fprintf(stderr, ": %s", strerror(errno)); fputc('\n', stderr); exit(1); } } break; case 'd': #ifdef DEBUG errno = 0; debug_level = strtoul(optarg, &end, 10); if (errno == ERANGE || end == optarg || *end) { fprintf(stderr, "%s: illegal debug level %s\n", prog_name, optarg); exit(1); } #else fprintf(stderr, "%s: sorry, need to recompile with -DDEBUG on...\n", prog_name); #endif break; case 'v': ++verbose; break; case 'V': printf("%s: httperf-" VERSION " compiled " __DATE__ " with" #ifndef DEBUG "out" #endif " DEBUG with" #ifndef TIME_SYSCALLS "out" #endif " TIME_SYSCALLS.\n", prog_name); exit(0); case 'n': ++periodic_stats; break; case 'h': usage(); exit(0); case ':': fprintf(stderr, "%s: parameter missing for option %s\n", prog_name, longopts[longindex].name); exit(1); case '?': /* * Invalid or ambiguous option name or extraneous * parameter. getopt_long () already issued an * explanation to the user, so all we do is call it * quites. */ exit(1); default: fprintf(stderr, "%s: getopt_long: unexpected value (%d)\n", prog_name, ch); exit(1); } } if (param.server != NULL && param.servers != NULL) { fprintf(stderr, "%s: --server S or --servers file\n", prog_name); exit(-1); } if (param.server == NULL && param.servers == NULL) param.server = "localhost"; #ifdef HAVE_SSL if (param.use_ssl) { char buf[1024]; if (param.port < 0) param.port = 443; SSL_library_init (); SSL_load_error_strings (); #if (OPENSSL_VERSION_NUMBER < 0x10100000L) SSLeay_add_all_algorithms (); #endif switch (param.ssl_protocol) { /* 0/auto for highest available */ case 0: #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) ssl_ctx = SSL_CTX_new (TLS_client_method ()); break; #else ssl_ctx = SSL_CTX_new (SSLv23_client_method ()); break; #endif #ifndef OPENSSL_NO_SSL2 /* 2/SSLv2 */ case 2: #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) ssl_ctx = SSL_CTX_new (TLS_client_method ()); #if (OPENSSL_VERSION_NUMBER >= 0x10101000L) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2 | SSL_OP_NO_TLSv1_3); break; #else SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2); break; #endif #else ssl_ctx = SSL_CTX_new (SSLv2_client_method ()); break; #endif #endif #ifndef OPENSSL_NO_SSL3 /* 3/SSLv3 */ case 3: #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) ssl_ctx = SSL_CTX_new (TLS_client_method ()); SSL_CTX_set_min_proto_version(ssl_ctx, SSL3_VERSION); SSL_CTX_set_max_proto_version(ssl_ctx, SSL3_VERSION); break; #else ssl_ctx = SSL_CTX_new (SSLv3_client_method ()); break; #endif #endif /* 4/TLSv1.0 */ case 4: #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) ssl_ctx = SSL_CTX_new (TLS_client_method ()); SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION); SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_VERSION); break; #else ssl_ctx = SSL_CTX_new (TLSv1_client_method ()); break; SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2); break; #endif /* 5/TLSv1.1 */ case 5: #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) ssl_ctx = SSL_CTX_new (TLS_client_method ()); SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_1_VERSION); SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_1_VERSION); break; #else ssl_ctx = SSL_CTX_new (TLSv1_client_method ()); break; SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_2); break; #endif /* 6/TLSv1.2 */ case 6: #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) ssl_ctx = SSL_CTX_new (TLS_client_method ()); SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_2_VERSION); SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_2_VERSION); break; #else ssl_ctx = SSL_CTX_new (TLSv1_client_method ()); break; SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); break; #endif #if (OPENSSL_VERSION_NUMBER >= 0x10101000L) /* 7/TLSv1.3 */ ssl_ctx = SSL_CTX_new (TLS_client_method ()); SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); break; #endif } if (!ssl_ctx) { ERR_print_errors_fp(stderr); exit(-1); } memset(buf, 0, sizeof(buf)); RAND_seed(buf, sizeof(buf)); /* set server certificate verification */ if (param.ssl_verify == 1) SSL_CTX_set_verify (ssl_ctx, SSL_VERIFY_PEER, NULL); else SSL_CTX_set_verify (ssl_ctx, SSL_VERIFY_NONE, NULL); /* set default certificate authority verification path */ SSL_CTX_set_default_verify_paths (ssl_ctx); /* load extra certificate authority files and paths */ if (param.ssl_ca_file || param.ssl_ca_path) { int ssl_err = SSL_CTX_load_verify_locations (ssl_ctx, param.ssl_ca_file, param.ssl_ca_path); if (DBG > 2) fprintf (stderr, "SSL_CTX_load_verify_locations returned %d\n", ssl_err); } /* if using client SSL authentication, load the certificate file */ if (param.ssl_cert) { int ssl_err = SSL_CTX_use_certificate_file (ssl_ctx, param.ssl_cert, SSL_FILETYPE_PEM); if (DBG > 2) fprintf (stderr, "SSL_CTX_use_certificate_file returned %d\n", ssl_err); } /* also load the client key */ if (param.ssl_key) { int ssl_err = SSL_CTX_use_PrivateKey_file (ssl_ctx, param.ssl_key, SSL_FILETYPE_PEM); if (DBG > 2) fprintf (stderr, "SSL_CTX_use_PrivateKey_file returned %d\n", ssl_err); } /* check client certificate and key consistency */ if (param.ssl_cert && param.ssl_key) { int ssl_err = SSL_CTX_check_private_key (ssl_ctx); if (DBG > 2) fprintf (stderr, "SSL_CTX_check_private_key returned %d\n", ssl_err); if (!ssl_err) { fprintf (stderr, "SSL certificate and key failed consistency check\n"); exit (1); } } } #endif if (param.port < 0) param.port = 80; if (param.print_reply || param.print_request) stat[num_stats++] = &stats_print_reply; if (param.session_cookies) { if (!session_workload) { fprintf(stderr, "%s: --session-cookie requires session-oriented " "workload (e.g., --wsess)\n", prog_name); exit(-1); } gen[num_gen++] = &sess_cookie; } if (param.additional_header || param.additional_header_file || param.method) gen[num_gen++] = &misc; /* * echo command invocation for logging purposes: */ printf("%s", prog_name); if (verbose) printf(" --verbose"); switch (param.print_reply) { case 0: break; case PRINT_HEADER: printf(" --print-reply=header"); break; case PRINT_BODY: printf(" --print-reply=body"); break; default: printf(" --print-reply"); break; } switch (param.print_request) { case 0: break; case PRINT_HEADER: printf(" --print-request=header"); break; case PRINT_BODY: printf(" --print-request=body"); break; default: printf(" --print-request"); break; } if (param.hog) printf(" --hog"); if (param.close_with_reset) printf(" --close-with-reset"); if (param.think_timeout > 0) printf(" --think-timeout=%g", param.think_timeout); if (param.timeout > 0) printf(" --timeout=%g", param.timeout); if (param.runtime > 0) printf(" --runtime=%g", param.runtime); printf(" --client=%u/%u", param.client.id, param.client.num_clients); if (param.server) printf(" --server=%s", param.server); if (param.server_name) printf(" --server_name=%s", param.server_name); if (param.servers) printf(" --servers=%s", param.servers); if (param.port) printf(" --port=%d", param.port); if (param.uri) printf(" --uri=%s", param.uri); if (param.failure_status) printf(" --failure-status=%u", param.failure_status); if (param.http_version != 0x10001) printf(" --http-version=%u.%u", param.http_version >> 16, param.http_version & 0xffff); if (param.max_conns) printf(" --max-connections=%lu", param.max_conns); if (param.max_piped) printf(" --max-piped-calls=%lu", param.max_piped); if (param.rate.rate_param > 0.0) { switch (param.rate.dist) { case DETERMINISTIC: /* * for backwards compatibility, continue to use * --rate: */ printf(" --rate=%g", param.rate.rate_param); break; case UNIFORM: printf(" --period=u%g,%g", param.rate.min_iat, param.rate.max_iat); break; case EXPONENTIAL: printf(" --period=e%g", param.rate.mean_iat); break; case VARIABLE: { int m; printf(" --period=v"); for (m = 0; m < param.rate.numRates; m++) { if (m != 0) printf(","); printf("%g,%g", param.rate.iat[m], param.rate.duration[m]); } } break; default: printf("--period=??"); break; } } printf(" --send-buffer=%lu", param.send_buffer_size); if (param.retry_on_failure) printf(" --retry-on-failure"); printf(" --recv-buffer=%lu", param.recv_buffer_size); if (param.session_cookies) printf(" --session-cookies"); #ifdef HAVE_SSL if (param.use_ssl) printf(" --ssl"); if (param.ssl_cipher_list) printf(" --ssl-ciphers=%s", param.ssl_cipher_list); if (!param.ssl_reuse) printf(" --ssl-no-reuse"); if (param.ssl_cert) printf (" --ssl-cert=%s", param.ssl_cert); if (param.ssl_key) printf (" --ssl-key=%s", param.ssl_key); if (param.ssl_ca_file) printf (" --ssl-ca-file=%s", param.ssl_ca_file); if (param.ssl_ca_path) printf (" --ssl-ca-path=%s", param.ssl_ca_path); if (param.ssl_verify) printf (" --ssl-verify"); switch (param.ssl_protocol) { case 0: printf (" --ssl-protocol=auto"); break; #ifndef OPENSSL_NO_SSL2 case 2: printf (" --ssl-protocol=SSLv2"); break; #endif #ifndef OPENSSL_NO_SSL3 case 3: printf (" --ssl-protocol=SSLv3"); break; #endif case 4: printf (" --ssl-protocol=TLSv1.0"); break; case 5: printf (" --ssl-protocol=TLSv1.1"); break; case 6: printf (" --ssl-protocol=TLSv1.2"); break; #if (OPENSSL_VERSION_NUMBER >= 0x10101000L) case 7: printf (" --ssl-protocol=TLSv1.3"); break; #endif } #endif if (param.additional_header) printf(" --add-header='%s'", param.additional_header); if (param.additional_header_file) printf(" --add-header-file='%s'", param.additional_header_file); if (param.method) printf(" --method=%s", param.method); if (param.use_timer_cache) printf(" --use-timer-cache"); if (param.wsesslog.num_sessions) { /* * This overrides any --wsess, --num-conns, --num-calls, * --burst-length and any uri generator */ printf(" --wsesslog=%u,%.3f,%s", param.wsesslog.num_sessions, param.wsesslog.think_time, param.wsesslog.file); } else if (param.wsesspage.num_sessions) { printf(" --wsesspage=%u,%u,%.3f", param.wsesspage.num_sessions, param.wsesspage.num_reqs, param.wsesspage.think_time); } else { if (param.wsess.num_sessions) printf(" --wsess=%u,%u,%.3f", param.wsess.num_sessions, param.wsess.num_calls, param.wsess.think_time); else { if (param.num_conns) printf(" --num-conns=%lu", param.num_conns); if (param.num_calls) printf(" --num-calls=%lu", param.num_calls); } if (param.burst_len != 1) printf(" --burst-length=%lu", param.burst_len); if (param.wset.num_files) printf(" --wset=%u,%.3f", param.wset.num_files, param.wset.target_miss_rate); } if (periodic_stats) printf(" --periodic-stats"); printf("\n"); if (timer_init() == false) { fprintf(stderr, "%s: timer_init(): failed initialization (%d)\n", prog_name, __LINE__); exit(1); } core_init(); signal(SIGINT, (void (*)()) core_exit); for (i = 0; i < num_stats; ++i) (*stat[i]->init) (); for (i = 0; i < num_gen; ++i) (*gen[i]->init) (); /* * Update `now'. This is to keep things accurate even when some of * the initialization routines take a long time to execute. */ timer_now_forced(); /* * ensure that clients sample rates at different times: */ t = (param.client.id + 1.0) * RATE_INTERVAL / param.client.num_clients; arg.l = 0; timer_schedule(perf_sample, arg, t); perf_sample_start = timer_now(); for (i = 0; i < num_gen; ++i) (*gen[i]->start) (); for (i = 0; i < num_stats; ++i) (*stat[i]->start) (); getrusage(RUSAGE_SELF, &test_rusage_start); test_time_start = timer_now(); core_loop(); test_time_stop = timer_now(); getrusage(RUSAGE_SELF, &test_rusage_stop); for (i = 0; i < num_stats; ++i) (*stat[i]->stop) (); for (i = 0; i < num_gen; ++i) (*gen[i]->stop) (); for (i = 0; i < num_stats; ++i) (*stat[i]->dump) (); timer_free_all(); return 0; }