summaryrefslogtreecommitdiffstats
path: root/src/resperf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/resperf.c')
-rw-r--r--src/resperf.c169
1 files changed, 120 insertions, 49 deletions
diff --git a/src/resperf.c b/src/resperf.c
index 97c998d..ed0ccc5 100644
--- a/src/resperf.c
+++ b/src/resperf.c
@@ -52,12 +52,13 @@
#define DEFAULT_SERVER_NAME "127.0.0.1"
#define DEFAULT_SERVER_PORT 53
-#define DEFAULT_SERVER_TLS_PORT 853
-#define DEFAULT_SERVER_PORTS "udp/tcp 53 or dot/tls 853"
+#define DEFAULT_SERVER_DOT_PORT 853
+#define DEFAULT_SERVER_PORTS "udp/tcp 53 or DoT 853"
#define DEFAULT_LOCAL_PORT 0
#define DEFAULT_SOCKET_BUFFER 32
#define DEFAULT_TIMEOUT 45
#define DEFAULT_MAX_OUTSTANDING (64 * 1024)
+#define DEFAULT_MAX_FALL_BEHIND 1000
#define MAX_INPUT_DATA (64 * 1024)
@@ -72,6 +73,8 @@ typedef perf_list(struct query_info) query_list;
typedef struct query_info {
uint64_t sent_timestamp;
+ bool is_inprogress;
+
/*
* This link links the query into the list of outstanding
* queries or the list of available query IDs.
@@ -88,11 +91,11 @@ static query_list instanding_list;
static query_info* queries;
-static perf_sockaddr_t server_addr;
-static perf_sockaddr_t local_addr;
-static unsigned int nsocks;
-static struct perf_net_socket* socks;
-static enum perf_net_mode mode;
+static perf_sockaddr_t server_addr;
+static perf_sockaddr_t local_addr;
+static unsigned int nsocks;
+static struct perf_net_socket** socks;
+static enum perf_net_mode mode;
static int dummypipe[2];
@@ -143,6 +146,7 @@ static uint64_t num_queries_outstanding;
static uint64_t num_responses_received;
static uint64_t num_queries_timed_out;
static uint64_t rcodecounts[16];
+static uint64_t num_reconnections;
static uint64_t time_now;
static uint64_t time_of_program_start;
@@ -164,6 +168,9 @@ typedef struct {
int responses;
int failures;
double latency_sum;
+
+ int connections;
+ double conn_latency_sum;
} ramp_bucket;
/* Pointer to array of n_buckets ramp_bucket structures */
@@ -192,7 +199,8 @@ static uint64_t sustain_phase_began, wait_phase_began;
static perf_tsigkey_t* tsigkey;
-static bool verbose;
+static bool verbose;
+static unsigned int max_fall_behind;
const char* progname = "resperf";
@@ -205,6 +213,9 @@ stringify(double value, int precision)
return buf;
}
+static void perf__net_event(struct perf_net_socket* sock, perf_socket_event_t event, uint64_t elapsed_time);
+static void perf__net_sent(struct perf_net_socket* sock, uint16_t qid);
+
static void
setup(int argc, char** argv)
{
@@ -232,11 +243,12 @@ setup(int argc, char** argv)
nsocks = 1;
mode = sock_udp;
verbose = false;
+ max_fall_behind = DEFAULT_MAX_FALL_BEHIND;
perf_opt_add('f', perf_opt_string, "family",
"address family of DNS transport, inet or inet6", "any",
&family);
- perf_opt_add('M', perf_opt_string, "mode", "set transport mode: udp, tcp or dot/tls", "udp", &_mode);
+ perf_opt_add('M', perf_opt_string, "mode", "set transport mode: udp, tcp or dot", "udp", &_mode);
perf_opt_add('s', perf_opt_string, "server_addr",
"the server to query", DEFAULT_SERVER_NAME, &server_name);
perf_opt_add('p', perf_opt_port, "port",
@@ -280,7 +292,7 @@ setup(int argc, char** argv)
"the maximum acceptable query loss, in percent",
stringify(max_loss_percent, 0), &max_loss_percent);
perf_opt_add('C', perf_opt_uint, "clients",
- "the number of clients to act as", NULL, &nsocks);
+ "the number of clients to act as", stringify(1, 0), &nsocks);
perf_opt_add('q', perf_opt_uint, "num_outstanding",
"the maximum number of queries outstanding",
stringify(DEFAULT_MAX_OUTSTANDING, 0), &max_outstanding);
@@ -289,6 +301,10 @@ setup(int argc, char** argv)
NULL, &verbose);
bool log_stdout = false;
perf_opt_add('W', perf_opt_boolean, NULL, "log warnings and errors to stdout instead of stderr", NULL, &log_stdout);
+ bool reopen_datafile = false;
+ perf_opt_add('R', perf_opt_boolean, NULL, "reopen datafile on end, allow for infinit use of it", NULL, &reopen_datafile);
+ perf_opt_add('F', perf_opt_zpint, "fall_behind", "the maximum number of queries that is allowed to fall behind, zero to disable",
+ stringify(DEFAULT_MAX_FALL_BEHIND, 0), &max_fall_behind);
perf_opt_parse(argc, argv);
@@ -300,7 +316,7 @@ setup(int argc, char** argv)
mode = perf_net_parsemode(_mode);
if (!server_port) {
- server_port = mode == sock_tls ? DEFAULT_SERVER_TLS_PORT : DEFAULT_SERVER_PORT;
+ server_port = mode == sock_dot ? DEFAULT_SERVER_DOT_PORT : DEFAULT_SERVER_PORT;
}
if (max_outstanding > nsocks * DEFAULT_MAX_OUTSTANDING)
@@ -330,6 +346,9 @@ setup(int argc, char** argv)
local_port, &local_addr);
input = perf_datafile_open(filename);
+ if (reopen_datafile) {
+ perf_datafile_setmaxruns(input, -1);
+ }
if (dnssec)
edns = true;
@@ -340,8 +359,15 @@ setup(int argc, char** argv)
if (!(socks = calloc(nsocks, sizeof(*socks)))) {
perf_log_fatal("out of memory");
}
- for (i = 0; i < nsocks; i++)
+ for (i = 0; i < nsocks; i++) {
socks[i] = perf_net_opensocket(mode, &server_addr, &local_addr, i, bufsize);
+ if (!socks[i]) {
+ perf_log_fatal("perf_net_opensocket(): no socket returned, out of memory?");
+ }
+ socks[i]->data = (void*)(intptr_t)i;
+ socks[i]->sent = perf__net_sent;
+ socks[i]->event = perf__net_event;
+ }
}
static void
@@ -351,7 +377,7 @@ cleanup(void)
perf_datafile_close(&input);
for (i = 0; i < nsocks; i++)
- (void)perf_net_close(&socks[i]);
+ (void)perf_net_close(socks[i]);
close(dummypipe[0]);
close(dummypipe[1]);
}
@@ -374,6 +400,30 @@ find_bucket(uint64_t when)
return &buckets[i];
}
+static void perf__net_event(struct perf_net_socket* sock, perf_socket_event_t event, uint64_t elapsed_time)
+{
+ ramp_bucket* b = find_bucket(time_now);
+
+ switch (event) {
+ case perf_socket_event_reconnect:
+ num_reconnections++;
+ case perf_socket_event_connect:
+ b->connections++;
+ b->conn_latency_sum += elapsed_time / (double)MILLION;
+ }
+}
+
+static void perf__net_sent(struct perf_net_socket* sock, uint16_t qid)
+{
+ ramp_bucket* b = find_bucket(time_now);
+
+ b->queries++;
+
+ size_t idx = (size_t)qid * nsocks + (intptr_t)sock->data;
+ assert(idx < max_outstanding);
+ queries[idx].sent_timestamp = time_now;
+}
+
/*
* print_statistics:
* Print out statistics based on the results of the test
@@ -409,6 +459,7 @@ print_statistics(void)
(rcodecounts[i] * 100.0) / num_responses_received);
}
printf("\n");
+ printf(" Reconnection(s): %" PRIu64 "\n", num_reconnections);
printf(" Run time (s): %u.%06u\n",
(unsigned int)(run_time / MILLION),
(unsigned int)(run_time % MILLION));
@@ -470,8 +521,8 @@ do_one_line(perf_buffer_t* lines, perf_buffer_t* msg)
qid = (q - queries) / nsocks;
sock = (q - queries) % nsocks;
- if (socks[sock].sending) {
- if (perf_net_sockready(&socks[sock], dummypipe[0], TIMEOUT_CHECK_TIME) == -1) {
+ while (q->is_inprogress) {
+ if (perf_net_sockready(socks[sock], dummypipe[0], TIMEOUT_CHECK_TIME) == -1) {
if (errno == EINPROGRESS) {
if (verbose) {
perf_log_warning("network congested, packet sending in progress");
@@ -479,12 +530,13 @@ do_one_line(perf_buffer_t* lines, perf_buffer_t* msg)
} else {
if (verbose) {
char __s[256];
- perf_log_warning("failed to send packet: %s", perf_strerror_r(errno, __s, sizeof(__s)));
+ perf_log_warning("failed to check socket readiness: %s", perf_strerror_r(errno, __s, sizeof(__s)));
}
}
return (PERF_R_FAILURE);
}
+ q->is_inprogress = false;
perf_list_unlink(instanding_list, q);
perf_list_prepend(outstanding_list, q);
q->list = &outstanding_list;
@@ -499,14 +551,20 @@ do_one_line(perf_buffer_t* lines, perf_buffer_t* msg)
sock = (q - queries) % nsocks;
}
- switch (perf_net_sockready(&socks[sock], dummypipe[0], TIMEOUT_CHECK_TIME)) {
+ switch (perf_net_sockready(socks[sock], dummypipe[0], TIMEOUT_CHECK_TIME)) {
case 0:
if (verbose) {
perf_log_warning("failed to send packet: socket %d not ready", sock);
}
return (PERF_R_FAILURE);
case -1:
- perf_log_warning("failed to send packet: socket %d readiness check timed out", sock);
+ if (errno == EINPROGRESS) {
+ if (verbose) {
+ perf_log_warning("network congested, packet sending in progress");
+ }
+ } else {
+ perf_log_warning("failed to send packet: socket %d not ready", sock);
+ }
return (PERF_R_FAILURE);
default:
break;
@@ -530,13 +588,14 @@ do_one_line(perf_buffer_t* lines, perf_buffer_t* msg)
base = perf_buffer_base(msg);
length = perf_buffer_usedlength(msg);
- if (perf_net_sendto(&socks[sock], base, length, 0,
+ if (perf_net_sendto(socks[sock], qid, base, length, 0,
&server_addr.sa.sa, server_addr.length)
< 1) {
if (errno == EINPROGRESS) {
if (verbose) {
perf_log_warning("network congested, packet sending in progress");
}
+ q->is_inprogress = true;
} else {
if (verbose) {
char __s[256];
@@ -591,8 +650,17 @@ try_process_response(unsigned int sockindex)
ramp_bucket* b;
int n;
+ if (perf_net_sockready(socks[sockindex], dummypipe[0], TIMEOUT_CHECK_TIME) == -1) {
+ if (errno != EINPROGRESS) {
+ if (verbose) {
+ char __s[256];
+ perf_log_warning("failed to check socket readiness: %s", perf_strerror_r(errno, __s, sizeof(__s)));
+ }
+ }
+ }
+
packet_header = (uint16_t*)packet_buffer;
- n = perf_net_recv(&socks[sockindex], packet_buffer, sizeof(packet_buffer), 0);
+ n = perf_net_recv(socks[sockindex], packet_buffer, sizeof(packet_buffer), 0);
if (n < 0) {
if (errno == EAGAIN || errno == EINTR) {
return;
@@ -611,11 +679,12 @@ try_process_response(unsigned int sockindex)
qid = ntohs(packet_header[0]);
rcode = ntohs(packet_header[1]) & 0xF;
- q = &queries[qid * nsocks + sockindex];
- if (q->list != &outstanding_list) {
+ size_t idx = qid * nsocks + sockindex;
+ if (idx >= max_outstanding || queries[idx].list != &outstanding_list) {
perf_log_warning("received a response with an unexpected id: %u", qid);
return;
}
+ q = &queries[idx];
perf_list_unlink(outstanding_list, q);
perf_list_append(instanding_list, q);
@@ -661,21 +730,6 @@ num_scheduled(uint64_t time_since_start)
}
}
-static void
-handle_sigpipe(int sig)
-{
- (void)sig;
- switch (mode) {
- case sock_tcp:
- case sock_tls:
- // if connection is closed it will generate a signal
- perf_log_fatal("SIGPIPE received, connection(s) likely closed, can't continue");
- break;
- default:
- break;
- }
-}
-
int main(int argc, char** argv)
{
int i;
@@ -701,7 +755,15 @@ int main(int argc, char** argv)
if (pipe(dummypipe) < 0)
perf_log_fatal("creating pipe");
- perf_os_handlesignal(SIGPIPE, handle_sigpipe);
+ switch (mode) {
+ case sock_tcp:
+ case sock_dot:
+ // block SIGPIPE for TCP/DOT mode, if connection is closed it will generate a signal
+ perf_os_blocksignal(SIGPIPE, true);
+ break;
+ default:
+ break;
+ }
perf_buffer_init(&lines, input_data, sizeof(input_data));
@@ -725,7 +787,8 @@ int main(int argc, char** argv)
printf("[Status] Sending\n");
- current_sock = 0;
+ int try_responses = (max_qps / max_outstanding) + 1;
+ current_sock = 0;
for (;;) {
int should_send;
uint64_t time_since_start = time_now - time_of_program_start;
@@ -745,7 +808,7 @@ int main(int argc, char** argv)
}
if (phase != PHASE_WAIT) {
should_send = num_scheduled(time_since_start) - num_queries_sent;
- if (should_send >= 1000) {
+ if (max_fall_behind && should_send >= max_fall_behind) {
printf("[Status] Fell behind by %d queries, "
"ending test at %.0f qps\n",
should_send, (max_qps * time_since_start) / ramp_time);
@@ -762,8 +825,11 @@ int main(int argc, char** argv)
}
}
}
- try_process_response(current_sock++);
- current_sock = current_sock % nsocks;
+ for (i = try_responses; i--;) {
+ try_process_response(current_sock++);
+ if (current_sock >= nsocks)
+ current_sock = 0;
+ }
retire_old_queries();
time_now = perf_get_time();
}
@@ -780,8 +846,8 @@ end_loop:
}
/* Print column headers */
- fprintf(plotf, "# time target_qps actual_qps "
- "responses_per_sec failures_per_sec avg_latency\n");
+ fprintf(plotf, "# time target_qps actual_qps responses_per_sec failures_per_sec avg_latency"
+ " connections conn_avg_latency\n");
/* Don't print unused buckets */
last_bucket_used = find_bucket(wait_phase_began) - buckets;
@@ -796,13 +862,18 @@ end_loop:
double target_qps = t <= ramp_dtime ? (t / ramp_dtime) * max_qps : max_qps;
double latency = buckets[i].responses ? buckets[i].latency_sum / buckets[i].responses : 0;
double interval = bucket_interval / (double)MILLION;
- fprintf(plotf, "%7.3f %8.2f %8.2f %8.2f %8.2f %8.6f\n",
+
+ double conn_latency = buckets[i].connections ? buckets[i].conn_latency_sum / buckets[i].connections : 0;
+
+ fprintf(plotf, "%7.3f %8.2f %8.2f %8.2f %8.2f %8.6f %8.2f %8.6f\n",
t,
target_qps,
- buckets[i].queries / interval,
- buckets[i].responses / interval,
- buckets[i].failures / interval,
- latency);
+ (double)buckets[i].queries / interval,
+ (double)buckets[i].responses / interval,
+ (double)buckets[i].failures / interval,
+ latency,
+ (double)buckets[i].connections / interval,
+ conn_latency);
}
fclose(plotf);