New option "--stats", to show a line of statistics about the transfer once it has ended (issue #49).
This commit is contained in:
@@ -73,6 +73,9 @@
|
||||
/* Define to 1 if you have the <locale.h> header file. */
|
||||
#undef HAVE_LOCALE_H
|
||||
|
||||
/* Define to 1 if you have the <math.h> header file. */
|
||||
#undef HAVE_MATH_H
|
||||
|
||||
/* Define to 1 if you have the `memcpy' function. */
|
||||
#undef HAVE_MEMCPY
|
||||
|
||||
@@ -112,6 +115,9 @@
|
||||
/* Define to 1 if you have the `splice' function. */
|
||||
#undef HAVE_SPLICE
|
||||
|
||||
/* Define to 1 if you have the `sqrtl' function. */
|
||||
#undef HAVE_SQRTL
|
||||
|
||||
/* Define to 1 if stdbool.h conforms to C99. */
|
||||
#undef HAVE_STDBOOL_H
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ struct opts_s {
|
||||
bool sync_after_write; /* set if we sync after every write */
|
||||
bool direct_io; /* set if O_DIRECT is to be used */
|
||||
bool discard_input; /* set to write nothing to output */
|
||||
bool show_stats; /* set to write statistics at the end */
|
||||
bool width_set_manually; /* width was set manually, not detected */
|
||||
bool height_set_manually; /* height was set manually, not detected */
|
||||
};
|
||||
|
||||
@@ -134,6 +134,7 @@ struct pvstate_s {
|
||||
bool direct_io_changed; /* set when direct_io is changed */
|
||||
bool no_splice; /* never use splice() */
|
||||
bool discard_input; /* write nothing to stdout */
|
||||
bool show_stats; /* show statistics on exit */
|
||||
bool width_set_manually; /* width was set manually, not detected */
|
||||
bool height_set_manually; /* height was set manually, not detected */
|
||||
} control;
|
||||
@@ -219,6 +220,12 @@ struct pvstate_s {
|
||||
long double prev_trans; /* amount transferred since last rate calculation */
|
||||
long double current_avg_rate; /* current average rate over last history intervals */
|
||||
|
||||
long double rate_min; /* minimum measured transfer rate */
|
||||
long double rate_max; /* maximum measured transfer rate */
|
||||
long double rate_sum; /* sum of all measured transfer rates */
|
||||
long double ratesquared_sum; /* sum of the squares of each transfer rate */
|
||||
unsigned long measurements_taken; /* how many times the rate was measured */
|
||||
|
||||
/* Keep track of progress over last intervals to compute current average rate. */
|
||||
/*@null@*/ struct { /* state at previous intervals (circular buffer) */
|
||||
long double elapsed_sec; /* time since start of transfer */
|
||||
|
||||
@@ -172,6 +172,7 @@ extern void pv_state_set_format(pvstate_t state, bool progress,
|
||||
*/
|
||||
extern void pv_state_force_set(pvstate_t, bool);
|
||||
extern void pv_state_cursor_set(pvstate_t, bool);
|
||||
extern void pv_state_show_stats_set(pvstate_t, bool);
|
||||
extern void pv_state_numeric_set(pvstate_t, bool);
|
||||
extern void pv_state_wait_set(pvstate_t, bool);
|
||||
extern void pv_state_delay_start_set(pvstate_t, double);
|
||||
|
||||
@@ -319,6 +319,9 @@ void display_help(void)
|
||||
{ "-F", "--format", N_("FORMAT"),
|
||||
N_("set output format to FORMAT"),
|
||||
{ 0, 0, 0, 0} },
|
||||
{ "-v", "--stats", NULL,
|
||||
N_("output transfer statistics at the end"),
|
||||
{ 0, 0, 0, 0} },
|
||||
{ "-n", "--numeric", NULL,
|
||||
N_("output percentages, not visual information"),
|
||||
{ 0, 0, 0, 0} },
|
||||
|
||||
@@ -306,6 +306,7 @@ int main(int argc, char **argv)
|
||||
pv_state_no_display_set(state, opts->no_display);
|
||||
pv_state_force_set(state, opts->force);
|
||||
pv_state_cursor_set(state, opts->cursor);
|
||||
pv_state_show_stats_set(state, opts->show_stats);
|
||||
pv_state_numeric_set(state, opts->numeric);
|
||||
pv_state_wait_set(state, opts->wait);
|
||||
pv_state_delay_start_set(state, opts->delay_start);
|
||||
|
||||
+5
-1
@@ -290,6 +290,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
|
||||
{ "height", 1, NULL, (int) 'H' },
|
||||
{ "name", 1, NULL, (int) 'N' },
|
||||
{ "format", 1, NULL, (int) 'F' },
|
||||
{ "stats", 0, NULL, (int) 'v' },
|
||||
{ "rate-limit", 1, NULL, (int) 'L' },
|
||||
{ "buffer-size", 1, NULL, (int) 'B' },
|
||||
{ "no-splice", 0, NULL, (int) 'C' },
|
||||
@@ -312,7 +313,7 @@ opts_t opts_parse(unsigned int argc, char **argv)
|
||||
/*@+nullassign@ */
|
||||
int option_index = 0;
|
||||
#endif /* HAVE_GETOPT_LONG */
|
||||
char *short_options = "hVpteIrab8kTA:fnqcWD:s:l0i:w:H:N:F:L:B:CEZ:SYKXR:P:d:m:o:"
|
||||
char *short_options = "hVpteIrab8kTA:fvnqcWD:s:l0i:w:H:N:F:L:B:CEZ:SYKXR:P:d:m:o:"
|
||||
#ifdef ENABLE_DEBUGGING
|
||||
"!:"
|
||||
#endif
|
||||
@@ -512,6 +513,9 @@ opts_t opts_parse(unsigned int argc, char **argv)
|
||||
case 'f':
|
||||
opts->force = true;
|
||||
break;
|
||||
case 'v':
|
||||
opts->show_stats = true;
|
||||
break;
|
||||
case 'n':
|
||||
opts->numeric = true;
|
||||
numopts++;
|
||||
|
||||
@@ -694,6 +694,24 @@ void pv_calculate_transfer_rate(pvstate_t state, bool final)
|
||||
transfer_rate = ((long double) bytes_since_last + state->calc.prev_trans) / time_since_last;
|
||||
state->calc.prev_elapsed_sec = state->transfer.elapsed_seconds;
|
||||
state->calc.prev_trans = 0;
|
||||
|
||||
/* Maintain information for statistics. */
|
||||
if (state->control.show_stats) {
|
||||
long double measured_rate = transfer_rate;
|
||||
|
||||
if (state->control.bits)
|
||||
measured_rate = 8.0 * measured_rate;
|
||||
|
||||
if ((state->calc.measurements_taken < 1) || (measured_rate < state->calc.rate_min)) {
|
||||
state->calc.rate_min = measured_rate;
|
||||
}
|
||||
if (measured_rate > state->calc.rate_max) {
|
||||
state->calc.rate_max = measured_rate;
|
||||
}
|
||||
state->calc.rate_sum += measured_rate;
|
||||
state->calc.ratesquared_sum += (measured_rate * measured_rate);
|
||||
state->calc.measurements_taken++;
|
||||
}
|
||||
}
|
||||
state->calc.prev_rate = transfer_rate;
|
||||
|
||||
|
||||
+84
-3
@@ -24,6 +24,32 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if HAVE_MATH_H
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if HAVE_SQRTL
|
||||
#else
|
||||
/*
|
||||
* Square root of a long double. Adapted from iputils ping/ping_common.c.
|
||||
*/
|
||||
static long ldsqrt(long double value)
|
||||
{
|
||||
long double previous = (long double) LLONG_MAX;
|
||||
long double result = value;
|
||||
|
||||
if (result > 0) {
|
||||
while (result < previous) {
|
||||
previous = result;
|
||||
result = (result + (value / result)) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Pipe data from a list of files to standard output, giving information
|
||||
@@ -243,8 +269,11 @@ int pv_main_loop(pvstate_t state)
|
||||
}
|
||||
}
|
||||
|
||||
/* Just go round the loop again if there's no display. */
|
||||
if (state->control.no_display)
|
||||
/*
|
||||
* Just go round the loop again if there's no display and
|
||||
* we're not reporting statistics.
|
||||
*/
|
||||
if (state->control.no_display && !state->control.show_stats)
|
||||
continue;
|
||||
|
||||
/*
|
||||
@@ -331,7 +360,13 @@ int pv_main_loop(pvstate_t state)
|
||||
state->control.height = new_height;
|
||||
}
|
||||
|
||||
pv_display(state, final_update);
|
||||
if (state->control.no_display) {
|
||||
/* If there's no display, calculate rate for the statistics. */
|
||||
pv_calculate_transfer_rate(state, final_update);
|
||||
} else {
|
||||
/* Produce the display. */
|
||||
pv_display(state, final_update);
|
||||
}
|
||||
}
|
||||
|
||||
debug("%s: %s=%s, %s=%s", "loop ended", "eof_in", eof_in ? "true" : "false", "eof_out",
|
||||
@@ -351,6 +386,52 @@ int pv_main_loop(pvstate_t state)
|
||||
if (fd >= 0)
|
||||
(void) close(fd);
|
||||
|
||||
/* Calculate and display the transfer statistics. */
|
||||
if (state->control.show_stats && state->calc.measurements_taken > 0) {
|
||||
char stats_buf[256]; /* flawfinder: ignore */
|
||||
long double rate_mean, rate_variance, rate_deviation;
|
||||
int stats_size;
|
||||
|
||||
/* flawfinder: made safe by use of pv_snprintf() */
|
||||
|
||||
rate_mean = state->calc.rate_sum / ((long double) (state->calc.measurements_taken));
|
||||
rate_variance =
|
||||
(state->calc.ratesquared_sum / ((long double) (state->calc.measurements_taken))) -
|
||||
(rate_mean * rate_mean);
|
||||
#if HAVE_SQRTL
|
||||
rate_deviation = sqrtl(rate_variance);
|
||||
#else
|
||||
rate_deviation = ldsqrt(rate_variance);
|
||||
#endif
|
||||
|
||||
debug("%s: %ld", "measurements taken", state->calc.measurements_taken);
|
||||
debug("%s: %.3Lf", "rate_sum", state->calc.rate_sum);
|
||||
debug("%s: %.3Lf", "ratesquared_sum", state->calc.ratesquared_sum);
|
||||
debug("%s: %.3Lf", "rate_mean", rate_mean);
|
||||
debug("%s: %.3Lf", "rate_variance", rate_variance);
|
||||
debug("%s: %.3Lf", "rate_deviation", rate_deviation);
|
||||
|
||||
memset(stats_buf, 0, sizeof(stats_buf));
|
||||
stats_size =
|
||||
pv_snprintf(stats_buf, sizeof(stats_buf), "%s = %.3Lf/%.3Lf/%.3Lf/%.3Lf %s\n",
|
||||
_("rate min/avg/max/mdev"), state->calc.rate_min, rate_mean, state->calc.rate_max,
|
||||
rate_deviation, state->control.bits ? _("b/s") : _("B/s"));
|
||||
|
||||
if (stats_size > 0 && stats_size < (int) (sizeof(stats_buf)))
|
||||
pv_tty_write(state, stats_buf, (size_t) stats_size);
|
||||
} else if (state->control.show_stats && state->calc.measurements_taken < 1) {
|
||||
char msg_buf[256]; /* flawfinder: ignore */
|
||||
int msg_size;
|
||||
|
||||
/* flawfinder: made safe by use of pv_snprintf() */
|
||||
|
||||
memset(msg_buf, 0, sizeof(msg_buf));
|
||||
msg_size = pv_snprintf(msg_buf, sizeof(msg_buf), "%s\n", _("rate not measured"));
|
||||
|
||||
if (msg_size > 0 && msg_size < (int) (sizeof(msg_buf)))
|
||||
pv_tty_write(state, msg_buf, (size_t) msg_size);
|
||||
}
|
||||
|
||||
return state->status.exit_status;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,6 +85,22 @@ pvstate_t pv_state_alloc(const char *program_name)
|
||||
#endif /* HAVE_SPLICE */
|
||||
state->display.display_visible = false;
|
||||
|
||||
/*
|
||||
* Explicitly set important floating point values to 0, as memset()
|
||||
* is not recommended for this.
|
||||
*/
|
||||
state->calc.transfer_rate = 0.0;
|
||||
state->calc.average_rate = 0.0;
|
||||
state->calc.prev_elapsed_sec = 0.0;
|
||||
state->calc.prev_rate = 0.0;
|
||||
state->calc.prev_trans = 0.0;
|
||||
state->calc.current_avg_rate = 0.0;
|
||||
state->calc.rate_min = 0.0;
|
||||
state->calc.rate_max = 0.0;
|
||||
state->calc.rate_sum = 0.0;
|
||||
state->calc.ratesquared_sum = 0.0;
|
||||
state->transfer.elapsed_seconds = 0.0;
|
||||
|
||||
/*
|
||||
* Get the current working directory, if possible, as a base for
|
||||
* showing relative filenames with --watchfd.
|
||||
@@ -234,6 +250,11 @@ void pv_state_cursor_set(pvstate_t state, bool val)
|
||||
state->control.cursor = val;
|
||||
}
|
||||
|
||||
void pv_state_show_stats_set(pvstate_t state, bool val)
|
||||
{
|
||||
state->control.show_stats = val;
|
||||
}
|
||||
|
||||
void pv_state_numeric_set(pvstate_t state, bool val)
|
||||
{
|
||||
state->control.numeric = val;
|
||||
|
||||
Reference in New Issue
Block a user