diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-31 14:46:59 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-31 14:46:59 -0500 |
commit | 495d714ad140e1732e66c45d0409054b24c1a0d6 (patch) | |
tree | 373ec6619adea47d848d36f140b32def27164bbd /kernel/trace/trace_dynevent.c | |
parent | f12e840c819bab42621685558a01d3f46ab9a226 (diff) | |
parent | 3d739c1f6156c70eb0548aa288dcfbac9e0bd162 (diff) |
Merge tag 'trace-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace
Pull tracing updates from Steven Rostedt:
- Rework of the kprobe/uprobe and synthetic events to consolidate all
the dynamic event code. This will make changes in the future easier.
- Partial rewrite of the function graph tracing infrastructure. This
will allow for multiple users of hooking onto functions to get the
callback (return) of the function. This is the ground work for having
kprobes and function graph tracer using one code base.
- Clean up of the histogram code that will facilitate adding more
features to the histograms in the future.
- Addition of str_has_prefix() and a few use cases. There currently is
a similar function strstart() that is used in a few places, but only
returns a bool and not a length. These instances will be removed in
the future to use str_has_prefix() instead.
- A few other various clean ups as well.
* tag 'trace-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace: (57 commits)
tracing: Use the return of str_has_prefix() to remove open coded numbers
tracing: Have the historgram use the result of str_has_prefix() for len of prefix
tracing: Use str_has_prefix() instead of using fixed sizes
tracing: Use str_has_prefix() helper for histogram code
string.h: Add str_has_prefix() helper function
tracing: Make function ‘ftrace_exports’ static
tracing: Simplify printf'ing in seq_print_sym
tracing: Avoid -Wformat-nonliteral warning
tracing: Merge seq_print_sym_short() and seq_print_sym_offset()
tracing: Add hist trigger comments for variable-related fields
tracing: Remove hist trigger synth_var_refs
tracing: Use hist trigger's var_ref array to destroy var_refs
tracing: Remove open-coding of hist trigger var_ref management
tracing: Use var_refs[] for hist trigger reference checking
tracing: Change strlen to sizeof for hist trigger static strings
tracing: Remove unnecessary hist trigger struct field
tracing: Fix ftrace_graph_get_ret_stack() to use task and not current
seq_buf: Use size_t for len in seq_buf_puts()
seq_buf: Make seq_buf_puts() null-terminate the buffer
arm64: Use ftrace_graph_get_ret_stack() instead of curr_ret_stack
...
Diffstat (limited to 'kernel/trace/trace_dynevent.c')
-rw-r--r-- | kernel/trace/trace_dynevent.c | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/kernel/trace/trace_dynevent.c b/kernel/trace/trace_dynevent.c new file mode 100644 index 000000000000..dd1f43588d70 --- /dev/null +++ b/kernel/trace/trace_dynevent.c | |||
@@ -0,0 +1,217 @@ | |||
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][0] == '-') { | ||
41 | if (argv[0][1] != ':') | ||
42 | return -EINVAL; | ||
43 | event = &argv[0][2]; | ||
44 | } else { | ||
45 | event = strchr(argv[0], ':'); | ||
46 | if (!event) | ||
47 | return -EINVAL; | ||
48 | event++; | ||
49 | } | ||
50 | |||
51 | p = strchr(event, '/'); | ||
52 | if (p) { | ||
53 | system = event; | ||
54 | event = p + 1; | ||
55 | *p = '\0'; | ||
56 | } | ||
57 | if (event[0] == '\0') | ||
58 | return -EINVAL; | ||
59 | |||
60 | mutex_lock(&event_mutex); | ||
61 | for_each_dyn_event_safe(pos, n) { | ||
62 | if (type && type != pos->ops) | ||
63 | continue; | ||
64 | if (pos->ops->match(system, event, pos)) { | ||
65 | ret = pos->ops->free(pos); | ||
66 | break; | ||
67 | } | ||
68 | } | ||
69 | mutex_unlock(&event_mutex); | ||
70 | |||
71 | return ret; | ||
72 | } | ||
73 | |||
74 | static int create_dyn_event(int argc, char **argv) | ||
75 | { | ||
76 | struct dyn_event_operations *ops; | ||
77 | int ret; | ||
78 | |||
79 | if (argv[0][0] == '-' || argv[0][0] == '!') | ||
80 | return dyn_event_release(argc, argv, NULL); | ||
81 | |||
82 | mutex_lock(&dyn_event_ops_mutex); | ||
83 | list_for_each_entry(ops, &dyn_event_ops_list, list) { | ||
84 | ret = ops->create(argc, (const char **)argv); | ||
85 | if (!ret || ret != -ECANCELED) | ||
86 | break; | ||
87 | } | ||
88 | mutex_unlock(&dyn_event_ops_mutex); | ||
89 | if (ret == -ECANCELED) | ||
90 | ret = -EINVAL; | ||
91 | |||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | /* Protected by event_mutex */ | ||
96 | LIST_HEAD(dyn_event_list); | ||
97 | |||
98 | void *dyn_event_seq_start(struct seq_file *m, loff_t *pos) | ||
99 | { | ||
100 | mutex_lock(&event_mutex); | ||
101 | return seq_list_start(&dyn_event_list, *pos); | ||
102 | } | ||
103 | |||
104 | void *dyn_event_seq_next(struct seq_file *m, void *v, loff_t *pos) | ||
105 | { | ||
106 | return seq_list_next(v, &dyn_event_list, pos); | ||
107 | } | ||
108 | |||
109 | void dyn_event_seq_stop(struct seq_file *m, void *v) | ||
110 | { | ||
111 | mutex_unlock(&event_mutex); | ||
112 | } | ||
113 | |||
114 | static int dyn_event_seq_show(struct seq_file *m, void *v) | ||
115 | { | ||
116 | struct dyn_event *ev = v; | ||
117 | |||
118 | if (ev && ev->ops) | ||
119 | return ev->ops->show(m, ev); | ||
120 | |||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | static const struct seq_operations dyn_event_seq_op = { | ||
125 | .start = dyn_event_seq_start, | ||
126 | .next = dyn_event_seq_next, | ||
127 | .stop = dyn_event_seq_stop, | ||
128 | .show = dyn_event_seq_show | ||
129 | }; | ||
130 | |||
131 | /* | ||
132 | * dyn_events_release_all - Release all specific events | ||
133 | * @type: the dyn_event_operations * which filters releasing events | ||
134 | * | ||
135 | * This releases all events which ->ops matches @type. If @type is NULL, | ||
136 | * all events are released. | ||
137 | * Return -EBUSY if any of them are in use, and return other errors when | ||
138 | * it failed to free the given event. Except for -EBUSY, event releasing | ||
139 | * process will be aborted at that point and there may be some other | ||
140 | * releasable events on the list. | ||
141 | */ | ||
142 | int dyn_events_release_all(struct dyn_event_operations *type) | ||
143 | { | ||
144 | struct dyn_event *ev, *tmp; | ||
145 | int ret = 0; | ||
146 | |||
147 | mutex_lock(&event_mutex); | ||
148 | for_each_dyn_event(ev) { | ||
149 | if (type && ev->ops != type) | ||
150 | continue; | ||
151 | if (ev->ops->is_busy(ev)) { | ||
152 | ret = -EBUSY; | ||
153 | goto out; | ||
154 | } | ||
155 | } | ||
156 | for_each_dyn_event_safe(ev, tmp) { | ||
157 | if (type && ev->ops != type) | ||
158 | continue; | ||
159 | ret = ev->ops->free(ev); | ||
160 | if (ret) | ||
161 | break; | ||
162 | } | ||
163 | out: | ||
164 | mutex_unlock(&event_mutex); | ||
165 | |||
166 | return ret; | ||
167 | } | ||
168 | |||
169 | static int dyn_event_open(struct inode *inode, struct file *file) | ||
170 | { | ||
171 | int ret; | ||
172 | |||
173 | if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) { | ||
174 | ret = dyn_events_release_all(NULL); | ||
175 | if (ret < 0) | ||
176 | return ret; | ||
177 | } | ||
178 | |||
179 | return seq_open(file, &dyn_event_seq_op); | ||
180 | } | ||
181 | |||
182 | static ssize_t dyn_event_write(struct file *file, const char __user *buffer, | ||
183 | size_t count, loff_t *ppos) | ||
184 | { | ||
185 | return trace_parse_run_command(file, buffer, count, ppos, | ||
186 | create_dyn_event); | ||
187 | } | ||
188 | |||
189 | static const struct file_operations dynamic_events_ops = { | ||
190 | .owner = THIS_MODULE, | ||
191 | .open = dyn_event_open, | ||
192 | .read = seq_read, | ||
193 | .llseek = seq_lseek, | ||
194 | .release = seq_release, | ||
195 | .write = dyn_event_write, | ||
196 | }; | ||
197 | |||
198 | /* Make a tracefs interface for controlling dynamic events */ | ||
199 | static __init int init_dynamic_event(void) | ||
200 | { | ||
201 | struct dentry *d_tracer; | ||
202 | struct dentry *entry; | ||
203 | |||
204 | d_tracer = tracing_init_dentry(); | ||
205 | if (IS_ERR(d_tracer)) | ||
206 | return 0; | ||
207 | |||
208 | entry = tracefs_create_file("dynamic_events", 0644, d_tracer, | ||
209 | NULL, &dynamic_events_ops); | ||
210 | |||
211 | /* Event list interface */ | ||
212 | if (!entry) | ||
213 | pr_warn("Could not create tracefs 'dynamic_events' entry\n"); | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | fs_initcall(init_dynamic_event); | ||