]>
Commit | Line | Data |
---|---|---|
9c5b9d3d MH |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * trace_boot.c | |
4 | * Tracing kernel boot-time | |
5 | */ | |
6 | ||
7 | #define pr_fmt(fmt) "trace_boot: " fmt | |
8 | ||
76a598ec MH |
9 | #include <linux/bootconfig.h> |
10 | #include <linux/cpumask.h> | |
9c5b9d3d MH |
11 | #include <linux/ftrace.h> |
12 | #include <linux/init.h> | |
76a598ec MH |
13 | #include <linux/kernel.h> |
14 | #include <linux/mutex.h> | |
15 | #include <linux/string.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/trace.h> | |
18 | #include <linux/trace_events.h> | |
9c5b9d3d MH |
19 | |
20 | #include "trace.h" | |
21 | ||
22 | #define MAX_BUF_LEN 256 | |
23 | ||
9c5b9d3d | 24 | static void __init |
4f712a4d | 25 | trace_boot_set_instance_options(struct trace_array *tr, struct xbc_node *node) |
9c5b9d3d MH |
26 | { |
27 | struct xbc_node *anode; | |
28 | const char *p; | |
29 | char buf[MAX_BUF_LEN]; | |
30 | unsigned long v = 0; | |
31 | ||
32 | /* Common ftrace options */ | |
33 | xbc_node_for_each_array_value(node, "options", anode, p) { | |
34 | if (strlcpy(buf, p, ARRAY_SIZE(buf)) >= ARRAY_SIZE(buf)) { | |
35 | pr_err("String is too long: %s\n", p); | |
36 | continue; | |
37 | } | |
38 | ||
39 | if (trace_set_options(tr, buf) < 0) | |
40 | pr_err("Failed to set option: %s\n", buf); | |
41 | } | |
42 | ||
43 | p = xbc_node_find_value(node, "trace_clock", NULL); | |
44 | if (p && *p != '\0') { | |
45 | if (tracing_set_clock(tr, p) < 0) | |
46 | pr_err("Failed to set trace clock: %s\n", p); | |
47 | } | |
48 | ||
49 | p = xbc_node_find_value(node, "buffer_size", NULL); | |
50 | if (p && *p != '\0') { | |
51 | v = memparse(p, NULL); | |
52 | if (v < PAGE_SIZE) | |
53 | pr_err("Buffer size is too small: %s\n", p); | |
54 | if (tracing_resize_ring_buffer(tr, v, RING_BUFFER_ALL_CPUS) < 0) | |
55 | pr_err("Failed to resize trace buffer to %s\n", p); | |
56 | } | |
9d15dbbd MH |
57 | |
58 | p = xbc_node_find_value(node, "cpumask", NULL); | |
59 | if (p && *p != '\0') { | |
60 | cpumask_var_t new_mask; | |
61 | ||
62 | if (alloc_cpumask_var(&new_mask, GFP_KERNEL)) { | |
63 | if (cpumask_parse(p, new_mask) < 0 || | |
64 | tracing_set_cpumask(tr, new_mask) < 0) | |
65 | pr_err("Failed to set new CPU mask %s\n", p); | |
66 | free_cpumask_var(new_mask); | |
67 | } | |
68 | } | |
9c5b9d3d MH |
69 | } |
70 | ||
71 | #ifdef CONFIG_EVENT_TRACING | |
9c5b9d3d MH |
72 | static void __init |
73 | trace_boot_enable_events(struct trace_array *tr, struct xbc_node *node) | |
74 | { | |
75 | struct xbc_node *anode; | |
76 | char buf[MAX_BUF_LEN]; | |
77 | const char *p; | |
78 | ||
79 | xbc_node_for_each_array_value(node, "events", anode, p) { | |
80 | if (strlcpy(buf, p, ARRAY_SIZE(buf)) >= ARRAY_SIZE(buf)) { | |
81 | pr_err("String is too long: %s\n", p); | |
82 | continue; | |
83 | } | |
84 | ||
85 | if (ftrace_set_clr_event(tr, buf, 1) < 0) | |
86 | pr_err("Failed to enable event: %s\n", p); | |
87 | } | |
88 | } | |
81a59555 | 89 | |
4d655281 | 90 | #ifdef CONFIG_KPROBE_EVENTS |
4d655281 MH |
91 | static int __init |
92 | trace_boot_add_kprobe_event(struct xbc_node *node, const char *event) | |
93 | { | |
29a15481 | 94 | struct dynevent_cmd cmd; |
4d655281 MH |
95 | struct xbc_node *anode; |
96 | char buf[MAX_BUF_LEN]; | |
97 | const char *val; | |
da0f1f41 | 98 | int ret = 0; |
4d655281 | 99 | |
da0f1f41 MH |
100 | xbc_node_for_each_array_value(node, "probes", anode, val) { |
101 | kprobe_event_cmd_init(&cmd, buf, MAX_BUF_LEN); | |
29a15481 | 102 | |
da0f1f41 MH |
103 | ret = kprobe_event_gen_cmd_start(&cmd, event, val); |
104 | if (ret) | |
105 | break; | |
4d655281 | 106 | |
da0f1f41 | 107 | ret = kprobe_event_gen_cmd_end(&cmd); |
29a15481 | 108 | if (ret) |
da0f1f41 | 109 | pr_err("Failed to add probe: %s\n", buf); |
4d655281 MH |
110 | } |
111 | ||
29a15481 | 112 | return ret; |
4d655281 MH |
113 | } |
114 | #else | |
115 | static inline int __init | |
116 | trace_boot_add_kprobe_event(struct xbc_node *node, const char *event) | |
117 | { | |
118 | pr_err("Kprobe event is not supported.\n"); | |
119 | return -ENOTSUPP; | |
120 | } | |
121 | #endif | |
122 | ||
3fbe2d6e | 123 | #ifdef CONFIG_HIST_TRIGGERS |
3fbe2d6e MH |
124 | static int __init |
125 | trace_boot_add_synth_event(struct xbc_node *node, const char *event) | |
126 | { | |
fdeb1aca | 127 | struct dynevent_cmd cmd; |
3fbe2d6e | 128 | struct xbc_node *anode; |
fdeb1aca | 129 | char buf[MAX_BUF_LEN]; |
3fbe2d6e | 130 | const char *p; |
fdeb1aca | 131 | int ret; |
3fbe2d6e | 132 | |
fdeb1aca TZ |
133 | synth_event_cmd_init(&cmd, buf, MAX_BUF_LEN); |
134 | ||
135 | ret = synth_event_gen_cmd_start(&cmd, event, NULL); | |
136 | if (ret) | |
137 | return ret; | |
3fbe2d6e MH |
138 | |
139 | xbc_node_for_each_array_value(node, "fields", anode, p) { | |
fdeb1aca TZ |
140 | ret = synth_event_add_field_str(&cmd, p); |
141 | if (ret) | |
142 | return ret; | |
3fbe2d6e MH |
143 | } |
144 | ||
fdeb1aca | 145 | ret = synth_event_gen_cmd_end(&cmd); |
3fbe2d6e MH |
146 | if (ret < 0) |
147 | pr_err("Failed to add synthetic event: %s\n", buf); | |
148 | ||
3fbe2d6e MH |
149 | return ret; |
150 | } | |
151 | #else | |
152 | static inline int __init | |
153 | trace_boot_add_synth_event(struct xbc_node *node, const char *event) | |
154 | { | |
155 | pr_err("Synthetic event is not supported.\n"); | |
156 | return -ENOTSUPP; | |
157 | } | |
158 | #endif | |
159 | ||
81a59555 MH |
160 | static void __init |
161 | trace_boot_init_one_event(struct trace_array *tr, struct xbc_node *gnode, | |
162 | struct xbc_node *enode) | |
163 | { | |
164 | struct trace_event_file *file; | |
165 | struct xbc_node *anode; | |
166 | char buf[MAX_BUF_LEN]; | |
167 | const char *p, *group, *event; | |
168 | ||
169 | group = xbc_node_get_data(gnode); | |
170 | event = xbc_node_get_data(enode); | |
171 | ||
4d655281 MH |
172 | if (!strcmp(group, "kprobes")) |
173 | if (trace_boot_add_kprobe_event(enode, event) < 0) | |
174 | return; | |
3fbe2d6e MH |
175 | if (!strcmp(group, "synthetic")) |
176 | if (trace_boot_add_synth_event(enode, event) < 0) | |
177 | return; | |
4d655281 | 178 | |
81a59555 MH |
179 | mutex_lock(&event_mutex); |
180 | file = find_event_file(tr, group, event); | |
181 | if (!file) { | |
182 | pr_err("Failed to find event: %s:%s\n", group, event); | |
183 | goto out; | |
184 | } | |
185 | ||
186 | p = xbc_node_find_value(enode, "filter", NULL); | |
187 | if (p && *p != '\0') { | |
188 | if (strlcpy(buf, p, ARRAY_SIZE(buf)) >= ARRAY_SIZE(buf)) | |
189 | pr_err("filter string is too long: %s\n", p); | |
190 | else if (apply_event_filter(file, buf) < 0) | |
191 | pr_err("Failed to apply filter: %s\n", buf); | |
192 | } | |
193 | ||
194 | xbc_node_for_each_array_value(enode, "actions", anode, p) { | |
195 | if (strlcpy(buf, p, ARRAY_SIZE(buf)) >= ARRAY_SIZE(buf)) | |
196 | pr_err("action string is too long: %s\n", p); | |
197 | else if (trigger_process_regex(file, buf) < 0) | |
198 | pr_err("Failed to apply an action: %s\n", buf); | |
199 | } | |
200 | ||
201 | if (xbc_node_find_value(enode, "enable", NULL)) { | |
202 | if (trace_event_enable_disable(file, 1, 0) < 0) | |
203 | pr_err("Failed to enable event node: %s:%s\n", | |
204 | group, event); | |
205 | } | |
206 | out: | |
207 | mutex_unlock(&event_mutex); | |
208 | } | |
209 | ||
210 | static void __init | |
211 | trace_boot_init_events(struct trace_array *tr, struct xbc_node *node) | |
212 | { | |
213 | struct xbc_node *gnode, *enode; | |
214 | ||
215 | node = xbc_node_find_child(node, "event"); | |
216 | if (!node) | |
217 | return; | |
218 | /* per-event key starts with "event.GROUP.EVENT" */ | |
219 | xbc_node_for_each_child(node, gnode) | |
220 | xbc_node_for_each_child(gnode, enode) | |
221 | trace_boot_init_one_event(tr, gnode, enode); | |
222 | } | |
9c5b9d3d MH |
223 | #else |
224 | #define trace_boot_enable_events(tr, node) do {} while (0) | |
81a59555 | 225 | #define trace_boot_init_events(tr, node) do {} while (0) |
9c5b9d3d MH |
226 | #endif |
227 | ||
fe1efe92 | 228 | #ifdef CONFIG_DYNAMIC_FTRACE |
fe1efe92 MH |
229 | static void __init |
230 | trace_boot_set_ftrace_filter(struct trace_array *tr, struct xbc_node *node) | |
231 | { | |
232 | struct xbc_node *anode; | |
233 | const char *p; | |
234 | char *q; | |
235 | ||
236 | xbc_node_for_each_array_value(node, "ftrace.filters", anode, p) { | |
237 | q = kstrdup(p, GFP_KERNEL); | |
238 | if (!q) | |
239 | return; | |
240 | if (ftrace_set_filter(tr->ops, q, strlen(q), 0) < 0) | |
241 | pr_err("Failed to add %s to ftrace filter\n", p); | |
242 | else | |
243 | ftrace_filter_param = true; | |
244 | kfree(q); | |
245 | } | |
246 | xbc_node_for_each_array_value(node, "ftrace.notraces", anode, p) { | |
247 | q = kstrdup(p, GFP_KERNEL); | |
248 | if (!q) | |
249 | return; | |
250 | if (ftrace_set_notrace(tr->ops, q, strlen(q), 0) < 0) | |
251 | pr_err("Failed to add %s to ftrace filter\n", p); | |
252 | else | |
253 | ftrace_filter_param = true; | |
254 | kfree(q); | |
255 | } | |
256 | } | |
257 | #else | |
258 | #define trace_boot_set_ftrace_filter(tr, node) do {} while (0) | |
259 | #endif | |
260 | ||
9c5b9d3d MH |
261 | static void __init |
262 | trace_boot_enable_tracer(struct trace_array *tr, struct xbc_node *node) | |
263 | { | |
264 | const char *p; | |
265 | ||
fe1efe92 MH |
266 | trace_boot_set_ftrace_filter(tr, node); |
267 | ||
9c5b9d3d MH |
268 | p = xbc_node_find_value(node, "tracer", NULL); |
269 | if (p && *p != '\0') { | |
270 | if (tracing_set_tracer(tr, p) < 0) | |
271 | pr_err("Failed to set given tracer: %s\n", p); | |
272 | } | |
273 | } | |
274 | ||
4f712a4d MH |
275 | static void __init |
276 | trace_boot_init_one_instance(struct trace_array *tr, struct xbc_node *node) | |
277 | { | |
278 | trace_boot_set_instance_options(tr, node); | |
279 | trace_boot_init_events(tr, node); | |
280 | trace_boot_enable_events(tr, node); | |
281 | trace_boot_enable_tracer(tr, node); | |
282 | } | |
283 | ||
284 | static void __init | |
285 | trace_boot_init_instances(struct xbc_node *node) | |
286 | { | |
287 | struct xbc_node *inode; | |
288 | struct trace_array *tr; | |
289 | const char *p; | |
290 | ||
291 | node = xbc_node_find_child(node, "instance"); | |
292 | if (!node) | |
293 | return; | |
294 | ||
295 | xbc_node_for_each_child(node, inode) { | |
296 | p = xbc_node_get_data(inode); | |
297 | if (!p || *p == '\0') | |
298 | continue; | |
299 | ||
300 | tr = trace_array_get_by_name(p); | |
532f49a6 | 301 | if (!tr) { |
4f712a4d MH |
302 | pr_err("Failed to get trace instance %s\n", p); |
303 | continue; | |
304 | } | |
305 | trace_boot_init_one_instance(tr, inode); | |
28394da2 | 306 | trace_array_put(tr); |
4f712a4d MH |
307 | } |
308 | } | |
309 | ||
9c5b9d3d MH |
310 | static int __init trace_boot_init(void) |
311 | { | |
312 | struct xbc_node *trace_node; | |
313 | struct trace_array *tr; | |
314 | ||
315 | trace_node = xbc_find_node("ftrace"); | |
316 | if (!trace_node) | |
317 | return 0; | |
318 | ||
319 | tr = top_trace_array(); | |
320 | if (!tr) | |
321 | return 0; | |
322 | ||
4f712a4d MH |
323 | /* Global trace array is also one instance */ |
324 | trace_boot_init_one_instance(tr, trace_node); | |
325 | trace_boot_init_instances(trace_node); | |
9c5b9d3d MH |
326 | |
327 | return 0; | |
328 | } | |
329 | ||
330 | fs_initcall(trace_boot_init); |