From 96a02f8f6d2192d3686cd1c719044082c89e8391 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Wed, 18 Apr 2007 14:27:45 -0400 Subject: common progress display support Instead of having this code duplicated in multiple places, let's have a common interface for progress display. If someday someone wishes to display a cheezy progress bar instead then only one file will have to be changed. Note: I left merge-recursive.c out since it has a strange notion of progress as it apparently increase the expected total number as it goes. Someone with more intimate knowledge of what that is supposed to mean might look at converting it to the common progress interface. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 progress.c (limited to 'progress.c') diff --git a/progress.c b/progress.c new file mode 100644 index 000000000..702e11606 --- /dev/null +++ b/progress.c @@ -0,0 +1,68 @@ +#include "git-compat-util.h" +#include "progress.h" + +static volatile sig_atomic_t progress_update; + +static void progress_interval(int signum) +{ + progress_update = 1; +} + +static void set_progress_signal(void) +{ + struct sigaction sa; + struct itimerval v; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = progress_interval; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + sigaction(SIGALRM, &sa, NULL); + + v.it_interval.tv_sec = 1; + v.it_interval.tv_usec = 0; + v.it_value = v.it_interval; + setitimer(ITIMER_REAL, &v, NULL); +} + +static void clear_progress_signal(void) +{ + struct itimerval v = {{0,},}; + setitimer(ITIMER_REAL, &v, NULL); + signal(SIGALRM, SIG_IGN); + progress_update = 0; +} + +int display_progress(struct progress *progress, unsigned n) +{ + if (progress->total) { + unsigned percent = n * 100 / progress->total; + if (percent != progress->last_percent || progress_update) { + progress->last_percent = percent; + fprintf(stderr, "%s%4u%% (%u/%u) done\r", + progress->msg, percent, n, progress->total); + progress_update = 0; + return 1; + } + } else if (progress_update) { + fprintf(stderr, "%s%u\r", progress->msg, n); + progress_update = 0; + return 1; + } + return 0; +} + +void start_progress(struct progress *progress, const char *msg, unsigned total) +{ + progress->msg = msg; + progress->total = total; + progress->last_percent = -1; + set_progress_signal(); +} + +void stop_progress(struct progress *progress) +{ + clear_progress_signal(); + if (progress->total) + fputc('\n', stderr); +} -- cgit v1.2.1 From 13aaf148258808437c485d3ef54c7ae7668384d7 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 20 Apr 2007 14:10:07 -0400 Subject: make progress "title" part of the common progress interface If the progress bar ends up in a box, better provide a title for it too. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'progress.c') diff --git a/progress.c b/progress.c index 702e11606..478134bc6 100644 --- a/progress.c +++ b/progress.c @@ -40,23 +40,27 @@ int display_progress(struct progress *progress, unsigned n) if (percent != progress->last_percent || progress_update) { progress->last_percent = percent; fprintf(stderr, "%s%4u%% (%u/%u) done\r", - progress->msg, percent, n, progress->total); + progress->prefix, percent, n, progress->total); progress_update = 0; return 1; } } else if (progress_update) { - fprintf(stderr, "%s%u\r", progress->msg, n); + fprintf(stderr, "%s%u\r", progress->prefix, n); progress_update = 0; return 1; } return 0; } -void start_progress(struct progress *progress, const char *msg, unsigned total) +void start_progress(struct progress *progress, const char *title, + const char *prefix, unsigned total) { - progress->msg = msg; + char buf[80]; + progress->prefix = prefix; progress->total = total; progress->last_percent = -1; + if (snprintf(buf, sizeof(buf), title, total)) + fprintf(stderr, "%s\n", buf); set_progress_signal(); } -- cgit v1.2.1 From 180a9f226860e18cdc2c3987ae89c239b318b408 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 20 Apr 2007 15:05:27 -0400 Subject: provide a facility for "delayed" progress reporting This allows for progress to be displayed only if the progress has not reached a specified percentage treshold within a given delay in seconds. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'progress.c') diff --git a/progress.c b/progress.c index 478134bc6..05f789031 100644 --- a/progress.c +++ b/progress.c @@ -13,6 +13,8 @@ static void set_progress_signal(void) struct sigaction sa; struct itimerval v; + progress_update = 0; + memset(&sa, 0, sizeof(sa)); sa.sa_handler = progress_interval; sigemptyset(&sa.sa_mask); @@ -35,6 +37,24 @@ static void clear_progress_signal(void) int display_progress(struct progress *progress, unsigned n) { + if (progress->delay) { + char buf[80]; + if (!progress_update || --progress->delay) + return 0; + if (progress->total) { + unsigned percent = n * 100 / progress->total; + if (percent > progress->delayed_percent_treshold) { + /* inhibit this progress report entirely */ + clear_progress_signal(); + progress->delay = -1; + progress->total = 0; + return 0; + } + } + if (snprintf(buf, sizeof(buf), + progress->delayed_title, progress->total)) + fprintf(stderr, "%s\n", buf); + } if (progress->total) { unsigned percent = n * 100 / progress->total; if (percent != progress->last_percent || progress_update) { @@ -59,11 +79,25 @@ void start_progress(struct progress *progress, const char *title, progress->prefix = prefix; progress->total = total; progress->last_percent = -1; + progress->delay = 0; if (snprintf(buf, sizeof(buf), title, total)) fprintf(stderr, "%s\n", buf); set_progress_signal(); } +void start_progress_delay(struct progress *progress, const char *title, + const char *prefix, unsigned total, + unsigned percent_treshold, unsigned delay) +{ + progress->prefix = prefix; + progress->total = total; + progress->last_percent = -1; + progress->delayed_percent_treshold = percent_treshold; + progress->delayed_title = title; + progress->delay = delay; + set_progress_signal(); +} + void stop_progress(struct progress *progress) { clear_progress_signal(); -- cgit v1.2.1 From 421f9d1685b4b7d286b5929489a0d95139c6ecce Mon Sep 17 00:00:00 2001 From: Alex Riesen Date: Wed, 23 May 2007 18:07:25 +0200 Subject: Fix the progress code to output LF only when it is really needed Signed-off-by: Alex Riesen Signed-off-by: Junio C Hamano --- progress.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'progress.c') diff --git a/progress.c b/progress.c index 05f789031..4344f4eed 100644 --- a/progress.c +++ b/progress.c @@ -62,11 +62,13 @@ int display_progress(struct progress *progress, unsigned n) fprintf(stderr, "%s%4u%% (%u/%u) done\r", progress->prefix, percent, n, progress->total); progress_update = 0; + progress->need_lf = 1; return 1; } } else if (progress_update) { fprintf(stderr, "%s%u\r", progress->prefix, n); progress_update = 0; + progress->need_lf = 1; return 1; } return 0; @@ -80,6 +82,7 @@ void start_progress(struct progress *progress, const char *title, progress->total = total; progress->last_percent = -1; progress->delay = 0; + progress->need_lf = 0; if (snprintf(buf, sizeof(buf), title, total)) fprintf(stderr, "%s\n", buf); set_progress_signal(); @@ -95,12 +98,13 @@ void start_progress_delay(struct progress *progress, const char *title, progress->delayed_percent_treshold = percent_treshold; progress->delayed_title = title; progress->delay = delay; + progress->need_lf = 0; set_progress_signal(); } void stop_progress(struct progress *progress) { clear_progress_signal(); - if (progress->total) + if (progress->need_lf) fputc('\n', stderr); } -- cgit v1.2.1 From 42e18fbf5f94dd6bd303bf702e030a29fa39d6c4 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 16 Oct 2007 21:55:45 -0400 Subject: more compact progress display Each progress can be on a single line instead of two. [sp: Changed "Checking files out" to "Checking out files" at Johannes Sixt's suggestion as it better explains the action that is taking place] Signed-off-by: Nicolas Pitre Signed-off-by: Shawn O. Pearce --- progress.c | 53 +++++++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 28 deletions(-) (limited to 'progress.c') diff --git a/progress.c b/progress.c index 4344f4eed..7629e0572 100644 --- a/progress.c +++ b/progress.c @@ -35,10 +35,11 @@ static void clear_progress_signal(void) progress_update = 0; } -int display_progress(struct progress *progress, unsigned n) +static int display(struct progress *progress, unsigned n, int done) { + char *eol; + if (progress->delay) { - char buf[80]; if (!progress_update || --progress->delay) return 0; if (progress->total) { @@ -51,60 +52,56 @@ int display_progress(struct progress *progress, unsigned n) return 0; } } - if (snprintf(buf, sizeof(buf), - progress->delayed_title, progress->total)) - fprintf(stderr, "%s\n", buf); } + + progress->last_value = n; + eol = done ? ", done. \n" : " \r"; if (progress->total) { unsigned percent = n * 100 / progress->total; if (percent != progress->last_percent || progress_update) { progress->last_percent = percent; - fprintf(stderr, "%s%4u%% (%u/%u) done\r", - progress->prefix, percent, n, progress->total); + fprintf(stderr, "%s: %3u%% (%u/%u)%s", progress->title, + percent, n, progress->total, eol); progress_update = 0; - progress->need_lf = 1; return 1; } } else if (progress_update) { - fprintf(stderr, "%s%u\r", progress->prefix, n); + fprintf(stderr, "%s: %u%s", progress->title, n, eol); progress_update = 0; - progress->need_lf = 1; return 1; } + return 0; } -void start_progress(struct progress *progress, const char *title, - const char *prefix, unsigned total) +int display_progress(struct progress *progress, unsigned n) { - char buf[80]; - progress->prefix = prefix; - progress->total = total; - progress->last_percent = -1; - progress->delay = 0; - progress->need_lf = 0; - if (snprintf(buf, sizeof(buf), title, total)) - fprintf(stderr, "%s\n", buf); - set_progress_signal(); + return display(progress, n, 0); } void start_progress_delay(struct progress *progress, const char *title, - const char *prefix, unsigned total, - unsigned percent_treshold, unsigned delay) + unsigned total, unsigned percent_treshold, unsigned delay) { - progress->prefix = prefix; + progress->title = title; progress->total = total; + progress->last_value = -1; progress->last_percent = -1; progress->delayed_percent_treshold = percent_treshold; - progress->delayed_title = title; progress->delay = delay; - progress->need_lf = 0; set_progress_signal(); } +void start_progress(struct progress *progress, const char *title, unsigned total) +{ + start_progress_delay(progress, title, total, 0, 0); +} + void stop_progress(struct progress *progress) { + if (progress->last_value != -1) { + /* Force the last update */ + progress_update = 1; + display(progress, progress->last_value, 1); + } clear_progress_signal(); - if (progress->need_lf) - fputc('\n', stderr); } -- cgit v1.2.1 From dc6a0757c4f966dd124bd85be2adad5a0b7b2167 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 30 Oct 2007 14:57:32 -0400 Subject: make struct progress an opaque type This allows for better management of progress "object" existence, as well as making the progress display implementation more independent from its callers. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) (limited to 'progress.c') diff --git a/progress.c b/progress.c index 7629e0572..c342e39c5 100644 --- a/progress.c +++ b/progress.c @@ -1,6 +1,15 @@ #include "git-compat-util.h" #include "progress.h" +struct progress { + const char *title; + int last_value; + unsigned total; + unsigned last_percent; + unsigned delay; + unsigned delayed_percent_treshold; +}; + static volatile sig_atomic_t progress_update; static void progress_interval(int signum) @@ -76,12 +85,18 @@ static int display(struct progress *progress, unsigned n, int done) int display_progress(struct progress *progress, unsigned n) { - return display(progress, n, 0); + return progress ? display(progress, n, 0) : 0; } -void start_progress_delay(struct progress *progress, const char *title, - unsigned total, unsigned percent_treshold, unsigned delay) +struct progress *start_progress_delay(const char *title, unsigned total, + unsigned percent_treshold, unsigned delay) { + struct progress *progress = malloc(sizeof(*progress)); + if (!progress) { + /* unlikely, but here's a good fallback */ + fprintf(stderr, "%s...\n", title); + return NULL; + } progress->title = title; progress->total = total; progress->last_value = -1; @@ -89,19 +104,25 @@ void start_progress_delay(struct progress *progress, const char *title, progress->delayed_percent_treshold = percent_treshold; progress->delay = delay; set_progress_signal(); + return progress; } -void start_progress(struct progress *progress, const char *title, unsigned total) +struct progress *start_progress(const char *title, unsigned total) { - start_progress_delay(progress, title, total, 0, 0); + return start_progress_delay(title, total, 0, 0); } -void stop_progress(struct progress *progress) +void stop_progress(struct progress **p_progress) { + struct progress *progress = *p_progress; + if (!progress) + return; + *p_progress = NULL; if (progress->last_value != -1) { /* Force the last update */ progress_update = 1; display(progress, progress->last_value, 1); } clear_progress_signal(); + free(progress); } -- cgit v1.2.1 From cf84d51c43fa05cce416bfa3f5db3ad70773abdf Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 30 Oct 2007 14:57:34 -0400 Subject: add throughput to progress display This adds the ability for the progress code to also display transfer throughput when that makes sense. The math was inspired by commit c548cf4ee0737a321ffe94f6a97c65baf87281be from Linus. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 4 deletions(-) (limited to 'progress.c') diff --git a/progress.c b/progress.c index c342e39c5..15197fbe6 100644 --- a/progress.c +++ b/progress.c @@ -1,6 +1,19 @@ #include "git-compat-util.h" #include "progress.h" +#define TP_IDX_MAX 8 + +struct throughput { + struct timeval prev_tv; + unsigned long count; + unsigned long avg_bytes; + unsigned long last_bytes[TP_IDX_MAX]; + unsigned int avg_misecs; + unsigned int last_misecs[TP_IDX_MAX]; + unsigned int idx; + char display[20]; +}; + struct progress { const char *title; int last_value; @@ -8,6 +21,7 @@ struct progress { unsigned last_percent; unsigned delay; unsigned delayed_percent_treshold; + struct throughput *throughput; }; static volatile sig_atomic_t progress_update; @@ -46,7 +60,7 @@ static void clear_progress_signal(void) static int display(struct progress *progress, unsigned n, int done) { - char *eol; + char *eol, *tp; if (progress->delay) { if (!progress_update || --progress->delay) @@ -64,18 +78,20 @@ static int display(struct progress *progress, unsigned n, int done) } progress->last_value = n; + tp = (progress->throughput) ? progress->throughput->display : ""; eol = done ? ", done. \n" : " \r"; if (progress->total) { unsigned percent = n * 100 / progress->total; if (percent != progress->last_percent || progress_update) { progress->last_percent = percent; - fprintf(stderr, "%s: %3u%% (%u/%u)%s", progress->title, - percent, n, progress->total, eol); + fprintf(stderr, "%s: %3u%% (%u/%u)%s%s", + progress->title, percent, n, + progress->total, tp, eol); progress_update = 0; return 1; } } else if (progress_update) { - fprintf(stderr, "%s: %u%s", progress->title, n, eol); + fprintf(stderr, "%s: %u%s%s", progress->title, n, tp, eol); progress_update = 0; return 1; } @@ -83,6 +99,60 @@ static int display(struct progress *progress, unsigned n, int done) return 0; } +void display_throughput(struct progress *progress, unsigned long n) +{ + struct throughput *tp; + struct timeval tv; + unsigned int misecs; + + if (!progress) + return; + tp = progress->throughput; + + gettimeofday(&tv, NULL); + + if (!tp) { + progress->throughput = tp = calloc(1, sizeof(*tp)); + if (tp) + tp->prev_tv = tv; + return; + } + + tp->count += n; + + /* + * We have x = bytes and y = microsecs. We want z = KiB/s: + * + * z = (x / 1024) / (y / 1000000) + * z = x / y * 1000000 / 1024 + * z = x / (y * 1024 / 1000000) + * z = x / y' + * + * To simplify things we'll keep track of misecs, or 1024th of a sec + * obtained with: + * + * y' = y * 1024 / 1000000 + * y' = y / (1000000 / 1024) + * y' = y / 977 + */ + misecs = (tv.tv_sec - tp->prev_tv.tv_sec) * 1024; + misecs += (int)(tv.tv_usec - tp->prev_tv.tv_usec) / 977; + + if (misecs > 512) { + tp->prev_tv = tv; + tp->avg_bytes += tp->count; + tp->avg_misecs += misecs; + snprintf(tp->display, sizeof(tp->display), + ", %lu KiB/s", tp->avg_bytes / tp->avg_misecs); + tp->avg_bytes -= tp->last_bytes[tp->idx]; + tp->avg_misecs -= tp->last_misecs[tp->idx]; + tp->last_bytes[tp->idx] = tp->count; + tp->last_misecs[tp->idx] = misecs; + tp->idx = (tp->idx + 1) % TP_IDX_MAX; + tp->count = 0; + } +} + int display_progress(struct progress *progress, unsigned n) { return progress ? display(progress, n, 0) : 0; @@ -103,6 +173,7 @@ struct progress *start_progress_delay(const char *title, unsigned total, progress->last_percent = -1; progress->delayed_percent_treshold = percent_treshold; progress->delay = delay; + progress->throughput = NULL; set_progress_signal(); return progress; } @@ -124,5 +195,6 @@ void stop_progress(struct progress **p_progress) display(progress, progress->last_value, 1); } clear_progress_signal(); + free(progress->throughput); free(progress); } -- cgit v1.2.1 From 74b6792f7b2d4b410459cf96977a2007ddea8675 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 30 Oct 2007 15:41:13 -0400 Subject: add some copyright notice to the progress display code Some self patting on the back to keep my ego alive. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'progress.c') diff --git a/progress.c b/progress.c index 15197fbe6..34a59611f 100644 --- a/progress.c +++ b/progress.c @@ -1,3 +1,13 @@ +/* + * Simple text-based progress display module for GIT + * + * Copyright (c) 2007 by Nicolas Pitre + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + #include "git-compat-util.h" #include "progress.h" -- cgit v1.2.1 From 3e935d19822db08cc0dedd8764135771ffd6ec7b Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 1 Nov 2007 16:59:56 -0400 Subject: make sure throughput display gets updated even if progress doesn't move Currently the progress/throughput display update happens only through display_progress(). If the progress based on object count remains unchanged because a large object is being received, the latest throughput won't be displayed. The display update should occur through display_throughput() as well. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'progress.c') diff --git a/progress.c b/progress.c index 34a59611f..39d5d2c9f 100644 --- a/progress.c +++ b/progress.c @@ -160,6 +160,9 @@ void display_throughput(struct progress *progress, unsigned long n) tp->last_misecs[tp->idx] = misecs; tp->idx = (tp->idx + 1) % TP_IDX_MAX; tp->count = 0; + + if (progress->last_value != -1 && progress_update) + display(progress, progress->last_value, 0); } } -- cgit v1.2.1 From 81f6654a47075a345ba63a394921f77fc87b6500 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 1 Nov 2007 16:59:57 -0400 Subject: Show total transferred as part of throughput progress Right now it is infeasible to offer to the user a reasonable concept of when a clone will be complete as we aren't able to come up with the final pack size until after we have actually transferred the entire thing to the client. However in many cases users can work with a rough rule-of-thumb; for example it is somewhat well known that git.git is about 16 MiB today and that linux-2.6.git is over 120 MiB. We now show the total amount of data we have transferred over the network as part of the throughput meter, organizing it in "human friendly" terms like `ls -h` would do. Users can glance at this, see that the total transferred size is about 3 MiB, see the throughput of X KiB/sec, and determine a reasonable figure of about when the clone will be complete, assuming they know the rough size of the source repository or are able to obtain it. This is also a helpful indicator that there is progress being made even if we stall on a very large object. The thoughput meter may remain relatively constant and the percentage complete and object count won't be changing, but the total transferred will be increasing as additional data is received for this object. [from an initial proposal from Shawn O. Pearce] Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) (limited to 'progress.c') diff --git a/progress.c b/progress.c index 39d5d2c9f..3f6a602a5 100644 --- a/progress.c +++ b/progress.c @@ -15,13 +15,14 @@ struct throughput { struct timeval prev_tv; + off_t total; unsigned long count; unsigned long avg_bytes; unsigned long last_bytes[TP_IDX_MAX]; unsigned int avg_misecs; unsigned int last_misecs[TP_IDX_MAX]; unsigned int idx; - char display[20]; + char display[32]; }; struct progress { @@ -128,6 +129,7 @@ void display_throughput(struct progress *progress, unsigned long n) return; } + tp->total += n; tp->count += n; /* @@ -149,11 +151,32 @@ void display_throughput(struct progress *progress, unsigned long n) misecs += (int)(tv.tv_usec - tp->prev_tv.tv_usec) / 977; if (misecs > 512) { + int l = sizeof(tp->display); tp->prev_tv = tv; tp->avg_bytes += tp->count; tp->avg_misecs += misecs; - snprintf(tp->display, sizeof(tp->display), - ", %lu KiB/s", tp->avg_bytes / tp->avg_misecs); + + if (tp->total > 1 << 30) { + l -= snprintf(tp->display, l, ", %u.%2.2u GiB", + (int)(tp->total >> 30), + (int)(tp->total & ((1 << 30) - 1)) / 10737419); + } else if (tp->total > 1 << 20) { + l -= snprintf(tp->display, l, ", %u.%2.2u MiB", + (int)(tp->total >> 20), + ((int)(tp->total & ((1 << 20) - 1)) + * 100) >> 20); + } else if (tp->total > 1 << 10) { + l -= snprintf(tp->display, l, ", %u.%2.2u KiB", + (int)(tp->total >> 10), + ((int)(tp->total & ((1 << 10) - 1)) + * 100) >> 10); + } else { + l -= snprintf(tp->display, l, ", %u bytes", + (int)tp->total); + } + snprintf(tp->display + sizeof(tp->display) - l, l, + " | %lu KiB/s", tp->avg_bytes / tp->avg_misecs); + tp->avg_bytes -= tp->last_bytes[tp->idx]; tp->avg_misecs -= tp->last_misecs[tp->idx]; tp->last_bytes[tp->idx] = tp->count; -- cgit v1.2.1 From 218558af599c01e5dec17a7399d9188a76c50203 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sun, 4 Nov 2007 22:15:41 -0500 Subject: make display of total transferred more accurate The throughput display needs a delay period before accounting and displaying anything. Yet it might be called after some amount of data has already been transferred. The display of total data is therefore accounted late and therefore smaller than the reality. Let's call display_throughput() with an absolute amount of transferred data instead of a relative number, and let the throughput code find the relative amount of data by itself as needed. This way the displayed total is always exact. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) (limited to 'progress.c') diff --git a/progress.c b/progress.c index 3f6a602a5..a963bd8bf 100644 --- a/progress.c +++ b/progress.c @@ -14,11 +14,10 @@ #define TP_IDX_MAX 8 struct throughput { + off_t prev_total; struct timeval prev_tv; - off_t total; - unsigned long count; - unsigned long avg_bytes; - unsigned long last_bytes[TP_IDX_MAX]; + unsigned int avg_bytes; + unsigned int last_bytes[TP_IDX_MAX]; unsigned int avg_misecs; unsigned int last_misecs[TP_IDX_MAX]; unsigned int idx; @@ -110,7 +109,7 @@ static int display(struct progress *progress, unsigned n, int done) return 0; } -void display_throughput(struct progress *progress, unsigned long n) +void display_throughput(struct progress *progress, off_t total) { struct throughput *tp; struct timeval tv; @@ -124,14 +123,13 @@ void display_throughput(struct progress *progress, unsigned long n) if (!tp) { progress->throughput = tp = calloc(1, sizeof(*tp)); - if (tp) + if (tp) { + tp->prev_total = total; tp->prev_tv = tv; + } return; } - tp->total += n; - tp->count += n; - /* * We have x = bytes and y = microsecs. We want z = KiB/s: * @@ -152,37 +150,37 @@ void display_throughput(struct progress *progress, unsigned long n) if (misecs > 512) { int l = sizeof(tp->display); + unsigned int count = total - tp->prev_total; + tp->prev_total = total; tp->prev_tv = tv; - tp->avg_bytes += tp->count; + tp->avg_bytes += count; tp->avg_misecs += misecs; - if (tp->total > 1 << 30) { + if (total > 1 << 30) { l -= snprintf(tp->display, l, ", %u.%2.2u GiB", - (int)(tp->total >> 30), - (int)(tp->total & ((1 << 30) - 1)) / 10737419); - } else if (tp->total > 1 << 20) { + (int)(total >> 30), + (int)(total & ((1 << 30) - 1)) / 10737419); + } else if (total > 1 << 20) { l -= snprintf(tp->display, l, ", %u.%2.2u MiB", - (int)(tp->total >> 20), - ((int)(tp->total & ((1 << 20) - 1)) + (int)(total >> 20), + ((int)(total & ((1 << 20) - 1)) * 100) >> 20); - } else if (tp->total > 1 << 10) { + } else if (total > 1 << 10) { l -= snprintf(tp->display, l, ", %u.%2.2u KiB", - (int)(tp->total >> 10), - ((int)(tp->total & ((1 << 10) - 1)) + (int)(total >> 10), + ((int)(total & ((1 << 10) - 1)) * 100) >> 10); } else { - l -= snprintf(tp->display, l, ", %u bytes", - (int)tp->total); + l -= snprintf(tp->display, l, ", %u bytes", (int)total); } snprintf(tp->display + sizeof(tp->display) - l, l, - " | %lu KiB/s", tp->avg_bytes / tp->avg_misecs); + " | %u KiB/s", tp->avg_bytes / tp->avg_misecs); tp->avg_bytes -= tp->last_bytes[tp->idx]; tp->avg_misecs -= tp->last_misecs[tp->idx]; - tp->last_bytes[tp->idx] = tp->count; + tp->last_bytes[tp->idx] = count; tp->last_misecs[tp->idx] = misecs; tp->idx = (tp->idx + 1) % TP_IDX_MAX; - tp->count = 0; if (progress->last_value != -1 && progress_update) display(progress, progress->last_value, 0); -- cgit v1.2.1 From 53ed7b5a5d7a0ad2ffafd4a4ba4a7861f5db624e Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Tue, 6 Nov 2007 16:30:28 -0500 Subject: make display of total transferred fully accurate The minimum delay of 1/2 sec between successive throughput updates might not have been elapsed when display_throughput() is called for the last time, potentially making the display of total transferred bytes not right when progress is said to be done. Let's force an update of the throughput display as well when the progress is complete. As a side effect, the total transferred will always be displayed even if the actual transfer rate doesn't have time to kickin. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 64 ++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 25 deletions(-) (limited to 'progress.c') diff --git a/progress.c b/progress.c index a963bd8bf..0700dcf24 100644 --- a/progress.c +++ b/progress.c @@ -14,11 +14,12 @@ #define TP_IDX_MAX 8 struct throughput { + off_t curr_total; off_t prev_total; struct timeval prev_tv; unsigned int avg_bytes; - unsigned int last_bytes[TP_IDX_MAX]; unsigned int avg_misecs; + unsigned int last_bytes[TP_IDX_MAX]; unsigned int last_misecs[TP_IDX_MAX]; unsigned int idx; char display[32]; @@ -109,6 +110,30 @@ static int display(struct progress *progress, unsigned n, int done) return 0; } +static void throughput_string(struct throughput *tp, off_t total, + unsigned int rate) +{ + int l = sizeof(tp->display); + if (total > 1 << 30) { + l -= snprintf(tp->display, l, ", %u.%2.2u GiB", + (int)(total >> 30), + (int)(total & ((1 << 30) - 1)) / 10737419); + } else if (total > 1 << 20) { + l -= snprintf(tp->display, l, ", %u.%2.2u MiB", + (int)(total >> 20), + ((int)(total & ((1 << 20) - 1)) * 100) >> 20); + } else if (total > 1 << 10) { + l -= snprintf(tp->display, l, ", %u.%2.2u KiB", + (int)(total >> 10), + ((int)(total & ((1 << 10) - 1)) * 100) >> 10); + } else { + l -= snprintf(tp->display, l, ", %u bytes", (int)total); + } + if (rate) + snprintf(tp->display + sizeof(tp->display) - l, l, + " | %u KiB/s", rate); +} + void display_throughput(struct progress *progress, off_t total) { struct throughput *tp; @@ -124,11 +149,12 @@ void display_throughput(struct progress *progress, off_t total) if (!tp) { progress->throughput = tp = calloc(1, sizeof(*tp)); if (tp) { - tp->prev_total = total; + tp->prev_total = tp->curr_total = total; tp->prev_tv = tv; } return; } + tp->curr_total = total; /* * We have x = bytes and y = microsecs. We want z = KiB/s: @@ -149,39 +175,21 @@ void display_throughput(struct progress *progress, off_t total) misecs += (int)(tv.tv_usec - tp->prev_tv.tv_usec) / 977; if (misecs > 512) { - int l = sizeof(tp->display); - unsigned int count = total - tp->prev_total; + unsigned int count, rate; + + count = total - tp->prev_total; tp->prev_total = total; tp->prev_tv = tv; tp->avg_bytes += count; tp->avg_misecs += misecs; - - if (total > 1 << 30) { - l -= snprintf(tp->display, l, ", %u.%2.2u GiB", - (int)(total >> 30), - (int)(total & ((1 << 30) - 1)) / 10737419); - } else if (total > 1 << 20) { - l -= snprintf(tp->display, l, ", %u.%2.2u MiB", - (int)(total >> 20), - ((int)(total & ((1 << 20) - 1)) - * 100) >> 20); - } else if (total > 1 << 10) { - l -= snprintf(tp->display, l, ", %u.%2.2u KiB", - (int)(total >> 10), - ((int)(total & ((1 << 10) - 1)) - * 100) >> 10); - } else { - l -= snprintf(tp->display, l, ", %u bytes", (int)total); - } - snprintf(tp->display + sizeof(tp->display) - l, l, - " | %u KiB/s", tp->avg_bytes / tp->avg_misecs); - + rate = tp->avg_bytes / tp->avg_misecs; tp->avg_bytes -= tp->last_bytes[tp->idx]; tp->avg_misecs -= tp->last_misecs[tp->idx]; tp->last_bytes[tp->idx] = count; tp->last_misecs[tp->idx] = misecs; tp->idx = (tp->idx + 1) % TP_IDX_MAX; + throughput_string(tp, total, rate); if (progress->last_value != -1 && progress_update) display(progress, progress->last_value, 0); } @@ -225,6 +233,12 @@ void stop_progress(struct progress **p_progress) *p_progress = NULL; if (progress->last_value != -1) { /* Force the last update */ + struct throughput *tp = progress->throughput; + if (tp) { + unsigned int rate = !tp->avg_misecs ? 0 : + tp->avg_bytes / tp->avg_misecs; + throughput_string(tp, tp->curr_total, rate); + } progress_update = 1; display(progress, progress->last_value, 1); } -- cgit v1.2.1 From a984a06a07cdd0a843eb6107ad56e346d99ac840 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 8 Nov 2007 15:45:41 -0500 Subject: nicer display of thin pack completion In the same spirit of prettifying Git's output display for mere mortals, here's a simple extension to the progress API allowing for a final message to be provided when terminating a progress line, and use it for the display of the number of objects needed to complete a thin pack, saving yet one more line of screen display. Signed-off-by: Nicolas Pitre Signed-off-by: Junio C Hamano --- progress.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'progress.c') diff --git a/progress.c b/progress.c index 0700dcf24..4bd650f9b 100644 --- a/progress.c +++ b/progress.c @@ -69,9 +69,9 @@ static void clear_progress_signal(void) progress_update = 0; } -static int display(struct progress *progress, unsigned n, int done) +static int display(struct progress *progress, unsigned n, const char *done) { - char *eol, *tp; + const char *eol, *tp; if (progress->delay) { if (!progress_update || --progress->delay) @@ -90,7 +90,7 @@ static int display(struct progress *progress, unsigned n, int done) progress->last_value = n; tp = (progress->throughput) ? progress->throughput->display : ""; - eol = done ? ", done. \n" : " \r"; + eol = done ? done : " \r"; if (progress->total) { unsigned percent = n * 100 / progress->total; if (percent != progress->last_percent || progress_update) { @@ -191,13 +191,13 @@ void display_throughput(struct progress *progress, off_t total) throughput_string(tp, total, rate); if (progress->last_value != -1 && progress_update) - display(progress, progress->last_value, 0); + display(progress, progress->last_value, NULL); } } int display_progress(struct progress *progress, unsigned n) { - return progress ? display(progress, n, 0) : 0; + return progress ? display(progress, n, NULL) : 0; } struct progress *start_progress_delay(const char *title, unsigned total, @@ -226,6 +226,11 @@ struct progress *start_progress(const char *title, unsigned total) } void stop_progress(struct progress **p_progress) +{ + stop_progress_msg(p_progress, "done"); +} + +void stop_progress_msg(struct progress **p_progress, const char *msg) { struct progress *progress = *p_progress; if (!progress) @@ -233,6 +238,7 @@ void stop_progress(struct progress **p_progress) *p_progress = NULL; if (progress->last_value != -1) { /* Force the last update */ + char buf[strlen(msg) + 5]; struct throughput *tp = progress->throughput; if (tp) { unsigned int rate = !tp->avg_misecs ? 0 : @@ -240,7 +246,8 @@ void stop_progress(struct progress **p_progress) throughput_string(tp, tp->curr_total, rate); } progress_update = 1; - display(progress, progress->last_value, 1); + sprintf(buf, ", %s.\n", msg); + display(progress, progress->last_value, buf); } clear_progress_signal(); free(progress->throughput); -- cgit v1.2.1 From 137a0d0ef568f0a2468c8c7f053ef113e295842f Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Mon, 19 Nov 2007 20:48:58 +0100 Subject: Flush progress message buffer in display(). This will make progress display from pack-objects (invoked via upload-pack) more responsive on platforms with an implementation of stdio whose stderr is line buffered. The standard error stream is defined to be merely "not fully buffered"; this is different from "unbuffered". If the implementation of the stdio library chooses to make it line buffered, progress reports that end with CR but do not contain LF will accumulate in the stdio buffer before written out. A fflush() after each progress display gives a nice continuous progress. Signed-off-by: Johannes Sixt Signed-off-by: Junio C Hamano --- progress.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'progress.c') diff --git a/progress.c b/progress.c index 4bd650f9b..d19f80c0b 100644 --- a/progress.c +++ b/progress.c @@ -98,11 +98,13 @@ static int display(struct progress *progress, unsigned n, const char *done) fprintf(stderr, "%s: %3u%% (%u/%u)%s%s", progress->title, percent, n, progress->total, tp, eol); + fflush(stderr); progress_update = 0; return 1; } } else if (progress_update) { fprintf(stderr, "%s: %u%s%s", progress->title, n, tp, eol); + fflush(stderr); progress_update = 0; return 1; } @@ -207,6 +209,7 @@ struct progress *start_progress_delay(const char *title, unsigned total, if (!progress) { /* unlikely, but here's a good fallback */ fprintf(stderr, "%s...\n", title); + fflush(stderr); return NULL; } progress->title = title; -- cgit v1.2.1 From d4c44443b665ee8e6bd638b5c1b3fa6aa2a1226c Mon Sep 17 00:00:00 2001 From: Boyd Lynn Gerber Date: Sun, 8 Jun 2008 09:26:15 -0600 Subject: progress.c: avoid use of dynamic-sized array Dynamically sized arrays are gcc and C99 construct. Using them hurts portability to older compilers, although using them is nice in this case it is not desirable. This patch removes the only use of the construct in stop_progress_msg(); the function is about writing out a single line of a message, and the existing callers of this function feed messages of only bounded size anyway, so use of dynamic array is simply overkill. Signed-off-by: Boyd Lynn Gerber Signed-off-by: Junio C Hamano --- progress.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'progress.c') diff --git a/progress.c b/progress.c index d19f80c0b..55a8687ad 100644 --- a/progress.c +++ b/progress.c @@ -241,16 +241,21 @@ void stop_progress_msg(struct progress **p_progress, const char *msg) *p_progress = NULL; if (progress->last_value != -1) { /* Force the last update */ - char buf[strlen(msg) + 5]; + char buf[128], *bufp; + size_t len = strlen(msg) + 5; struct throughput *tp = progress->throughput; + + bufp = (len < sizeof(buf)) ? buf : xmalloc(len + 1); if (tp) { unsigned int rate = !tp->avg_misecs ? 0 : tp->avg_bytes / tp->avg_misecs; throughput_string(tp, tp->curr_total, rate); } progress_update = 1; - sprintf(buf, ", %s.\n", msg); - display(progress, progress->last_value, buf); + sprintf(bufp, ", %s.\n", msg); + display(progress, progress->last_value, bufp); + if (buf != bufp) + free(bufp); } clear_progress_signal(); free(progress->throughput); -- cgit v1.2.1 From 66913284f06da58e2243acfad951a57501b8d813 Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Fri, 24 Apr 2009 17:46:15 -0400 Subject: progress bar: round to the nearest instead of truncating down Often the throughput output is requested when the data read so far is one smaller than multiple of 1024; because 1023/1024 is ~0.999, it often shows up as 0.99 because the code currently truncates. Signed-off-by: Junio C Hamano --- progress.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'progress.c') diff --git a/progress.c b/progress.c index 55a8687ad..621c34edc 100644 --- a/progress.c +++ b/progress.c @@ -121,13 +121,13 @@ static void throughput_string(struct throughput *tp, off_t total, (int)(total >> 30), (int)(total & ((1 << 30) - 1)) / 10737419); } else if (total > 1 << 20) { + int x = total + 5243; /* for rounding */ l -= snprintf(tp->display, l, ", %u.%2.2u MiB", - (int)(total >> 20), - ((int)(total & ((1 << 20) - 1)) * 100) >> 20); + x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20); } else if (total > 1 << 10) { + int x = total + 5; /* for rounding */ l -= snprintf(tp->display, l, ", %u.%2.2u KiB", - (int)(total >> 10), - ((int)(total & ((1 << 10) - 1)) * 100) >> 10); + x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10); } else { l -= snprintf(tp->display, l, ", %u bytes", (int)total); } -- cgit v1.2.1