diff options
author | Masami Hiramatsu <mhiramat@kernel.org> | 2018-11-05 04:02:08 -0500 |
---|---|---|
committer | Steven Rostedt (VMware) <rostedt@goodmis.org> | 2018-12-08 20:54:09 -0500 |
commit | 5448d44c38557fc15d1c53b608a9c9f0e1ca8f86 (patch) | |
tree | 8ac0ff173059aedfc79bf1c34b10317d30a3e523 | |
parent | d00bbea9456f35fb34310d454e561f05d68d07fe (diff) |
tracing: Add unified dynamic event framework
Add unified dynamic event framework for ftrace kprobes, uprobes
and synthetic events. Those dynamic events can be co-exist on
same file because those syntax doesn't overlap.
This introduces a framework part which provides a unified tracefs
interface and operations.
Link: http://lkml.kernel.org/r/154140852824.17322.12250362185969352095.stgit@devbox
Reviewed-by: Tom Zanussi <tom.zanussi@linux.intel.com>
Tested-by: Tom Zanussi <tom.zanussi@linux.intel.com>
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
-rw-r--r-- | kernel/trace/Kconfig | 3 | ||||
-rw-r--r-- | kernel/trace/Makefile | 1 | ||||
-rw-r--r-- | kernel/trace/trace.c | 4 | ||||
-rw-r--r-- | kernel/trace/trace_dynevent.c | 210 | ||||
-rw-r--r-- | kernel/trace/trace_dynevent.h | 119 |
5 files changed, 337 insertions, 0 deletions
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 5e3de28c7677..bf2e8a5a91f1 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig | |||
@@ -518,6 +518,9 @@ config BPF_EVENTS | |||
518 | help | 518 | help |
519 | This allows the user to attach BPF programs to kprobe events. | 519 | This allows the user to attach BPF programs to kprobe events. |
520 | 520 | ||
521 | config DYNAMIC_EVENTS | ||
522 | def_bool n | ||
523 | |||
521 | config PROBE_EVENTS | 524 | config PROBE_EVENTS |
522 | def_bool n | 525 | def_bool n |
523 | 526 | ||
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index c7ade7965464..c2b2148bb1d2 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile | |||
@@ -79,6 +79,7 @@ endif | |||
79 | ifeq ($(CONFIG_TRACING),y) | 79 | ifeq ($(CONFIG_TRACING),y) |
80 | obj-$(CONFIG_KGDB_KDB) += trace_kdb.o | 80 | obj-$(CONFIG_KGDB_KDB) += trace_kdb.o |
81 | endif | 81 | endif |
82 | obj-$(CONFIG_DYNAMIC_EVENTS) += trace_dynevent.o | ||
82 | obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o | 83 | obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o |
83 | obj-$(CONFIG_UPROBE_EVENTS) += trace_uprobe.o | 84 | obj-$(CONFIG_UPROBE_EVENTS) += trace_uprobe.o |
84 | 85 | ||
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 194c01838e3f..7e0332f90ed4 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
@@ -4604,6 +4604,10 @@ static const char readme_msg[] = | |||
4604 | "\t\t\t traces\n" | 4604 | "\t\t\t traces\n" |
4605 | #endif | 4605 | #endif |
4606 | #endif /* CONFIG_STACK_TRACER */ | 4606 | #endif /* CONFIG_STACK_TRACER */ |
4607 | #ifdef CONFIG_DYNAMIC_EVENTS | ||
4608 | " dynamic_events\t\t- Add/remove/show the generic dynamic events\n" | ||
4609 | "\t\t\t Write into this file to define/undefine new trace events.\n" | ||
4610 | #endif | ||
4607 | #ifdef CONFIG_KPROBE_EVENTS | 4611 | #ifdef CONFIG_KPROBE_EVENTS |
4608 | " kprobe_events\t\t- Add/remove/show the kernel dynamic events\n" | 4612 | " kprobe_events\t\t- Add/remove/show the kernel dynamic events\n" |
4609 | "\t\t\t Write into this file to define/undefine new trace events.\n" | 4613 | "\t\t\t Write into this file to define/undefine new trace events.\n" |
diff --git a/kernel/trace/trace_dynevent.c b/kernel/trace/trace_dynevent.c new file mode 100644 index 000000000000..f17a887abb66 --- /dev/null +++ b/kernel/trace/trace_dynevent.c | |||
@@ -0,0 +1,210 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Generic dynamic event control interface | ||
4 | * | ||
5 | * Copyright (C) 2018 Masami Hiramatsu <mhiramat@kernel.org> | ||
6 | */ | ||
7 | |||
8 | #include <linux/debugfs.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/list.h> | ||
11 | #include <linux/mm.h> | ||
12 | #include <linux/mutex.h> | ||
13 | #include <linux/tracefs.h> | ||
14 | |||
15 | #include "trace.h" | ||
16 | #include "trace_dynevent.h" | ||
17 | |||
18 | static DEFINE_MUTEX(dyn_event_ops_mutex); | ||
19 | static LIST_HEAD(dyn_event_ops_list); | ||
20 | |||
21 | int dyn_event_register(struct dyn_event_operations *ops) | ||
22 | { | ||
23 | if (!ops || !ops->create || !ops->show || !ops->is_busy || | ||
24 | !ops->free || !ops->match) | ||
25 | return -EINVAL; | ||
26 | |||
27 | INIT_LIST_HEAD(&ops->list); | ||
28 | mutex_lock(&dyn_event_ops_mutex); | ||
29 | list_add_tail(&ops->list, &dyn_event_ops_list); | ||
30 | mutex_unlock(&dyn_event_ops_mutex); | ||
31 | return 0; | ||
32 | } | ||
33 | |||
34 | int dyn_event_release(int argc, char **argv, struct dyn_event_operations *type) | ||
35 | { | ||
36 | struct dyn_event *pos, *n; | ||
37 | char *system = NULL, *event, *p; | ||
38 | int ret = -ENOENT; | ||
39 | |||
40 | if (argv[0][1] != ':') | ||
41 | return -EINVAL; | ||
42 | |||
43 | event = &argv[0][2]; | ||
44 | p = strchr(event, '/'); | ||
45 | if (p) { | ||
46 | system = event; | ||
47 | event = p + 1; | ||
48 | *p = '\0'; | ||
49 | } | ||
50 | if (event[0] == '\0') | ||
51 | return -EINVAL; | ||
52 | |||
53 | mutex_lock(&event_mutex); | ||
54 | for_each_dyn_event_safe(pos, n) { | ||
55 | if (type && type != pos->ops) | ||
56 | continue; | ||
57 | if (pos->ops->match(system, event, pos)) { | ||
58 | ret = pos->ops->free(pos); | ||
59 | break; | ||
60 | } | ||
61 | } | ||
62 | mutex_unlock(&event_mutex); | ||
63 | |||
64 | return ret; | ||
65 | } | ||
66 | |||
67 | static int create_dyn_event(int argc, char **argv) | ||
68 | { | ||
69 | struct dyn_event_operations *ops; | ||
70 | int ret; | ||
71 | |||
72 | if (argv[0][0] == '-') | ||
73 | return dyn_event_release(argc, argv, NULL); | ||
74 | |||
75 | mutex_lock(&dyn_event_ops_mutex); | ||
76 | list_for_each_entry(ops, &dyn_event_ops_list, list) { | ||
77 | ret = ops->create(argc, (const char **)argv); | ||
78 | if (!ret || ret != -ECANCELED) | ||
79 | break; | ||
80 | } | ||
81 | mutex_unlock(&dyn_event_ops_mutex); | ||
82 | if (ret == -ECANCELED) | ||
83 | ret = -EINVAL; | ||
84 | |||
85 | return ret; | ||
86 | } | ||
87 | |||
88 | /* Protected by event_mutex */ | ||
89 | LIST_HEAD(dyn_event_list); | ||
90 | |||
91 | void *dyn_event_seq_start(struct seq_file *m, loff_t *pos) | ||
92 | { | ||
93 | mutex_lock(&event_mutex); | ||
94 | return seq_list_start(&dyn_event_list, *pos); | ||
95 | } | ||
96 | |||
97 | void *dyn_event_seq_next(struct seq_file *m, void *v, loff_t *pos) | ||
98 | { | ||
99 | return seq_list_next(v, &dyn_event_list, pos); | ||
100 | } | ||
101 | |||
102 | void dyn_event_seq_stop(struct seq_file *m, void *v) | ||
103 | { | ||
104 | mutex_unlock(&event_mutex); | ||
105 | } | ||
106 | |||
107 | static int dyn_event_seq_show(struct seq_file *m, void *v) | ||
108 | { | ||
109 | struct dyn_event *ev = v; | ||
110 | |||
111 | if (ev && ev->ops) | ||
112 | return ev->ops->show(m, ev); | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static const struct seq_operations dyn_event_seq_op = { | ||
118 | .start = dyn_event_seq_start, | ||
119 | .next = dyn_event_seq_next, | ||
120 | .stop = dyn_event_seq_stop, | ||
121 | .show = dyn_event_seq_show | ||
122 | }; | ||
123 | |||
124 | /* | ||
125 | * dyn_events_release_all - Release all specific events | ||
126 | * @type: the dyn_event_operations * which filters releasing events | ||
127 | * | ||
128 | * This releases all events which ->ops matches @type. If @type is NULL, | ||
129 | * all events are released. | ||
130 | * Return -EBUSY if any of them are in use, and return other errors when | ||
131 | * it failed to free the given event. Except for -EBUSY, event releasing | ||
132 | * process will be aborted at that point and there may be some other | ||
133 | * releasable events on the list. | ||
134 | */ | ||
135 | int dyn_events_release_all(struct dyn_event_operations *type) | ||
136 | { | ||
137 | struct dyn_event *ev, *tmp; | ||
138 | int ret = 0; | ||
139 | |||
140 | mutex_lock(&event_mutex); | ||
141 | for_each_dyn_event(ev) { | ||
142 | if (type && ev->ops != type) | ||
143 | continue; | ||
144 | if (ev->ops->is_busy(ev)) { | ||
145 | ret = -EBUSY; | ||
146 | goto out; | ||
147 | } | ||
148 | } | ||
149 | for_each_dyn_event_safe(ev, tmp) { | ||
150 | if (type && ev->ops != type) | ||
151 | continue; | ||
152 | ret = ev->ops->free(ev); | ||
153 | if (ret) | ||
154 | break; | ||
155 | } | ||
156 | out: | ||
157 | mutex_unlock(&event_mutex); | ||
158 | |||
159 | return ret; | ||
160 | } | ||
161 | |||
162 | static int dyn_event_open(struct inode *inode, struct file *file) | ||
163 | { | ||
164 | int ret; | ||
165 | |||
166 | if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { | ||
167 | ret = dyn_events_release_all(NULL); | ||
168 | if (ret < 0) | ||
169 | return ret; | ||
170 | } | ||
171 | |||
172 | return seq_open(file, &dyn_event_seq_op); | ||
173 | } | ||
174 | |||
175 | static ssize_t dyn_event_write(struct file *file, const char __user *buffer, | ||
176 | size_t count, loff_t *ppos) | ||
177 | { | ||
178 | return trace_parse_run_command(file, buffer, count, ppos, | ||
179 | create_dyn_event); | ||
180 | } | ||
181 | |||
182 | static const struct file_operations dynamic_events_ops = { | ||
183 | .owner = THIS_MODULE, | ||
184 | .open = dyn_event_open, | ||
185 | .read = seq_read, | ||
186 | .llseek = seq_lseek, | ||
187 | .release = seq_release, | ||
188 | .write = dyn_event_write, | ||
189 | }; | ||
190 | |||
191 | /* Make a tracefs interface for controlling dynamic events */ | ||
192 | static __init int init_dynamic_event(void) | ||
193 | { | ||
194 | struct dentry *d_tracer; | ||
195 | struct dentry *entry; | ||
196 | |||
197 | d_tracer = tracing_init_dentry(); | ||
198 | if (IS_ERR(d_tracer)) | ||
199 | return 0; | ||
200 | |||
201 | entry = tracefs_create_file("dynamic_events", 0644, d_tracer, | ||
202 | NULL, &dynamic_events_ops); | ||
203 | |||
204 | /* Event list interface */ | ||
205 | if (!entry) | ||
206 | pr_warn("Could not create tracefs 'dynamic_events' entry\n"); | ||
207 | |||
208 | return 0; | ||
209 | } | ||
210 | fs_initcall(init_dynamic_event); | ||
diff --git a/kernel/trace/trace_dynevent.h b/kernel/trace/trace_dynevent.h new file mode 100644 index 000000000000..8c334064e4d6 --- /dev/null +++ b/kernel/trace/trace_dynevent.h | |||
@@ -0,0 +1,119 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * Common header file for generic dynamic events. | ||
4 | */ | ||
5 | |||
6 | #ifndef _TRACE_DYNEVENT_H | ||
7 | #define _TRACE_DYNEVENT_H | ||
8 | |||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/list.h> | ||
11 | #include <linux/mutex.h> | ||
12 | #include <linux/seq_file.h> | ||
13 | |||
14 | #include "trace.h" | ||
15 | |||
16 | struct dyn_event; | ||
17 | |||
18 | /** | ||
19 | * struct dyn_event_operations - Methods for each type of dynamic events | ||
20 | * | ||
21 | * These methods must be set for each type, since there is no default method. | ||
22 | * Before using this for dyn_event_init(), it must be registered by | ||
23 | * dyn_event_register(). | ||
24 | * | ||
25 | * @create: Parse and create event method. This is invoked when user passes | ||
26 | * a event definition to dynamic_events interface. This must not destruct | ||
27 | * the arguments and return -ECANCELED if given arguments doesn't match its | ||
28 | * command prefix. | ||
29 | * @show: Showing method. This is invoked when user reads the event definitions | ||
30 | * via dynamic_events interface. | ||
31 | * @is_busy: Check whether given event is busy so that it can not be deleted. | ||
32 | * Return true if it is busy, otherwides false. | ||
33 | * @free: Delete the given event. Return 0 if success, otherwides error. | ||
34 | * @match: Check whether given event and system name match this event. | ||
35 | * Return true if it matches, otherwides false. | ||
36 | * | ||
37 | * Except for @create, these methods are called under holding event_mutex. | ||
38 | */ | ||
39 | struct dyn_event_operations { | ||
40 | struct list_head list; | ||
41 | int (*create)(int argc, const char *argv[]); | ||
42 | int (*show)(struct seq_file *m, struct dyn_event *ev); | ||
43 | bool (*is_busy)(struct dyn_event *ev); | ||
44 | int (*free)(struct dyn_event *ev); | ||
45 | bool (*match)(const char *system, const char *event, | ||
46 | struct dyn_event *ev); | ||
47 | }; | ||
48 | |||
49 | /* Register new dyn_event type -- must be called at first */ | ||
50 | int dyn_event_register(struct dyn_event_operations *ops); | ||
51 | |||
52 | /** | ||
53 | * struct dyn_event - Dynamic event list header | ||
54 | * | ||
55 | * The dyn_event structure encapsulates a list and a pointer to the operators | ||
56 | * for making a global list of dynamic events. | ||
57 | * User must includes this in each event structure, so that those events can | ||
58 | * be added/removed via dynamic_events interface. | ||
59 | */ | ||
60 | struct dyn_event { | ||
61 | struct list_head list; | ||
62 | struct dyn_event_operations *ops; | ||
63 | }; | ||
64 | |||
65 | extern struct list_head dyn_event_list; | ||
66 | |||
67 | static inline | ||
68 | int dyn_event_init(struct dyn_event *ev, struct dyn_event_operations *ops) | ||
69 | { | ||
70 | if (!ev || !ops) | ||
71 | return -EINVAL; | ||
72 | |||
73 | INIT_LIST_HEAD(&ev->list); | ||
74 | ev->ops = ops; | ||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static inline int dyn_event_add(struct dyn_event *ev) | ||
79 | { | ||
80 | lockdep_assert_held(&event_mutex); | ||
81 | |||
82 | if (!ev || !ev->ops) | ||
83 | return -EINVAL; | ||
84 | |||
85 | list_add_tail(&ev->list, &dyn_event_list); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static inline void dyn_event_remove(struct dyn_event *ev) | ||
90 | { | ||
91 | lockdep_assert_held(&event_mutex); | ||
92 | list_del_init(&ev->list); | ||
93 | } | ||
94 | |||
95 | void *dyn_event_seq_start(struct seq_file *m, loff_t *pos); | ||
96 | void *dyn_event_seq_next(struct seq_file *m, void *v, loff_t *pos); | ||
97 | void dyn_event_seq_stop(struct seq_file *m, void *v); | ||
98 | int dyn_events_release_all(struct dyn_event_operations *type); | ||
99 | int dyn_event_release(int argc, char **argv, struct dyn_event_operations *type); | ||
100 | |||
101 | /* | ||
102 | * for_each_dyn_event - iterate over the dyn_event list | ||
103 | * @pos: the struct dyn_event * to use as a loop cursor | ||
104 | * | ||
105 | * This is just a basement of for_each macro. Wrap this for | ||
106 | * each actual event structure with ops filtering. | ||
107 | */ | ||
108 | #define for_each_dyn_event(pos) \ | ||
109 | list_for_each_entry(pos, &dyn_event_list, list) | ||
110 | |||
111 | /* | ||
112 | * for_each_dyn_event - iterate over the dyn_event list safely | ||
113 | * @pos: the struct dyn_event * to use as a loop cursor | ||
114 | * @n: the struct dyn_event * to use as temporary storage | ||
115 | */ | ||
116 | #define for_each_dyn_event_safe(pos, n) \ | ||
117 | list_for_each_entry_safe(pos, n, &dyn_event_list, list) | ||
118 | |||
119 | #endif | ||