bug-coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

dd patch to output transfer rate, byte count, and time


From: Paul Eggert
Subject: dd patch to output transfer rate, byte count, and time
Date: Sun, 14 Nov 2004 22:57:51 -0800
User-agent: Gnus/5.1006 (Gnus v5.10.6) Emacs/21.3 (gnu/linux)

The Debian unstable version of "dd" also outputs a total byte count,
seconds taken, and transfer rate.  This is an often-asked-for feature,
and seems useful.  I installed this implementation instead, which I
wrote from scratch.  The Debian version suppresses this new output if
POSIXLY_CORRECT is set, but I just checked POSIX and it allows this
behavior so I turned it on unconditionally.

In theory this change might break some scripts.  Does anybody think
this will be a problem in practice?  If so, I suppose we could enable
the new behavior only conditionally.

2004-11-14  Paul Eggert  <address@hidden>

        * NEWS: dd now outputs total bytes, seconds, and bytes per second.
        * src/Makefile.am (dd_LDADD): Add $(LIB_CLOCK_GETTIME).
        * src/dd.c: Include "human.h".
        (w_bytes, start_time): New vars.
        (usage): Document new I/O statistics output
        (print_stats): Output new I/O statistics.
        (cleanup): Do statistics after closing stdin and stdout, so that
        the times are more accurate.
        (write_output, dd_copy): Count output bytes.
        (main): Get initial value of clock.
        * doc/coreutils.texi (dd invocation): dd now outputs total bytes,
        seconds, and bytes per second.

Index: NEWS
===================================================================
RCS file: /fetish/cu/NEWS,v
retrieving revision 1.248
diff -p -u -r1.248 NEWS
--- NEWS        13 Nov 2004 00:52:32 -0000      1.248
+++ NEWS        15 Nov 2004 06:47:53 -0000
@@ -182,6 +182,9 @@ GNU coreutils NEWS                      
   copying or moving multiple times to the same destination in a file
   system with a coarse time stamp resolution.
 
+  dd now also prints the number of bytes copied, the time, and the
+  transfer rate.
+
   dd has new conversions for the conv= option:
 
     nocreat   do not create the output file
Index: src/Makefile.am
===================================================================
RCS file: /fetish/cu/src/Makefile.am,v
retrieving revision 1.45
diff -p -u -r1.45 Makefile.am
--- src/Makefile.am     3 Nov 2004 23:12:55 -0000       1.45
+++ src/Makefile.am     15 Nov 2004 06:47:53 -0000
@@ -41,7 +41,7 @@ rm_LDADD = $(LDADD) $(LIB_EACCESS)
 test_LDADD = $(LDADD) $(LIB_EACCESS)
 
 # for clock_gettime and fdatasync
-dd_LDADD = $(LDADD) $(LIB_FDATASYNC)
+dd_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_FDATASYNC)
 dir_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME)
 ls_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME)
 shred_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) $(LIB_FDATASYNC)
Index: src/dd.c
===================================================================
RCS file: /fetish/cu/src/dd.c,v
retrieving revision 1.167
diff -p -u -r1.167 dd.c
--- src/dd.c    21 Sep 2004 22:07:51 -0000      1.167
+++ src/dd.c    15 Nov 2004 06:47:53 -0000
@@ -30,6 +30,7 @@
 #include "error.h"
 #include "full-write.h"
 #include "getpagesize.h"
+#include "human.h"
 #include "inttostr.h"
 #include "long-options.h"
 #include "quote.h"
@@ -140,6 +141,12 @@ static uintmax_t r_partial = 0;
 /* Number of full blocks read. */
 static uintmax_t r_full = 0;
 
+/* Number of bytes written.  */
+static uintmax_t w_bytes = 0;
+
+/* Time that dd started.  */
+static struct timespec start_time;
+
 /* True if input is seekable.  */
 static bool input_seekable;
 
