From cab8de142f6f9cba01161c444d3f6bda08561e45 Mon Sep 17 00:00:00 2001 From: Bryan Cantrill Date: Fri, 11 Jan 2013 08:53:08 -0800 Subject: [PATCH] 3668 add "%H" to mdb_printf() for human-readable sizes Reviewed by: Robert Mustacchi Reviewed by: Carlos Cardenas Reviewed by: Dan McDonald Approved by: Gordon Ross --- usr/src/cmd/mdb/common/mdb/mdb_io.c | 142 ++++++++++++++++++++++++++++++++- usr/src/cmd/mdb/common/mdb/mdb_print.c | 6 +- 2 files changed, 145 insertions(+), 3 deletions(-) diff --git a/usr/src/cmd/mdb/common/mdb/mdb_io.c b/usr/src/cmd/mdb/common/mdb/mdb_io.c index bd2aabac79..4365b0a677 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_io.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_io.c @@ -23,7 +23,9 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2012, Joyent, Inc. All rights reserved. + */ /* * MDB uses its own enhanced standard i/o mechanism for all input and output. @@ -686,6 +688,9 @@ fmt_switch: size = SZ_SHORT; goto fmt_switch; + case 'H': + return ("human-readable size"); + case 'I': return ("IPv4 address"); @@ -868,6 +873,137 @@ iob_addr2str(uintptr_t addr) return (name); } +/* + * Produce human-readable size, similar in spirit (and identical in output) + * to libzfs's zfs_nicenum() -- but made significantly more complicated by + * the constraint that we cannot use snprintf() as an implementation detail. + */ +static const char * +iob_bytes2str(varglist_t *ap, intsize_t size) +{ + const int sigfig = 3; + uint64_t n, orig; + static char buf[68], *c; + int index = 0; + char u; + + switch (size) { + case SZ_LONGLONG: + n = (u_longlong_t)VA_ARG(ap, u_longlong_t); + break; + + case SZ_LONG: + n = (ulong_t)VA_ARG(ap, ulong_t); + break; + + case SZ_SHORT: + n = (ushort_t)VA_ARG(ap, uint_t); + + default: + n = (uint_t)VA_ARG(ap, uint_t); + } + + orig = n; + + while (n >= 1024) { + n /= 1024; + index++; + } + + u = " KMGTPE"[index]; + buf[0] = '\0'; + + if (index == 0) { + return (numtostr(n, 10, 0)); + } else if ((orig & ((1ULL << 10 * index) - 1)) == 0) { + /* + * If this is an even multiple of the base, always display + * without any decimal precision. + */ + (void) strcat(buf, numtostr(n, 10, 0)); + } else { + /* + * We want to choose a precision that results in the specified + * number of significant figures (by default, 3). This is + * similar to the output that one would get specifying the %.*g + * format specifier (where the asterisk denotes the number of + * significant digits), but (1) we include trailing zeros if + * the there are non-zero digits beyond the number of + * significant digits (that is, 10241 is '10.0K', not the + * '10K' that it would be with %.3g) and (2) we never resort + * to %e notation when the number of digits exceeds the + * number of significant figures (that is, 1043968 is '1020K', + * not '1.02e+03K'). This is also made somewhat complicated + * by the fact that we need to deal with rounding (10239 is + * '10.0K', not '9.99K'), for which we perform nearest-even + * rounding. + */ + double val = (double)orig / (1ULL << 10 * index); + int i, mag = 1, thresh; + + for (i = 0; i < sigfig - 1; i++) + mag *= 10; + + for (thresh = mag * 10; mag >= 1; mag /= 10, i--) { + double mult = val * (double)mag; + uint32_t v; + + /* + * Note that we cast mult to a 32-bit value. We know + * that val is less than 1024 due to the logic above, + * and that mag is at most 10^(sigfig - 1). This means + * that as long as sigfig is 9 or lower, this will not + * overflow. (We perform this cast because it assures + * that we are never converting a double to a uint64_t, + * which for some compilers requires a call to a + * function not guaranteed to be in libstand.) + */ + if (mult - (double)(uint32_t)mult != 0.5) { + v = (uint32_t)(mult + 0.5); + } else { + /* + * We are exactly between integer multiples + * of units; perform nearest-even rounding + * to be consistent with the behavior of + * printf(). + */ + if ((v = (uint32_t)mult) & 1) + v++; + } + + if (mag == 1) { + (void) strcat(buf, numtostr(v, 10, 0)); + break; + } + + if (v < thresh) { + (void) strcat(buf, numtostr(v / mag, 10, 0)); + (void) strcat(buf, "."); + + c = (char *)numtostr(v % mag, 10, 0); + i -= strlen(c); + + /* + * We need to zero-fill from the right of the + * decimal point to the first significant digit + * of the fractional component. + */ + while (i--) + (void) strcat(buf, "0"); + + (void) strcat(buf, c); + break; + } + } + } + + c = &buf[strlen(buf)]; + *c++ = u; + *c++ = '\0'; + + return (buf); +} + static int iob_setattr(mdb_iob_t *iob, const char *s, size_t nbytes) { @@ -1173,6 +1309,10 @@ iob_doprnt(mdb_iob_t *iob, const char *format, varglist_t *ap) size = SZ_SHORT; goto fmt_switch; + case 'H': + u.str = iob_bytes2str(ap, size); + break; + case 'I': u.ui32 = VA_ARG(ap, uint32_t); u.str = iob_inaddr2str(u.ui32); diff --git a/usr/src/cmd/mdb/common/mdb/mdb_print.c b/usr/src/cmd/mdb/common/mdb/mdb_print.c index 365f96ce11..910cc9ecfe 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_print.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_print.c @@ -2911,6 +2911,7 @@ cmd_printf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) funcs[nfmts] = printf_ipv6; break; + case 'H': case 'o': case 'r': case 'u': @@ -3028,8 +3029,9 @@ static char _mdb_printf_help[] = " %a Prints the member in symbolic form.\n" " %d Prints the member as a decimal integer. If the member is a signed\n" " integer type, the output will be signed.\n" -" %I Prints the member a IPv4 address (must be a 32-bit integer type).\n" -" %N Prints the member an IPv6 address (must be of type in6_addr_t).\n" +" %H Prints the member as a human-readable size.\n" +" %I Prints the member as an IPv4 address (must be 32-bit integer type).\n" +" %N Prints the member as an IPv6 address (must be of type in6_addr_t).\n" " %o Prints the member as an unsigned octal integer.\n" " %p Prints the member as a pointer, in hexadecimal.\n" " %q Prints the member in signed octal. Honk if you ever use this!\n" -- 2.11.4.GIT