linux/kernel/printk/index.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Userspace indexing of printk formats
   4 */
   5
   6#include <linux/debugfs.h>
   7#include <linux/module.h>
   8#include <linux/printk.h>
   9#include <linux/slab.h>
  10#include <linux/string_helpers.h>
  11
  12#include "internal.h"
  13
  14extern struct pi_entry *__start_printk_index[];
  15extern struct pi_entry *__stop_printk_index[];
  16
  17/* The base dir for module formats, typically debugfs/printk/index/ */
  18static struct dentry *dfs_index;
  19
  20static struct pi_entry *pi_get_entry(const struct module *mod, loff_t pos)
  21{
  22        struct pi_entry **entries;
  23        unsigned int nr_entries;
  24
  25#ifdef CONFIG_MODULES
  26        if (mod) {
  27                entries = mod->printk_index_start;
  28                nr_entries = mod->printk_index_size;
  29        }
  30#endif
  31
  32        if (!mod) {
  33                /* vmlinux, comes from linker symbols */
  34                entries = __start_printk_index;
  35                nr_entries = __stop_printk_index - __start_printk_index;
  36        }
  37
  38        if (pos >= nr_entries)
  39                return NULL;
  40
  41        return entries[pos];
  42}
  43
  44static void *pi_next(struct seq_file *s, void *v, loff_t *pos)
  45{
  46        const struct module *mod = s->file->f_inode->i_private;
  47        struct pi_entry *entry = pi_get_entry(mod, *pos);
  48
  49        (*pos)++;
  50
  51        return entry;
  52}
  53
  54static void *pi_start(struct seq_file *s, loff_t *pos)
  55{
  56        /*
  57         * Make show() print the header line. Do not update *pos because
  58         * pi_next() still has to return the entry at index 0 later.
  59         */
  60        if (*pos == 0)
  61                return SEQ_START_TOKEN;
  62
  63        return pi_next(s, NULL, pos);
  64}
  65
  66/*
  67 * We need both ESCAPE_ANY and explicit characters from ESCAPE_SPECIAL in @only
  68 * because otherwise ESCAPE_NAP will cause double quotes and backslashes to be
  69 * ignored for quoting.
  70 */
  71#define seq_escape_printf_format(s, src) \
  72        seq_escape_str(s, src, ESCAPE_ANY | ESCAPE_NAP | ESCAPE_APPEND, "\"\\")
  73
  74static int pi_show(struct seq_file *s, void *v)
  75{
  76        const struct pi_entry *entry = v;
  77        int level = LOGLEVEL_DEFAULT;
  78        enum printk_info_flags flags = 0;
  79        u16 prefix_len = 0;
  80
  81        if (v == SEQ_START_TOKEN) {
  82                seq_puts(s, "# <level/flags> filename:line function \"format\"\n");
  83                return 0;
  84        }
  85
  86        if (!entry->fmt)
  87                return 0;
  88
  89        if (entry->level)
  90                printk_parse_prefix(entry->level, &level, &flags);
  91        else
  92                prefix_len = printk_parse_prefix(entry->fmt, &level, &flags);
  93
  94
  95        if (flags & LOG_CONT) {
  96                /*
  97                 * LOGLEVEL_DEFAULT here means "use the same level as the
  98                 * message we're continuing from", not the default message
  99                 * loglevel, so don't display it as such.
 100                 */
 101                if (level == LOGLEVEL_DEFAULT)
 102                        seq_puts(s, "<c>");
 103                else
 104                        seq_printf(s, "<%d,c>", level);
 105        } else
 106                seq_printf(s, "<%d>", level);
 107
 108        seq_printf(s, " %s:%d %s \"", entry->file, entry->line, entry->func);
 109        if (entry->subsys_fmt_prefix)
 110                seq_escape_printf_format(s, entry->subsys_fmt_prefix);
 111        seq_escape_printf_format(s, entry->fmt + prefix_len);
 112        seq_puts(s, "\"\n");
 113
 114        return 0;
 115}
 116
 117static void pi_stop(struct seq_file *p, void *v) { }
 118
 119static const struct seq_operations dfs_index_sops = {
 120        .start = pi_start,
 121        .next  = pi_next,
 122        .show  = pi_show,
 123        .stop  = pi_stop,
 124};
 125
 126DEFINE_SEQ_ATTRIBUTE(dfs_index);
 127
 128#ifdef CONFIG_MODULES
 129static const char *pi_get_module_name(struct module *mod)
 130{
 131        return mod ? mod->name : "vmlinux";
 132}
 133#else
 134static const char *pi_get_module_name(struct module *mod)
 135{
 136        return "vmlinux";
 137}
 138#endif
 139
 140static void pi_create_file(struct module *mod)
 141{
 142        debugfs_create_file(pi_get_module_name(mod), 0444, dfs_index,
 143                                       mod, &dfs_index_fops);
 144}
 145
 146#ifdef CONFIG_MODULES
 147static void pi_remove_file(struct module *mod)
 148{
 149        debugfs_remove(debugfs_lookup(pi_get_module_name(mod), dfs_index));
 150}
 151
 152static int pi_module_notify(struct notifier_block *nb, unsigned long op,
 153                            void *data)
 154{
 155        struct module *mod = data;
 156
 157        switch (op) {
 158        case MODULE_STATE_COMING:
 159                pi_create_file(mod);
 160                break;
 161        case MODULE_STATE_GOING:
 162                pi_remove_file(mod);
 163                break;
 164        default: /* we don't care about other module states */
 165                break;
 166        }
 167
 168        return NOTIFY_OK;
 169}
 170
 171static struct notifier_block module_printk_fmts_nb = {
 172        .notifier_call = pi_module_notify,
 173};
 174
 175static void __init pi_setup_module_notifier(void)
 176{
 177        register_module_notifier(&module_printk_fmts_nb);
 178}
 179#else
 180static inline void __init pi_setup_module_notifier(void) { }
 181#endif
 182
 183static int __init pi_init(void)
 184{
 185        struct dentry *dfs_root = debugfs_create_dir("printk", NULL);
 186
 187        dfs_index = debugfs_create_dir("index", dfs_root);
 188        pi_setup_module_notifier();
 189        pi_create_file(NULL);
 190
 191        return 0;
 192}
 193
 194/* debugfs comes up on core and must be initialised first */
 195postcore_initcall(pi_init);
 196