chore: Assorted debugging enhancements

master
Sean McBride 5 years ago
parent c8c7c6a000
commit 92c17d7717

@ -4,7 +4,8 @@ PAGE_SIZE=$(shell getconf PAGESIZE)
# Compiler Settings
CC=clang
CC_OPTIONS = -O3 -flto -g -pthread -D_GNU_SOURCE
# CC_OPTIONS = -O3 -flto -g -pthread -D_GNU_SOURCE
CC_OPTIONS = -O3 -g -pthread -D_GNU_SOURCE
BINARY_NAME=sledgert
@ -35,8 +36,21 @@ CFLAGS += -DDEBUG
# CFLAGS += -DLOG_REQUEST_ALLOCATION
# CFLAGS += -DLOG_PREEMPTION
# CFLAGS += -DLOG_MODULE_LOADING
# This flag dumps totals of incoming requests and outgoing responses, broken out by status code
# family, such as 2XX, 4XX, 5XX. It is useful to debug clients hanging waiting for a response.
# To log, run `call runtime_log_requests_responses()` while in GDB
# CFLAGS += -DLOG_TOTAL_REQS_RESPS
# This flag logs the total number of sandboxes in the various states
# It is useful to debug if sandboxes are "getting caught" in a particular state
# To log, run `call runtime_log_sandbox_states()` while in GDB
# CFLAGS += -DLOG_SANDBOX_TOTALS
# This flag enables an per-worker atomic count of sandbox's local runqueue count in thread local storage
# Useful to debug if sandboxes are "getting caught" or "leaking" while in a local runqueue
# CFLAGS += -DLOG_LOCAL_RUNQUEUE
# System Configuraiton Flags
# Sets a flag equal to the processor architecture

@ -6,9 +6,8 @@
#include "types.h"
#ifdef LOG_TOTAL_REQS_RESPS
#if defined(LOG_TOTAL_REQS_RESPS) || defined(LOG_SANDBOX_TOTALS)
#include <stdatomic.h>
#include "debuglog.h"
#endif
#define LISTENER_THREAD_CORE_ID 0 /* Dedicated Listener Core */
@ -42,6 +41,19 @@ extern _Atomic uint32_t runtime_total_4XX_responses;
extern _Atomic uint32_t runtime_total_5XX_responses;
#endif
#ifdef LOG_SANDBOX_TOTALS
/* Counts to track sanboxes running through state transitions */
extern _Atomic uint32_t runtime_total_freed_requests;
extern _Atomic uint32_t runtime_total_initialized_sandboxes;
extern _Atomic uint32_t runtime_total_runnable_sandboxes;
extern _Atomic uint32_t runtime_total_blocked_sandboxes;
extern _Atomic uint32_t runtime_total_running_sandboxes;
extern _Atomic uint32_t runtime_total_preempted_sandboxes;
extern _Atomic uint32_t runtime_total_returned_sandboxes;
extern _Atomic uint32_t runtime_total_error_sandboxes;
extern _Atomic uint32_t runtime_total_complete_sandboxes;
#endif
/*
* Unitless estimate of the instantaneous fraction of system capacity required to complete all previously
* admitted work. This is used to calculate free capacity as part of admissions control
@ -78,17 +90,3 @@ runtime_is_worker()
return false;
}
#ifdef LOG_TOTAL_REQS_RESPS
static inline void
runtime_log_requests_responses()
{
int64_t total_responses = runtime_total_2XX_responses + runtime_total_4XX_responses
+ runtime_total_5XX_responses;
int64_t outstanding_requests = (int64_t)runtime_total_requests - total_responses;
debuglog("Requests: %u (%ld outstanding)\n\tResponses: %ld\n\t\t2XX: %u\n\t\t4XX: %u\n\t\t5XX: %u\n",
runtime_total_requests, outstanding_requests, total_responses, runtime_total_2XX_responses,
runtime_total_4XX_responses, runtime_total_5XX_responses);
};
#endif

@ -1,7 +1,17 @@
#ifdef LOG_LOCAL_RUNQUEUE
#include <stdatomic.h>
#include <stdint.h>
#endif
#include "local_runqueue.h"
static struct local_runqueue_config local_runqueue;
#ifdef LOG_LOCAL_RUNQUEUE
__thread _Atomic uint32_t local_runqueue_count = 0;
#endif
/* Initializes a concrete implementation of the sandbox request scheduler interface */
void
local_runqueue_initialize(struct local_runqueue_config *config)
@ -17,6 +27,9 @@ void
local_runqueue_add(struct sandbox *sandbox)
{
assert(local_runqueue.add_fn != NULL);
#ifdef LOG_LOCAL_RUNQUEUE
local_runqueue_count++;
#endif
return local_runqueue.add_fn(sandbox);
}
@ -28,6 +41,9 @@ void
local_runqueue_delete(struct sandbox *sandbox)
{
assert(local_runqueue.delete_fn != NULL);
#ifdef LOG_LOCAL_RUNQUEUE
local_runqueue_count--;
#endif
local_runqueue.delete_fn(sandbox);
}

