Messages in this thread | | | Date | Tue, 9 Dec 2008 23:21:59 -0500 (EST) | From | Steven Rostedt <> | Subject | Re: [PATCH] ftrace: support binary record |
| |
On Wed, 10 Dec 2008, Lai Jiangshan wrote:
> > Binary record will save memory in trace buffer, and more events > can be recorded in buffer. > > This patch add infrastructure for supporting binary record. > > This infrastructure records events by binary and shows events > by human readable text.
Hmm, interesting. But could you show an example of its use. It is quite a lot of code, and looks a little complex.
> > And add a API ftrace_struct_printk() using this infrastructure. > (it's like ftrace_printk(), but it record the date by binary) > > Signed-off-by: Lai Jiangshan <laijs@cn.fujitsu.com> > --- > include/linux/ftrace.h | 29 ++++ > include/linux/string.h | 8 + > kernel/trace/Makefile | 1 > kernel/trace/trace.c | 130 +++++++++++++++++++++ > kernel/trace/trace.h | 10 + > kernel/trace/trace_struct.c | 248 +++++++++++++++++++++++++++++++++++++++++ > lib/vsprintf.c | 261 ++++++++++++++++++++++++++++++++++++++++++++ > 7 files changed, 687 insertions(+) > diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h > index 703eb53..d8ed7da 100644 > --- a/include/linux/ftrace.h > +++ b/include/linux/ftrace.h > @@ -248,6 +248,35 @@ static inline void start_boot_trace(void) { } > static inline void stop_boot_trace(void) { } > #endif > > +#ifdef CONFIG_TRACING > +/** > + * ftrace_struct - ftrace binary storage & show format > + * ftrace_struct must be permanent in the kernel
If this is true, then do not export the API to modules. You can never know when a module might be unloaded. I was originally going to have the ftrace_printk just copy a pointer to the format and the args, but because there's no guarantee that modules that use ftrace_printk will stick around when the data is viewed, I dumped the idea.
> + * inspired from Python's struct
I have an anxiety with snakes.
> + */ > +struct ftrace_struct { > + const char *storage_fmt; > + const char *show_fmt; > +}; > +int ftrace_struct_pack(char *buf, unsigned int len, const char *storage_fmt, > + va_list args); > +int ftrace_struct_spintf(char *buf, size_t size,
spintf?
> + const struct ftrace_struct *fmt, > + const char *binary, size_t binary_len); > +struct ftrace_struct *ftrace_struct_permanent_alloc(const char *storage_fmt, > + const char *show_fmt); > + > +int trace_struct_vprintk(unsigned long ip, const struct ftrace_struct *fmt, > + va_list args); > +int __ftrace_struct_printk(unsigned long ip, const struct ftrace_struct *fmt, > + ...); > +#define ftrace_struct_printk(fmt...) __ftrace_struct_printk(_THIS_IP_, fmt) > > +#else > +static inline int ftrace_struct_printk(const struct ftrace_struct *fmt, ...) > +{ > + return 0; > +} > +#endif > > #endif /* _LINUX_FTRACE_H */ > diff --git a/include/linux/string.h b/include/linux/string.h > index d18fc19..cf13e9d 100644 > --- a/include/linux/string.h > +++ b/include/linux/string.h > @@ -111,6 +111,14 @@ extern void argv_free(char **argv); > > extern bool sysfs_streq(const char *s1, const char *s2); > > +struct arg_iter { > + int (*get_next_arg)(struct arg_iter *iter, unsigned long long *ptr, > + unsigned int type_len); > +}; > + > +int snprintf_iter_args(char *str, size_t size, const char *format, > + struct arg_iter *args); > +
This new string interface may deserve its own patch.
> extern ssize_t memory_read_from_buffer(void *to, size_t count, loff_t *ppos, > const void *from, size_t available); > > diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile > index c8228b1..46c2b7a 100644 > --- a/kernel/trace/Makefile > +++ b/kernel/trace/Makefile > @@ -14,6 +14,7 @@ obj-$(CONFIG_FUNCTION_TRACER) += libftrace.o > obj-$(CONFIG_RING_BUFFER) += ring_buffer.o > > obj-$(CONFIG_TRACING) += trace.o > +obj-$(CONFIG_TRACING) += trace_struct.o > obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o > obj-$(CONFIG_SYSPROF_TRACER) += trace_sysprof.o > obj-$(CONFIG_FUNCTION_TRACER) += trace_functions.o > diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c > index d86e325..4ad84d3 100644 > --- a/kernel/trace/trace.c > +++ b/kernel/trace/trace.c > @@ -289,6 +289,28 @@ trace_seq_printf(struct trace_seq *s, const char *fmt, ...) > return len; > } > > +static int trace_seq_struct_print(struct trace_seq *s, > + const struct ftrace_struct *fmt, > + const char *binary, size_t binary_len) > +{ > + int len = (PAGE_SIZE - 1) - s->len; > + int ret; > + > + if (!len) > + return 0; > + > + ret = ftrace_struct_spintf(s->buffer + s->len, len, fmt,
There's that spintf again.
> + binary, binary_len); > + > + /* If we can't write it all, don't bother writing anything */ > + if (ret >= len) > + return 0; > + > + s->len += ret; > + > + return len; > +} > + > /** > * trace_seq_puts - trace sequence printing of simple string > * @s: trace sequence descriptor > @@ -1448,6 +1470,19 @@ print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu) > trace_seq_print_cont(s, iter); > break; > } > + case TRACE_STRUCT_PRINT: { > + struct struct_print_entry *field; > + > + trace_assign_type(field, entry); > + > + seq_print_ip_sym(s, field->ip, sym_flags); > + trace_seq_puts(s, ": "); > + trace_seq_struct_print(s, field->fmt, (char *)field->buf, > + TRACE_BUF_SIZE); > + if (entry->flags & TRACE_FLAG_CONT) > + trace_seq_print_cont(s, iter); > + break; > + } > default: > trace_seq_printf(s, "Unknown type %d\n", entry->type); > } > @@ -1581,6 +1616,19 @@ static enum print_line_t print_trace_fmt(struct trace_iterator *iter) > trace_seq_print_cont(s, iter); > break; > } > + case TRACE_STRUCT_PRINT: { > + struct struct_print_entry *field; > + > + trace_assign_type(field, entry); > + > + seq_print_ip_sym(s, field->ip, sym_flags); > + trace_seq_puts(s, ": "); > + trace_seq_struct_print(s, field->fmt, (char *)field->buf, > + TRACE_BUF_SIZE); > + if (entry->flags & TRACE_FLAG_CONT) > + trace_seq_print_cont(s, iter); > + break; > + } > } > return TRACE_TYPE_HANDLED; > } > @@ -1663,6 +1711,18 @@ static enum print_line_t print_raw_fmt(struct trace_iterator *iter) > trace_seq_print_cont(s, iter); > break; > } > + case TRACE_STRUCT_PRINT: { > + struct struct_print_entry *field; > + > + trace_assign_type(field, entry); > + > + trace_seq_printf(s, ": %lx : ", field->ip); > + trace_seq_struct_print(s, field->fmt, (char *)field->buf, > + TRACE_BUF_SIZE); > + if (entry->flags & TRACE_FLAG_CONT) > + trace_seq_print_cont(s, iter); > + break; > + }
I need to really clean up that code. I really hate that duplicate code. But that is something on my todo list, not something with this patch.
> } > return TRACE_TYPE_HANDLED; > } > @@ -3043,6 +3103,76 @@ int __ftrace_printk(unsigned long ip, const char *fmt, ...) > } > EXPORT_SYMBOL_GPL(__ftrace_printk); > > +int trace_struct_vprintk(unsigned long ip, const struct ftrace_struct *fmt, > + va_list args) > +{ > + static DEFINE_SPINLOCK(trace_buf_lock); > + static char trace_buf[TRACE_BUF_SIZE]; > + > + struct ring_buffer_event *event; > + struct trace_array *tr = &global_trace; > + struct trace_array_cpu *data; > + struct struct_print_entry *entry; > + unsigned long flags, irq_flags; > + int cpu, len = 0, size, pc; > + > + if (!tr->ctrl || tracing_disabled) > + return 0; > + > + pc = preempt_count(); > + preempt_disable_notrace(); > + cpu = raw_smp_processor_id(); > + data = tr->data[cpu]; > + > + if (unlikely(atomic_read(&data->disabled))) > + goto out; > + > + spin_lock_irqsave(&trace_buf_lock, flags);
If you are going to disable interrupts, might as well do it up front and remove the preempt_disable_notrace code. It is only protecting against the unlikely case that data->disabled is true.
> + len = ftrace_struct_pack(trace_buf, TRACE_BUF_SIZE, fmt->storage_fmt, > + args); > + > + if (len > TRACE_BUF_SIZE || len < 0) > + goto out_unlock; > + > + size = sizeof(*entry) + len; > + event = ring_buffer_lock_reserve(tr->buffer, size, &irq_flags); > + if (!event) > + goto out_unlock; > + entry = ring_buffer_event_data(event); > + tracing_generic_entry_update(&entry->ent, flags, pc); > + entry->ent.type = TRACE_STRUCT_PRINT; > + entry->ip = ip; > + entry->fmt = fmt;
Here is the danger of using this in a module.
> + > + memcpy(&entry->buf, trace_buf, len); > + ring_buffer_unlock_commit(tr->buffer, event, irq_flags); > + > + out_unlock: > + spin_unlock_irqrestore(&trace_buf_lock, flags); > + > + out: > + preempt_enable_notrace(); > + > + return len; > +} > +EXPORT_SYMBOL_GPL(trace_struct_vprintk);
So you need to get rid of these exports.
> + > +int __ftrace_struct_printk(unsigned long ip, const struct ftrace_struct *fmt, > + ...) > +{ > + int ret; > + va_list ap; > + > + if (!(trace_flags & TRACE_ITER_PRINTK)) > + return 0; > + > + va_start(ap, fmt); > + ret = trace_struct_vprintk(ip, fmt, ap); > + va_end(ap); > + return ret; > +} > +EXPORT_SYMBOL_GPL(__ftrace_struct_printk);
This one too.
> + > static int trace_panic_handler(struct notifier_block *this, > unsigned long event, void *unused) > { > diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h > index 8465ad0..f95b766 100644 > --- a/kernel/trace/trace.h > +++ b/kernel/trace/trace.h > @@ -22,6 +22,7 @@ enum trace_type { > TRACE_MMIO_RW, > TRACE_MMIO_MAP, > TRACE_BOOT, > + TRACE_STRUCT_PRINT, > > __TRACE_LAST_TYPE > }; > @@ -94,6 +95,13 @@ struct print_entry { > char buf[]; > }; > > +struct struct_print_entry { > + struct trace_entry ent; > + unsigned long ip; > + const struct ftrace_struct *fmt; > + u32 buf[]; > +}; > + > #define TRACE_OLD_SIZE 88 > > struct trace_field_cont { > @@ -219,6 +227,8 @@ extern void __ftrace_bad_type(void); > IF_ASSIGN(var, ent, struct trace_mmiotrace_map, \ > TRACE_MMIO_MAP); \ > IF_ASSIGN(var, ent, struct trace_boot, TRACE_BOOT); \ > + IF_ASSIGN(var, ent, struct struct_print_entry, \ > + TRACE_STRUCT_PRINT); \ > __ftrace_bad_type(); \ > } while (0) > > diff --git a/kernel/trace/trace_struct.c b/kernel/trace/trace_struct.c > new file mode 100644 > index 0000000..6814b18 > --- /dev/null > +++ b/kernel/trace/trace_struct.c > @@ -0,0 +1,248 @@ > +/* > + * ftrace_struct binary storage tracer > + * > + * Copyright (C) 2008 Lai Jiangshan <laijs@cn.fujitsu.com> > + */ > +#include <stdarg.h> > +#include <linux/kernel.h> > +#include <linux/ftrace.h> > +#include <linux/string.h> > +#include <linux/list.h> > +#include <linux/mutex.h> > +#include <linux/slab.h> > +#include <linux/module.h> > + > +/* > + * storage format: > + * x pad byte > + * c char > + * b signed char > + * B unsigned char > + * h short > + * H unsigned short > + * i int > + * I unsigned int > + * l long > + * L unsigned long > + * q long long (64bits) > + * Q unsigned long long (64bits) > + * p pointer > + * s C-style string > + * > + * align is 4bytes > + */ > + > +#define CASE_PACK(fmt_byte, type) \ > +case fmt_byte: \ > + if (sizeof(type) == 8) { \ > + str = PTR_ALIGN(str, 4); \ > + value = va_arg(args, unsigned long long); \ > + if (str + sizeof(type) <= end) { \ > + *(u32 *)str = *(u32 *)&value; \ > + *(u32 *)(str + 4) = *((u32 *)&value + 1); \ > + } \ > + } else { \ > + str = PTR_ALIGN(str, sizeof(type)); \ > + value = va_arg(args, int); \ > + if (str + sizeof(type) <= end) \ > + *(type *)str = (type)value; \ > + } \ > + str += sizeof(type); \ > + break; > + > + > +int ftrace_struct_pack(char *buf, unsigned int len, const char *storage_fmt, > + va_list args) > +{ > + unsigned long long value; > + char *str = buf, *end = buf + len; > + > + /* align is 4bytes, FIXME: support other align */ > + if (!IS_ALIGNED((unsigned long)buf, 4)) > + return -1; > + > + while (*storage_fmt) { > + switch (*storage_fmt) { > + case 'x': > + str++; > + break; > + CASE_PACK('c', char) > + CASE_PACK('b', signed char) > + CASE_PACK('B', unsigned char) > + CASE_PACK('h', short) > + CASE_PACK('H', unsigned short) > + CASE_PACK('i', int) > + CASE_PACK('I', unsigned int) > + CASE_PACK('l', long) > + CASE_PACK('L', unsigned long) > + CASE_PACK('q', long long) > + CASE_PACK('Q', unsigned long long) > + CASE_PACK('p', long) /* use long instead void * */ > + case 's': > + str += strlcpy(str, va_arg(args, char *), end - str); > + str++; > + break; > + default: > + return -1; > + } > + storage_fmt++; > + } > + return str - buf; > +} > + > +#define CASE_UNPACK(fmt_byte, type) \ > +case fmt_byte: \ > + if (sizeof(type) == 8) { \ > + str = PTR_ALIGN(str, 4); \ > + if (str + sizeof(type) <= end) { \ > + *(u32 *)&value = *(u32 *)str; \ > + *((u32 *)&value + 1) = *(u32 *)(str + 4); \ > + } else \ > + ret = -1; \ > + } else { \ > + str = PTR_ALIGN(str, sizeof(type)); \ > + if (str + sizeof(type) <= end) \ > + value = *(type *)str; \ > + else \ > + ret = -1; \ > + } \ > + str += sizeof(type); \ > + break; > + > + > +static > +int ftrace_struct_unpack_one(char storage_type, unsigned long long *ptr, > + const char *binary, unsigned int len) > +{ > + unsigned long long value; > + const char *str = binary, *end = binary + len; > + int ret = 0; > + > + switch (storage_type) { > + case 'x': > + return 1; > + CASE_UNPACK('c', char) > + CASE_UNPACK('b', signed char) > + CASE_UNPACK('B', unsigned char) > + CASE_UNPACK('h', short) > + CASE_UNPACK('H', unsigned short) > + CASE_UNPACK('i', int) > + CASE_UNPACK('I', unsigned int) > + CASE_UNPACK('l', long) > + CASE_UNPACK('L', unsigned long) > + CASE_UNPACK('q', long long) > + CASE_UNPACK('Q', unsigned long long) > + CASE_UNPACK('p', long) /* use long instead void * */ > + case 's': > + ret = strlen(str) + 1; > + if (ret <= end - str) > + *ptr = (unsigned long)str; > + else > + ret = -1; > + return ret; > + default: > + return -1; > + } > + > + if (ret == -1) > + return -1; > + > + *ptr = value; > + return str - binary; > +} > +/* > + * FIXME: write a portable ftrace_struct_unpack() > + * - unpack binary data and rebuild a va_list > + */ > + > +struct ftrace_struct_arg_iter { > + struct arg_iter arg_iter_head; > + const char *storage_fmt; > + const char *binary; > + const char *binary_end; > +}; > + > +static > +int ftrace_struct_get_next_arg(struct arg_iter *head, unsigned long long *ptr, > + unsigned int type_len) > +{ > + int ret; > + struct ftrace_struct_arg_iter *iter = container_of(head, > + struct ftrace_struct_arg_iter, arg_iter_head); > + > + while (*iter->storage_fmt == 'x') { > + iter->storage_fmt++; > + iter->binary++; > + } > + ret = ftrace_struct_unpack_one(*iter->storage_fmt, ptr, iter->binary, > + iter->binary_end - iter->binary); > + if (ret < 0) > + return ret; > + iter->storage_fmt++; > + iter->binary += ret; > + return ret; > +} > + > +int ftrace_struct_spintf(char *buf, size_t size,
Small PINT Foam
> + const struct ftrace_struct *fmt, > + const char *binary, size_t binary_len) > +{ > + struct ftrace_struct_arg_iter args = { > + { ftrace_struct_get_next_arg }, > + fmt->storage_fmt, > + binary, > + binary + binary_len, > + }; > + return snprintf_iter_args(buf, size, fmt->show_fmt, > + &args.arg_iter_head); > +} > + > +struct ftrace_struct_permanent { > + struct list_head head; > + struct ftrace_struct fmt; > +}; > + > +#define MAX_PERMANENT_ALLOC 1024 > +static int permanent_count; > +static LIST_HEAD(permanent_list); > +static DEFINE_MUTEX(lock); > + > +struct ftrace_struct *ftrace_struct_permanent_alloc(const char *storage_fmt, > + const char *show_fmt) > +{ > + struct ftrace_struct_permanent *pos; > + struct ftrace_struct fmt = {NULL, NULL}; > + mutex_lock(&lock); > + list_for_each_entry(pos, &permanent_list, head) { > + if (strcmp(pos->fmt.storage_fmt, storage_fmt) == 0 > + && strcmp(pos->fmt.show_fmt, show_fmt) == 0) > + goto finish; > + } > + > + if (permanent_count >= MAX_PERMANENT_ALLOC) > + goto err; > + fmt.storage_fmt = kstrdup(storage_fmt, GFP_KERNEL); > + if (!fmt.storage_fmt) > + goto err; > + fmt.show_fmt = kstrdup(show_fmt, GFP_KERNEL); > + if (!fmt.show_fmt) > + goto err; > + pos = kmalloc(sizeof(*pos), GFP_KERNEL); > + if (!pos) > + goto err; > + > + pos->fmt = fmt; > + list_add(&pos->head, &permanent_list); > + permanent_count++; > +finish: > + mutex_unlock(&lock); > + return &pos->fmt; > +err: > + kfree(fmt.storage_fmt); > + kfree(fmt.show_fmt); > + mutex_unlock(&lock); > + return NULL; > +} > +EXPORT_SYMBOL(ftrace_struct_permanent_alloc); > + > + > diff --git a/lib/vsprintf.c b/lib/vsprintf.c > index a013bbc..f2f794f 100644 > --- a/lib/vsprintf.c > +++ b/lib/vsprintf.c > @@ -856,6 +856,267 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) > EXPORT_SYMBOL(vsnprintf); > > /** > + * snprintf_iter_args - 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: struct arg_iter style arguments for the format string > + * > + * This function follows C99 vsnprintf, but has some extensions: > + * %pS output the name of a text symbol > + * %pF output the name of a function pointer > + * %pR output the address range in a struct resource > + * > + * 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. > + */ > +int snprintf_iter_args(char *buf, size_t size, const char *fmt, > + struct arg_iter *args) > +{ > + unsigned long long num; > + int base; > + char *str, *end, c; > + > + int flags; /* flags to number() */ > + > + int field_width; /* width of output field */ > + int precision; /* min. # of digits for integers; max > + number of chars for from string */ > + int qualifier; /* '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 */ > + > + /* Reject out-of-range values early. Large positive sizes are > + used for unknown buffer sizes. */ > + if (unlikely((int) size < 0)) { > + /* There can be only one.. */ > + static char warn = 1; > + WARN_ON(warn); > + warn = 0; > + return 0; > + } > + > +#define iter_arg(args, type) ({ \ > + unsigned long long __value; \ > + if (args->get_next_arg(args, &__value, sizeof(type)) < 0) \ > + goto error; \ > + (type)__value; \ > +}) > + > + str = buf; > + end = buf + size; > + > + /* Make sure end is always >= buf */ > + if (end < buf) { > + end = ((void *)-1); > + size = end - buf; > + } > + > + for (; *fmt ; ++fmt) { > + if (*fmt != '%') { > + if (str < end) > + *str = *fmt; > + ++str; > + continue; > + } > + > + /* process flags */ > + flags = 0; > +repeat: > + ++fmt; /* this also skips first '%' */ > + switch (*fmt) { > + case '-': > + flags |= LEFT; > + goto repeat; > + case '+': > + flags |= PLUS; > + goto repeat; > + case ' ': > + flags |= SPACE; > + goto repeat; > + case '#': > + flags |= SPECIAL; > + goto repeat; > + case '0': > + flags |= ZEROPAD; > + goto repeat; > + } > + > + /* get field width */ > + field_width = -1; > + if (isdigit(*fmt)) > + field_width = skip_atoi(&fmt); > + else if (*fmt == '*') { > + ++fmt; > + /* it's the next argument */ > + field_width = iter_arg(args, int); > + if (field_width < 0) { > + field_width = -field_width; > + flags |= LEFT; > + } > + } > + > + /* get the precision */ > + precision = -1; > + if (*fmt == '.') { > + ++fmt; > + if (isdigit(*fmt)) > + precision = skip_atoi(&fmt); > + else if (*fmt == '*') { > + ++fmt; > + /* it's the next argument */ > + precision = iter_arg(args, int); > + } > + if (precision < 0) > + precision = 0; > + } > + > + /* get the conversion qualifier */ > + qualifier = -1; > + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || > + *fmt == 'Z' || *fmt == 'z' || *fmt == 't') { > + qualifier = *fmt; > + ++fmt; > + if (qualifier == 'l' && *fmt == 'l') { > + qualifier = 'L'; > + ++fmt; > + } > + } > + > + /* default base */ > + base = 10; > + > + switch (*fmt) { > + case 'c': > + if (!(flags & LEFT)) { > + while (--field_width > 0) { > + if (str < end) > + *str = ' '; > + ++str; > + } > + } > + c = iter_arg(args, unsigned char); > + if (str < end) > + *str = c; > + ++str; > + while (--field_width > 0) { > + if (str < end) > + *str = ' '; > + ++str; > + } > + continue; > + > + case 's': > + str = string(str, end, (char *)iter_arg(args, long), > + field_width, precision, flags); > + continue; > + > + case 'p': > + str = pointer(fmt+1, str, end, > + (void *)iter_arg(args, long), > + field_width, precision, flags); > + /* Skip all alphanumeric pointer suffixes */ > + while (isalnum(fmt[1])) > + fmt++; > + continue; > + > + case 'n': > + /* FIXME: > + * What does C99 say about the overflow case here? */ > + if (qualifier == 'l') { > + long *ip = (long *)iter_arg(args, long); > + *ip = (str - buf); > + } else if (qualifier == 'Z' || qualifier == 'z') { > + size_t *ip = (size_t *)iter_arg(args, long); > + *ip = (str - buf); > + } else { > + int *ip = (int *)iter_arg(args, long); > + *ip = (str - buf); > + } > + continue; > + > + case '%': > + if (str < end) > + *str = '%'; > + ++str; > + continue; > + > + /* > + * integer number formats > + * - set up the flags and "break" > + */ > + case 'o': > + base = 8; > + break; > + > + case 'x': > + flags |= SMALL; > + case 'X': > + base = 16; > + break; > + > + case 'd': > + case 'i': > + flags |= SIGN; > + case 'u': > + break; > + > + default: > + if (str < end) > + *str = '%'; > + ++str; > + if (*fmt) { > + if (str < end) > + *str = *fmt; > + ++str; > + } else { > + --fmt; > + } > + continue; > + } > + if (qualifier == 'L') > + num = iter_arg(args, long long); > + else if (qualifier == 'l') { > + num = iter_arg(args, unsigned long); > + if (flags & SIGN) > + num = (signed long) num; > + } else if (qualifier == 'Z' || qualifier == 'z') { > + num = iter_arg(args, size_t); > + } else if (qualifier == 't') { > + num = iter_arg(args, ptrdiff_t); > + } else if (qualifier == 'h') { > + num = iter_arg(args, unsigned short); > + if (flags & SIGN) > + num = (signed short) num; > + } else { > + num = iter_arg(args, unsigned int); > + if (flags & SIGN) > + num = (signed int) num; > + } > + str = number(str, end, num, base, > + field_width, precision, flags); > + } > + 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; > + > +error: > + return -1; > +#undef iter_arg > +} > + > +/**
Again this should be in its own patch.
-- Steve
> * vscnprintf - 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 > > > > > >
| |