Commit | Line | Data |
---|---|---|
e0a7a7c4 | 1 | /* |
886d51a3 | 2 | * probes/lttng-ftrace.c |
e0a7a7c4 MD |
3 | * |
4 | * LTTng function tracer integration module. | |
5 | * | |
886d51a3 MD |
6 | * Copyright (C) 2009-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> |
7 | * | |
8 | * This library is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License as published by the Free Software Foundation; only | |
11 | * version 2.1 of the License. | |
12 | * | |
13 | * This library is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * Lesser General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU Lesser General Public | |
19 | * License along with this library; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
e0a7a7c4 MD |
21 | */ |
22 | ||
e3de3dde MD |
23 | /* |
24 | * Ftrace function tracer does not seem to provide synchronization between probe | |
25 | * teardown and callback execution. Therefore, we make this module permanently | |
26 | * loaded (unloadable). | |
4133fce7 MD |
27 | * |
28 | * TODO: Move to register_ftrace_function() (which is exported for | |
29 | * modules) for Linux >= 3.0. It is faster (only enables the selected | |
30 | * functions), and will stay there. | |
e3de3dde MD |
31 | */ |
32 | ||
e0a7a7c4 MD |
33 | #include <linux/module.h> |
34 | #include <linux/ftrace.h> | |
35 | #include <linux/slab.h> | |
156a3977 MD |
36 | #include <lttng-events.h> |
37 | #include <wrapper/ringbuffer/frontend_types.h> | |
38 | #include <wrapper/ftrace.h> | |
39 | #include <wrapper/vmalloc.h> | |
40 | #include <lttng-tracer.h> | |
e0a7a7c4 | 41 | |
2b31a1ab MJ |
42 | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,12,0)) |
43 | static | |
44 | void lttng_ftrace_handler(unsigned long ip, unsigned long parent_ip, | |
45 | struct trace_array *tr, struct ftrace_probe_ops *ops, | |
46 | void *data) | |
47 | { | |
48 | struct lttng_event *event = data; | |
49 | struct lttng_probe_ctx lttng_probe_ctx = { | |
50 | .event = event, | |
51 | .interruptible = !irqs_disabled(), | |
52 | }; | |
53 | struct lttng_channel *chan = event->chan; | |
54 | struct lib_ring_buffer_ctx ctx; | |
55 | struct { | |
56 | unsigned long ip; | |
57 | unsigned long parent_ip; | |
58 | } payload; | |
59 | int ret; | |
60 | ||
d4fe3450 | 61 | if (unlikely(!READ_ONCE(chan->session->active))) |
2b31a1ab | 62 | return; |
d4fe3450 | 63 | if (unlikely(!READ_ONCE(chan->enabled))) |
2b31a1ab | 64 | return; |
d4fe3450 | 65 | if (unlikely(!READ_ONCE(event->enabled))) |
2b31a1ab MJ |
66 | return; |
67 | ||
68 | lib_ring_buffer_ctx_init(&ctx, chan->chan, <tng_probe_ctx, | |
69 | sizeof(payload), lttng_alignof(payload), -1); | |
70 | ret = chan->ops->event_reserve(&ctx, event->id); | |
71 | if (ret < 0) | |
72 | return; | |
73 | payload.ip = ip; | |
74 | payload.parent_ip = parent_ip; | |
75 | lib_ring_buffer_align_ctx(&ctx, lttng_alignof(payload)); | |
76 | chan->ops->event_write(&ctx, &payload, sizeof(payload)); | |
77 | chan->ops->event_commit(&ctx); | |
78 | return; | |
79 | } | |
80 | #else | |
e0a7a7c4 | 81 | static |
5a9479dc | 82 | void lttng_ftrace_handler(unsigned long ip, unsigned long parent_ip, void **data) |
e0a7a7c4 | 83 | { |
a90917c3 | 84 | struct lttng_event *event = *data; |
79150a49 JD |
85 | struct lttng_probe_ctx lttng_probe_ctx = { |
86 | .event = event, | |
ccecf3fb | 87 | .interruptible = !irqs_disabled(), |
79150a49 | 88 | }; |
a90917c3 | 89 | struct lttng_channel *chan = event->chan; |
e0a7a7c4 MD |
90 | struct lib_ring_buffer_ctx ctx; |
91 | struct { | |
92 | unsigned long ip; | |
93 | unsigned long parent_ip; | |
94 | } payload; | |
95 | int ret; | |
96 | ||
d4fe3450 | 97 | if (unlikely(!READ_ONCE(chan->session->active))) |
5a9479dc | 98 | return; |
d4fe3450 | 99 | if (unlikely(!READ_ONCE(chan->enabled))) |
e64957da | 100 | return; |
d4fe3450 | 101 | if (unlikely(!READ_ONCE(event->enabled))) |
e64957da MD |
102 | return; |
103 | ||
79150a49 | 104 | lib_ring_buffer_ctx_init(&ctx, chan->chan, <tng_probe_ctx, |
a90917c3 | 105 | sizeof(payload), lttng_alignof(payload), -1); |
4e1f08f4 | 106 | ret = chan->ops->event_reserve(&ctx, event->id); |
e0a7a7c4 | 107 | if (ret < 0) |
5a9479dc | 108 | return; |
e0a7a7c4 MD |
109 | payload.ip = ip; |
110 | payload.parent_ip = parent_ip; | |
a90917c3 | 111 | lib_ring_buffer_align_ctx(&ctx, lttng_alignof(payload)); |
e0a7a7c4 MD |
112 | chan->ops->event_write(&ctx, &payload, sizeof(payload)); |
113 | chan->ops->event_commit(&ctx); | |
5a9479dc | 114 | return; |
e0a7a7c4 | 115 | } |
2b31a1ab | 116 | #endif |
e0a7a7c4 MD |
117 | |
118 | /* | |
119 | * Create event description | |
120 | */ | |
121 | static | |
a90917c3 | 122 | int lttng_create_ftrace_event(const char *name, struct lttng_event *event) |
e0a7a7c4 MD |
123 | { |
124 | struct lttng_event_field *fields; | |
125 | struct lttng_event_desc *desc; | |
126 | int ret; | |
127 | ||
128 | desc = kzalloc(sizeof(*event->desc), GFP_KERNEL); | |
129 | if (!desc) | |
130 | return -ENOMEM; | |
131 | desc->name = kstrdup(name, GFP_KERNEL); | |
132 | if (!desc->name) { | |
133 | ret = -ENOMEM; | |
134 | goto error_str; | |
135 | } | |
136 | desc->nr_fields = 2; | |
137 | desc->fields = fields = | |
138 | kzalloc(2 * sizeof(struct lttng_event_field), GFP_KERNEL); | |
0d1a681e MD |
139 | if (!desc->fields) { |
140 | ret = -ENOMEM; | |
141 | goto error_fields; | |
142 | } | |
e0a7a7c4 MD |
143 | fields[0].name = "ip"; |
144 | fields[0].type.atype = atype_integer; | |
ba1f5986 | 145 | fields[0].type.u.basic.integer.size = sizeof(unsigned long) * CHAR_BIT; |
a90917c3 | 146 | fields[0].type.u.basic.integer.alignment = lttng_alignof(unsigned long) * CHAR_BIT; |
06254b0f | 147 | fields[0].type.u.basic.integer.signedness = lttng_is_signed_type(unsigned long); |
e0a7a7c4 MD |
148 | fields[0].type.u.basic.integer.reverse_byte_order = 0; |
149 | fields[0].type.u.basic.integer.base = 16; | |
150 | fields[0].type.u.basic.integer.encoding = lttng_encode_none; | |
151 | ||
152 | fields[1].name = "parent_ip"; | |
153 | fields[1].type.atype = atype_integer; | |
ba1f5986 | 154 | fields[1].type.u.basic.integer.size = sizeof(unsigned long) * CHAR_BIT; |
a90917c3 | 155 | fields[1].type.u.basic.integer.alignment = lttng_alignof(unsigned long) * CHAR_BIT; |
06254b0f | 156 | fields[1].type.u.basic.integer.signedness = lttng_is_signed_type(unsigned long); |
e0a7a7c4 MD |
157 | fields[1].type.u.basic.integer.reverse_byte_order = 0; |
158 | fields[1].type.u.basic.integer.base = 16; | |
159 | fields[1].type.u.basic.integer.encoding = lttng_encode_none; | |
160 | ||
dc7f600a | 161 | desc->owner = THIS_MODULE; |
e0a7a7c4 MD |
162 | event->desc = desc; |
163 | ||
164 | return 0; | |
165 | ||
0d1a681e MD |
166 | error_fields: |
167 | kfree(desc->name); | |
e0a7a7c4 MD |
168 | error_str: |
169 | kfree(desc); | |
170 | return ret; | |
171 | } | |
172 | ||
173 | static | |
174 | struct ftrace_probe_ops lttng_ftrace_ops = { | |
175 | .func = lttng_ftrace_handler, | |
176 | }; | |
177 | ||
178 | int lttng_ftrace_register(const char *name, | |
179 | const char *symbol_name, | |
a90917c3 | 180 | struct lttng_event *event) |
e0a7a7c4 MD |
181 | { |
182 | int ret; | |
183 | ||
184 | ret = lttng_create_ftrace_event(name, event); | |
185 | if (ret) | |
186 | goto error; | |
187 | ||
8a586098 | 188 | event->u.ftrace.symbol_name = kstrdup(symbol_name, GFP_KERNEL); |
e0a7a7c4 MD |
189 | if (!event->u.ftrace.symbol_name) |
190 | goto name_error; | |
191 | ||
16a9a591 MD |
192 | /* Ensure the memory we just allocated don't trigger page faults */ |
193 | wrapper_vmalloc_sync_all(); | |
194 | ||
5a9479dc | 195 | ret = wrapper_register_ftrace_function_probe(event->u.ftrace.symbol_name, |
e0a7a7c4 | 196 | <tng_ftrace_ops, event); |
8a586098 | 197 | if (ret < 0) |
e0a7a7c4 MD |
198 | goto register_error; |
199 | return 0; | |
200 | ||
201 | register_error: | |
202 | kfree(event->u.ftrace.symbol_name); | |
203 | name_error: | |
204 | kfree(event->desc->name); | |
205 | kfree(event->desc); | |
206 | error: | |
207 | return ret; | |
208 | } | |
209 | EXPORT_SYMBOL_GPL(lttng_ftrace_register); | |
210 | ||
a90917c3 | 211 | void lttng_ftrace_unregister(struct lttng_event *event) |
e0a7a7c4 | 212 | { |
5a9479dc | 213 | wrapper_unregister_ftrace_function_probe(event->u.ftrace.symbol_name, |
e0a7a7c4 | 214 | <tng_ftrace_ops, event); |
edeb3137 MD |
215 | } |
216 | EXPORT_SYMBOL_GPL(lttng_ftrace_unregister); | |
217 | ||
a90917c3 | 218 | void lttng_ftrace_destroy_private(struct lttng_event *event) |
edeb3137 | 219 | { |
e0a7a7c4 | 220 | kfree(event->u.ftrace.symbol_name); |
25f53c39 | 221 | kfree(event->desc->fields); |
e0a7a7c4 MD |
222 | kfree(event->desc->name); |
223 | kfree(event->desc); | |
224 | } | |
edeb3137 | 225 | EXPORT_SYMBOL_GPL(lttng_ftrace_destroy_private); |
e0a7a7c4 | 226 | |
e3de3dde MD |
227 | int lttng_ftrace_init(void) |
228 | { | |
16a9a591 | 229 | wrapper_vmalloc_sync_all(); |
e3de3dde MD |
230 | return 0; |
231 | } | |
232 | module_init(lttng_ftrace_init) | |
233 | ||
1695dc9a MD |
234 | /* |
235 | * Ftrace takes care of waiting for a grace period (RCU sched) at probe | |
236 | * unregistration, and disables preemption around probe call. | |
237 | */ | |
238 | void lttng_ftrace_exit(void) | |
239 | { | |
240 | } | |
241 | module_exit(lttng_ftrace_exit) | |
242 | ||
e0a7a7c4 MD |
243 | MODULE_LICENSE("GPL and additional rights"); |
244 | MODULE_AUTHOR("Mathieu Desnoyers"); | |
245 | MODULE_DESCRIPTION("Linux Trace Toolkit Ftrace Support"); | |
13ab8b0a MD |
246 | MODULE_VERSION(__stringify(LTTNG_MODULES_MAJOR_VERSION) "." |
247 | __stringify(LTTNG_MODULES_MINOR_VERSION) "." | |
248 | __stringify(LTTNG_MODULES_PATCHLEVEL_VERSION) | |
249 | LTTNG_MODULES_EXTRAVERSION); |