194 lines · 4.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Userspace indexing of printk formats */ #include <linux/debugfs.h>#include <linux/module.h>#include <linux/printk.h>#include <linux/slab.h>#include <linux/string_helpers.h> #include "internal.h" extern struct pi_entry *__start_printk_index[];extern struct pi_entry *__stop_printk_index[]; /* The base dir for module formats, typically debugfs/printk/index/ */static struct dentry *dfs_index; static struct pi_entry *pi_get_entry(const struct module *mod, loff_t pos){	struct pi_entry **entries;	unsigned int nr_entries; #ifdef CONFIG_MODULES	if (mod) {		entries = mod->printk_index_start;		nr_entries = mod->printk_index_size;	} else#endif	{		/* vmlinux, comes from linker symbols */		entries = __start_printk_index;		nr_entries = __stop_printk_index - __start_printk_index;	} 	if (pos >= nr_entries)		return NULL; 	return entries[pos];} static void *pi_next(struct seq_file *s, void *v, loff_t *pos){	const struct module *mod = s->file->f_inode->i_private;	struct pi_entry *entry = pi_get_entry(mod, *pos); 	(*pos)++; 	return entry;} static void *pi_start(struct seq_file *s, loff_t *pos){	/*	 * Make show() print the header line. Do not update *pos because	 * pi_next() still has to return the entry at index 0 later.	 */	if (*pos == 0)		return SEQ_START_TOKEN; 	return pi_next(s, NULL, pos);} /* * We need both ESCAPE_ANY and explicit characters from ESCAPE_SPECIAL in @only * because otherwise ESCAPE_NAP will cause double quotes and backslashes to be * ignored for quoting. */#define seq_escape_printf_format(s, src) \	seq_escape_str(s, src, ESCAPE_ANY | ESCAPE_NAP | ESCAPE_APPEND, "\"\\") static int pi_show(struct seq_file *s, void *v){	const struct pi_entry *entry = v;	int level = LOGLEVEL_DEFAULT;	enum printk_info_flags flags = 0;	u16 prefix_len = 0; 	if (v == SEQ_START_TOKEN) {		seq_puts(s, "# <level/flags> filename:line function \"format\"\n");		return 0;	} 	if (!entry->fmt)		return 0; 	if (entry->level)		printk_parse_prefix(entry->level, &level, &flags);	else		prefix_len = printk_parse_prefix(entry->fmt, &level, &flags);  	if (flags & LOG_CONT) {		/*		 * LOGLEVEL_DEFAULT here means "use the same level as the		 * message we're continuing from", not the default message		 * loglevel, so don't display it as such.		 */		if (level == LOGLEVEL_DEFAULT)			seq_puts(s, "<c>");		else			seq_printf(s, "<%d,c>", level);	} else		seq_printf(s, "<%d>", level); 	seq_printf(s, " %s:%d %s \"", entry->file, entry->line, entry->func);	if (entry->subsys_fmt_prefix)		seq_escape_printf_format(s, entry->subsys_fmt_prefix);	seq_escape_printf_format(s, entry->fmt + prefix_len);	seq_puts(s, "\"\n"); 	return 0;} static void pi_stop(struct seq_file *p, void *v) { } static const struct seq_operations dfs_index_sops = {	.start = pi_start,	.next  = pi_next,	.show  = pi_show,	.stop  = pi_stop,}; DEFINE_SEQ_ATTRIBUTE(dfs_index); #ifdef CONFIG_MODULESstatic const char *pi_get_module_name(struct module *mod){	return mod ? mod->name : "vmlinux";}#elsestatic const char *pi_get_module_name(struct module *mod){	return "vmlinux";}#endif static void pi_create_file(struct module *mod){	debugfs_create_file(pi_get_module_name(mod), 0444, dfs_index,				       mod, &dfs_index_fops);} #ifdef CONFIG_MODULESstatic void pi_remove_file(struct module *mod){	debugfs_lookup_and_remove(pi_get_module_name(mod), dfs_index);} static int pi_module_notify(struct notifier_block *nb, unsigned long op,			    void *data){	struct module *mod = data; 	switch (op) {	case MODULE_STATE_COMING:		pi_create_file(mod);		break;	case MODULE_STATE_GOING:		pi_remove_file(mod);		break;	default: /* we don't care about other module states */		break;	} 	return NOTIFY_OK;} static struct notifier_block module_printk_fmts_nb = {	.notifier_call = pi_module_notify,}; static void __init pi_setup_module_notifier(void){	register_module_notifier(&module_printk_fmts_nb);}#elsestatic inline void __init pi_setup_module_notifier(void) { }#endif static int __init pi_init(void){	struct dentry *dfs_root = debugfs_create_dir("printk", NULL); 	dfs_index = debugfs_create_dir("index", dfs_root);	pi_setup_module_notifier();	pi_create_file(NULL); 	return 0;} /* debugfs comes up on core and must be initialised first */postcore_initcall(pi_init);