Skip to contentSkip to conversation

ftrace: support binary record

PATCH In review Lai Jiangshan 2 participants 4 messages Dec 10, 2008

Subsystems (6)
VFS Library code Tracing ftrace +2 more subsystems
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 ftrace: support binary record

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.

And add a API ftrace_struct_printk() using this infrastructure. (it's like ftrace_printk(), but it record the date by binary)

---
 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(+)
include/linux/ftrace.h +29 -0
index 703eb53..d8ed7da 100644
@@ -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
+ * inspired from Python's struct
+ */
+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,
+		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 */
include/linux/string.h +8 -0
index d18fc19..cf13e9d 100644
@@ -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);
+
 extern ssize_t memory_read_from_buffer(void *to, size_t count, loff_t *ppos,
 			const void *from, size_t available);
 
kernel/trace/Makefile +1 -0
index c8228b1..46c2b7a 100644
@@ -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
kernel/trace/trace.c +130 -0

160 lines not on this page · raw message

kernel/trace/trace.h +10 -0
index 8465ad0..f95b766 100644
@@ -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)
 
kernel/trace/trace_struct.c new file +248 -0

248 lines not on this page · raw message

lib/vsprintf.c +261 -0

267 lines not on this page · raw message

maintainer
#1 lore raw
Quoted 8 lines · Lai Jiangshan

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.

Quoted 19 lines

And add a API ftrace_struct_printk() using this infrastructure. (it's like ftrace_printk(), but it record the date by binary)

---
 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(+)
include/linux/ftrace.h +0 -0
index 703eb53..d8ed7da 100644

include/linux/ftrace.h static inline void start_boot_trace(void) { } · line 254

Quoted 7 lines
include/linux/ftrace.h +4 -0
@@ -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.

include/linux/ftrace.h static inline void start_boot_trace(void) { } · line 255

Quoted 1 line
include/linux/ftrace.h +1 -0
+ * inspired from Python's struct

I have an anxiety with snakes.

include/linux/ftrace.h static inline void start_boot_trace(void) { } · line 263

Quoted 8 lines
include/linux/ftrace.h +8 -0
+ */
+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?

Quoted 25 lines
include/linux/ftrace.h +16 -0
+		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 */
include/linux/string.h +0 -0
index d18fc19..cf13e9d 100644
@@ -111,6 +111,14 @@ extern void argv_free(char **argv);
 

include/linux/string.h extern void argv_free(char **argv); · line 121

Quoted 10 lines
include/linux/string.h +8 -0
 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.

Quoted 24 lines
include/linux/string.h +0 -0
 extern ssize_t memory_read_from_buffer(void *to, size_t count, loff_t *ppos,
 			const void *from, size_t available);
 
kernel/trace/Makefile +1 -0
index c8228b1..46c2b7a 100644
@@ -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
kernel/trace/trace.c +1 -0
index d86e325..4ad84d3 100644
@@ -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,

kernel/trace/trace.c trace_seq_printf(struct trace_seq *s, const char *fmt, ...) · line 302

Quoted 10 lines
kernel/trace/trace.c +10 -0
+		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.

Quoted 60 lines
kernel/trace/trace.c +39 -0
+			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;

kernel/trace/trace.c static enum print_line_t print_raw_fmt(struct trace_iterator *iter) · line 1725

Quoted 10 lines
kernel/trace/trace.c +10 -0
+
+		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.

Quoted 22 lines
kernel/trace/trace.c +15 -0
 	}
 	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;

kernel/trace/trace.c int __ftrace_printk(unsigned long ip, const char *fmt, ...) · line 3130

Quoted 10 lines
kernel/trace/trace.c +10 -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.

Quoted 5 lines
kernel/trace/trace.c +5 -0
+	len = ftrace_struct_pack(trace_buf, TRACE_BUF_SIZE, fmt->storage_fmt,
+			args);
+
+	if (len > TRACE_BUF_SIZE || len < 0)
+		goto out_unlock;

kernel/trace/trace.c int __ftrace_printk(unsigned long ip, const char *fmt, ...) · line 3145

Quoted 10 lines
kernel/trace/trace.c +10 -0
+
+	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.

kernel/trace/trace.c int __ftrace_printk(unsigned long ip, const char *fmt, ...) · line 3158

Quoted 13 lines
kernel/trace/trace.c +13 -0
+
+	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.

