tech-userlevel archive

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

Re: humanize_number(3) for dd(1) summary?



On 04.12.2010 19:05, Jean-Yves Migeon wrote:
On 04.12.2010 17:54, Matt Thomas wrote:
I use dd to test performance and 2 digits of data is not neough.
I actually like the current output.  I also support the idea if
you want humanized numbers, a conv= option would be best.

I'll do as Christos suggested: leave the output as is, but implement a
msgfmt parameter where you can either:
- pass options:
        'msgfmt=human' (human readable output)
        'msgfmt=quiet' (silent mode -- if you don't care about output)
- pass a string format, similar to date(1) with strftime(3):
        msgfmt="%i records in\n%B bytes\nfoo %bar baz\n"

Default output will remain the same. Those that want to use dd(1) for
specific stuff like benchmarks or testing could use the msgfmt string to
tailor output to their liking.

Wow, I forgot I wrote this a long time ago and kept it sleeping in part of my tree *sigh*

The attached patch extends dd(1) in a way that permits specifying either:
- a msgfmt, with "human", "quiet", "posix" currently in place
- an arbitrary format, when passed as msgfmt=... to command line by user.

Of course the default behavior of dd(1) remains POSIX. It's only when the user passes msgfmt as argument that dd(1) will modify its summary() message.

Examples:

$ ./dd if=/dev/zero of=/dev/null bs=1m count=128 msgfmt=quiet
$ ./dd if=/dev/zero of=/dev/null bs=1m count=128 msgfmt=human
128+0 records in
128+0 records out
134217728 bytes (128 MB) transferred in 0.010 secs (13421772800 bytes/sec - 13 GB/sec)
$ ./dd if=/dev/zero of=/dev/null bs=1m count=128 msgfmt='
<speed>%E</speed>'
<speed>13 GB/sec</speed>

The current format characters being:
'b' total bytes transferred
'B' total bytes transferred, via humanize_number(3)
'e' speed transfer
'E' speed transfer, via humanize_number(3)
'i' # partial input block(s)
'I' # full input block(s)
'o' # partial output block(s)
'O' # full output block(s)
's' time elapsed, in sec.ms format
'p' # sparse block(s)
't' # truncated block(s)
'w' # swab block(s)
'P' plural for "record", when # sparse blocks > 1
'T' plural for "record", when # truncated blocks > 1
'W' plural for "record", when # swab blocks > 1

This resembles what Christos did with sockaddr_snprintf(3), except that mine (dd_snprintf(3)) remains private to dd(1) and is only exposed to outside world via the msgfmt= argument.

After some quick testing, I noticed that it may be more convenient to handle special chars through unvis(3), especially as '\n' could appear in the format. Good idea or not?

BTW, OG mandates that the term "record" should be used for truncated blocks [1], so I replaced "block" with "record" to comply with it here. Note that coreutils comply, but not FreeBSD's dd(1). Not sure if it's really necessary.

http://www.opengroup.org/sud/sud1/xcu/dd.htm#dd-user-exde-stde

--
Jean-Yves Migeon
jeanyves.migeon%free.fr@localhost
Index: extern.h
===================================================================
RCS file: /cvsroot/src/bin/dd/extern.h,v
retrieving revision 1.20
diff -u -p -r1.20 extern.h
--- extern.h    16 Sep 2011 16:06:23 -0000      1.20
+++ extern.h    2 Nov 2011 00:50:52 -0000
@@ -56,6 +56,7 @@ void pos_in(void);
 void pos_out(void);
 void summary(void);
 void summaryx(int);
+int  dd_snprintf(char *, size_t, const char *);
 __dead void terminate(int);
 void unblock(void);
 void unblock_close(void);
Index: misc.c
===================================================================
RCS file: /cvsroot/src/bin/dd/misc.c,v
retrieving revision 1.21
diff -u -p -r1.21 misc.c
--- misc.c      5 Oct 2007 07:23:09 -0000       1.21
+++ misc.c      2 Nov 2011 00:50:53 -0000
@@ -74,35 +74,23 @@ summary(void)
        if (mS == 0)
                mS = 1;
        /* Use snprintf(3) so that we don't reenter stdio(3). */
-       (void)snprintf(buf, sizeof(buf),
-           "%llu+%llu records in\n%llu+%llu records out\n",
-           (unsigned long long)st.in_full,  (unsigned long long)st.in_part,
-           (unsigned long long)st.out_full, (unsigned long long)st.out_part);
+       (void)dd_snprintf(buf, sizeof(buf),
+           "%I+%i records in\n%O+%o records out\n");
        (void)write(STDERR_FILENO, buf, strlen(buf));
        if (st.swab) {
-               (void)snprintf(buf, sizeof(buf), "%llu odd length swab %s\n",
-                   (unsigned long long)st.swab,
-                   (st.swab == 1) ? "block" : "blocks");
+               (void)dd_snprintf(buf, sizeof(buf), "%w odd length swab %W\n");
                (void)write(STDERR_FILENO, buf, strlen(buf));
        }
        if (st.trunc) {
-               (void)snprintf(buf, sizeof(buf), "%llu truncated %s\n",
-                   (unsigned long long)st.trunc,
-                   (st.trunc == 1) ? "block" : "blocks");
+               (void)dd_snprintf(buf, sizeof(buf), "%t truncated %T\n");
                (void)write(STDERR_FILENO, buf, strlen(buf));
        }
        if (st.sparse) {
-               (void)snprintf(buf, sizeof(buf), "%llu sparse output %s\n",
-                   (unsigned long long)st.sparse,
-                   (st.sparse == 1) ? "block" : "blocks");
+               (void)dd_snprintf(buf, sizeof(buf), "%p sparse output %P\n");
                (void)write(STDERR_FILENO, buf, strlen(buf));
        }
-       (void)snprintf(buf, sizeof(buf),
-           "%llu bytes transferred in %lu.%03d secs (%llu bytes/sec)\n",
-           (unsigned long long) st.bytes,
-           (long) (mS / 1000),
-           (int) (mS % 1000),
-           (unsigned long long) (st.bytes * 1000LL / mS));
+       (void)dd_snprintf(buf, sizeof(buf),
+           "%b bytes transferred in %s secs (%e bytes/sec)\n");
        (void)write(STDERR_FILENO, buf, strlen(buf));
 }
 
