tracing/boot: Add per-event settings
[linux-2.6-microblaze.git] / kernel / trace / trace_boot.c
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
9 #include <linux/ftrace.h>
10 #include <linux/init.h>
11 #include <linux/bootconfig.h>
12
13 #include "trace.h"
14
15 #define MAX_BUF_LEN 256
16
17 extern int trace_set_options(struct trace_array *tr, char *option);
18 extern int tracing_set_tracer(struct trace_array *tr, const char *buf);
19 extern ssize_t tracing_resize_ring_buffer(struct trace_array *tr,
20                                           unsigned long size, int cpu_id);
21
22 static void __init
23 trace_boot_set_ftrace_options(struct trace_array *tr, struct xbc_node *node)
24 {
25         struct xbc_node *anode;
26         const char *p;
27         char buf[MAX_BUF_LEN];
28         unsigned long v = 0;
29
30         /* Common ftrace options */
31         xbc_node_for_each_array_value(node, "options", anode, p) {
32                 if (strlcpy(buf, p, ARRAY_SIZE(buf)) >= ARRAY_SIZE(buf)) {
33                         pr_err("String is too long: %s\n", p);
34                         continue;
35                 }
36
37                 if (trace_set_options(tr, buf) < 0)
38                         pr_err("Failed to set option: %s\n", buf);
39         }
40
41         p = xbc_node_find_value(node, "trace_clock", NULL);
42         if (p && *p != '\0') {
43                 if (tracing_set_clock(tr, p) < 0)
44                         pr_err("Failed to set trace clock: %s\n", p);
45         }
46
47         p = xbc_node_find_value(node, "buffer_size", NULL);
48         if (p && *p != '\0') {
49                 v = memparse(p, NULL);
50                 if (v < PAGE_SIZE)
51                         pr_err("Buffer size is too small: %s\n", p);
52                 if (tracing_resize_ring_buffer(tr, v, RING_BUFFER_ALL_CPUS) < 0)
53                         pr_err("Failed to resize trace buffer to %s\n", p);
54         }
55 }
56
57 #ifdef CONFIG_EVENT_TRACING
58 extern int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set);
59 extern int trigger_process_regex(struct trace_event_file *file, char *buff);
60
61 static void __init
62 trace_boot_enable_events(struct trace_array *tr, struct xbc_node *node)
63 {
64         struct xbc_node *anode;
65         char buf[MAX_BUF_LEN];
66         const char *p;
67
68         xbc_node_for_each_array_value(node, "events", anode, p) {
69                 if (strlcpy(buf, p, ARRAY_SIZE(buf)) >= ARRAY_SIZE(buf)) {
70                         pr_err("String is too long: %s\n", p);
71                         continue;
72                 }
73
74                 if (ftrace_set_clr_event(tr, buf, 1) < 0)
75                         pr_err("Failed to enable event: %s\n", p);
76         }
77 }
78
79 static void __init
80 trace_boot_init_one_event(struct trace_array *tr, struct xbc_node *gnode,
81                           struct xbc_node *enode)
82 {
83         struct trace_event_file *file;
84         struct xbc_node *anode;
85         char buf[MAX_BUF_LEN];
86         const char *p, *group, *event;
87
88         group = xbc_node_get_data(gnode);
89         event = xbc_node_get_data(enode);
90
91         mutex_lock(&event_mutex);
92         file = find_event_file(tr, group, event);
93         if (!file) {
94                 pr_err("Failed to find event: %s:%s\n", group, event);
95                 goto out;
96         }
97
98         p = xbc_node_find_value(enode, "filter", NULL);
99         if (p && *p != '\0') {
100                 if (strlcpy(buf, p, ARRAY_SIZE(buf)) >= ARRAY_SIZE(buf))
101                         pr_err("filter string is too long: %s\n", p);
102                 else if (apply_event_filter(file, buf) < 0)
103                         pr_err("Failed to apply filter: %s\n", buf);
104         }
105
106         xbc_node_for_each_array_value(enode, "actions", anode, p) {
107                 if (strlcpy(buf, p, ARRAY_SIZE(buf)) >= ARRAY_SIZE(buf))
108                         pr_err("action string is too long: %s\n", p);
109                 else if (trigger_process_regex(file, buf) < 0)
110                         pr_err("Failed to apply an action: %s\n", buf);
111         }
112
113         if (xbc_node_find_value(enode, "enable", NULL)) {
114                 if (trace_event_enable_disable(file, 1, 0) < 0)
115                         pr_err("Failed to enable event node: %s:%s\n",
116                                 group, event);
117         }
118 out:
119         mutex_unlock(&event_mutex);
120 }
121
122 static void __init
123 trace_boot_init_events(struct trace_array *tr, struct xbc_node *node)
124 {
125         struct xbc_node *gnode, *enode;
126
127         node = xbc_node_find_child(node, "event");
128         if (!node)
129                 return;
130         /* per-event key starts with "event.GROUP.EVENT" */
131         xbc_node_for_each_child(node, gnode)
132                 xbc_node_for_each_child(gnode, enode)
133                         trace_boot_init_one_event(tr, gnode, enode);
134 }
135 #else
136 #define trace_boot_enable_events(tr, node) do {} while (0)
137 #define trace_boot_init_events(tr, node) do {} while (0)
138 #endif
139
140 static void __init
141 trace_boot_enable_tracer(struct trace_array *tr, struct xbc_node *node)
142 {
143         const char *p;
144
145         p = xbc_node_find_value(node, "tracer", NULL);
146         if (p && *p != '\0') {
147                 if (tracing_set_tracer(tr, p) < 0)
148                         pr_err("Failed to set given tracer: %s\n", p);
149         }
150 }
151
152 static int __init trace_boot_init(void)
153 {
154         struct xbc_node *trace_node;
155         struct trace_array *tr;
156
157         trace_node = xbc_find_node("ftrace");
158         if (!trace_node)
159                 return 0;
160
161         tr = top_trace_array();
162         if (!tr)
163                 return 0;
164
165         trace_boot_set_ftrace_options(tr, trace_node);
166         trace_boot_init_events(tr, trace_node);
167         trace_boot_enable_events(tr, trace_node);
168         trace_boot_enable_tracer(tr, trace_node);
169
170         return 0;
171 }
172
173 fs_initcall(trace_boot_init);