Hi,
Glibc printf is not fast, and we use it A LOT for log formatting,
so I experimented with replacing it with kernel's one.
We don't do any Unicode and weird conversion things, so we can cut
a few corners and be faster.
Attached is the first quick attempt at this code.
It can be sped up a bit more. For example, length checks
in kernel_vsnprintf can be simplified (we don't need
to be precise there, it's ok to report overflow spuriously).
We never emit uppercase hex numbers, can simplify that code too.
Testing: I run
time ./strace -o/dev/null ./strace -o/dev/null ./strace -o/dev/null ./strace
-o/dev/null ./strace -o/dev/null true
several times, results:
Before:
user 0m5.517s
user 0m5.589s
user 0m7.070s
user 0m7.072s
user 0m7.140s
user 0m7.151s
user 0m7.191s
user 0m7.255s
After:
user 0m4.087s
user 0m6.641s
user 0m6.658s
user 0m6.740s
user 0m6.746s
user 0m6.794s
user 0m6.799s
user 0m6.908s
As you see, the win is there, but it is not particularly big.
What do you guys think? Is it even worth doing?
--
vda
diff -urpN strace.3/block.c strace.5/block.c
--- strace.3/block.c 2012-03-17 16:08:13.000000000 +0100
+++ strace.5/block.c 2012-03-30 11:45:53.795064923 +0200
@@ -136,7 +136,7 @@ block_ioctl(struct tcb *tcp, long code,
if (syserror(tcp) || umove(tcp, arg, &val) < 0)
tprintf(", %#lx", arg);
else
- tprintf(", %hu", val);
+ tprintf(", %u", (unsigned)val);
}
break;
@@ -223,10 +223,12 @@ block_ioctl(struct tcb *tcp, long code,
if (syserror(tcp) || umove(tcp, arg, &geo) < 0)
tprintf(", %#lx", arg);
else
- tprintf(", {heads=%hhu, sectors=%hhu, "
- "cylinders=%hu, start=%lu}",
- geo.heads, geo.sectors,
- geo.cylinders, geo.start);
+ tprintf(", {heads=%u, sectors=%u, "
+ "cylinders=%u, start=%lu}",
+ (unsigned)geo.heads,
+ (unsigned)geo.sectors,
+ (unsigned)geo.cylinders,
+ geo.start);
}
break;
@@ -248,10 +250,10 @@ block_ioctl(struct tcb *tcp, long code,
if (umove(tcp, arg, &buts) < 0)
tprintf(", %#lx", arg);
else
- tprintf(", {act_mask=%hu, buf_size=%u, "
+ tprintf(", {act_mask=%u, buf_size=%u, "
"buf_nr=%u, start_lba=%" PRIu64 ", "
"end_lba=%" PRIu64 ", pid=%u}",
- buts.act_mask, buts.buf_size,
+ (unsigned)buts.act_mask, buts.buf_size,
buts.buf_nr, buts.start_lba,
buts.end_lba, buts.pid);
}
diff -urpN strace.3/desc.c strace.5/desc.c
--- strace.3/desc.c 2012-03-19 09:33:12.000000000 +0100
+++ strace.5/desc.c 2012-03-30 11:45:00.100672270 +0200
@@ -950,10 +950,10 @@ sys_io_cancel(struct tcb *tcp)
tprintf("%lu, ", tcp->u_arg[0]);
#ifdef HAVE_LIBAIO_H
if (umove(tcp, tcp->u_arg[1], &iocb) == 0) {
- tprintf("{%p, %u, %hu, %hu, %d}, ",
+ tprintf("{%p, %u, %u, %u, %d}, ",
iocb.data, iocb.key,
- iocb.aio_lio_opcode,
- iocb.aio_reqprio, iocb.aio_fildes);
+ (unsigned)iocb.aio_lio_opcode,
+ (unsigned)iocb.aio_reqprio, iocb.aio_fildes);
} else
#endif
tprints("{...}, ");
diff -urpN strace.3/Makefile.am strace.5/Makefile.am
--- strace.3/Makefile.am 2012-03-17 16:08:13.000000000 +0100
+++ strace.5/Makefile.am 2012-03-23 20:52:46.130227156 +0100
@@ -17,7 +17,7 @@ AM_CPPFLAGS = -I$(srcdir)/$(OS)/$(ARCH)
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
+ scsi.c stream.c block.c pathtrace.c vsprintf.c
noinst_HEADERS = defs.h
EXTRA_DIST = $(man_MANS) errnoent.sh signalent.sh syscallent.sh ioctlsort.c \
diff -urpN strace.3/resource.c strace.5/resource.c
--- strace.3/resource.c 2012-03-19 09:33:12.000000000 +0100
+++ strace.5/resource.c 2012-03-30 11:44:34.227966460 +0200
@@ -379,9 +379,9 @@ sys_sysinfo(struct tcb *tcp)
(long) si.totalram, (long) si.freeram);
tprintf("sharedram=%lu, bufferram=%lu} ",
(long) si.sharedram, (long) si.bufferram);
- tprintf("totalswap=%lu, freeswap=%lu, procs=%hu}",
+ tprintf("totalswap=%lu, freeswap=%lu, procs=%u}",
(long) si.totalswap, (long) si.freeswap,
- si.procs);
+ (unsigned)si.procs);
}
}
return 0;
diff -urpN strace.3/strace.c strace.5/strace.c
--- strace.3/strace.c 2012-03-23 12:14:05.769193128 +0100
+++ strace.5/strace.c 2012-03-23 21:17:56.702895134 +0100
@@ -500,6 +500,8 @@ strace_popen(const char *command)
return fp;
}
+int my_vfprintf(FILE *fp, const char *fmt, va_list args);
+
void
tprintf(const char *fmt, ...)
{
@@ -507,7 +509,7 @@ tprintf(const char *fmt, ...)
va_start(args, fmt);
if (current_tcp) {
- int n = vfprintf(current_tcp->outf, fmt, args);
+ int n = my_vfprintf(current_tcp->outf, fmt, args);
if (n < 0) {
if (current_tcp->outf != stderr)
perror(outfname == NULL
diff -urpN strace.3/vsprintf.c strace.5/vsprintf.c
--- strace.3/vsprintf.c 1970-01-01 01:00:00.000000000 +0100
+++ strace.5/vsprintf.c 2012-03-30 12:49:55.000000000 +0200
@@ -0,0 +1,773 @@
+/*
+ * linux/lib/vsprintf.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
+/*
+ * Wirzenius wrote this portably, Torvalds fucked it up :-)
+ */
+
+/*
+ * Fri Jul 13 2001 Crutcher Dunnavant <[email protected]>
+ * - changed to provide snprintf and vsnprintf functions
+ * So Feb 1 16:51:32 CET 2004 Juergen Quade <[email protected]>
+ * - scnprintf and vscnprintf
+ */
+
+#include "defs.h"
+#include <stdarg.h>
+#include <limits.h>
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef int16_t s16;
+
+#define EXPORT_SYMBOL(s) /*nothing*/
+#define noinline_for_stack /*nothing*/
+#define likely(expr) (expr)
+#define unlikely(expr) (expr)
+#define WARN_ON_ONCE(expr) (0)
+#undef PAGE_SIZE
+#define PAGE_SIZE (4*1024)
+
+#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 != 2147483647 || LLONG_MAX != 9223372036854775807
+/* 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 != 2147483647 || LLONG_MAX != 9223372036854775807
+
+/* 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
+
+#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 */
+#define 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 {
+ u8 type; /* format_type enum */
+ u8 flags; /* flags to number() */
+ u8 base; /* number base, 8, 10 or 16 only */
+ u8 qualifier; /* number qualifier, one of 'hHlLtzZ' */
+ s16 field_width; /* width of output field */
+ s16 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[66];
+ char sign;
+ char locase;
+ int need_pfx = ((spec.flags & SPECIAL) && spec.base != 10);
+ int i;
+
+ /* locase = 0 or 0x20. ORing digits or letters with 'locase'
+ * produces same digits or (maybe lowercased) letters */
+ locase = (spec.flags & SMALL);
+ 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] | locase;
+ /* Generic code, for any base:
+ else do {
+ tmp[i++] = (digits[do_div(num,base)] | locase);
+ } 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] | locase);
+ num >>= shift;
+ } while (num);
+ } else { /* base 10 */
+ i = put_dec(tmp, num) - tmp;
+ }
+
+ /* printing 100 using %2d gives "100", not "00" */
+ if (i > spec.precision)
+ spec.precision = i;
+ /* leading space padding */
+ spec.field_width -= spec.precision;
+ 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' | locase);
+ ++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 ((unsigned long)s < PAGE_SIZE)
+ s = "(null)";
+
+ len = strnlen(s, spec.precision);
+
+ 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;
+
+}
+EXPORT_SYMBOL(vsnprintf);
+
+int my_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;
+}
------------------------------------------------------------------------------
This SF email is sponsosred by:
Try Windows Azure free for 90 days Click Here
http://p.sf.net/sfu/sfd2d-msazure
_______________________________________________
Strace-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/strace-devel