diff options
Diffstat (limited to 'kernel/trace/trace_printk.c')
| -rw-r--r-- | kernel/trace/trace_printk.c | 117 | 
1 files changed, 105 insertions, 12 deletions
| diff --git a/kernel/trace/trace_printk.c b/kernel/trace/trace_printk.c index 2547d8813cf0..1f06468a10d7 100644 --- a/kernel/trace/trace_printk.c +++ b/kernel/trace/trace_printk.c @@ -32,7 +32,7 @@ static DEFINE_MUTEX(btrace_mutex);  struct trace_bprintk_fmt {  	struct list_head list; -	char fmt[0]; +	const char *fmt;  };  static inline struct trace_bprintk_fmt *lookup_format(const char *fmt) @@ -49,6 +49,7 @@ static  void hold_module_trace_bprintk_format(const char **start, const char **end)  {  	const char **iter; +	char *fmt;  	mutex_lock(&btrace_mutex);  	for (iter = start; iter < end; iter++) { @@ -58,14 +59,18 @@ void hold_module_trace_bprintk_format(const char **start, const char **end)  			continue;  		} -		tb_fmt = kmalloc(offsetof(struct trace_bprintk_fmt, fmt) -				+ strlen(*iter) + 1, GFP_KERNEL); -		if (tb_fmt) { +		tb_fmt = kmalloc(sizeof(*tb_fmt), GFP_KERNEL); +		if (tb_fmt) +			fmt = kmalloc(strlen(*iter) + 1, GFP_KERNEL); +		if (tb_fmt && fmt) {  			list_add_tail(&tb_fmt->list, &trace_bprintk_fmt_list); -			strcpy(tb_fmt->fmt, *iter); +			strcpy(fmt, *iter); +			tb_fmt->fmt = fmt;  			*iter = tb_fmt->fmt; -		} else +		} else { +			kfree(tb_fmt);  			*iter = NULL; +		}  	}  	mutex_unlock(&btrace_mutex);  } @@ -84,6 +89,76 @@ static int module_trace_bprintk_format_notify(struct notifier_block *self,  	return 0;  } +/* + * The debugfs/tracing/printk_formats file maps the addresses with + * the ASCII formats that are used in the bprintk events in the + * buffer. For userspace tools to be able to decode the events from + * the buffer, they need to be able to map the address with the format. + * + * The addresses of the bprintk formats are in their own section + * __trace_printk_fmt. But for modules we copy them into a link list. + * The code to print the formats and their addresses passes around the + * address of the fmt string. If the fmt address passed into the seq + * functions is within the kernel core __trace_printk_fmt section, then + * it simply uses the next pointer in the list. + * + * When the fmt pointer is outside the kernel core __trace_printk_fmt + * section, then we need to read the link list pointers. The trick is + * we pass the address of the string to the seq function just like + * we do for the kernel core formats. To get back the structure that + * holds the format, we simply use containerof() and then go to the + * next format in the list. + */ +static const char ** +find_next_mod_format(int start_index, void *v, const char **fmt, loff_t *pos) +{ +	struct trace_bprintk_fmt *mod_fmt; + +	if (list_empty(&trace_bprintk_fmt_list)) +		return NULL; + +	/* +	 * v will point to the address of the fmt record from t_next +	 * v will be NULL from t_start. +	 * If this is the first pointer or called from start +	 * then we need to walk the list. +	 */ +	if (!v || start_index == *pos) { +		struct trace_bprintk_fmt *p; + +		/* search the module list */ +		list_for_each_entry(p, &trace_bprintk_fmt_list, list) { +			if (start_index == *pos) +				return &p->fmt; +			start_index++; +		} +		/* pos > index */ +		return NULL; +	} + +	/* +	 * v points to the address of the fmt field in the mod list +	 * structure that holds the module print format. +	 */ +	mod_fmt = container_of(v, typeof(*mod_fmt), fmt); +	if (mod_fmt->list.next == &trace_bprintk_fmt_list) +		return NULL; + +	mod_fmt = container_of(mod_fmt->list.next, typeof(*mod_fmt), list); + +	return &mod_fmt->fmt; +} + +static void format_mod_start(void) +{ +	mutex_lock(&btrace_mutex); +} + +static void format_mod_stop(void) +{ +	mutex_unlock(&btrace_mutex); +} +  #else /* !CONFIG_MODULES */  __init static int  module_trace_bprintk_format_notify(struct notifier_block *self, @@ -91,6 +166,13 @@ module_trace_bprintk_format_notify(struct notifier_block *self,  {  	return 0;  } +static inline const char ** +find_next_mod_format(int start_index, void *v, const char **fmt, loff_t *pos) +{ +	return NULL; +} +static inline void format_mod_start(void) { } +static inline void format_mod_stop(void) { }  #endif /* CONFIG_MODULES */ @@ -153,20 +235,30 @@ int __ftrace_vprintk(unsigned long ip, const char *fmt, va_list ap)  }  EXPORT_SYMBOL_GPL(__ftrace_vprintk); +static const char **find_next(void *v, loff_t *pos) +{ +	const char **fmt = v; +	int start_index; + +	start_index = __stop___trace_bprintk_fmt - __start___trace_bprintk_fmt; + +	if (*pos < start_index) +		return __start___trace_bprintk_fmt + *pos; + +	return find_next_mod_format(start_index, v, fmt, pos); +} +  static void *  t_start(struct seq_file *m, loff_t *pos)  { -	const char **fmt = __start___trace_bprintk_fmt + *pos; - -	if ((unsigned long)fmt >= (unsigned long)__stop___trace_bprintk_fmt) -		return NULL; -	return fmt; +	format_mod_start(); +	return find_next(NULL, pos);  }  static void *t_next(struct seq_file *m, void * v, loff_t *pos)  {  	(*pos)++; -	return t_start(m, pos); +	return find_next(v, pos);  }  static int t_show(struct seq_file *m, void *v) @@ -205,6 +297,7 @@ static int t_show(struct seq_file *m, void *v)  static void t_stop(struct seq_file *m, void *p)  { +	format_mod_stop();  }  static const struct seq_operations show_format_seq_ops = { |