Quoted 6 lines
kernel/trace/trace.c +6 -0
+
+int __ftrace_struct_printk(unsigned long ip, const struct ftrace_struct *fmt,
+		...)
+{
+	int ret;
+	va_list ap;

kernel/trace/trace.c int __ftrace_printk(unsigned long ip, const char *fmt, ...) · line 3174

Quoted 10 lines
kernel/trace/trace.c +10 -0
+
+	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.

Quoted 221 lines
kernel/trace/trace.c +1 -0
+
 static int trace_panic_handler(struct notifier_block *this,
 			       unsigned long event, void *unused)
 {
kernel/trace/trace.h +10 -0
index 8465ad0..f95b766 100644
@@ -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)
 
kernel/trace/trace_struct.c new file +176 -0
new file mode 100644
index 0000000..6814b18
@@ -0,0 +1,248 @@
+/*
+ * ftrace_struct binary storage tracer
+ *
+ * Copyright (C) 2008 Lai Jiangshan <[email protected]>
+ */
+#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++;
+	}

kernel/trace/trace_struct.c line 186

Quoted 10 lines
kernel/trace/trace_struct.c new file +10 -0
+	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

Quoted 321 lines
kernel/trace/trace_struct.c new file +62 -0
+		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);
+
+
lib/vsprintf.c +251 -0
index a013bbc..f2f794f 100644
@@ -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';

lib/vsprintf.c int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) · line 1119

Quoted 10 lines
lib/vsprintf.c +10 -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

Quoted 9 lines
lib/vsprintf.c +0 -0
  * 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
#2 lore raw
Quoted 7 lines · Steven Rostedt
Quoted 3 lines · Lai Jiangshan

Binary record will save memory in trace buffer, and more events can be recorded in buffer.

Quoted 7 lines
Quoted 4 lines

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.

consider ftrace_struct_printk() as ftrace_printk(), but the first parameter is "struct ftrace_struct *fmt"

new file: kernel/trace/trace_struct.c : this infrastructure's code

lib/vsprintf.c : formator
other files add ftrace_struct_printk()

Quoted 4 lines
Quoted 4 lines

And add a API ftrace_struct_printk() using this infrastructure. (it's like ftrace_printk(), but it record the date by binary)

example:
...

           <...>-3246  [000]  1243.166054: sys_read: sys_read by ftrace_printk, 3, asdfuuuuu
           <...>-3246  [000]  1243.166057: sys_read: sys_read by ftrace_struct_printk, 3, asdfuuuuu
           <...>-3246  [000]  1243.166490: sys_read: sys_read by ftrace_printk, 9, asdfuuuuu
           <...>-3246  [000]  1243.166491: sys_read: sys_read by ftrace_struct_printk, 9, asdfuuuuu

...

fs/read_write.c +4 -1
index 969a6d9..0d9fbed 100644
@@ -17,7 +17,7 @@
 #include <linux/pagemap.h>
 #include <linux/splice.h>
 #include "read_write.h"
-
+#include <linux/ftrace.h>
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
 
@@ -356,12 +356,15 @@ static inline void file_pos_write(struct file *file, loff_t pos)
 	file->f_pos = pos;
 }
 
+struct ftrace_struct test = {"ixxs", "sys_read by ftrace_struct_printk, %d, %s\n"};
 asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count)
 {
 	struct file *file;
 	ssize_t ret = -EBADF;
 	int fput_needed;
 
+	ftrace_printk("sys_read by ftrace_printk, %d, %s\n", fd, "asdfuuuuu");
+	ftrace_struct_printk(&test, fd, "asdfuuuuu");
 	file = fget_light(fd, &fput_needed);
 	if (file) {
 		loff_t pos = file_pos_read(file);
#3 lore raw
In reply to Steven Rostedt
Quoted 12 lines · Steven Rostedt

include/linux/ftrace.h static inline void start_boot_trace(void) { } · line 254

Quoted 6 lines
include/linux/ftrace.h +4 -0
+#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.

I have add API in kernel/trace/trace_struct.c:

struct ftrace_struct *ftrace_struct_permanent_alloc(const char *storage_fmt,

		const char *show_fmt);

So it's OK for modules.

Quoted 5 lines
Quoted 2 lines

+ * inspired from Python's struct

I have an anxiety with snakes.

Quoted 10 lines

include/linux/ftrace.h static inline void start_boot_trace(void) { } · line 263

Quoted 8 lines

+ */

include/linux/ftrace.h +7 -0
+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?

I think it the most strange mistake that I have made.

Quoted 178 lines
Quoted 26 lines
include/linux/ftrace.h +16 -0
+		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 */
include/linux/string.h +0 -0
index d18fc19..cf13e9d 100644
@@ -111,6 +111,14 @@ extern void argv_free(char **argv);
 

include/linux/string.h extern void argv_free(char **argv); · line 121

Quoted 10 lines
include/linux/string.h +8 -0
 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.

Quoted 24 lines
include/linux/string.h +0 -0
 extern ssize_t memory_read_from_buffer(void *to, size_t count, loff_t *ppos,
 			const void *from, size_t available);
 
kernel/trace/Makefile +1 -0
index c8228b1..46c2b7a 100644
@@ -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
kernel/trace/trace.c +1 -0
index d86e325..4ad84d3 100644
@@ -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,

kernel/trace/trace.c trace_seq_printf(struct trace_seq *s, const char *fmt, ...) · line 302

Quoted 10 lines
kernel/trace/trace.c +10 -0
+		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.

Quoted 60 lines
kernel/trace/trace.c +39 -0
+			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;

kernel/trace/trace.c static enum print_line_t print_raw_fmt(struct trace_iterator *iter) · line 1725

Quoted 10 lines
kernel/trace/trace.c +10 -0
+
+		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.

Quoted 28 lines
kernel/trace/trace.c +21 -0
 	}
 	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];