@@ -402,14 +409,14 @@ Each FLAG symbol may be:\n\
        fputs (_("  nofollow  do not follow symlinks\n"), stdout);
       fputs (_("\
 \n\
-Note that sending a SIGUSR1 signal to a running `dd' process makes it\n\
-print to standard error the number of records read and written so far,\n\
-then to resume copying.\n\
+Sending a SIGUSR1 signal to a running `dd' process makes it\n\
+print I/O statistics to standard error, then to resume copying.\n\
 \n\
   $ dd if=/dev/zero of=/dev/null& pid=$!\n\
   $ kill -USR1 $pid; sleep 1; kill $pid\n\
-  10899206+0 records in\n\
-  10899206+0 records out\n\
+  10807656+0 records in\n\
+  10807656+0 records out\n\
+  5.5GB copied in 20.8225s (266MB/s)\n\
 \n\
 Options are:\n\
 \n\
@@ -442,7 +449,16 @@ multiple_bits_set (int i)
 static void
 print_stats (void)
 {
-  char buf[2][INT_BUFSIZE_BOUND (uintmax_t)];
+  char buf[2][MAX (INT_BUFSIZE_BOUND (uintmax_t), LONGEST_HUMAN_READABLE + 1)];
+  struct timespec now;
+  int human_opts =
+    human_autoscale | human_round_to_nearest | human_SI | human_B;
+  uintmax_t start_sec = start_time.tv_sec;
+  enum { BILLION = 1000000000 };
+  double delta_s;
+  char const *bytes_per_second;
+
+  gettime (&now);
   fprintf (stderr, _("%s+%s records in\n"),
           umaxtostr (r_full, buf[0]), umaxtostr (r_partial, buf[1]));
   fprintf (stderr, _("%s+%s records out\n"),
@@ -455,18 +471,49 @@ print_stats (void)
                ? _("truncated record")
                : _("truncated records")));
     }
+
+  /* Use integer arithmetic to compute the transfer rate if possible,
+     since that makes it easy to use SI abbreviations; otherwise, fall
+     back on floating-point without abbreviations.  */
+
+  if ((start_time.tv_sec < now.tv_sec
+       || (start_time.tv_sec == now.tv_sec
+          && start_time.tv_nsec < now.tv_nsec))
+      && now.tv_sec - start_sec < UINTMAX_MAX / BILLION)
+    {
+      uintmax_t delta_ns = (BILLION * (now.tv_sec - start_sec)
+                           + now.tv_nsec - start_time.tv_nsec);
+      delta_s = delta_ns / 1e9;
+      bytes_per_second = human_readable (w_bytes, buf[1], human_opts,
+                                        BILLION, delta_ns);
+    }
+  else
+    {
+      delta_s = now.tv_sec;
+      delta_s -= start_time.tv_sec;
+      delta_s += 1e-9 * (now.tv_nsec - start_time.tv_nsec);
+      if (0 < delta_s)
+       sprintf (buf[1], "%gB", w_bytes / delta_s);
+      else
+       sprintf (buf[1], "%s B", _("Infinity"));
+      bytes_per_second = buf[1];
+    }
+
+  fprintf (stderr, _("%s copied in %gs (%s/s)\n"),
+          human_readable (w_bytes, buf[0], human_opts, 1, 1),
+          delta_s, bytes_per_second);
 }
 
 static void
 cleanup (void)
 {
-  print_stats ();
   if (close (STDIN_FILENO) < 0)
     error (EXIT_FAILURE, errno,
           _("closing input file %s"), quote (input_file));
   if (close (STDOUT_FILENO) < 0)
     error (EXIT_FAILURE, errno,
           _("closing output file %s"), quote (output_file));
+  print_stats ();
 }
 
 static inline void
@@ -548,6 +595,7 @@ static void
 write_output (void)
 {
   size_t nwritten = full_write (STDOUT_FILENO, obuf, output_blocksize);
+  w_bytes += nwritten;
   if (nwritten != output_blocksize)
     {
       error (0, errno, _("writing to %s"), quote (output_file));
@@ -1238,6 +1286,7 @@ dd_copy (void)
       if (ibuf == obuf)                /* If not C_TWOBUFS. */
        {
          size_t nwritten = full_write (STDOUT_FILENO, obuf, n_bytes_read);
+         w_bytes += nwritten;
          if (nwritten != n_bytes_read)
            {
              error (0, errno, _("writing %s"), quote (output_file));
@@ -1297,6 +1346,7 @@ dd_copy (void)
   if (oc != 0)
     {
       size_t nwritten = full_write (STDOUT_FILENO, obuf, oc);
+      w_bytes += nwritten;
       if (nwritten != 0)
        w_partial++;
       if (nwritten != oc)
@@ -1450,6 +1500,8 @@ main (int argc, char **argv)
   install_handler (SIGPIPE, interrupt_handler);
   install_handler (SIGINFO, siginfo_handler);
 
+  gettime (&start_time);
+
   exit_status = dd_copy ();
 
   quit (exit_status);
Index: doc/coreutils.texi
===================================================================
RCS file: /fetish/cu/doc/coreutils.texi,v
retrieving revision 1.224
diff -p -u -r1.224 coreutils.texi
--- doc/coreutils.texi  29 Oct 2004 23:22:09 -0000      1.224
+++ doc/coreutils.texi  15 Nov 2004 06:47:55 -0000
@@ -6863,20 +6863,21 @@ tape=/dev/rmt/0
 @end example
 
 Note that sending a @samp{SIGUSR1} signal to a running @command{dd}
-process makes it print to standard error the number of records read
-and written so far, then to resume copying.  In the example below,
+process makes it print I/O statistics to standard error,
+then to resume copying.  In the example below,
 @command{dd} is run in the background to copy 10 million blocks.
-The @command{kill} command makes it output the first pair of
-intermediate record counts,
-and when @command{dd} completes, it outputs the final pair.
+The @command{kill} command makes it output intermediate I/O statistics,
+and when @command{dd} completes, it outputs the final statistics.
 
 @example
-$ dd if=/dev/zero of=/dev/null count=10M & pid=$!
-$ kill -s USR1 $pid; sleep 99
-5403604+0 records in
-5403604+0 records out
-10485760+0 records in
-10485760+0 records out
+$ dd if=/dev/zero of=/dev/null count=10MB & pid=$!
+$ kill -s USR1 $pid; wait $pid
+4111640+0 records in
+4111639+0 records out
+2.1GB copied in 7.95411s (265MB/s)
+10000000+0 records in
+10000000+0 records out
+5.1GB copied in 19.3794s (264MB/s)
 @end example
 
 @exitstatus




reply via email to

[Prev in Thread] Current Thread [Next in Thread]