@ -22,10 +22,48 @@ int runtime_epoll_file_descriptor;
double runtime_admitted;
#ifdef LOG_TOTAL_REQS_RESPS
_Atomic uint32_t runtime_total_requests;
_Atomic uint32_t runtime_total_2XX_responses;
_Atomic uint32_t runtime_total_4XX_responses;
_Atomic uint32_t runtime_total_5XX_responses;
_Atomic uint32_t runtime_total_requests = 0;
_Atomic uint32_t runtime_total_2XX_responses = 0;
_Atomic uint32_t runtime_total_4XX_responses = 0;
_Atomic uint32_t runtime_total_5XX_responses = 0;
void
runtime_log_requests_responses()
{
int64_t total_responses = runtime_total_2XX_responses + runtime_total_4XX_responses
+ runtime_total_5XX_responses;
int64_t outstanding_requests = (int64_t)runtime_total_requests - total_responses;
debuglog("Requests: %u (%ld outstanding)\n\tResponses: %ld\n\t\t2XX: %u\n\t\t4XX: %u\n\t\t5XX: %u\n",
runtime_total_requests, outstanding_requests, total_responses, runtime_total_2XX_responses,
runtime_total_4XX_responses, runtime_total_5XX_responses);
};
#endif
#ifdef LOG_SANDBOX_TOTALS
_Atomic uint32_t runtime_total_freed_requests = 0;
_Atomic uint32_t runtime_total_initialized_sandboxes = 0;
_Atomic uint32_t runtime_total_runnable_sandboxes = 0;
_Atomic uint32_t runtime_total_blocked_sandboxes = 0;
_Atomic uint32_t runtime_total_running_sandboxes = 0;
_Atomic uint32_t runtime_total_preempted_sandboxes = 0;
_Atomic uint32_t runtime_total_returned_sandboxes = 0;
_Atomic uint32_t runtime_total_error_sandboxes = 0;
_Atomic uint32_t runtime_total_complete_sandboxes = 0;
/*
* Function intended to be interactively run in a debugger to look at sandbox totals
* via `call runtime_log_sandbox_states()`
*/
void
runtime_log_sandbox_states()
{
debuglog("Initialized: %u\n\tRunnable: %u\n\tBlocked: %u\n\tRunning: %u\n\tPreempted: %u\n\tReturned: "
"%u\n\tError: %u\n\tComplete: %u\n",
runtime_total_initialized_sandboxes, runtime_total_runnable_sandboxes, runtime_total_blocked_sandboxes,
runtime_total_running_sandboxes, runtime_total_preempted_sandboxes, runtime_total_returned_sandboxes,
runtime_total_error_sandboxes, runtime_total_complete_sandboxes);
};
#endif
/******************************************
@ -42,15 +80,6 @@ runtime_initialize(void)
runtime_epoll_file_descriptor = epoll_create1(0);
assert(runtime_epoll_file_descriptor >= 0);
#ifdef LOG_TOTAL_REQS_RESPS
/* Setup Counts */
runtime_total_requests = 0;
runtime_total_2XX_responses = 0;
runtime_total_4XX_responses = 0;
runtime_total_5XX_responses = 0;
#endif
/* Allocate and Initialize the global deque
TODO: Improve to expose variant as a config #Issue 93
*/
@ -85,8 +114,7 @@ listener_thread_reject(int client_socket)
int to_send = strlen(HTTP_RESPONSE_504_SERVICE_UNAVAILABLE);
while (sent < to_send) {
rc = write(client_socket, HTTP_RESPONSE_504_SERVICE_UNAVAILABLE,
strlen(HTTP_RESPONSE_504_SERVICE_UNAVAILABLE));
rc = write(client_socket, &HTTP_RESPONSE_504_SERVICE_UNAVAILABLE[sent], to_send - sent);
if (rc < 0) {
if (errno == EAGAIN) continue;
@ -97,7 +125,6 @@ listener_thread_reject(int client_socket)
#ifdef LOG_TOTAL_REQS_RESPS
runtime_total_5XX_responses++;
runtime_log_requests_responses();
#endif
close:
@ -128,15 +155,31 @@ listener_thread_main(void *dummy)
* Block indefinitely on the epoll file descriptor, waiting on up to a max number of events
* TODO: Is LISTENER_THREAD_MAX_EPOLL_EVENTS actually limited to the max number of modules?
*/
int request_count = epoll_wait(runtime_epoll_file_descriptor, (struct epoll_event *)&epoll_events,
LISTENER_THREAD_MAX_EPOLL_EVENTS, -1);
if (request_count < 0) panic("epoll_wait: %s", strerror(errno));
if (request_count == 0) panic("Unexpectedly returned with epoll_wait timeout not set\n");
int descriptor_count = epoll_wait(runtime_epoll_file_descriptor, (struct epoll_event *)&epoll_events,
LISTENER_THREAD_MAX_EPOLL_EVENTS, -1);
if (descriptor_count < 0) {
if (errno == EINTR) continue;
panic("epoll_wait: %s", strerror(errno));
}
if (descriptor_count == 0) panic("Unexpectedly returned with epoll_wait timeout not set\n");
/* Capture Start Time to calculate absolute deadline */
uint64_t request_arrival_timestamp = __getcycles();
for (int i = 0; i < request_count; i++) {
if (epoll_events[i].events & EPOLLERR) panic("epoll_wait: %s", strerror(errno));
for (int i = 0; i < descriptor_count; i++) {
/* Check Event to determine if epoll returned an error */
if ((epoll_events[i].events & EPOLLERR) == EPOLLERR) {
int error = 0;
socklen_t errlen = sizeof(error);
if (getsockopt(epoll_events[i].data.fd, SOL_SOCKET, SO_ERROR, (void *)&error, &errlen)
== 0) {
panic("epoll_wait: %s\n", strerror(error));
}
assert(0);
};
/* Assumption: We have only registered EPOLLIN events, so we should see no others here */
assert((epoll_events[i].events & EPOLLIN) == EPOLLIN);
/* Unpack module from epoll event */
struct module *module = (struct module *)epoll_events[i].data.ptr;
@ -145,80 +188,73 @@ listener_thread_main(void *dummy)
/* Accept Client Request as a nonblocking socket, saving address information */
struct sockaddr_in client_address;
socklen_t address_length = sizeof(client_address);
int client_socket = accept4(module->socket_descriptor, (struct sockaddr *)&client_address,
&address_length, SOCK_NONBLOCK);
if (client_socket < 0) {
switch (errno) {
/* Note: Assumes EAGAIN and EWOULDBLOCK are identical, as on Linux */
case EWOULDBLOCK: {
/*
* According to accept(2), it is possible that a connection might
* have been removed between us receiving an event via epoll_wait
* and us calling accept. Thus we just want to gracefully ignore the
* epoll event.
*/
#ifdef LOG_EPOLL
debuglog("Encountered an epoll notification for %s that did not actually have "
"an associated request\n",
module->name);
#endif
continue;
}
default:
/*
* Accept as many requests as possible, terminating when we would have blocked
* This inner loop is used in case there are more datagrams than epoll events for some reason
*/
while (true) {
int client_socket = accept4(module->socket_descriptor,
(struct sockaddr *)&client_address, &address_length,
SOCK_NONBLOCK);
if (client_socket < 0) {
if (errno == EWOULDBLOCK || errno == EAGAIN) break;
panic("accept4: %s", strerror(errno));
}
};
/*
* According to accept(2), it is possible that the the sockaddr structure client_address may be
* too small, resulting in data being truncated to fit. The appect call mutates the size value
* to indicate that this is the case.
*/
if (address_length > sizeof(client_address)) {
debuglog("A client address to %s has been truncated because buffer was too small\n",
module->name);
}
/*
* According to accept(2), it is possible that the the sockaddr structure client_address
* may be too small, resulting in data being truncated to fit. The appect call mutates
* the size value to indicate that this is the case.
*/
if (address_length > sizeof(client_address)) {
debuglog("A client address to %s has been truncated because buffer was too "
"small\n",
module->name);
}
#ifdef LOG_TOTAL_REQS_RESPS
runtime_total_requests++;
runtime_log_requests_responses();
runtime_total_requests++;
#endif
/* Perform Admission Control */
/* Perform Admission Control */
uint32_t estimated_execution = perf_window_get_percentile(&module->perf_window, 0.5);
/*
* If this is the first execution, assume a default execution
* TODO: Enhance module specification to provide "seed" value of estimated duration
*/
if (estimated_execution == -1) estimated_execution = 1000;
uint32_t estimated_execution = perf_window_get_percentile(&module->perf_window, 0.5);
/*
* If this is the first execution, assume a default execution
* TODO: Enhance module specification to provide "seed" value of estimated duration
*/
if (estimated_execution == -1) estimated_execution = 1000;
double admissions_estimate = (double)estimated_execution / module->relative_deadline;
double admissions_estimate = (double)estimated_execution / module->relative_deadline;
if (runtime_admitted + admissions_estimate >= runtime_worker_threads_count) {
listener_thread_reject(client_socket);
continue;
}
if (runtime_admitted + admissions_estimate >= runtime_worker_threads_count) {
listener_thread_reject(client_socket);
continue;
}
/* Allocate a Sandbox Request */
struct sandbox_request *sandbox_request =
sandbox_request_allocate(module, module->name, client_socket,
(const struct sockaddr *)&client_address, request_arrival_timestamp,
admissions_estimate);
/* Allocate a Sandbox Request */
struct sandbox_request *sandbox_request =
sandbox_request_allocate(module, module->name, client_socket,
(const struct sockaddr *)&client_address,
request_arrival_timestamp, admissions_estimate);
/* Add to the Global Sandbox Request Scheduler */
global_request_scheduler_add(sandbox_request);
/* Clear the */
epoll_events[i].data.ptr = NULL;
/* Add to work accepted by the runtime */
runtime_admitted += admissions_estimate;
/* Add to the Global Sandbox Request Scheduler */
global_request_scheduler_add(sandbox_request);
/* Add to work accepted by the runtime */
runtime_admitted += admissions_estimate;
#ifdef LOG_ADMISSIONS_CONTROL
debuglog("Runtime Admitted: %f / %u\n", runtime_admitted, runtime_worker_threads_count);
debuglog("Runtime Admitted: %f / %u\n", runtime_admitted, runtime_worker_threads_count);
#endif
}
}
} /* while true */
} /* for loop */
} /* while true */
panic("Listener thread unexpectedly broke loop\n");
}

