// 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);
// 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);