Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | // 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; } #endif if (!mod) { /* 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); |