123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- // 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_MODULES
- static const char *pi_get_module_name(struct module *mod)
- {
- return mod ? mod->name : "vmlinux";
- }
- #else
- static 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_MODULES
- static 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);
- }
- #else
- static 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);
|