diff --git a/scripts/json.lua b/scripts/json.lua new file mode 100644 index 0000000..f5022a2 --- /dev/null +++ b/scripts/json.lua @@ -0,0 +1,42 @@ +-- example reporting script which demonstrates a custom +-- done() function that prints results as JSON + +done = function(summary, latency, requests) + -- io.write("\nJSON Output:\n") + io.write("{\n") + io.write(string.format("\t\"requests\": %d,\n", summary.requests)) + io.write(string.format("\t\"duration_in_microseconds\": %0.2f,\n", summary.duration)) + io.write(string.format("\t\"bytes\": %d,\n", summary.bytes)) + io.write(string.format("\t\"requests_per_sec\": %0.2f,\n", (summary.requests/summary.duration)*1e6)) + io.write(string.format("\t\"bytes_transfer_per_sec\": %0.2f,\n", (summary.bytes/summary.duration)*1e6)) + io.write(string.format("\t Min: %0.2f,\n", latency.min)) + io.write(string.format("\t Avg: %0.2f,\n", latency.mean)) + io.write(string.format("\t Max: %0.2f,\n", latency.max)) + io.write(string.format("\t StdDev: %0.2f,\n", latency.stdev)) + + + -- print(string.format("Total Requests: %d", summary.requests)) + -- print(string.format("HTTP errors: %d", summary.errors.status)) + -- print(string.format("Requests timed out: %d", summary.errors.timeout)) + -- print(string.format("Bytes received: %d", summary.bytes)) + -- print(string.format("Socket connect errors: %d", summary.errors.connect)) + -- print(string.format("Socket read errors: %d", summary.errors.read)) + -- print(string.format("Socket write errors: %d", summary.errors.write)) + + io.write("\t\"Percentiles\": [\n") + for _, p in pairs({ 50, 75, 90, 99, 99.9, 99.99, 99.999, 100 }) do + io.write("\t\t{\n") + --print(latency.total_count(50)) + n = latency:percentile(p) + -- k = latency:total_count(p) + -- io.write(string.format("\t\t\t\"SAKOO\": %s,\n", latency:percentile(p))) + + io.write(string.format("Percent: %g,\nValue: %f\n", p, n*0.001)) + if p == 100 then + io.write("\t\t}\n") + else + io.write("\t\t},\n") + end + end + io.write("\t]\n}\n") + end \ No newline at end of file diff --git a/scripts/multi-server.lua b/scripts/multi-server.lua deleted file mode 100644 index 09eccb4..0000000 --- a/scripts/multi-server.lua +++ /dev/null @@ -1,145 +0,0 @@ --- This script extends wrk2 to handle multiple server addresses, and multiple --- paths on each server. - -local threads = {} -local counter = 1 - --- Global context - -function setup(thread) - table.insert(threads, thread) - thread:set("id",counter) - counter = counter +1 - math.randomseed(os.time()) - math.random(); math.random(); math.random() -end - --- Thread context - -function thread_next_server() - idx = math.random(#reqs) - local next_addr = string.format("%s", addrs[idx]) - if curr_addr ~= "" and next_addr ~= curr_addr then - wrk.thread.addr = addrs[idx] - end - curr_addr = next_addr -end - - -function init(args) - urls = "" - counts = "" - reqs = {""} - c_resps = 0 - c_requs = 0 - addrs = {} - curr_addr = "" - idx = 0 - - if 2 > #args then - --print("Usage: wrk ") - os.exit(1) - end - - -- the dash ("-") is magic in lua, so we need to escape it. - local match = string.gsub(args[1], '-', '%%%-') - local count = tonumber(args[2],10) - local p = wrk.port - if not p then p="" else p=":" .. p end - - local paths={} - for i=3, #args, 1 do - if "/" == args[i]:sub(1,1) then - paths[i-2] = args[i] - else - paths[i-2] = "/" .. args[i] - end - end - if 0 == #paths then - paths[1] = "/" - end - - for i=1, count, 1 do - local repl = string.gsub(match, "-[0-9]+$", string.format("-%d",i)) - local host = string.gsub(wrk.host, match, repl) - for j=1, #paths, 1 do - local idx = (i-1) * #paths + j - for k, v in ipairs(wrk.lookup(host, wrk.port or "http")) do - addrs[idx] = v; break - end - reqs[idx] = string.format( - "GET %s HTTP/1.1\r\n" - .. "Host:%s%s\r\n\r\n", - paths[j], host, p) - urls = string.format("%s,(%s) %s://%s%s%s", - urls, addrs[idx], wrk.scheme, host, p, paths[j]) - end - end - - urls = urls .. "," - thread_next_server() -end - - -function request() - local ret = reqs[idx] - - if counts == "" then - counts = tostring(idx) - else - counts = string.format("%s,%s", counts, tostring(idx)) - end - - c_requs = c_requs + 1 - return ret -end - - -function response(status, headers, body) - c_resps = c_resps + 1 - thread_next_server() -end - --- Global context - -function done(summary, latency, requests) - print(string.format("Total Requests: %d", summary.requests)) - print(string.format("HTTP errors: %d", summary.errors.status)) - print(string.format("Requests timed out: %d", summary.errors.timeout)) - print(string.format("Bytes received: %d", summary.bytes)) - print(string.format("Socket connect errors: %d", summary.errors.connect)) - print(string.format("Socket read errors: %d", summary.errors.read)) - print(string.format("Socket write errors: %d", summary.errors.write)) - - -- generate table of URLs from first thread's string (all threads use same list) - local urls = {} - local c_requs = 0 - local c_resps = 0 - t = unpack(threads,1,2) - t:get("urls"):gsub("([^,]+),", function(u) table.insert(urls, u) end) - - local counts = {} - for i=1, #urls, 1 do - counts[i] = 0 - end - - -- fetch url call counts of individual threads - local c = t:get("counts") - c = c .. "," - for i, t in ipairs(threads) do - c:gsub("([0-9]+),", function(s) - i = tonumber(s) - counts[i] = counts[i] + 1 - end) - c_requs = c_requs + t:get("c_requs") - c_resps = c_resps + t:get("c_resps") - end - - print(string.format("total requests issued:%d, responses received within runtime:%d", c_requs, c_resps)) - - print("\nURL call count") - for i=1, #urls, 1 do - print(string.format("%s %d", urls[i], counts[i])) - end - -end diff --git a/scripts/multiple-endpoints_in_json.lua b/scripts/multiple-endpoints_in_json.lua new file mode 100644 index 0000000..6914a9d --- /dev/null +++ b/scripts/multiple-endpoints_in_json.lua @@ -0,0 +1,184 @@ +-- This script extends wrk2 to handle multiple server addresses +-- as well as multiple paths (endpoints) per server + + ----------------- +-- main() context + + -- main() globals + local threads = {} + local counter = 1 + + function setup(thread) + -- Fill global threads table with thread handles so done() + -- can process per-thread data + table.insert(threads, thread) + thread:set("id",counter) + counter = counter +1 + math.randomseed(os.time()) + end + + ----------------- + -- Thread context + + function xtract(str, match, default, err_msg) + local ret, count = string.gsub(str, match, "%1", 1) + if count == 0 then + if not default then + -- print(string.format("Error parsing URL '%s': %s",str,err_msg)) + os.exit(1) + end + ret = default + end + return ret + end + + function init(args) + -- Thread globals used by done() + called_idxs = "" + urls = "" + -- Thread globals used by request(), response() + addrs = {} + idx = 0 + + -- table of lists; per entry: + -- proto, host, hostaddr, port, path + params + endpoints={} + -- tablre of prepared HTTP requests for endpoints above + reqs={} + + -- parse command line URLs and prepare requests + for i=0, #args, 1 do + -- note that URL parsing does not support user/pass as + -- wrk2 does not support auth + local proto = xtract(args[i], + "^(http[s]?)://.*", nil, "missing or unsupported protocol") + local host = xtract( + args[i], "^http[s]?://([^/:]+)[:/]?.*", nil, "missing host") + local port = xtract(args[i], "^http[s]?://[^/:]+:(%d+).*", 80) + local path = xtract(args[i], "^http[s]?://[^/]+(/.*)","/") + + -- get IP addr(s) from hostname, validate by connecting + local addr = nil + for k, v in ipairs(wrk.lookup(host, port)) do + if wrk.connect(v) then + addr = v + break + end + end + if not addr then + print(string.format( + "Error: Unable to connect to %s port %s.", host, port)) + os.exit(2) + end + + -- store the endpoint + endpoints[i] = {} + endpoints[i][0] = proto + endpoints[i][1] = host + endpoints[i][2] = addr + endpoints[i][3] = port + endpoints[i][4] = path + endpoints[i][5] = string.format( + "GET %s HTTP/1.1\r\nHost:%s:%s\r\n\r\n", path, host, port) + if urls == "" then + urls = args[i] + else + urls = string.format("%s,%s",urls,args[i]) + end + end + + urls = urls .. "," + -- initialize idx, assign req and addr + idx = math.random(0, #endpoints) + wrk.thread.addr = endpoints[idx][2] + end + + function request() + local ret = endpoints[idx][5] + return ret + end + + + function response(status, headers, body) + -- add current index to string of endpointsi calle + local c = "," + if called_idxs == "" then c="" end + called_idxs = string.format("%s%s%s",called_idxs,c,idx) + + -- Pick a new random endpoint for the next request + -- Also, update the thread's remote server addr if endpoint + -- is on a different server. + local prev_srv = endpoints[idx][2] + idx = math.random(0, #endpoints) + if prev_srv ~= endpoints[idx][2] then + -- Re-setting the thread's server address forces a reconnect + wrk.thread.addr = endpoints[idx][2] + end + end + + ----------------- + -- main() context + + function done(summary, latency, requests) + -- print(string.format("Total Requests: %d", summary.requests)) + -- print(string.format("HTTP errors: %d", summary.errors.status)) + -- print(string.format("Requests timed out: %d", summary.errors.timeout)) + -- print(string.format("Bytes received: %d", summary.bytes)) + -- print(string.format("Socket connect errors: %d", summary.errors.connect)) + -- print(string.format("Socket read errors: %d", summary.errors.read)) + -- print(string.format("Socket write errors: %d", summary.errors.write)) + -- generate table of URL strings from first thread's endpoints table + -- (all threads generate the same table in init()) + io.write("{\n") + -- io.write(string.format("\"TotalRequests\": \"%d\",\n", summary.requests)) + io.write(string.format("\"DurationInMicroseconds\": \"%0.2f\",\n", summary.duration)) + io.write(string.format("\"Bytes\": \"%d\",\n", summary.bytes)) + io.write(string.format("\"RequestsPerSec\": \"%0.2f\",\n", (summary.requests/summary.duration)*1e6)) + io.write(string.format("\"BytesTransferPerSec\": \"%0.2f\",\n", (summary.bytes/summary.duration)*1e6)) + io.write(string.format("\"MinLatency\": \"%0.2f\",\n", latency.min)) + io.write(string.format("\"AvgLatency\": \"%0.2f\",\n", latency.mean)) + io.write(string.format("\"MaxLatency\": \"%0.2f\",\n", latency.max)) + io.write(string.format("\"StdDev\": \"%0.2f\",\n", latency.stdev)) + + local urls = {} + local counts = {} + local i = 0 + t = unpack(threads,1,2) + t:get("urls"):gsub("([^,]+),", + function(u) + urls[i]=u + counts[i] = 0 + i = i+1 + end) + + -- fetch url call counts of individual threads + local c = t:get("called_idxs") + c = c .. "," + for i, t in ipairs(threads) do + c:gsub("([0-9]+),", function(s) + i = tonumber(s) + counts[i] = counts[i] + 1 + end) + end + for i=0, #urls, 1 do + print(string.format("\"Url_".. i+1 .."\": \"%s\",\n\"UrlRequestCount_".. i+1 .."\": %d,", urls[i], counts[i])) + end + + + io.write("\"Percentiles\": [\n") + for _, p in pairs({ 50, 75, 90, 99, 99.9, 99.99, 99.999, 100 }) do + io.write("\t\t{\n") + --print(latency.total_count(50)) + n = latency:percentile(p) + -- k = latency:total_count(p) + -- io.write(string.format("\t\t\t\"SAKOO\": %s,\n", latency:percentile(p))) + + io.write(string.format("\"Percent\": \"%g\",\n\"Value\": \"%f\"\n", p, n*0.001)) + if p == 100 then + io.write("\t\t}\n") + else + io.write("\t\t},\n") + end + end + io.write("\t]\n}\n") + end \ No newline at end of file diff --git a/src/hdr_histogram.c b/src/hdr_histogram.c index 57c6324..d8ae50a 100644 --- a/src/hdr_histogram.c +++ b/src/hdr_histogram.c @@ -350,6 +350,7 @@ int64_t hdr_value_at_percentile(struct hdr_histogram* h, double percentile) if (total >= count_at_percentile) { int64_t value_from_index = iter.value_from_index; + printf("\"Count\": \"%lld\",\n",count_at_percentile); return highest_equivalent_value(h, value_from_index); } } diff --git a/src/wrk.c b/src/wrk.c index 42d1483..7dd08c7 100644 --- a/src/wrk.c +++ b/src/wrk.c @@ -92,7 +92,7 @@ int main(int argc, char **argv) { if (!strncmp("https", schema, 5)) { if ((cfg.ctx = ssl_init()) == NULL) { - fprintf(stderr, "unable to initialize SSL\n"); + //f//printf(stderr, "unable to initialize SSL\n"); ERR_print_errors_fp(stderr); exit(1); } @@ -118,7 +118,7 @@ int main(int argc, char **argv) { lua_State *L = script_create(cfg.script, url, headers); if (!script_resolve(L, host, service)) { char *msg = strerror(errno); - fprintf(stderr, "unable to connect to %s:%s %s\n", host, service, msg); + //f//printf(stderr, "unable to connect to %s:%s %s\n", host, service, msg); exit(1); } @@ -149,7 +149,7 @@ int main(int argc, char **argv) { if (!t->loop || pthread_create(&t->thread, NULL, &thread_main, t)) { char *msg = strerror(errno); - fprintf(stderr, "unable to create thread %"PRIu64": %s\n", i, msg); + //f//printf(stderr, "unable to create thread %"PRIu64": %s\n", i, msg); exit(2); } } @@ -162,9 +162,9 @@ int main(int argc, char **argv) { sigaction(SIGINT, &sa, NULL); char *time = format_time_s(cfg.duration); - printf("Running %s test @ %s\n", time, url); - printf(" %"PRIu64" threads and %"PRIu64" connections\n", - cfg.threads, cfg.connections); + //printf("Running %s test @ %s\n", time, url); + //printf(" %"PRIu64" threads and %"PRIu64" connections\n", + // cfg.threads, cfg.connections); uint64_t start = time_us(); uint64_t complete = 0; @@ -207,39 +207,39 @@ int main(int argc, char **argv) { latency_stats->max = hdr_max(latency_histogram); latency_stats->histogram = latency_histogram; - print_stats_header(); - print_stats("Latency", latency_stats, format_time_us); - print_stats("Req/Sec", statistics.requests, format_metric); + // print_stats_header(); + // print_stats("Latency", latency_stats, format_time_us); + print_stats("Req/Sec", statistics.requests, format_metric); // if (cfg.latency) print_stats_latency(latency_stats); if (cfg.latency) { - print_hdr_latency(latency_histogram, - "Recorded Latency"); - printf("----------------------------------------------------------\n"); + // print_hdr_latency(latency_histogram, + // "Recorded Latency"); + ////printf("----------------------------------------------------------\n"); } if (cfg.u_latency) { - printf("\n"); - print_hdr_latency(u_latency_histogram, - "Uncorrected Latency (measured without taking delayed starts into account)"); - printf("----------------------------------------------------------\n"); + // //printf("\n"); + // print_hdr_latency(u_latency_histogram, + // "Uncorrected Latency (measured without taking delayed starts into account)"); + // //printf("----------------------------------------------------------\n"); } char *runtime_msg = format_time_us(runtime_us); - printf(" %"PRIu64" requests in %s, %sB read\n", - complete, runtime_msg, format_binary(bytes)); + //printf(" %"PRIu64" requests in %s, %sB read\n", + // complete, runtime_msg, format_binary(bytes)); if (errors.connect || errors.read || errors.write || errors.timeout) { - printf(" Socket errors: connect %d, read %d, write %d, timeout %d\n", - errors.connect, errors.read, errors.write, errors.timeout); + //printf(" Socket errors: connect %d, read %d, write %d, timeout %d\n", + // errors.connect, errors.read, errors.write, errors.timeout); } if (errors.status) { - printf(" Non-2xx or 3xx responses: %d\n", errors.status); + // //printf(" Non-2xx or 3xx responses: %d\n", errors.status); } - printf("Requests/sec: %9.2Lf\n", req_per_s); - printf("Transfer/sec: %10sB\n", format_binary(bytes_per_s)); + // //printf("Requests/sec: %9.2Lf\n", req_per_s); + // //printf("Transfer/sec: %10sB\n", format_binary(bytes_per_s)); if (script_has_done(L)) { script_summary(L, runtime_us, complete, bytes); @@ -372,9 +372,9 @@ static int calibrate(aeEventLoop *loop, long long id, void *data) { thread->interval = interval; thread->requests = 0; - printf(" Thread calibration: mean lat.: %.3fms, rate sampling interval: %dms\n", - (thread->mean)/1000.0, - thread->interval); + //printf(" Thread calibration: mean lat.: %.3fms, rate sampling interval: %dms\n", + // (thread->mean)/1000.0, + // thread->interval); aeCreateTimeEvent(loop, thread->interval, sample_rate, thread, NULL); @@ -534,25 +534,25 @@ static int response_complete(http_parser *parser) { int64_t expected_latency_timing = now - expected_latency_start; if (expected_latency_timing < 0) { - printf("\n\n ---------- \n\n"); - printf("We are about to crash and die (recoridng a negative #)"); - printf("This wil never ever ever happen..."); - printf("But when it does. The following information will help in debugging"); - printf("response_complete:\n"); - printf(" expected_latency_timing = %lld\n", expected_latency_timing); - printf(" now = %lld\n", now); - printf(" expected_latency_start = %lld\n", expected_latency_start); - printf(" c->thread_start = %lld\n", c->thread_start); - printf(" c->complete = %lld\n", c->complete); - printf(" throughput = %g\n", c->throughput); - printf(" latest_should_send_time = %lld\n", c->latest_should_send_time); - printf(" latest_expected_start = %lld\n", c->latest_expected_start); - printf(" latest_connect = %lld\n", c->latest_connect); - printf(" latest_write = %lld\n", c->latest_write); + //printf("\n\n ---------- \n\n"); + //printf("We are about to crash and die (recoridng a negative #)"); + //printf("This wil never ever ever happen..."); + //printf("But when it does. The following information will help in debugging"); + //printf("response_complete:\n"); + //printf(" expected_latency_timing = %lld\n", expected_latency_timing); + //printf(" now = %lld\n", now); + //printf(" expected_latency_start = %lld\n", expected_latency_start); + //printf(" c->thread_start = %lld\n", c->thread_start); + //printf(" c->complete = %lld\n", c->complete); + //printf(" throughput = %g\n", c->throughput); + //printf(" latest_should_send_time = %lld\n", c->latest_should_send_time); + //printf(" latest_expected_start = %lld\n", c->latest_expected_start); + //printf(" latest_connect = %lld\n", c->latest_connect); + //printf(" latest_write = %lld\n", c->latest_write); expected_latency_start = c->thread_start + ((c->complete ) / c->throughput); - printf(" next expected_latency_start = %lld\n", expected_latency_start); + //printf(" next expected_latency_start = %lld\n", expected_latency_start); } c->latest_should_send_time = 0; @@ -770,8 +770,8 @@ static int parse_args(struct config *cfg, char **url, struct http_parser_url *pa if (scan_metric(optarg, &cfg->rate)) return -1; break; case 'v': - printf("wrk %s [%s] ", VERSION, aeGetApiName()); - printf("Copyright (C) 2012 Will Glozer\n"); + //printf("wrk %s [%s] ", VERSION, aeGetApiName()); + //printf("Copyright (C) 2012 Will Glozer\n"); break; case 'h': case '?': @@ -784,18 +784,18 @@ static int parse_args(struct config *cfg, char **url, struct http_parser_url *pa if (optind == argc || !cfg->threads || !cfg->duration) return -1; if (!script_parse_url(argv[optind], parts)) { - fprintf(stderr, "invalid URL: %s\n", argv[optind]); + //f//printf(stderr, "invalid URL: %s\n", argv[optind]); return -1; } if (!cfg->connections || cfg->connections < cfg->threads) { - fprintf(stderr, "number of connections must be >= threads\n"); + //f//printf(stderr, "number of connections must be >= threads\n"); return -1; } if (cfg->rate == 0) { - fprintf(stderr, - "Throughput MUST be specified with the --rate or -R option\n"); + //f//printf(stderr, + // "Throughput MUST be specified with the --rate or -R option\n"); return -1; } @@ -806,7 +806,7 @@ static int parse_args(struct config *cfg, char **url, struct http_parser_url *pa } static void print_stats_header() { - printf(" Thread Stats%6s%11s%8s%12s\n", "Avg", "Stdev", "Max", "+/- Stdev"); + //printf(" Thread Stats%6s%11s%8s%12s\n", "Avg", "Stdev", "Max", "+/- Stdev"); } static void print_units(long double n, char *(*fmt)(long double), int width) { @@ -817,7 +817,7 @@ static void print_units(long double n, char *(*fmt)(long double), int width) { if (isalpha(msg[len-2])) pad--; width -= pad; - printf("%*.*s%.*s", width, width, msg, pad, " "); + //printf("%*.*s%.*s", width, width, msg, pad, " "); free(msg); } @@ -827,35 +827,35 @@ static void print_stats(char *name, stats *stats, char *(*fmt)(long double)) { long double mean = stats_summarize(stats); long double stdev = stats_stdev(stats, mean); - printf(" %-10s", name); + //printf(" %-10s", name); print_units(mean, fmt, 8); print_units(stdev, fmt, 10); print_units(max, fmt, 9); - printf("%8.2Lf%%\n", stats_within_stdev(stats, mean, stdev, 1)); + //printf("%8.2Lf%%\n", stats_within_stdev(stats, mean, stdev, 1)); } static void print_hdr_latency(struct hdr_histogram* histogram, const char* description) { long double percentiles[] = { 50.0, 75.0, 90.0, 99.0, 99.9, 99.99, 99.999, 100.0}; - printf(" Latency Distribution (HdrHistogram - %s)\n", description); + //printf(" Latency Distribution (HdrHistogram - %s)\n", description); for (size_t i = 0; i < sizeof(percentiles) / sizeof(long double); i++) { long double p = percentiles[i]; int64_t n = hdr_value_at_percentile(histogram, p); - printf("%7.3Lf%%", p); + //printf("%7.3Lf%%", p); print_units(n, format_time_us, 10); - printf("\n"); + //printf("\n"); } - printf("\n%s\n", " Detailed Percentile spectrum:"); + //printf("\n%s\n", " Detailed Percentile spectrum:"); hdr_percentiles_print(histogram, stdout, 5, 1000.0, CLASSIC); } static void print_stats_latency(stats *stats) { long double percentiles[] = { 50.0, 75.0, 90.0, 99.0, 99.9, 99.99, 99.999, 100.0 }; - printf(" Latency Distribution\n"); + //printf(" Latency Distribution\n"); for (size_t i = 0; i < sizeof(percentiles) / sizeof(long double); i++) { long double p = percentiles[i]; uint64_t n = stats_percentile(stats, p); - printf("%7.3Lf%%", p); + //printf("%7.3Lf%%", p); print_units(n, format_time_us, 10); - printf("\n"); + //printf("\n"); } }