@ -49,24 +49,24 @@ sandbox_setup_arguments(struct sandbox *sandbox)
/**
* Run the http-parser on the sandbox's request_response_data using the configured settings global
* Success means that a "chunk" was fully parsed, not that parsing of a full request is complete
* @param sandbox the sandbox containing the req_resp data that we want to parse
* @param length The size of the request_response_data that we want to parse
* @param length The size of the data that we want to parse
* @returns 0 on success, -1 on failure
*/
int
sandbox_parse_http_request(struct sandbox *sandbox, size_t length)
sandbox_parse_http_request(struct sandbox *sandbox, size_t length_read)
{
assert(sandbox != NULL);
if (length == 0) return 0;
if (length_read == 0) return 0;
/* Why is our start address sandbox->request_response_data + sandbox->request_response_data_length?
it's like a cursor to keep track of what we've read so far */
size_t size_parsed = http_parser_execute(&sandbox->http_parser, http_parser_settings_get(),
sandbox->request_response_data + sandbox->request_response_data_length,
length);
size_t length_parsed = http_parser_execute(&sandbox->http_parser, http_parser_settings_get(),
sandbox->request_response_data
+ sandbox->request_response_data_length,
length_read);
if (size_parsed != length) return -1;
if (length_parsed != length_read) return -1;
return 0;
}
@ -81,28 +81,32 @@ sandbox_receive_and_parse_client_request(struct sandbox *sandbox)
assert(sandbox->module->max_request_size > 0);
assert(sandbox->request_response_data_length == 0);
int rc;
#ifndef USE_HTTP_UVIO
do {
/* Read from the Socket */
rc = read(sandbox->client_socket_descriptor, sandbox->request_response_data,
sandbox->module->max_request_size);
if (rc < 0) {
if (errno == EAGAIN) continue;
/* Read from the Socket */
int length_read = recv(sandbox->client_socket_descriptor,
&sandbox->request_response_data[sandbox->request_response_data_length],
sandbox->module->max_request_size - sandbox->request_response_data_length, 0);
if (length_read < 0) {
if (errno == EAGAIN) goto eagain;
debuglog("Error reading socket %d - %s\n", sandbox->client_socket_descriptor, strerror(errno));
goto err;
}
/* All other errors */
debuglog("Error reading socket %d - %s\n", sandbox->client_socket_descriptor, strerror(errno));
goto err;
}
/* Parse what we've read */
if (sandbox_parse_http_request(sandbox, rc) == -1) {
debuglog("Error parsing socket %d\n", sandbox->client_socket_descriptor);
goto err;
}
sandbox->request_response_data_length += rc;
} while (rc > 0);
/* Try to parse what we've read */
if (sandbox_parse_http_request(sandbox, length_read) < 0) {
debuglog("Error parsing socket %d\n", sandbox->client_socket_descriptor);
goto err;
}
sandbox->request_response_data_length += length_read;
if (!sandbox->http_request.message_end) goto eagain;
sandbox->request_length = sandbox->request_response_data_length;
#else
rc = uv_read_start((uv_stream_t *)&sandbox->client_libuv_stream,
@ -111,10 +115,7 @@ sandbox_receive_and_parse_client_request(struct sandbox *sandbox)
worker_thread_process_io();
#endif
if (!sandbox->http_request.message_end) goto eagain;
sandbox->request_length = sandbox->request_response_data_length;
rc = 0;
int rc = 0;
done:
return rc;
eagain:
@ -310,11 +311,10 @@ current_sandbox_main(void)
sandbox_open_http(sandbox);
/* Parse the request. Treat EAGAIN as an error after three retries*/
int tries = 0;
/* Parse the request, polling until complete */
do {
rc = sandbox_receive_and_parse_client_request(sandbox);
} while (rc == -EAGAIN && tries < 3);
} while (rc == -EAGAIN);
if (rc < 0) {
error_message = "Unable to receive and parse client request\n";
@ -324,7 +324,7 @@ current_sandbox_main(void)
/* Initialize the module */
struct module *current_module = sandbox_get_module(sandbox);
int argument_count = module_get_argument_count(current_module);
// alloc_linear_memory();
module_initialize_globals(current_module);
module_initialize_memory(current_module);
@ -344,7 +344,6 @@ current_sandbox_main(void)
#ifdef LOG_TOTAL_REQS_RESPS
runtime_total_2XX_responses++;
runtime_log_requests_responses();
#endif
sandbox->response_timestamp = __getcycles();
@ -364,7 +363,7 @@ done:
*/
assert(0);
err:
fprintf(stderr, "%s", error_message);
debuglog("%s", error_message);
assert(sandbox->state == SANDBOX_RUNNING);
int to_send = strlen(HTTP_RESPONSE_400_BAD_REQUEST);
@ -385,7 +384,6 @@ err:
#ifdef LOG_TOTAL_REQS_RESPS
runtime_total_4XX_responses++;
debuglog("At %llu, Sandbox %lu - 4XX\n", __getcycles(), sandbox->request_arrival_timestamp);
runtime_log_requests_responses();
#endif
software_interrupt_disable();
sandbox_close_http(sandbox);
@ -525,6 +523,9 @@ sandbox_set_as_initialized(struct sandbox *sandbox, struct sandbox_request *sand
memcpy(&sandbox->client_address, sandbox_request->socket_address, sizeof(struct sockaddr));
sandbox->state = SANDBOX_INITIALIZED;
#ifdef LOG_SANDBOX_TOTALS
runtime_total_initialized_sandboxes++;
#endif
}
/**
@ -557,10 +558,18 @@ sandbox_set_as_runnable(struct sandbox *sandbox, sandbox_state_t last_state)
switch (last_state) {
case SANDBOX_INITIALIZED: {
sandbox->initializing_duration += duration_of_last_state;
#ifdef LOG_SANDBOX_TOTALS
runtime_total_initialized_sandboxes--;
runtime_total_runnable_sandboxes++;
#endif
break;
}
case SANDBOX_BLOCKED: {
sandbox->blocked_duration += duration_of_last_state;
#ifdef LOG_SANDBOX_TOTALS
runtime_total_blocked_sandboxes--;
runtime_total_runnable_sandboxes++;
#endif
break;
}
default: {
@ -605,10 +614,18 @@ sandbox_set_as_running(struct sandbox *sandbox, sandbox_state_t last_state)
switch (last_state) {
case SANDBOX_RUNNABLE: {
sandbox->runnable_duration += duration_of_last_state;
#ifdef LOG_SANDBOX_TOTALS
runtime_total_runnable_sandboxes--;
runtime_total_running_sandboxes++;
#endif
break;
}
case SANDBOX_PREEMPTED: {
sandbox->preempted_duration += duration_of_last_state;
#ifdef LOG_SANDBOX_TOTALS
runtime_total_preempted_sandboxes--;
runtime_total_running_sandboxes++;
#endif
break;
}
default: {
@ -650,6 +667,10 @@ sandbox_set_as_preempted(struct sandbox *sandbox, sandbox_state_t last_state)
switch (last_state) {
case SANDBOX_RUNNING: {
sandbox->running_duration += duration_of_last_state;
#ifdef LOG_SANDBOX_TOTALS
runtime_total_running_sandboxes--;
runtime_total_preempted_sandboxes++;
#endif
break;
}
default: {
@ -689,6 +710,10 @@ sandbox_set_as_blocked(struct sandbox *sandbox, sandbox_state_t last_state)
case SANDBOX_RUNNING: {
sandbox->running_duration += duration_of_last_state;
local_runqueue_delete(sandbox);
#ifdef LOG_SANDBOX_TOTALS
runtime_total_running_sandboxes--;
runtime_total_blocked_sandboxes++;
#endif
break;
}
default: {
@ -732,6 +757,10 @@ sandbox_set_as_returned(struct sandbox *sandbox, sandbox_state_t last_state)
sandbox->running_duration += duration_of_last_state;
local_runqueue_delete(sandbox);
sandbox_free_linear_memory(sandbox);
#ifdef LOG_SANDBOX_TOTALS
runtime_total_running_sandboxes--;
runtime_total_returned_sandboxes++;
#endif
break;
}
default: {
@ -774,10 +803,18 @@ sandbox_set_as_error(struct sandbox *sandbox, sandbox_state_t last_state)
case SANDBOX_SET_AS_INITIALIZED:
/* Technically, this is a degenerate sandbox that we generate by hand */
sandbox->initializing_duration += duration_of_last_state;
#ifdef LOG_SANDBOX_TOTALS
runtime_total_initialized_sandboxes--;
runtime_total_error_sandboxes++;
#endif
break;
case SANDBOX_RUNNING: {
sandbox->running_duration += duration_of_last_state;
local_runqueue_delete(sandbox);
#ifdef LOG_SANDBOX_TOTALS
runtime_total_running_sandboxes--;
runtime_total_error_sandboxes++;
#endif
break;
}
default: {
@ -829,6 +866,10 @@ sandbox_set_as_complete(struct sandbox *sandbox, sandbox_state_t last_state)
case SANDBOX_RETURNED: {
sandbox->completion_timestamp = now;
sandbox->returned_duration += duration_of_last_state;
#ifdef LOG_SANDBOX_TOTALS
runtime_total_returned_sandboxes--;
runtime_total_complete_sandboxes++;
#endif
break;
}
default: {
@ -897,6 +938,9 @@ sandbox_allocate(struct sandbox_request *sandbox_request)
/* Set state to initializing */
sandbox_set_as_initialized(sandbox, sandbox_request, now);
#ifdef LOG_SANDBOX_TOTALS
runtime_total_freed_requests++;
#endif
free(sandbox_request);
done:
return sandbox;

@ -1,4 +1,4 @@
#!/bin/bash
# fib(20)
# Perhaps this can be improved to pass a body without an additional file
ab -n 100000 -c 100 -p client1_body.txt -v 4 -r localhost:10000/
ab -n 100000 -c 64 -s 999999999 -p client1_body.txt -v 4 -r localhost:10000/

@ -1,4 +1,4 @@
#!/bin/bash
# fib(10)
# Perhaps this can be improved to pass a body without an additional file
ab -n 100000 -c 100 -p client2_body.txt -v 4 -r localhost:10001/
ab -n 100000 -c 64 -s 999999999 -p client2_body.txt -v 4 -r localhost:10001/

Loading…
Cancel
Save