X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=progress.c;h=d19f80c0bb25928383f8a1aff3f6f49f9a65131c;hb=5d921e2931e5041884d8f86fdccc9004a7b071cc;hp=c342e39c5d2e5673fc95e0cdc91856b0bc3cdd35;hpb=dc6a0757c4f966dd124bd85be2adad5a0b7b2167;p=git.git diff --git a/progress.c b/progress.c index c342e39c5..d19f80c0b 100644 --- a/progress.c +++ b/progress.c @@ -1,6 +1,30 @@ +/* + * 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" +#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 avg_misecs; + unsigned int last_bytes[TP_IDX_MAX]; + unsigned int last_misecs[TP_IDX_MAX]; + unsigned int idx; + char display[32]; +}; + struct progress { const char *title; int last_value; @@ -8,6 +32,7 @@ struct progress { unsigned last_percent; unsigned delay; unsigned delayed_percent_treshold; + struct throughput *throughput; }; static volatile sig_atomic_t progress_update; @@ -44,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; + const char *eol, *tp; if (progress->delay) { if (!progress_update || --progress->delay) @@ -64,18 +89,22 @@ static int display(struct progress *progress, unsigned n, int done) } progress->last_value = n; - eol = done ? ", done. \n" : " \r"; + tp = (progress->throughput) ? progress->throughput->display : ""; + eol = done ? done : " \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); + fflush(stderr); 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); + fflush(stderr); progress_update = 0; return 1; } @@ -83,9 +112,94 @@ 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; + 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_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: + * + * 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) { + 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; + 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, 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, @@ -95,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; @@ -103,6 +218,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; } @@ -113,6 +229,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) @@ -120,9 +241,18 @@ 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 : + tp->avg_bytes / tp->avg_misecs; + 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); free(progress); }