1
1

Fix various problems with summary statistics (#562)

* Untangle some problems with printing summary statistics.

There were (at least) two problems:

o The server cannot print summary statistics as seen from the
client, because the server has to generate its summaries
before receiving any statistics from the client.  This
shortcoming is somewhat hard-coded into the semantics of
messages on the control channel, and probably can't be easily
changed.

o UDP summary statistics for each stream were ambiguous in that
it wasn't clear whether they were intended to apply to the
sender or receiver.

To fix this, we split UDP summary statistics into two lines,
one for the sender side and one for the receiver side.  This
hopefully eliminates any ambiguity about the statistics.  On the
server, we don't attempt to print the (not very meaningful and
potentially misleading) statistics corresponding to the client.

Possible fix for #560.

* Try to report more accurate ending statistics.

Basically the client side was using only its measured test duration
to compute figures such as bitrate, but the server's test duration
could be different due to network delays/jitter.  So we make sure
that the test durations (for each stream) are passed in the test
results and used appropriately when we print statistics for the
sender and receiver.

Towards #560, also this could help towards #238.

* Silence a warning over an uninitialized variable.
Этот коммит содержится в:
Bruce A. Mah 2017-05-03 10:21:34 -07:00 коммит произвёл GitHub
родитель c8531ca31f
Коммит 10e2cc241e
4 изменённых файлов: 116 добавлений и 18 удалений

Просмотреть файл

@ -102,6 +102,8 @@ struct iperf_stream_result
struct timeval start_time;
struct timeval end_time;
struct timeval start_time_fixed;
double sender_time;
double receiver_time;
TAILQ_HEAD(irlisthead, iperf_interval_results) interval_results;
void *data;
};

Просмотреть файл