@@ -123,3 +111,124 @@ terminate(int signo)
        (void)raise_default_signal(signo);
        _exit(127);
 }
+
+int
+dd_snprintf(char *sbuf, size_t len, const char *fmt)
+{
+       char nbuf[64], hbuf[7];
+       char *ebuf = &sbuf[len - 1], *buf = sbuf;
+       const char *ptr, *s;
+       int64_t mS;
+        struct timeval tv;
+
+       (void)gettimeofday(&tv, NULL);
+       mS = tv2mS(tv) - tv2mS(st.start);
+       if (mS == 0)
+               mS = 1;
+
+#define ADDC(c) do { if (buf < ebuf) *buf++ = c; else buf++; } \
+       while (/*CONSTCOND*/0)
+#define ADDS(p) do { for (s = p; *s; s++) ADDC(*s); } \
+       while (/*CONSTCOND*/0)
+
+       for (ptr = fmt; *ptr; ptr++) {
+               if (*ptr != '%') {
+                       ADDC(*ptr);
+                       continue;
+               }
+
+               switch (*++ptr) {
+               case 'b':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.bytes);
+                       ADDS(nbuf);
+                       break;
+               case 'B':
+                       if (humanize_number(hbuf, sizeof(hbuf),
+                           st.bytes, "B",
+                           HN_AUTOSCALE, HN_DECIMAL) == -1)
+                               warnx("humanize_number (bytes transferred)");
+                       ADDS(hbuf);
+                       break;
+               case 'e':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long) (st.bytes * 1000LL / mS));
+                       ADDS(nbuf);
+                       break;
+               case 'E':
+                       if (humanize_number(hbuf, sizeof(hbuf),
+                           st.bytes * 1000LL / mS, "B",
+                           HN_AUTOSCALE, HN_DECIMAL) == -1)
+                               warnx("humanize_number (bytes per second)");
+                       ADDS(hbuf);
+                       break;
+               case 'i':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.in_part);
+                       ADDS(nbuf);
+                       break;
+               case 'I':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.in_full);
+                       ADDS(nbuf);
+                       break;
+               case 'o':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.out_part);
+                       ADDS(nbuf);
+                       break;
+               case 'O':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.out_full);
+                       ADDS(nbuf);
+                       break;
+               case 's':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%li.%03d",
+                           (long) (mS / 1000), (int) (mS % 1000));
+                       ADDS(nbuf);
+                       break;
+               case 'p':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.sparse);
+                       ADDS(nbuf);
+                       break;
+               case 't':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.trunc);
+                       ADDS(nbuf);
+                       break;
+               case 'w':
+                       (void)snprintf(nbuf, sizeof(nbuf), "%llu",
+                           (unsigned long long)st.swab);
+                       ADDS(nbuf);
+                       break;
+               case 'P':
+                       ADDS("record");
+                       if (st.sparse != 1) ADDS("s");
+                       break;
+               case 'T':
+                       ADDS("record");
+                       if (st.trunc != 1) ADDS("s");
+                       break;
+               case 'W':
+                       ADDS("record");
+                       if (st.swab != 1) ADDS("s");
+                       break;
+               default:
+                       ADDC('%');
+                       if (*ptr == '\0')
+                               goto done;
+                       /*FALLTHROUGH*/
+               case '%':
+                       ADDC(*ptr);
+                       break;
+               }
+       }
+
+done:
+       if (buf < ebuf)
+               *buf = '\0';
+       else if (len != 0)
+               sbuf[len - 1] = '\0';
+       return (int)(buf - sbuf);
+}


Home | Main Index | Thread Index | Old Index