+
Quoted 8 lines

kernel/trace/trace.c int __ftrace_printk(unsigned long ip, const char *fmt, ...) · line 3130

Quoted 4 lines
kernel/trace/trace.c +4 -0
+	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.

This patch is against for mainline. trace_struct_vprintk() is copied from trace_vprintk().
So it's little old. I will fix it.

Quoted 11 lines
Quoted 11 lines
kernel/trace/trace.c +9 -0
+	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)
Quoted 8 lines

kernel/trace/trace.c int __ftrace_printk(unsigned long ip, const char *fmt, ...) · line 3145

Quoted 6 lines
kernel/trace/trace.c +6 -0
+		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.

fmt is permanent, so it is OK for any modules and the unloading of modules.

Quoted 16 lines
Quoted 4 lines
kernel/trace/trace.c +3 -0
+
+	memcpy(&entry->buf, trace_buf, len);
+	ring_buffer_unlock_commit(tr->buffer, event, irq_flags);

kernel/trace/trace.c int __ftrace_printk(unsigned long ip, const char *fmt, ...) · line 3158

Quoted 10 lines
kernel/trace/trace.c +10 -0
+
+ 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.

It's OK, not need to get rid of these exports.

Quoted 271 lines
Quoted 7 lines
kernel/trace/trace.c +6 -0
+
+int __ftrace_struct_printk(unsigned long ip, const struct ftrace_struct *fmt,
+		...)
+{
+	int ret;
+	va_list ap;

kernel/trace/trace.c int __ftrace_printk(unsigned long ip, const char *fmt, ...) · line 3174

Quoted 10 lines
kernel/trace/trace.c +10 -0
+
+	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.

Quoted 221 lines
kernel/trace/trace.c +1 -0
+
 static int trace_panic_handler(struct notifier_block *this,
 			       unsigned long event, void *unused)
 {
kernel/trace/trace.h +10 -0
index 8465ad0..f95b766 100644
@@ -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)
 
kernel/trace/trace_struct.c new file +176 -0
new file mode 100644
index 0000000..6814b18
@@ -0,0 +1,248 @@
+/*
+ * ftrace_struct binary storage tracer
+ *
+ * Copyright (C) 2008 Lai Jiangshan <[email protected]>
+ */
+#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++;
+	}

kernel/trace/trace_struct.c line 186

Quoted 10 lines
kernel/trace/trace_struct.c new file +10 -0
+	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

Quoted 17 lines
kernel/trace/trace_struct.c new file +17 -0
+		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;
+};
Quoted 8 lines

kernel/trace/trace_struct.c line 211

Quoted 8 lines
kernel/trace/trace_struct.c new file +8 -0
+
+#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)

this function is designed for modules.

Ouch! I forgot to declare it in ftrace.h

Quoted 27 lines
Quoted 27 lines
kernel/trace/trace_struct.c new file +27 -0
+{
+	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);
Quoted 8 lines

kernel/trace/trace_struct.c line 246

Quoted 8 lines
kernel/trace/trace_struct.c new file +8 -0
+	return &pos->fmt;
+err:
+	kfree(fmt.storage_fmt);
+	kfree(fmt.show_fmt);
+	mutex_unlock(&lock);
+	return NULL;
+}
+EXPORT_SYMBOL(ftrace_struct_permanent_alloc);

export this symbol to modules as same as ftrace_struct_printk().

Thank you very much!

Lai.