@ -1585,6 +1585,7 @@ send_results(struct iperf_test *test)
int sender_has_retransmits;
iperf_size_t bytes_transferred;
int retransmits;
double start_time, end_time;
j = cJSON_CreateObject();
if (j == NULL) {
@ -1652,6 +1653,12 @@ send_results(struct iperf_test *test)
cJSON_AddNumberToObject(j_stream, "jitter", sp->jitter);
cJSON_AddNumberToObject(j_stream, "errors", sp->cnt_error);
cJSON_AddNumberToObject(j_stream, "packets", sp->packet_count);
start_time = timeval_diff(&sp->result->start_time, &sp->result->start_time);
end_time = timeval_diff(&sp->result->start_time, &sp->result->end_time);
cJSON_AddNumberToObject(j_stream, "start_time", start_time);
cJSON_AddNumberToObject(j_stream, "end_time", end_time);
}
}
if (r == 0 && test->debug) {
@ -1690,6 +1697,7 @@ get_results(struct iperf_test *test)
cJSON *j_errors;
cJSON *j_packets;
cJSON *j_server_output;
cJSON *j_start_time, *j_end_time;
int sid, cerror, pcount;
double jitter;
iperf_size_t bytes_transferred;
@ -1737,6 +1745,8 @@ get_results(struct iperf_test *test)
j_jitter = cJSON_GetObjectItem(j_stream, "jitter");
j_errors = cJSON_GetObjectItem(j_stream, "errors");
j_packets = cJSON_GetObjectItem(j_stream, "packets");
j_start_time = cJSON_GetObjectItem(j_stream, "start_time");
j_end_time = cJSON_GetObjectItem(j_stream, "end_time");
if (j_id == NULL || j_bytes == NULL || j_retransmits == NULL || j_jitter == NULL || j_errors == NULL || j_packets == NULL) {
i_errno = IERECVRESULTS;
r = -1;
@ -1758,9 +1768,30 @@ get_results(struct iperf_test *test)
sp->cnt_error = cerror;
sp->packet_count = pcount;
sp->result->bytes_received = bytes_transferred;
/*
* We have to handle the possibilty that
* start_time and end_time might not be
* available; this is the case for older (pre-3.2)
* servers.
*
* We need to have result structure members to hold
* the both sides' start_time and end_time.
*/
if (j_start_time && j_end_time) {
sp->result->receiver_time = j_end_time->valuedouble - j_start_time->valuedouble;
}
else {
sp->result->receiver_time = 0.0;
}
} else {
sp->result->bytes_sent = bytes_transferred;
sp->result->stream_retrans = retransmits;
if (j_start_time && j_end_time) {
sp->result->sender_time = j_end_time->valuedouble - j_start_time->valuedouble;
}
else {
sp->result->sender_time = 0.0;
}
}
}
}
@ -2495,7 +2526,8 @@ iperf_print_results(struct iperf_test *test)
struct iperf_stream *sp = NULL;
iperf_size_t bytes_sent, total_sent = 0;
iperf_size_t bytes_received, total_received = 0;
double start_time, end_time = 0.0, avg_jitter = 0.0, lost_percent;
double start_time, end_time = 0.0, avg_jitter = 0.0, lost_percent = 0.0;
double sender_time, receiver_time;
double bandwidth;
/* print final summary for all intervals */
@ -2528,6 +2560,14 @@ iperf_print_results(struct iperf_test *test)
*/
if (sp) {
end_time = timeval_diff(&sp->result->start_time, &sp->result->end_time);
if (test->sender) {
sp->result->sender_time = end_time;
}
else {
sp->result->receiver_time = end_time;
}
sender_time = sp->result->sender_time;
receiver_time = sp->result->receiver_time;
SLIST_FOREACH(sp, &test->streams, streams) {
if (test->json_output) {
json_summary_stream = cJSON_CreateObject();
@ -2552,24 +2592,36 @@ iperf_print_results(struct iperf_test *test)
}
unit_snprintf(ubuf, UNIT_LEN, (double) bytes_sent, 'A');
bandwidth = (double) bytes_sent / (double) end_time;
bandwidth = (double) bytes_sent / (double) sender_time;
unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
if (test->sender_has_retransmits) {
/* Summary, TCP with retransmits. */
/* Sender summary, TCP and SCTP with retransmits. */
if (test->json_output)
cJSON_AddItemToObject(json_summary_stream, "sender", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d max_snd_cwnd: %d max_rtt: %d min_rtt: %d mean_rtt: %d", (int64_t) sp->socket, (double) start_time, (double) end_time, (double) end_time, (int64_t) bytes_sent, bandwidth * 8, (int64_t) sp->result->stream_retrans, (int64_t) sp->result->stream_max_snd_cwnd, (int64_t) sp->result->stream_max_rtt, (int64_t) sp->result->stream_min_rtt, (int64_t) ((sp->result->stream_count_rtt == 0) ? 0 : sp->result->stream_sum_rtt / sp->result->stream_count_rtt)));
cJSON_AddItemToObject(json_summary_stream, "sender", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f retransmits: %d max_snd_cwnd: %d max_rtt: %d min_rtt: %d mean_rtt: %d", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8, (int64_t) sp->result->stream_retrans, (int64_t) sp->result->stream_max_snd_cwnd, (int64_t) sp->result->stream_max_rtt, (int64_t) sp->result->stream_min_rtt, (int64_t) ((sp->result->stream_count_rtt == 0) ? 0 : sp->result->stream_sum_rtt / sp->result->stream_count_rtt)));
else
iperf_printf(test, report_bw_retrans_format, sp->socket, start_time, end_time, ubuf, nbuf, sp->result->stream_retrans, report_sender);
} else {
/* Summary, TCP without retransmits. */
if (test->json_output)
cJSON_AddItemToObject(json_summary_stream, "sender", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f", (int64_t) sp->socket, (double) start_time, (double) end_time, (double) end_time, (int64_t) bytes_sent, bandwidth * 8));
else
iperf_printf(test, report_bw_format, sp->socket, start_time, end_time, ubuf, nbuf, report_sender);
if (test->role == 's' && !test->sender) {
if (test->verbose)
iperf_printf(test, report_sender_not_available_format, sp->socket);
}
else {
iperf_printf(test, report_bw_retrans_format, sp->socket, start_time, sender_time, ubuf, nbuf, sp->result->stream_retrans, report_sender);
}
} else {
/* Summary, UDP. */
/* Sender summary, TCP and SCTP without retransmits. */
if (test->json_output)
cJSON_AddItemToObject(json_summary_stream, "sender", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8));
else
if (test->role == 's' && !test->sender) {
if (test->verbose)
iperf_printf(test, report_sender_not_available_format, sp->socket);
}
else {
iperf_printf(test, report_bw_format, sp->socket, start_time, sender_time, ubuf, nbuf, report_sender);
}
}
} else {
/* Sender summary, UDP. */
if (sp->packet_count - sp->omitted_packet_count > 0) {
lost_percent = 100.0 * (sp->cnt_error - sp->omitted_cnt_error) / (sp->packet_count - sp->omitted_packet_count);
}
@ -2577,13 +2629,30 @@ iperf_print_results(struct iperf_test *test)
lost_percent = 0.0;
}
if (test->json_output)
cJSON_AddItemToObject(json_summary_stream, "udp", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f out_of_order: %d", (int64_t) sp->socket, (double) start_time, (double) end_time, (double) end_time, (int64_t) bytes_sent, bandwidth * 8, (double) sp->jitter * 1000.0, (int64_t) (sp->cnt_error - sp->omitted_cnt_error), (int64_t) (sp->packet_count - sp->omitted_packet_count), (double) lost_percent, (int64_t) (sp->outoforder_packets - sp->omitted_outoforder_packets)));
/*
* For hysterical raisins, we only emit one JSON
* object for the UDP summary, and it contains
* information for both the sender and receiver
* side.
*/
cJSON_AddItemToObject(json_summary_stream, "udp", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f jitter_ms: %f lost_packets: %d packets: %d lost_percent: %f out_of_order: %d", (int64_t) sp->socket, (double) start_time, (double) sender_time, (double) sender_time, (int64_t) bytes_sent, bandwidth * 8, (double) sp->jitter * 1000.0, (int64_t) (sp->cnt_error - sp->omitted_cnt_error), (int64_t) (sp->packet_count - sp->omitted_packet_count), (double) lost_percent, (int64_t) (sp->outoforder_packets - sp->omitted_outoforder_packets)));
else {
iperf_printf(test, report_bw_udp_format, sp->socket, start_time, end_time, ubuf, nbuf, sp->jitter * 1000.0, (sp->cnt_error - sp->omitted_cnt_error), (sp->packet_count - sp->omitted_packet_count), lost_percent, "");
if (test->role == 'c')
iperf_printf(test, report_datagrams, sp->socket, (sp->packet_count - sp->omitted_packet_count));
/*
* Due to ordering of messages on the control channel,
* the server cannot report on client-side summary
* statistics. If we're the server, omit one set of
* summary statistics to avoid giving meaningless
* results.
*/
if (test->role == 's' && !test->sender) {
if (test->verbose)
iperf_printf(test, report_sender_not_available_format, sp->socket);
}
else {
iperf_printf(test, report_bw_udp_format, sp->socket, start_time, sender_time, ubuf, nbuf, sp->jitter * 1000.0, 0, (sp->packet_count - sp->omitted_packet_count), (double) 0, report_sender);
}
if ((sp->outoforder_packets - sp->omitted_outoforder_packets) > 0)
iperf_printf(test, report_sum_outoforder, start_time, end_time, (sp->outoforder_packets - sp->omitted_outoforder_packets));
iperf_printf(test, report_sum_outoforder, start_time, sender_time, (sp->outoforder_packets - sp->omitted_outoforder_packets));
}
}
@ -2603,13 +2672,36 @@ iperf_print_results(struct iperf_test *test)
}
unit_snprintf(ubuf, UNIT_LEN, (double) bytes_received, 'A');
bandwidth = (double) bytes_received / (double) end_time;
bandwidth = (double) bytes_received / (double) receiver_time;
unit_snprintf(nbuf, UNIT_LEN, bandwidth, test->settings->unit_format);
if (test->protocol->id == Ptcp || test->protocol->id == Psctp) {
/* Receiver summary, TCP and SCTP */
if (test->json_output)
cJSON_AddItemToObject(json_summary_stream, "receiver", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f", (int64_t) sp->socket, (double) start_time, (double) end_time, (double) end_time, (int64_t) bytes_received, bandwidth * 8));
cJSON_AddItemToObject(json_summary_stream, "receiver", iperf_json_printf("socket: %d start: %f end: %f seconds: %f bytes: %d bits_per_second: %f", (int64_t) sp->socket, (double) start_time, (double) receiver_time, (double) end_time, (int64_t) bytes_received, bandwidth * 8));
else
iperf_printf(test, report_bw_format, sp->socket, start_time, end_time, ubuf, nbuf, report_receiver);
if (test->role == 's' && test->sender) {
if (test->verbose)
iperf_printf(test, report_receiver_not_available_format, sp->socket);
}
else {
iperf_printf(test, report_bw_format, sp->socket, start_time, receiver_time, ubuf, nbuf, report_receiver);
}
}
else {
/*
* Receiver summary, UDP. Note that JSON was emitted with
* the sender summary, so we only deal with human-readable
* data here.
*/
if (! test->json_output) {
if (test->role == 's' && test->sender) {
if (test->verbose)
iperf_printf(test, report_receiver_not_available_format, sp->socket);
}
else {
iperf_printf(test, report_bw_udp_format, sp->socket, start_time, receiver_time, ubuf, nbuf, sp->jitter * 1000.0, (sp->cnt_error - sp->omitted_cnt_error), (sp->packet_count - sp->omitted_packet_count), lost_percent, report_receiver);
}
}
}
}
}

Просмотреть файл

@ -383,6 +383,8 @@ const char report_local[] = "local";
const char report_remote[] = "remote";
const char report_sender[] = "sender";
const char report_receiver[] = "receiver";
const char report_sender_not_available_format[] = "[%3d] (sender statistics not available)\n";
const char report_receiver_not_available_format[] = "[%3d] (receiver statistics not available)\n";
#if defined(linux)
const char report_tcpInfo[] =

Просмотреть файл

@ -1,5 +1,5 @@
/*
* iperf, Copyright (c) 2014, The Regents of the University of
* iperf, Copyright (c) 2014, 2017, The Regents of the University of
* California, through Lawrence Berkeley National Laboratory (subject
* to receipt of any required approvals from the U.S. Dept. of
* Energy). All rights reserved.
@ -95,6 +95,8 @@ extern const char report_local[] ;
extern const char report_remote[] ;
extern const char report_sender[] ;
extern const char report_receiver[] ;
extern const char report_sender_not_available_format[];
extern const char report_receiver_not_available_format[];
extern const char report_tcpInfo[] ;
extern const char report_tcpInfo[] ;