aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace/trace_dynevent.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-12-31 14:46:59 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2018-12-31 14:46:59 -0500
commit495d714ad140e1732e66c45d0409054b24c1a0d6 (patch)
tree373ec6619adea47d848d36f140b32def27164bbd /kernel/trace/trace_dynevent.c
parentf12e840c819bab42621685558a01d3f46ab9a226 (diff)
parent3d739c1f6156c70eb0548aa288dcfbac9e0bd162 (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.c217
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
18static DEFINE_MUTEX(dyn_event_ops_mutex);
19static LIST_HEAD(dyn_event_ops_list);
20
21int 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
34int 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
74static 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 */
96LIST_HEAD(dyn_event_list);
97
98void *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
104void *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
109void dyn_event_seq_stop(struct seq_file *m, void *v)
110{
111 mutex_unlock(&event_mutex);
112}
113
114static 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
124static 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 */
142int 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 }
163out:
164 mutex_unlock(&event_mutex);
165
166 return ret;
167}
168
169static 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
182static 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
189static 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 */
199static __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}
217fs_initcall(init_dynamic_event);