1
2
3
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
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
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
58
59
60 if (*pos == 0)
61 return SEQ_START_TOKEN;
62
63 return pi_next(s, NULL, pos);
64}
65
66
67
68
69
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
98
99
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:
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
195postcore_initcall(pi_init);
196