]> granicus.if.org Git - strace/commitdiff
Add custom (faster) vfprintf implementation (disabled by default)
authorDenys Vlasenko <vda.linux@googlemail.com>
Mon, 16 Apr 2012 16:22:19 +0000 (18:22 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Mon, 16 Apr 2012 16:22:19 +0000 (18:22 +0200)
* defs.h: Declare strace_vfprintf either as a alias to vfprintf
or as a bona fide function. USE_CUSTOM_PRINTF define controls whether
we use strace_vfprintf. By default, we don't.
* strace.c (tprintf): Call strace_vfprintf instead of vfprintf.
* vsprintf.c: New file, implements strace_vfprintf.

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Makefile.am
defs.h
strace.c
vsprintf.c [new file with mode: 0644]

index bbc5790b2ee0160e688ce9b3d0a85f7e5afe2ed2..7398b79394c8a21cf8a1679994d890ab6ed30fad 100644 (file)
@@ -17,7 +17,7 @@ AM_CPPFLAGS = -I$(srcdir)/$(OS)/$(ARCH) -I$(srcdir)/$(OS) -I$(builddir)/$(OS)
 strace_SOURCES = strace.c syscall.c count.c util.c desc.c file.c ipc.c \
                 io.c ioctl.c mem.c net.c process.c bjm.c quota.c \
                 resource.c signal.c sock.c system.c term.c time.c \
-                scsi.c stream.c block.c pathtrace.c mtd.c
+                scsi.c stream.c block.c pathtrace.c mtd.c vsprintf.c
 noinst_HEADERS = defs.h
 
 EXTRA_DIST = $(man_MANS) errnoent.sh signalent.sh syscallent.sh ioctlsort.c \
diff --git a/defs.h b/defs.h
index 50a54dd203026a6536d729089580f6059610588e..ddde2715198e5a99cdf13cc99b845cb64f6e76a9 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -469,6 +469,23 @@ void error_msg_and_die(const char *fmt, ...) __attribute__ ((noreturn, format(pr
 void perror_msg_and_die(const char *fmt, ...) __attribute__ ((noreturn, format(printf, 1, 2)));
 void die_out_of_memory(void) __attribute__ ((noreturn));
 
+#ifdef USE_CUSTOM_PRINTF
+/*
+ * Speed-optimized vfprintf implementation.
+ * See comment in vsprintf.c for allowed formats.
+ * Short version: %h[h]u, %zu, %tu are not allowed, use %[l[l]]u.
+ *
+ * It results in strace using about 5% less CPU in user space
+ * (compared to glibc version).
+ * But strace spends a lot of time in kernel space,
+ * so overall it does not appear to be a significant win.
+ * Thus disabled by default.
+ */
+int strace_vfprintf(FILE *fp, const char *fmt, va_list args);
+#else
+# define strace_vfprintf vfprintf
+#endif
+
 extern void set_sortby(const char *);
 extern void set_overhead(int);
 extern void qualify(const char *);
index 34690fc3c7659c5f143183bfeac72fc4ff4ee99b..857136d594f9cbb8a6d40dcd4bfc946e0979327e 100644 (file)
--- a/strace.c
+++ b/strace.c
@@ -512,7 +512,7 @@ tprintf(const char *fmt, ...)
 
        va_start(args, fmt);
        if (current_tcp) {
-               int n = vfprintf(current_tcp->outf, fmt, args);
+               int n = strace_vfprintf(current_tcp->outf, fmt, args);
                if (n < 0) {
                        if (current_tcp->outf != stderr)
                                perror(outfname == NULL
diff --git a/vsprintf.c b/vsprintf.c
new file mode 100644 (file)
index 0000000..71bdf8c
--- /dev/null
@@ -0,0 +1,783 @@
+/*
+ * Taken from Linux kernel's linux/lib/vsprintf.c
+ * and somewhat simplified.
+ *
+ * Copyright (C) 1991, 1992  Linus Torvalds
+ */
+/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
+/*
+ * Wirzenius wrote this portably, Torvalds fucked it up :-)
+ */
+
+#include "defs.h"
+#include <stdarg.h>
+#include <limits.h>
+
+#ifdef USE_CUSTOM_PRINTF
+
+#define noinline_for_stack /*nothing*/
+#define likely(expr)       (expr)
+#define unlikely(expr)     (expr)
+
+#define do_div(n, d)       ({ __typeof(num) t = n % d; n /= d; t; })
+
+#undef isdigit
+#define isdigit(a) ((unsigned char)((a) - '0') <= 9)
+
+static inline
+int skip_atoi(const char **s)
+{
+       int i = 0;
+       const char *p = *s;
+
+       while (isdigit(*p))
+               i = i*10 + *p++ - '0';
+
+       *s = p;
+       return i;
+}
+
+/* Decimal conversion is by far the most typical, and is used
+ * for /proc and /sys data. This directly impacts e.g. top performance
+ * with many processes running. We optimize it for speed
+ * using ideas described at <http://www.cs.uiowa.edu/~jones/bcd/divide.html>
+ * (with permission from the author, Douglas W. Jones).
+ */
+
+#if LONG_MAX != 0x7fffffffUL || LLONG_MAX != 0x7fffffffffffffffULL
+/* Formats correctly any integer in [0, 999999999] */
+static noinline_for_stack
+char *put_dec_full9(char *buf, unsigned q)
+{
+       unsigned r;
+
+       /* Possible ways to approx. divide by 10
+        * (x * 0x1999999a) >> 32 x < 1073741829 (multiply must be 64-bit)
+        * (x * 0xcccd) >> 19     x <      81920 (x < 262149 when 64-bit mul)
+        * (x * 0x6667) >> 18     x <      43699
+        * (x * 0x3334) >> 17     x <      16389
+        * (x * 0x199a) >> 16     x <      16389
+        * (x * 0x0ccd) >> 15     x <      16389
+        * (x * 0x0667) >> 14     x <       2739
+        * (x * 0x0334) >> 13     x <       1029
+        * (x * 0x019a) >> 12     x <       1029
+        * (x * 0x00cd) >> 11     x <       1029 shorter code than * 0x67 (on i386)
+        * (x * 0x0067) >> 10     x <        179
+        * (x * 0x0034) >>  9     x <         69 same
+        * (x * 0x001a) >>  8     x <         69 same
+        * (x * 0x000d) >>  7     x <         69 same, shortest code (on i386)
+        * (x * 0x0007) >>  6     x <         19
+        * See <http://www.cs.uiowa.edu/~jones/bcd/divide.html>
+        */
+       r      = (q * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (q - 10 * r) + '0'; /* 1 */
+       q      = (r * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (r - 10 * q) + '0'; /* 2 */
+       r      = (q * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (q - 10 * r) + '0'; /* 3 */
+       q      = (r * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (r - 10 * q) + '0'; /* 4 */
+       r      = (q * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (q - 10 * r) + '0'; /* 5 */
+       /* Now value is under 10000, can avoid 64-bit multiply */
+       q      = (r * 0x199a) >> 16;
+       *buf++ = (r - 10 * q)  + '0'; /* 6 */
+       r      = (q * 0xcd) >> 11;
+       *buf++ = (q - 10 * r)  + '0'; /* 7 */
+       q      = (r * 0xcd) >> 11;
+       *buf++ = (r - 10 * q) + '0'; /* 8 */
+       *buf++ = q + '0'; /* 9 */
+       return buf;
+}
+#endif
+
+/* Similar to above but do not pad with zeros.
+ * Code can be easily arranged to print 9 digits too, but our callers
+ * always call put_dec_full9() instead when the number has 9 decimal digits.
+ */
+static noinline_for_stack
+char *put_dec_trunc8(char *buf, unsigned r)
+{
+       unsigned q;
+
+       /* Copy of previous function's body with added early returns */
+       q      = (r * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (r - 10 * q) + '0'; /* 2 */
+       if (q == 0) return buf;
+       r      = (q * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (q - 10 * r) + '0'; /* 3 */
+       if (r == 0) return buf;
+       q      = (r * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (r - 10 * q) + '0'; /* 4 */
+       if (q == 0) return buf;
+       r      = (q * (uint64_t)0x1999999a) >> 32;
+       *buf++ = (q - 10 * r) + '0'; /* 5 */
+       if (r == 0) return buf;
+       q      = (r * 0x199a) >> 16;
+       *buf++ = (r - 10 * q)  + '0'; /* 6 */
+       if (q == 0) return buf;
+       r      = (q * 0xcd) >> 11;
+       *buf++ = (q - 10 * r)  + '0'; /* 7 */
+       if (r == 0) return buf;
+       q      = (r * 0xcd) >> 11;
+       *buf++ = (r - 10 * q) + '0'; /* 8 */
+       if (q == 0) return buf;
+       *buf++ = q + '0'; /* 9 */
+       return buf;
+}
+
+/* There are two algorithms to print larger numbers.
+ * One is generic: divide by 1000000000 and repeatedly print
+ * groups of (up to) 9 digits. It's conceptually simple,
+ * but requires a (unsigned long long) / 1000000000 division.
+ *
+ * Second algorithm splits 64-bit unsigned long long into 16-bit chunks,
+ * manipulates them cleverly and generates groups of 4 decimal digits.
+ * It so happens that it does NOT require long long division.
+ *
+ * If long is > 32 bits, division of 64-bit values is relatively easy,
+ * and we will use the first algorithm.
+ * If long long is > 64 bits (strange architecture with VERY large long long),
+ * second algorithm can't be used, and we again use the first one.
+ *
+ * Else (if long is 32 bits and long long is 64 bits) we use second one.
+ */
+
+#if LONG_MAX != 0x7fffffffUL || LLONG_MAX != 0x7fffffffffffffffULL
+
+/* First algorithm: generic */
+
+static
+char *put_dec(char *buf, unsigned long long n)
+{
+       if (n >= 100*1000*1000) {
+               while (n >= 1000*1000*1000)
+                       buf = put_dec_full9(buf, do_div(n, 1000*1000*1000));
+               if (n >= 100*1000*1000)
+                       return put_dec_full9(buf, n);
+       }
+       return put_dec_trunc8(buf, n);
+}
+
+#else
+
+/* Second algorithm: valid only for 64-bit long longs */
+
+static noinline_for_stack
+char *put_dec_full4(char *buf, unsigned q)
+{
+       unsigned r;
+       r      = (q * 0xcccd) >> 19;
+       *buf++ = (q - 10 * r) + '0';
+       q      = (r * 0x199a) >> 16;
+       *buf++ = (r - 10 * q)  + '0';
+       r      = (q * 0xcd) >> 11;
+       *buf++ = (q - 10 * r)  + '0';
+       *buf++ = r + '0';
+       return buf;
+}
+
+/* Based on code by Douglas W. Jones found at
+ * <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour>
+ * (with permission from the author).
+ * Performs no 64-bit division and hence should be fast on 32-bit machines.
+ */
+static
+char *put_dec(char *buf, unsigned long long n)
+{
+       uint32_t d3, d2, d1, q, h;
+
+       if (n < 100*1000*1000)
+               return put_dec_trunc8(buf, n);
+
+       d1  = ((uint32_t)n >> 16); /* implicit "& 0xffff" */
+       h   = (n >> 32);
+       d2  = (h      ) & 0xffff;
+       d3  = (h >> 16); /* implicit "& 0xffff" */
+
+       q   = 656 * d3 + 7296 * d2 + 5536 * d1 + ((uint32_t)n & 0xffff);
+
+       buf = put_dec_full4(buf, q % 10000);
+       q   = q / 10000;
+
+       d1  = q + 7671 * d3 + 9496 * d2 + 6 * d1;
+       buf = put_dec_full4(buf, d1 % 10000);
+       q   = d1 / 10000;
+
+       d2  = q + 4749 * d3 + 42 * d2;
+       buf = put_dec_full4(buf, d2 % 10000);
+       q   = d2 / 10000;
+
+       d3  = q + 281 * d3;
+       if (!d3)
+               goto done;
+       buf = put_dec_full4(buf, d3 % 10000);
+       q   = d3 / 10000;
+       if (!q)
+               goto done;
+       buf = put_dec_full4(buf, q);
+ done:
+       while (buf[-1] == '0')
+               --buf;
+
+       return buf;
+}
+
+#endif
+
+/*
+ * For strace, the following formats are not supported:
+ * %h[h]u, %zu, %tu  - use [unsigned] int/long/long long fmt instead
+ * %8.4u  - no precision field for integers allowed (ok for strings)
+ * %+d, % d  - no forced sign or force "space positive" sign
+ * %-07u  - use %-7u instead
+ * %X  - works as %x
+ */
+
+#define ZEROPAD        1               /* pad with zero */
+#define SIGN   2               /* unsigned/signed long */
+//#define PLUS 4               /* show plus */
+//#define SPACE        8               /* space if plus */
+#define LEFT   16              /* left justified */
+//#deefine SMALL       32              /* use lowercase in hex (must be 32 == 0x20) */
+#define SPECIAL        64              /* prefix hex with "0x", octal with "0" */
+
+enum format_type {
+       FORMAT_TYPE_NONE, /* Just a string part */
+       FORMAT_TYPE_WIDTH,
+       FORMAT_TYPE_PRECISION,
+       FORMAT_TYPE_CHAR,
+       FORMAT_TYPE_STR,
+       FORMAT_TYPE_PTR,
+       FORMAT_TYPE_PERCENT_CHAR,
+       FORMAT_TYPE_INVALID,
+       FORMAT_TYPE_LONG_LONG,
+       FORMAT_TYPE_ULONG,
+       FORMAT_TYPE_LONG,
+       FORMAT_TYPE_UINT,
+       FORMAT_TYPE_INT,
+};
+
+struct printf_spec {
+       uint8_t type;           /* format_type enum */
+       uint8_t flags;          /* flags to number() */
+       uint8_t base;           /* number base, 8, 10 or 16 only */
+       uint8_t qualifier;      /* number qualifier, one of 'hHlLtzZ' */
+       int     field_width;    /* width of output field */
+       int     precision;      /* # of digits/chars */
+};
+
+static noinline_for_stack
+char *number(char *buf, char *end, unsigned long long num,
+            struct printf_spec spec)
+{
+       /* we are called with base 8, 10 or 16, only, thus don't need "G..."  */
+       static const char digits[16] = "0123456789abcdef"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
+
+       char tmp[sizeof(long long)*3 + 4];
+       char sign;
+       int need_pfx = ((spec.flags & SPECIAL) && spec.base != 10);
+       int i;
+
+       /* We may overflow the buf. Crudely check for it */
+       i = sizeof(long long)*3 + 4;
+       if (i < spec.field_width)
+               i = spec.field_width;
+       if ((end - buf) <= i)
+               return buf + i;
+
+//we don't use formats like "%-07u"
+//     if (spec.flags & LEFT)
+//             spec.flags &= ~ZEROPAD;
+       sign = 0;
+       if (spec.flags & SIGN) {
+               if ((signed long long)num < 0) {
+                       sign = '-';
+                       num = -(signed long long)num;
+                       spec.field_width--;
+//             } else if (spec.flags & PLUS) {
+//                     sign = '+';
+//                     spec.field_width--;
+//             } else if (spec.flags & SPACE) {
+//                     sign = ' ';
+//                     spec.field_width--;
+               }
+       }
+       if (need_pfx) {
+               spec.field_width--;
+               if (spec.base == 16)
+                       spec.field_width--;
+       }
+
+       /* generate full string in tmp[], in reverse order */
+       i = 0;
+       if (num < spec.base)
+               tmp[i++] = digits[num];
+       /* Generic code, for any base:
+       else do {
+               tmp[i++] = (digits[do_div(num,base)]);
+       } while (num != 0);
+       */
+       else if (spec.base != 10) { /* 8 or 16 */
+               int mask = spec.base - 1;
+               int shift = 3;
+
+               if (spec.base == 16)
+                       shift = 4;
+               do {
+                       tmp[i++] = digits[((unsigned char)num) & mask];
+                       num >>= shift;
+               } while (num);
+       } else { /* base 10 */
+               i = put_dec(tmp, num) - tmp;
+       }
+
+//spec.precision is assumed 0 ("not specified")
+//     /* printing 100 using %2d gives "100", not "00" */
+//     if (i > spec.precision)
+//             spec.precision = i;
+//     /* leading space padding */
+//     spec.field_width -= spec.precision;
+       spec.field_width -= i;
+       if (!(spec.flags & (ZEROPAD+LEFT))) {
+               while (--spec.field_width >= 0) {
+                       ///if (buf < end)
+                               *buf = ' ';
+                       ++buf;
+               }
+       }
+       /* sign */
+       if (sign) {
+               ///if (buf < end)
+                       *buf = sign;
+               ++buf;
+       }
+       /* "0x" / "0" prefix */
+       if (need_pfx) {
+               ///if (buf < end)
+                       *buf = '0';
+               ++buf;
+               if (spec.base == 16) {
+                       ///if (buf < end)
+                               *buf = 'x';
+                       ++buf;
+               }
+       }
+       /* zero or space padding */
+       if (!(spec.flags & LEFT)) {
+               char c = (spec.flags & ZEROPAD) ? '0' : ' ';
+               while (--spec.field_width >= 0) {
+                       ///if (buf < end)
+                               *buf = c;
+                       ++buf;
+               }
+       }
+//     /* hmm even more zero padding? */
+//     while (i <= --spec.precision) {
+//             ///if (buf < end)
+//                     *buf = '0';
+//             ++buf;
+//     }
+       /* actual digits of result */
+       while (--i >= 0) {
+               ///if (buf < end)
+                       *buf = tmp[i];
+               ++buf;
+       }
+       /* trailing space padding */
+       while (--spec.field_width >= 0) {
+               ///if (buf < end)
+                       *buf = ' ';
+               ++buf;
+       }
+
+       return buf;
+}
+
+static noinline_for_stack
+char *string(char *buf, char *end, const char *s, struct printf_spec spec)
+{
+       int len, i;
+
+       if (!s)
+               s = "(null)";
+
+       len = strnlen(s, spec.precision);
+
+       /* We may overflow the buf. Crudely check for it */
+       i = len;
+       if (i < spec.field_width)
+               i = spec.field_width;
+       if ((end - buf) <= i)
+               return buf + i;
+
+       if (!(spec.flags & LEFT)) {
+               while (len < spec.field_width--) {
+                       ///if (buf < end)
+                               *buf = ' ';
+                       ++buf;
+               }
+       }
+       for (i = 0; i < len; ++i) {
+               ///if (buf < end)
+                       *buf = *s;
+               ++buf; ++s;
+       }
+       while (len < spec.field_width--) {
+               ///if (buf < end)
+                       *buf = ' ';
+               ++buf;
+       }
+
+       return buf;
+}
+
+static noinline_for_stack
+char *pointer(const char *fmt, char *buf, char *end, void *ptr,
+             struct printf_spec spec)
+{
+//     spec.flags |= SMALL;
+       if (spec.field_width == -1) {
+               spec.field_width = 2 * sizeof(void *);
+               spec.flags |= ZEROPAD;
+       }
+       spec.base = 16;
+
+       return number(buf, end, (unsigned long) ptr, spec);
+}
+
+/*
+ * Helper function to decode printf style format.
+ * Each call decode a token from the format and return the
+ * number of characters read (or likely the delta where it wants
+ * to go on the next call).
+ * The decoded token is returned through the parameters
+ *
+ * 'h', 'l', or 'L' for integer fields
+ * 'z' support added 23/7/1999 S.H.
+ * 'z' changed to 'Z' --davidm 1/25/99
+ * 't' added for ptrdiff_t
+ *
+ * @fmt: the format string
+ * @type of the token returned
+ * @flags: various flags such as +, -, # tokens..
+ * @field_width: overwritten width
+ * @base: base of the number (octal, hex, ...)
+ * @precision: precision of a number
+ * @qualifier: qualifier of a number (long, size_t, ...)
+ */
+static noinline_for_stack
+int format_decode(const char *fmt, struct printf_spec *spec)
+{
+       const char *start = fmt;
+
+       /* we finished early by reading the field width */
+       if (spec->type == FORMAT_TYPE_WIDTH) {
+               if (spec->field_width < 0) {
+                       spec->field_width = -spec->field_width;
+                       spec->flags |= LEFT;
+               }
+               spec->type = FORMAT_TYPE_NONE;
+               goto precision;
+       }
+
+       /* we finished early by reading the precision */
+       if (spec->type == FORMAT_TYPE_PRECISION) {
+               if (spec->precision < 0)
+                       spec->precision = 0;
+
+               spec->type = FORMAT_TYPE_NONE;
+               goto qualifier;
+       }
+
+       /* By default */
+       spec->type = FORMAT_TYPE_NONE;
+
+       for (;;) {
+               if (*fmt == '\0')
+                       return fmt - start;
+               if (*fmt == '%')
+                       break;
+               ++fmt;
+       }
+
+       /* Return the current non-format string */
+       if (fmt != start)
+               return fmt - start;
+
+       /* Process flags */
+       spec->flags = 0;
+
+       while (1) { /* this also skips first '%' */
+               bool found = true;
+
+               ++fmt;
+
+               switch (*fmt) {
+               case '-': spec->flags |= LEFT;    break;
+//             case '+': spec->flags |= PLUS;    break;
+//             case ' ': spec->flags |= SPACE;   break;
+               case '#': spec->flags |= SPECIAL; break;
+               case '0': spec->flags |= ZEROPAD; break;
+               default:  found = false;
+               }
+
+               if (!found)
+                       break;
+       }
+
+       /* get field width */
+       spec->field_width = -1;
+
+       if (isdigit(*fmt))
+               spec->field_width = skip_atoi(&fmt);
+       else if (*fmt == '*') {
+               /* it's the next argument */
+               spec->type = FORMAT_TYPE_WIDTH;
+               return ++fmt - start;
+       }
+
+precision:
+       /* get the precision */
+       spec->precision = -1;
+       if (*fmt == '.') {
+               ++fmt;
+               if (isdigit(*fmt)) {
+                       spec->precision = skip_atoi(&fmt);
+//                     if (spec->precision < 0)
+//                             spec->precision = 0;
+               } else if (*fmt == '*') {
+                       /* it's the next argument */
+                       spec->type = FORMAT_TYPE_PRECISION;
+                       return ++fmt - start;
+               }
+       }
+
+qualifier:
+       /* get the conversion qualifier */
+       spec->qualifier = -1;
+       if (*fmt == 'l') {
+               spec->qualifier = *fmt++;
+               if (unlikely(spec->qualifier == *fmt)) {
+                       spec->qualifier = 'L';
+                       ++fmt;
+               }
+       }
+
+       /* default base */
+       spec->base = 10;
+       switch (*fmt) {
+       case 'c':
+               spec->type = FORMAT_TYPE_CHAR;
+               return ++fmt - start;
+
+       case 's':
+               spec->type = FORMAT_TYPE_STR;
+               return ++fmt - start;
+
+       case 'p':
+               spec->type = FORMAT_TYPE_PTR;
+               return ++fmt - start;
+
+       case '%':
+               spec->type = FORMAT_TYPE_PERCENT_CHAR;
+               return ++fmt - start;
+
+       /* integer number formats - set up the flags and "break" */
+       case 'o':
+               spec->base = 8;
+               break;
+
+       case 'x':
+//             spec->flags |= SMALL;
+
+       case 'X':
+               spec->base = 16;
+               break;
+
+       case 'd':
+       case 'i':
+               spec->flags |= SIGN;
+       case 'u':
+               break;
+
+       default:
+               spec->type = FORMAT_TYPE_INVALID;
+               return fmt - start;
+       }
+
+       if (spec->qualifier == 'L')
+               spec->type = FORMAT_TYPE_LONG_LONG;
+       else if (spec->qualifier == 'l') {
+               if (spec->flags & SIGN)
+                       spec->type = FORMAT_TYPE_LONG;
+               else
+                       spec->type = FORMAT_TYPE_ULONG;
+       } else {
+               if (spec->flags & SIGN)
+                       spec->type = FORMAT_TYPE_INT;
+               else
+                       spec->type = FORMAT_TYPE_UINT;
+       }
+
+       return ++fmt - start;
+}
+
+/**
+ * vsnprintf - Format a string and place it in a buffer
+ * @buf: The buffer to place the result into
+ * @size: The size of the buffer, including the trailing null space
+ * @fmt: The format string to use
+ * @args: Arguments for the format string
+ *
+ * The return value is the number of characters which would
+ * be generated for the given input, excluding the trailing
+ * '\0', as per ISO C99. If you want to have the exact
+ * number of characters written into @buf as return value
+ * (not including the trailing '\0'), use vscnprintf(). If the
+ * return is greater than or equal to @size, the resulting
+ * string is truncated.
+ *
+ * If you're not already dealing with a va_list consider using snprintf().
+ */
+static
+int kernel_vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
+{
+       unsigned long long num;
+       char *str, *end;
+       struct printf_spec spec = {0};
+
+       str = buf;
+       end = buf + size;
+
+       while (*fmt) {
+               const char *old_fmt = fmt;
+               int read = format_decode(fmt, &spec);
+
+               fmt += read;
+
+               switch (spec.type) {
+               case FORMAT_TYPE_NONE: {
+                       int copy = read;
+                       if (str < end) {
+                               if (copy > end - str)
+                                       copy = end - str;
+                               memcpy(str, old_fmt, copy);
+                       }
+                       str += read;
+                       break;
+               }
+
+               case FORMAT_TYPE_WIDTH:
+                       spec.field_width = va_arg(args, int);
+                       break;
+
+               case FORMAT_TYPE_PRECISION:
+                       spec.precision = va_arg(args, int);
+                       break;
+
+               case FORMAT_TYPE_CHAR: {
+                       char c;
+
+                       if (!(spec.flags & LEFT)) {
+                               while (--spec.field_width > 0) {
+                                       if (str < end)
+                                               *str = ' ';
+                                       ++str;
+
+                               }
+                       }
+                       c = (unsigned char) va_arg(args, int);
+                       if (str < end)
+                               *str = c;
+                       ++str;
+                       while (--spec.field_width > 0) {
+                               if (str < end)
+                                       *str = ' ';
+                               ++str;
+                       }
+                       break;
+               }
+
+               case FORMAT_TYPE_STR:
+                       str = string(str, end, va_arg(args, char *), spec);
+                       break;
+
+               case FORMAT_TYPE_PTR:
+                       str = pointer(fmt+1, str, end, va_arg(args, void *),
+                                     spec);
+//                     while (isalnum(*fmt))
+//                             fmt++;
+                       break;
+
+               case FORMAT_TYPE_PERCENT_CHAR:
+                       if (str < end)
+                               *str = '%';
+                       ++str;
+                       break;
+
+               case FORMAT_TYPE_INVALID:
+                       if (str < end)
+                               *str = '%';
+                       ++str;
+                       break;
+
+               default:
+                       switch (spec.type) {
+                       case FORMAT_TYPE_LONG_LONG:
+                               num = va_arg(args, long long);
+                               break;
+                       case FORMAT_TYPE_ULONG:
+                               num = va_arg(args, unsigned long);
+                               break;
+                       case FORMAT_TYPE_LONG:
+                               num = va_arg(args, long);
+                               break;
+                       case FORMAT_TYPE_INT:
+                               num = (int) va_arg(args, int);
+                               break;
+                       default:
+                               num = va_arg(args, unsigned int);
+                       }
+
+                       str = number(str, end, num, spec);
+               }
+       }
+
+//     if (size > 0) {
+               if (str < end)
+                       *str = '\0';
+//             else
+//                     end[-1] = '\0';
+//     }
+
+       /* the trailing null byte doesn't count towards the total */
+       return str-buf;
+
+}
+
+int strace_vfprintf(FILE *fp, const char *fmt, va_list args)
+{
+       static char *buf;
+       static unsigned buflen;
+
+       int r;
+       va_list a1;
+
+       va_copy(a1, args);
+       unsigned len = kernel_vsnprintf(buf, buflen, fmt, a1);
+       va_end(a1);
+
+       if (len >= buflen) {
+               buflen = len + 256;
+               free(buf);
+               buf = malloc(buflen);
+               /*len =*/ kernel_vsnprintf(buf, buflen, fmt, args);
+       }
+
+       r = fputs_unlocked(buf, fp);
+       if (r < 0) return r;
+       return len;
+}
+
+#endif /* USE_CUSTOM_PRINTF */