Commit | Line | Data |
---|---|---|
68c1021b PMF |
1 | /* |
2 | * Copyright (C) 2007 Mathieu Desnoyers | |
3 | * | |
34e4b7db PMF |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public | |
6 | * License as published by the Free Software Foundation; either | |
7 | * version 2.1 of the License, or (at your option) any later version. | |
68c1021b | 8 | * |
34e4b7db | 9 | * This library is distributed in the hope that it will be useful, |
68c1021b | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
34e4b7db PMF |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. | |
68c1021b | 13 | * |
34e4b7db PMF |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library; if not, write to the Free Software | |
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
68c1021b | 17 | */ |
59b161cd | 18 | |
909bc43f PMF |
19 | #include <stdlib.h> |
20 | #include <errno.h> | |
769d0157 | 21 | #define _LGPL_SOURCE |
b7ea1a1c | 22 | #include <urcu-bp.h> |
17bb07b4 | 23 | #include <urcu/rculist.h> |
10c56168 | 24 | #include <urcu/hlist.h> |
769d0157 | 25 | |
518d7abb | 26 | #include <ust/core.h> |
93d0f2ea | 27 | #include <ust/marker.h> |
fbae86d6 | 28 | #include <ust/tracepoint.h> |
909bc43f | 29 | |
30ffe279 | 30 | #include "usterr_signal_safe.h" |
59b161cd PMF |
31 | #include "channels.h" |
32 | #include "tracercore.h" | |
c93858f1 | 33 | #include "tracer.h" |
68c1021b | 34 | |
636ca5d6 PMF |
35 | __thread long ust_reg_stack[500]; |
36 | volatile __thread long *ust_reg_stack_ptr = (long *) 0; | |
37 | ||
b521931e MD |
38 | extern struct ust_marker * const __start___ust_marker_ptrs[] __attribute__((visibility("hidden"))); |
39 | extern struct ust_marker * const __stop___ust_marker_ptrs[] __attribute__((visibility("hidden"))); | |
defa46a7 | 40 | |
b521931e MD |
41 | /* Set to 1 to enable ust_marker debug output */ |
42 | static const int ust_marker_debug; | |
68c1021b PMF |
43 | |
44 | /* | |
b521931e MD |
45 | * ust_marker_mutex nests inside module_mutex. ust_marker mutex protects |
46 | * the builtin and module ust_marker and the hash table. | |
68c1021b | 47 | */ |
b521931e | 48 | static DEFINE_MUTEX(ust_marker_mutex); |
68c1021b | 49 | |
b521931e | 50 | static CDS_LIST_HEAD(ust_marker_libs); |
772030fe PMF |
51 | |
52 | ||
b521931e | 53 | void lock_ust_marker(void) |
68c1021b | 54 | { |
b521931e | 55 | pthread_mutex_lock(&ust_marker_mutex); |
68c1021b PMF |
56 | } |
57 | ||
b521931e | 58 | void unlock_ust_marker(void) |
68c1021b | 59 | { |
b521931e | 60 | pthread_mutex_unlock(&ust_marker_mutex); |
68c1021b PMF |
61 | } |
62 | ||
63 | /* | |
b521931e | 64 | * ust_marker hash table, containing the active ust_marker. |
68c1021b PMF |
65 | * Protected by module_mutex. |
66 | */ | |
b521931e MD |
67 | #define ust_marker_HASH_BITS 6 |
68 | #define ust_marker_TABLE_SIZE (1 << ust_marker_HASH_BITS) | |
69 | static struct cds_hlist_head ust_marker_table[ust_marker_TABLE_SIZE]; | |
68c1021b PMF |
70 | |
71 | /* | |
72 | * Note about RCU : | |
b521931e MD |
73 | * It is used to make sure every handler has finished using its private |
74 | * data between two consecutive operation (add or remove) on a given | |
75 | * ust_marker. It is also used to delay the free of multiple probes | |
76 | * array until a quiescent state is reached. ust_marker entries | |
77 | * modifications are protected by the ust_marker_mutex. | |
68c1021b | 78 | */ |
b521931e | 79 | struct ust_marker_entry { |
10c56168 | 80 | struct cds_hlist_node hlist; |
68c1021b PMF |
81 | char *format; |
82 | char *name; | |
83 | /* Probe wrapper */ | |
b521931e MD |
84 | void (*call)(const struct ust_marker *mdata, void *call_private, struct registers *regs, ...); |
85 | struct ust_marker_probe_closure single; | |
86 | struct ust_marker_probe_closure *multi; | |
68c1021b PMF |
87 | int refcount; /* Number of times armed. 0 if disarmed. */ |
88 | struct rcu_head rcu; | |
89 | void *oldptr; | |
90 | int rcu_pending; | |
91 | u16 channel_id; | |
92 | u16 event_id; | |
93 | unsigned char ptype:1; | |
94 | unsigned char format_allocated:1; | |
95 | char channel[0]; /* Contains channel'\0'name'\0'format'\0' */ | |
96 | }; | |
97 | ||
b521931e MD |
98 | #ifdef CONFIG_UST_MARKER_USERSPACE |
99 | static void ust_marker_update_processes(void); | |
68c1021b | 100 | #else |
b521931e | 101 | static void ust_marker_update_processes(void) |
68c1021b PMF |
102 | { |
103 | } | |
104 | #endif | |
105 | ||
106 | /** | |
b521931e MD |
107 | * __ust_marker_empty_function - Empty probe callback |
108 | * @mdata: ust_marker data | |
68c1021b PMF |
109 | * @probe_private: probe private data |
110 | * @call_private: call site private data | |
111 | * @fmt: format string | |
112 | * @...: variable argument list | |
113 | * | |
b521931e MD |
114 | * Empty callback provided as a probe to the ust_marker. By providing |
115 | * this to a disabled ust_marker, we make sure the execution flow is | |
116 | * always valid even though the function pointer change and the | |
117 | * ust_marker enabling are two distinct operations that modifies the | |
118 | * execution flow of preemptible code. | |
68c1021b | 119 | */ |
b521931e | 120 | notrace void __ust_marker_empty_function(const struct ust_marker *mdata, |
75667d04 | 121 | void *probe_private, struct registers *regs, void *call_private, const char *fmt, va_list *args) |
68c1021b PMF |
122 | { |
123 | } | |
b521931e | 124 | //ust// EXPORT_SYMBOL_GPL(__ust_marker_empty_function); |
68c1021b PMF |
125 | |
126 | /* | |
b521931e MD |
127 | * ust_marker_probe_cb Callback that prepares the variable argument list for probes. |
128 | * @mdata: pointer of type struct ust_marker | |
68c1021b PMF |
129 | * @call_private: caller site private data |
130 | * @...: Variable argument list. | |
131 | * | |
132 | * Since we do not use "typical" pointer based RCU in the 1 argument case, we | |
0222e121 | 133 | * need to put a full cmm_smp_rmb() in this branch. This is why we do not use |
68c1021b PMF |
134 | * rcu_dereference() for the pointer read. |
135 | */ | |
b521931e | 136 | notrace void ust_marker_probe_cb(const struct ust_marker *mdata, |
75667d04 | 137 | void *call_private, struct registers *regs, ...) |
68c1021b PMF |
138 | { |
139 | va_list args; | |
140 | char ptype; | |
141 | ||
142 | /* | |
143 | * rcu_read_lock_sched does two things : disabling preemption to make | |
144 | * sure the teardown of the callbacks can be done correctly when they | |
145 | * are in modules and they insure RCU read coherency. | |
146 | */ | |
59b161cd | 147 | //ust// rcu_read_lock_sched_notrace(); |
68c1021b PMF |
148 | ptype = mdata->ptype; |
149 | if (likely(!ptype)) { | |
b521931e | 150 | ust_marker_probe_func *func; |
68c1021b | 151 | /* Must read the ptype before ptr. They are not data dependant, |
0222e121 MD |
152 | * so we put an explicit cmm_smp_rmb() here. */ |
153 | cmm_smp_rmb(); | |
68c1021b PMF |
154 | func = mdata->single.func; |
155 | /* Must read the ptr before private data. They are not data | |
0222e121 MD |
156 | * dependant, so we put an explicit cmm_smp_rmb() here. */ |
157 | cmm_smp_rmb(); | |
75667d04 PMF |
158 | va_start(args, regs); |
159 | func(mdata, mdata->single.probe_private, regs, call_private, | |
68c1021b PMF |
160 | mdata->format, &args); |
161 | va_end(args); | |
162 | } else { | |
b521931e | 163 | struct ust_marker_probe_closure *multi; |
68c1021b PMF |
164 | int i; |
165 | /* | |
166 | * Read mdata->ptype before mdata->multi. | |
167 | */ | |
0222e121 | 168 | cmm_smp_rmb(); |
68c1021b PMF |
169 | multi = mdata->multi; |
170 | /* | |
171 | * multi points to an array, therefore accessing the array | |
172 | * depends on reading multi. However, even in this case, | |
173 | * we must insure that the pointer is read _before_ the array | |
0222e121 MD |
174 | * data. Same as rcu_dereference, but we need a full cmm_smp_rmb() |
175 | * in the fast path, so put the explicit cmm_barrier here. | |
68c1021b | 176 | */ |
0222e121 | 177 | cmm_smp_read_barrier_depends(); |
68c1021b | 178 | for (i = 0; multi[i].func; i++) { |
75667d04 | 179 | va_start(args, regs); |
68c1021b | 180 | multi[i].func(mdata, multi[i].probe_private, |
75667d04 | 181 | regs, call_private, mdata->format, &args); |
68c1021b PMF |
182 | va_end(args); |
183 | } | |
184 | } | |
59b161cd | 185 | //ust// rcu_read_unlock_sched_notrace(); |
68c1021b | 186 | } |
b521931e | 187 | //ust// EXPORT_SYMBOL_GPL(ust_marker_probe_cb); |
68c1021b PMF |
188 | |
189 | /* | |
b521931e MD |
190 | * ust_marker_probe_cb Callback that does not prepare the variable argument list. |
191 | * @mdata: pointer of type struct ust_marker | |
68c1021b PMF |
192 | * @call_private: caller site private data |
193 | * @...: Variable argument list. | |
194 | * | |
b521931e | 195 | * Should be connected to ust_marker "UST_MARKER_NOARGS". |
68c1021b | 196 | */ |
b521931e | 197 | static notrace void ust_marker_probe_cb_noarg(const struct ust_marker *mdata, |
75667d04 | 198 | void *call_private, struct registers *regs, ...) |
68c1021b PMF |
199 | { |
200 | va_list args; /* not initialized */ | |
201 | char ptype; | |
202 | ||
59b161cd | 203 | //ust// rcu_read_lock_sched_notrace(); |
68c1021b PMF |
204 | ptype = mdata->ptype; |
205 | if (likely(!ptype)) { | |
b521931e | 206 | ust_marker_probe_func *func; |
68c1021b | 207 | /* Must read the ptype before ptr. They are not data dependant, |
0222e121 MD |
208 | * so we put an explicit cmm_smp_rmb() here. */ |
209 | cmm_smp_rmb(); | |
68c1021b PMF |
210 | func = mdata->single.func; |
211 | /* Must read the ptr before private data. They are not data | |
0222e121 MD |
212 | * dependant, so we put an explicit cmm_smp_rmb() here. */ |
213 | cmm_smp_rmb(); | |
75667d04 | 214 | func(mdata, mdata->single.probe_private, regs, call_private, |
68c1021b PMF |
215 | mdata->format, &args); |
216 | } else { | |
b521931e | 217 | struct ust_marker_probe_closure *multi; |
68c1021b PMF |
218 | int i; |
219 | /* | |
220 | * Read mdata->ptype before mdata->multi. | |
221 | */ | |
0222e121 | 222 | cmm_smp_rmb(); |
68c1021b PMF |
223 | multi = mdata->multi; |
224 | /* | |
225 | * multi points to an array, therefore accessing the array | |
226 | * depends on reading multi. However, even in this case, | |
227 | * we must insure that the pointer is read _before_ the array | |
0222e121 MD |
228 | * data. Same as rcu_dereference, but we need a full cmm_smp_rmb() |
229 | * in the fast path, so put the explicit cmm_barrier here. | |
68c1021b | 230 | */ |
0222e121 | 231 | cmm_smp_read_barrier_depends(); |
68c1021b | 232 | for (i = 0; multi[i].func; i++) |
75667d04 | 233 | multi[i].func(mdata, multi[i].probe_private, regs, |
68c1021b PMF |
234 | call_private, mdata->format, &args); |
235 | } | |
59b161cd | 236 | //ust// rcu_read_unlock_sched_notrace(); |
68c1021b PMF |
237 | } |
238 | ||
239 | static void free_old_closure(struct rcu_head *head) | |
240 | { | |
b521931e MD |
241 | struct ust_marker_entry *entry = _ust_container_of(head, |
242 | struct ust_marker_entry, rcu); | |
909bc43f | 243 | free(entry->oldptr); |
68c1021b | 244 | /* Make sure we free the data before setting the pending flag to 0 */ |
0222e121 | 245 | cmm_smp_wmb(); |
68c1021b PMF |
246 | entry->rcu_pending = 0; |
247 | } | |
248 | ||
b521931e | 249 | static void debug_print_probes(struct ust_marker_entry *entry) |
68c1021b PMF |
250 | { |
251 | int i; | |
252 | ||
b521931e | 253 | if (!ust_marker_debug) |
68c1021b PMF |
254 | return; |
255 | ||
256 | if (!entry->ptype) { | |
c1f20530 | 257 | DBG("Single probe : %p %p", |
68c1021b PMF |
258 | entry->single.func, |
259 | entry->single.probe_private); | |
260 | } else { | |
261 | for (i = 0; entry->multi[i].func; i++) | |
c1f20530 | 262 | DBG("Multi probe %d : %p %p", i, |
68c1021b PMF |
263 | entry->multi[i].func, |
264 | entry->multi[i].probe_private); | |
265 | } | |
266 | } | |
267 | ||
b521931e MD |
268 | static struct ust_marker_probe_closure * |
269 | ust_marker_entry_add_probe(struct ust_marker_entry *entry, | |
270 | ust_marker_probe_func *probe, void *probe_private) | |
68c1021b PMF |
271 | { |
272 | int nr_probes = 0; | |
b521931e | 273 | struct ust_marker_probe_closure *old, *new; |
68c1021b PMF |
274 | |
275 | WARN_ON(!probe); | |
276 | ||
277 | debug_print_probes(entry); | |
278 | old = entry->multi; | |
279 | if (!entry->ptype) { | |
280 | if (entry->single.func == probe && | |
281 | entry->single.probe_private == probe_private) | |
282 | return ERR_PTR(-EBUSY); | |
b521931e | 283 | if (entry->single.func == __ust_marker_empty_function) { |
68c1021b PMF |
284 | /* 0 -> 1 probes */ |
285 | entry->single.func = probe; | |
286 | entry->single.probe_private = probe_private; | |
287 | entry->refcount = 1; | |
288 | entry->ptype = 0; | |
289 | debug_print_probes(entry); | |
290 | return NULL; | |
291 | } else { | |
292 | /* 1 -> 2 probes */ | |
293 | nr_probes = 1; | |
294 | old = NULL; | |
295 | } | |
296 | } else { | |
297 | /* (N -> N+1), (N != 0, 1) probes */ | |
298 | for (nr_probes = 0; old[nr_probes].func; nr_probes++) | |
299 | if (old[nr_probes].func == probe | |
300 | && old[nr_probes].probe_private | |
301 | == probe_private) | |
302 | return ERR_PTR(-EBUSY); | |
303 | } | |
304 | /* + 2 : one for new probe, one for NULL func */ | |
b521931e | 305 | new = zmalloc((nr_probes + 2) * sizeof(struct ust_marker_probe_closure)); |
68c1021b PMF |
306 | if (new == NULL) |
307 | return ERR_PTR(-ENOMEM); | |
308 | if (!old) | |
309 | new[0] = entry->single; | |
310 | else | |
311 | memcpy(new, old, | |
b521931e | 312 | nr_probes * sizeof(struct ust_marker_probe_closure)); |
68c1021b PMF |
313 | new[nr_probes].func = probe; |
314 | new[nr_probes].probe_private = probe_private; | |
315 | entry->refcount = nr_probes + 1; | |
316 | entry->multi = new; | |
317 | entry->ptype = 1; | |
318 | debug_print_probes(entry); | |
319 | return old; | |
320 | } | |
321 | ||
b521931e MD |
322 | static struct ust_marker_probe_closure * |
323 | ust_marker_entry_remove_probe(struct ust_marker_entry *entry, | |
324 | ust_marker_probe_func *probe, void *probe_private) | |
68c1021b PMF |
325 | { |
326 | int nr_probes = 0, nr_del = 0, i; | |
b521931e | 327 | struct ust_marker_probe_closure *old, *new; |
68c1021b PMF |
328 | |
329 | old = entry->multi; | |
330 | ||
331 | debug_print_probes(entry); | |
332 | if (!entry->ptype) { | |
333 | /* 0 -> N is an error */ | |
b521931e | 334 | WARN_ON(entry->single.func == __ust_marker_empty_function); |
68c1021b PMF |
335 | /* 1 -> 0 probes */ |
336 | WARN_ON(probe && entry->single.func != probe); | |
337 | WARN_ON(entry->single.probe_private != probe_private); | |
b521931e | 338 | entry->single.func = __ust_marker_empty_function; |
68c1021b PMF |
339 | entry->refcount = 0; |
340 | entry->ptype = 0; | |
341 | debug_print_probes(entry); | |
342 | return NULL; | |
343 | } else { | |
344 | /* (N -> M), (N > 1, M >= 0) probes */ | |
345 | for (nr_probes = 0; old[nr_probes].func; nr_probes++) { | |
346 | if ((!probe || old[nr_probes].func == probe) | |
347 | && old[nr_probes].probe_private | |
348 | == probe_private) | |
349 | nr_del++; | |
350 | } | |
351 | } | |
352 | ||
353 | if (nr_probes - nr_del == 0) { | |
354 | /* N -> 0, (N > 1) */ | |
b521931e | 355 | entry->single.func = __ust_marker_empty_function; |
68c1021b PMF |
356 | entry->refcount = 0; |
357 | entry->ptype = 0; | |
358 | } else if (nr_probes - nr_del == 1) { | |
359 | /* N -> 1, (N > 1) */ | |
360 | for (i = 0; old[i].func; i++) | |
361 | if ((probe && old[i].func != probe) || | |
362 | old[i].probe_private != probe_private) | |
363 | entry->single = old[i]; | |
364 | entry->refcount = 1; | |
365 | entry->ptype = 0; | |
366 | } else { | |
367 | int j = 0; | |
368 | /* N -> M, (N > 1, M > 1) */ | |
369 | /* + 1 for NULL */ | |
b521931e | 370 | new = zmalloc((nr_probes - nr_del + 1) * sizeof(struct ust_marker_probe_closure)); |
68c1021b PMF |
371 | if (new == NULL) |
372 | return ERR_PTR(-ENOMEM); | |
373 | for (i = 0; old[i].func; i++) | |
374 | if ((probe && old[i].func != probe) || | |
375 | old[i].probe_private != probe_private) | |
376 | new[j++] = old[i]; | |
377 | entry->refcount = nr_probes - nr_del; | |
378 | entry->ptype = 1; | |
379 | entry->multi = new; | |
380 | } | |
381 | debug_print_probes(entry); | |
382 | return old; | |
383 | } | |
384 | ||
385 | /* | |
b521931e MD |
386 | * Get ust_marker if the ust_marker is present in the ust_marker hash table. |
387 | * Must be called with ust_marker_mutex held. | |
68c1021b PMF |
388 | * Returns NULL if not present. |
389 | */ | |
b521931e | 390 | static struct ust_marker_entry *get_ust_marker(const char *channel, const char *name) |
68c1021b | 391 | { |
10c56168 DG |
392 | struct cds_hlist_head *head; |
393 | struct cds_hlist_node *node; | |
b521931e | 394 | struct ust_marker_entry *e; |
68c1021b PMF |
395 | size_t channel_len = strlen(channel) + 1; |
396 | size_t name_len = strlen(name) + 1; | |
397 | u32 hash; | |
398 | ||
399 | hash = jhash(channel, channel_len-1, 0) ^ jhash(name, name_len-1, 0); | |
b521931e | 400 | head = &ust_marker_table[hash & ((1 << ust_marker_HASH_BITS)-1)]; |
10c56168 | 401 | cds_hlist_for_each_entry(e, node, head, hlist) { |
68c1021b PMF |
402 | if (!strcmp(channel, e->channel) && !strcmp(name, e->name)) |
403 | return e; | |
404 | } | |
405 | return NULL; | |
406 | } | |
407 | ||
408 | /* | |
b521931e MD |
409 | * Add the ust_marker to the ust_marker hash table. Must be called with |
410 | * ust_marker_mutex held. | |
68c1021b | 411 | */ |
b521931e | 412 | static struct ust_marker_entry *add_ust_marker(const char *channel, const char *name, |
68c1021b PMF |
413 | const char *format) |
414 | { | |
10c56168 DG |
415 | struct cds_hlist_head *head; |
416 | struct cds_hlist_node *node; | |
b521931e | 417 | struct ust_marker_entry *e; |
68c1021b PMF |
418 | size_t channel_len = strlen(channel) + 1; |
419 | size_t name_len = strlen(name) + 1; | |
420 | size_t format_len = 0; | |
421 | u32 hash; | |
422 | ||
423 | hash = jhash(channel, channel_len-1, 0) ^ jhash(name, name_len-1, 0); | |
424 | if (format) | |
425 | format_len = strlen(format) + 1; | |
b521931e | 426 | head = &ust_marker_table[hash & ((1 << ust_marker_HASH_BITS)-1)]; |
10c56168 | 427 | cds_hlist_for_each_entry(e, node, head, hlist) { |
68c1021b | 428 | if (!strcmp(channel, e->channel) && !strcmp(name, e->name)) { |
b521931e | 429 | DBG("ust_marker %s.%s busy", channel, name); |
68c1021b PMF |
430 | return ERR_PTR(-EBUSY); /* Already there */ |
431 | } | |
432 | } | |
433 | /* | |
1dba3e6c | 434 | * Using zmalloc here to allocate a variable length element. Could |
68c1021b PMF |
435 | * cause some memory fragmentation if overused. |
436 | */ | |
b521931e | 437 | e = zmalloc(sizeof(struct ust_marker_entry) |
909bc43f | 438 | + channel_len + name_len + format_len); |
68c1021b PMF |
439 | if (!e) |
440 | return ERR_PTR(-ENOMEM); | |
441 | memcpy(e->channel, channel, channel_len); | |
442 | e->name = &e->channel[channel_len]; | |
443 | memcpy(e->name, name, name_len); | |
444 | if (format) { | |
eddb0f66 | 445 | e->format = &e->name[name_len]; |
68c1021b | 446 | memcpy(e->format, format, format_len); |
b521931e MD |
447 | if (strcmp(e->format, UST_MARKER_NOARGS) == 0) |
448 | e->call = ust_marker_probe_cb_noarg; | |
68c1021b | 449 | else |
b521931e | 450 | e->call = ust_marker_probe_cb; |
686debc3 | 451 | __ust_marker(0, metadata, core_marker_format, NULL, |
68c1021b PMF |
452 | "channel %s name %s format %s", |
453 | e->channel, e->name, e->format); | |
454 | } else { | |
455 | e->format = NULL; | |
b521931e | 456 | e->call = ust_marker_probe_cb; |
68c1021b | 457 | } |
b521931e | 458 | e->single.func = __ust_marker_empty_function; |
68c1021b PMF |
459 | e->single.probe_private = NULL; |
460 | e->multi = NULL; | |
461 | e->ptype = 0; | |
462 | e->format_allocated = 0; | |
463 | e->refcount = 0; | |
464 | e->rcu_pending = 0; | |
10c56168 | 465 | cds_hlist_add_head(&e->hlist, head); |
68c1021b PMF |
466 | return e; |
467 | } | |
468 | ||
469 | /* | |
b521931e | 470 | * Remove the ust_marker from the ust_marker hash table. Must be called with mutex_lock |
68c1021b PMF |
471 | * held. |
472 | */ | |
b521931e | 473 | static int remove_ust_marker(const char *channel, const char *name) |
68c1021b | 474 | { |
10c56168 DG |
475 | struct cds_hlist_head *head; |
476 | struct cds_hlist_node *node; | |
b521931e | 477 | struct ust_marker_entry *e; |
68c1021b PMF |
478 | int found = 0; |
479 | size_t channel_len = strlen(channel) + 1; | |
480 | size_t name_len = strlen(name) + 1; | |
481 | u32 hash; | |
482 | int ret; | |
483 | ||
484 | hash = jhash(channel, channel_len-1, 0) ^ jhash(name, name_len-1, 0); | |
b521931e | 485 | head = &ust_marker_table[hash & ((1 << ust_marker_HASH_BITS)-1)]; |
10c56168 | 486 | cds_hlist_for_each_entry(e, node, head, hlist) { |
68c1021b PMF |
487 | if (!strcmp(channel, e->channel) && !strcmp(name, e->name)) { |
488 | found = 1; | |
489 | break; | |
490 | } | |
491 | } | |
492 | if (!found) | |
493 | return -ENOENT; | |
b521931e | 494 | if (e->single.func != __ust_marker_empty_function) |
68c1021b | 495 | return -EBUSY; |
10c56168 | 496 | cds_hlist_del(&e->hlist); |
68c1021b | 497 | if (e->format_allocated) |
909bc43f | 498 | free(e->format); |
68c1021b PMF |
499 | ret = ltt_channels_unregister(e->channel); |
500 | WARN_ON(ret); | |
501 | /* Make sure the call_rcu has been executed */ | |
6cb88bc0 | 502 | //ust// if (e->rcu_pending) |
0222e121 | 503 | //ust// rcu_cmm_barrier_sched(); |
909bc43f | 504 | free(e); |
68c1021b PMF |
505 | return 0; |
506 | } | |
507 | ||
508 | /* | |
509 | * Set the mark_entry format to the format found in the element. | |
510 | */ | |
b521931e | 511 | static int ust_marker_set_format(struct ust_marker_entry *entry, const char *format) |
68c1021b | 512 | { |
909bc43f | 513 | entry->format = strdup(format); |
68c1021b PMF |
514 | if (!entry->format) |
515 | return -ENOMEM; | |
516 | entry->format_allocated = 1; | |
517 | ||
686debc3 | 518 | __ust_marker(0, metadata, core_marker_format, NULL, |
68c1021b PMF |
519 | "channel %s name %s format %s", |
520 | entry->channel, entry->name, entry->format); | |
521 | return 0; | |
522 | } | |
523 | ||
524 | /* | |
b521931e | 525 | * Sets the probe callback corresponding to one ust_marker. |
68c1021b | 526 | */ |
b521931e | 527 | static int set_ust_marker(struct ust_marker_entry *entry, struct ust_marker *elem, |
68c1021b PMF |
528 | int active) |
529 | { | |
530 | int ret = 0; | |
531 | WARN_ON(strcmp(entry->name, elem->name) != 0); | |
532 | ||
533 | if (entry->format) { | |
534 | if (strcmp(entry->format, elem->format) != 0) { | |
b521931e | 535 | ERR("Format mismatch for probe %s (%s), ust_marker (%s)", |
68c1021b PMF |
536 | entry->name, |
537 | entry->format, | |
538 | elem->format); | |
539 | return -EPERM; | |
540 | } | |
541 | } else { | |
b521931e | 542 | ret = ust_marker_set_format(entry, elem->format); |
68c1021b PMF |
543 | if (ret) |
544 | return ret; | |
545 | } | |
546 | ||
547 | /* | |
548 | * probe_cb setup (statically known) is done here. It is | |
549 | * asynchronous with the rest of execution, therefore we only | |
550 | * pass from a "safe" callback (with argument) to an "unsafe" | |
551 | * callback (does not set arguments). | |
552 | */ | |
553 | elem->call = entry->call; | |
554 | elem->channel_id = entry->channel_id; | |
555 | elem->event_id = entry->event_id; | |
556 | /* | |
557 | * Sanity check : | |
558 | * We only update the single probe private data when the ptr is | |
559 | * set to a _non_ single probe! (0 -> 1 and N -> 1, N != 1) | |
560 | */ | |
b521931e | 561 | WARN_ON(elem->single.func != __ust_marker_empty_function |
68c1021b PMF |
562 | && elem->single.probe_private != entry->single.probe_private |
563 | && !elem->ptype); | |
564 | elem->single.probe_private = entry->single.probe_private; | |
565 | /* | |
566 | * Make sure the private data is valid when we update the | |
567 | * single probe ptr. | |
568 | */ | |
0222e121 | 569 | cmm_smp_wmb(); |
68c1021b PMF |
570 | elem->single.func = entry->single.func; |
571 | /* | |
572 | * We also make sure that the new probe callbacks array is consistent | |
573 | * before setting a pointer to it. | |
574 | */ | |
575 | rcu_assign_pointer(elem->multi, entry->multi); | |
576 | /* | |
577 | * Update the function or multi probe array pointer before setting the | |
578 | * ptype. | |
579 | */ | |
0222e121 | 580 | cmm_smp_wmb(); |
68c1021b PMF |
581 | elem->ptype = entry->ptype; |
582 | ||
12e81b07 PMF |
583 | if (elem->tp_name && (active ^ _imv_read(elem->state))) { |
584 | WARN_ON(!elem->tp_cb); | |
585 | /* | |
586 | * It is ok to directly call the probe registration because type | |
686debc3 | 587 | * checking has been done in the __ust_marker_tp() macro. |
12e81b07 PMF |
588 | */ |
589 | ||
590 | if (active) { | |
591 | /* | |
592 | * try_module_get should always succeed because we hold | |
b521931e | 593 | * ust_marker_mutex to get the tp_cb address. |
12e81b07 | 594 | */ |
59b161cd PMF |
595 | //ust// ret = try_module_get(__module_text_address( |
596 | //ust// (unsigned long)elem->tp_cb)); | |
597 | //ust// BUG_ON(!ret); | |
12e81b07 PMF |
598 | ret = tracepoint_probe_register_noupdate( |
599 | elem->tp_name, | |
9b9e13aa | 600 | elem->tp_cb, NULL); |
12e81b07 PMF |
601 | } else { |
602 | ret = tracepoint_probe_unregister_noupdate( | |
603 | elem->tp_name, | |
9b9e13aa | 604 | elem->tp_cb, NULL); |
12e81b07 PMF |
605 | /* |
606 | * tracepoint_probe_update_all() must be called | |
607 | * before the module containing tp_cb is unloaded. | |
608 | */ | |
59b161cd PMF |
609 | //ust// module_put(__module_text_address( |
610 | //ust// (unsigned long)elem->tp_cb)); | |
12e81b07 PMF |
611 | } |
612 | } | |
68c1021b PMF |
613 | elem->state__imv = active; |
614 | ||
615 | return ret; | |
616 | } | |
617 | ||
618 | /* | |
b521931e | 619 | * Disable a ust_marker and its probe callback. |
68c1021b PMF |
620 | * Note: only waiting an RCU period after setting elem->call to the empty |
621 | * function insures that the original callback is not used anymore. This insured | |
622 | * by rcu_read_lock_sched around the call site. | |
623 | */ | |
b521931e | 624 | static void disable_ust_marker(struct ust_marker *elem) |
68c1021b | 625 | { |
12e81b07 PMF |
626 | int ret; |
627 | ||
628 | /* leave "call" as is. It is known statically. */ | |
629 | if (elem->tp_name && _imv_read(elem->state)) { | |
630 | WARN_ON(!elem->tp_cb); | |
631 | /* | |
632 | * It is ok to directly call the probe registration because type | |
686debc3 | 633 | * checking has been done in the __ust_marker_tp() macro. |
12e81b07 PMF |
634 | */ |
635 | ret = tracepoint_probe_unregister_noupdate(elem->tp_name, | |
9b9e13aa | 636 | elem->tp_cb, NULL); |
12e81b07 PMF |
637 | WARN_ON(ret); |
638 | /* | |
639 | * tracepoint_probe_update_all() must be called | |
640 | * before the module containing tp_cb is unloaded. | |
641 | */ | |
59b161cd | 642 | //ust// module_put(__module_text_address((unsigned long)elem->tp_cb)); |
12e81b07 | 643 | } |
68c1021b | 644 | elem->state__imv = 0; |
b521931e | 645 | elem->single.func = __ust_marker_empty_function; |
68c1021b | 646 | /* Update the function before setting the ptype */ |
0222e121 | 647 | cmm_smp_wmb(); |
68c1021b PMF |
648 | elem->ptype = 0; /* single probe */ |
649 | /* | |
650 | * Leave the private data and channel_id/event_id there, because removal | |
651 | * is racy and should be done only after an RCU period. These are never | |
652 | * used until the next initialization anyway. | |
653 | */ | |
654 | } | |
655 | ||
a5311005 | 656 | /* |
b521931e | 657 | * is_ust_marker_enabled - Check if a ust_marker is enabled |
a5311005 | 658 | * @channel: channel name |
b521931e | 659 | * @name: ust_marker name |
a5311005 | 660 | * |
b521931e | 661 | * Returns 1 if the ust_marker is enabled, 0 if disabled. |
a5311005 | 662 | */ |
b521931e | 663 | int is_ust_marker_enabled(const char *channel, const char *name) |
a5311005 | 664 | { |
b521931e | 665 | struct ust_marker_entry *entry; |
a5311005 | 666 | |
b521931e MD |
667 | pthread_mutex_lock(&ust_marker_mutex); |
668 | entry = get_ust_marker(channel, name); | |
669 | pthread_mutex_unlock(&ust_marker_mutex); | |
a5311005 PMF |
670 | |
671 | return entry && !!entry->refcount; | |
672 | } | |
673 | ||
68c1021b | 674 | /** |
b521931e | 675 | * ust_marker_update_probe_range - Update a probe range |
68c1021b PMF |
676 | * @begin: beginning of the range |
677 | * @end: end of the range | |
678 | * | |
b521931e | 679 | * Updates the probe callback corresponding to a range of ust_marker. |
68c1021b | 680 | */ |
b521931e MD |
681 | void ust_marker_update_probe_range(struct ust_marker * const *begin, |
682 | struct ust_marker * const *end) | |
68c1021b | 683 | { |
b521931e MD |
684 | struct ust_marker * const *iter; |
685 | struct ust_marker_entry *mark_entry; | |
68c1021b | 686 | |
b521931e | 687 | pthread_mutex_lock(&ust_marker_mutex); |
68c1021b | 688 | for (iter = begin; iter < end; iter++) { |
f08ebbe2 MD |
689 | if (!*iter) |
690 | continue; /* skip dummy */ | |
b521931e | 691 | mark_entry = get_ust_marker((*iter)->channel, (*iter)->name); |
68c1021b | 692 | if (mark_entry) { |
b521931e | 693 | set_ust_marker(mark_entry, *iter, !!mark_entry->refcount); |
68c1021b PMF |
694 | /* |
695 | * ignore error, continue | |
696 | */ | |
697 | } else { | |
b521931e | 698 | disable_ust_marker(*iter); |
68c1021b PMF |
699 | } |
700 | } | |
b521931e | 701 | pthread_mutex_unlock(&ust_marker_mutex); |
68c1021b PMF |
702 | } |
703 | ||
b521931e | 704 | static void lib_update_ust_marker(void) |
772030fe | 705 | { |
b521931e | 706 | struct ust_marker_lib *lib; |
772030fe PMF |
707 | |
708 | /* FIXME: we should probably take a mutex here on libs */ | |
f7b16408 | 709 | //ust// pthread_mutex_lock(&module_mutex); |
b521931e MD |
710 | cds_list_for_each_entry(lib, &ust_marker_libs, list) |
711 | ust_marker_update_probe_range(lib->ust_marker_start, | |
712 | lib->ust_marker_start + lib->ust_marker_count); | |
f7b16408 | 713 | //ust// pthread_mutex_unlock(&module_mutex); |
772030fe PMF |
714 | } |
715 | ||
68c1021b PMF |
716 | /* |
717 | * Update probes, removing the faulty probes. | |
718 | * | |
719 | * Internal callback only changed before the first probe is connected to it. | |
720 | * Single probe private data can only be changed on 0 -> 1 and 2 -> 1 | |
721 | * transitions. All other transitions will leave the old private data valid. | |
722 | * This makes the non-atomicity of the callback/private data updates valid. | |
723 | * | |
724 | * "special case" updates : | |
725 | * 0 -> 1 callback | |
726 | * 1 -> 0 callback | |
727 | * 1 -> 2 callbacks | |
728 | * 2 -> 1 callbacks | |
729 | * Other updates all behave the same, just like the 2 -> 3 or 3 -> 2 updates. | |
b521931e | 730 | * Site effect : ust_marker_set_format may delete the ust_marker entry (creating a |
68c1021b PMF |
731 | * replacement). |
732 | */ | |
b521931e | 733 | static void ust_marker_update_probes(void) |
68c1021b | 734 | { |
b521931e | 735 | lib_update_ust_marker(); |
12e81b07 | 736 | tracepoint_probe_update_all(); |
68c1021b PMF |
737 | /* Update immediate values */ |
738 | core_imv_update(); | |
474d745f | 739 | //ust// module_imv_update(); /* FIXME: need to port for libs? */ |
b521931e | 740 | ust_marker_update_processes(); |
68c1021b PMF |
741 | } |
742 | ||
743 | /** | |
b521931e MD |
744 | * ust_marker_probe_register - Connect a probe to a ust_marker |
745 | * @channel: ust_marker channel | |
746 | * @name: ust_marker name | |
68c1021b PMF |
747 | * @format: format string |
748 | * @probe: probe handler | |
749 | * @probe_private: probe private data | |
750 | * | |
751 | * private data must be a valid allocated memory address, or NULL. | |
752 | * Returns 0 if ok, error value on error. | |
753 | * The probe address must at least be aligned on the architecture pointer size. | |
754 | */ | |
b521931e MD |
755 | int ust_marker_probe_register(const char *channel, const char *name, |
756 | const char *format, ust_marker_probe_func *probe, | |
68c1021b PMF |
757 | void *probe_private) |
758 | { | |
b521931e | 759 | struct ust_marker_entry *entry; |
68c1021b | 760 | int ret = 0, ret_err; |
b521931e | 761 | struct ust_marker_probe_closure *old; |
68c1021b PMF |
762 | int first_probe = 0; |
763 | ||
b521931e MD |
764 | pthread_mutex_lock(&ust_marker_mutex); |
765 | entry = get_ust_marker(channel, name); | |
68c1021b PMF |
766 | if (!entry) { |
767 | first_probe = 1; | |
b521931e | 768 | entry = add_ust_marker(channel, name, format); |
68c1021b PMF |
769 | if (IS_ERR(entry)) |
770 | ret = PTR_ERR(entry); | |
771 | if (ret) | |
772 | goto end; | |
773 | ret = ltt_channels_register(channel); | |
774 | if (ret) | |
b521931e | 775 | goto error_remove_ust_marker; |
68c1021b PMF |
776 | ret = ltt_channels_get_index_from_name(channel); |
777 | if (ret < 0) | |
778 | goto error_unregister_channel; | |
779 | entry->channel_id = ret; | |
780 | ret = ltt_channels_get_event_id(channel, name); | |
781 | if (ret < 0) | |
782 | goto error_unregister_channel; | |
783 | entry->event_id = ret; | |
784 | ret = 0; | |
686debc3 | 785 | __ust_marker(0, metadata, core_marker_id, NULL, |
68c1021b PMF |
786 | "channel %s name %s event_id %hu " |
787 | "int #1u%zu long #1u%zu pointer #1u%zu " | |
788 | "size_t #1u%zu alignment #1u%u", | |
789 | channel, name, entry->event_id, | |
790 | sizeof(int), sizeof(long), sizeof(void *), | |
791 | sizeof(size_t), ltt_get_alignment()); | |
792 | } else if (format) { | |
793 | if (!entry->format) | |
b521931e | 794 | ret = ust_marker_set_format(entry, format); |
68c1021b PMF |
795 | else if (strcmp(entry->format, format)) |
796 | ret = -EPERM; | |
797 | if (ret) | |
798 | goto end; | |
799 | } | |
800 | ||
801 | /* | |
b521931e | 802 | * If we detect that a call_rcu is pending for this ust_marker, |
68c1021b PMF |
803 | * make sure it's executed now. |
804 | */ | |
6cb88bc0 | 805 | //ust// if (entry->rcu_pending) |
0222e121 | 806 | //ust// rcu_cmm_barrier_sched(); |
b521931e | 807 | old = ust_marker_entry_add_probe(entry, probe, probe_private); |
68c1021b PMF |
808 | if (IS_ERR(old)) { |
809 | ret = PTR_ERR(old); | |
810 | if (first_probe) | |
811 | goto error_unregister_channel; | |
812 | else | |
813 | goto end; | |
814 | } | |
b521931e | 815 | pthread_mutex_unlock(&ust_marker_mutex); |
68c1021b | 816 | |
b521931e MD |
817 | /* Activate ust_marker if necessary */ |
818 | ust_marker_update_probes(); | |
68c1021b | 819 | |
b521931e MD |
820 | pthread_mutex_lock(&ust_marker_mutex); |
821 | entry = get_ust_marker(channel, name); | |
68c1021b PMF |
822 | if (!entry) |
823 | goto end; | |
6cb88bc0 | 824 | //ust// if (entry->rcu_pending) |
0222e121 | 825 | //ust// rcu_cmm_barrier_sched(); |
68c1021b PMF |
826 | entry->oldptr = old; |
827 | entry->rcu_pending = 1; | |
828 | /* write rcu_pending before calling the RCU callback */ | |
0222e121 | 829 | cmm_smp_wmb(); |
6cb88bc0 PMF |
830 | //ust// call_rcu_sched(&entry->rcu, free_old_closure); |
831 | synchronize_rcu(); free_old_closure(&entry->rcu); | |
68c1021b PMF |
832 | goto end; |
833 | ||
834 | error_unregister_channel: | |
835 | ret_err = ltt_channels_unregister(channel); | |
836 | WARN_ON(ret_err); | |
b521931e MD |
837 | error_remove_ust_marker: |
838 | ret_err = remove_ust_marker(channel, name); | |
68c1021b PMF |
839 | WARN_ON(ret_err); |
840 | end: | |
b521931e | 841 | pthread_mutex_unlock(&ust_marker_mutex); |
68c1021b PMF |
842 | return ret; |
843 | } | |
b521931e | 844 | //ust// EXPORT_SYMBOL_GPL(ust_marker_probe_register); |
68c1021b PMF |
845 | |
846 | /** | |
b521931e MD |
847 | * ust_marker_probe_unregister - Disconnect a probe from a ust_marker |
848 | * @channel: ust_marker channel | |
849 | * @name: ust_marker name | |
68c1021b PMF |
850 | * @probe: probe function pointer |
851 | * @probe_private: probe private data | |
852 | * | |
b521931e | 853 | * Returns the private data given to ust_marker_probe_register, or an ERR_PTR(). |
68c1021b PMF |
854 | * We do not need to call a synchronize_sched to make sure the probes have |
855 | * finished running before doing a module unload, because the module unload | |
856 | * itself uses stop_machine(), which insures that every preempt disabled section | |
857 | * have finished. | |
858 | */ | |
b521931e MD |
859 | int ust_marker_probe_unregister(const char *channel, const char *name, |
860 | ust_marker_probe_func *probe, void *probe_private) | |
68c1021b | 861 | { |
b521931e MD |
862 | struct ust_marker_entry *entry; |
863 | struct ust_marker_probe_closure *old; | |
68c1021b PMF |
864 | int ret = -ENOENT; |
865 | ||
b521931e MD |
866 | pthread_mutex_lock(&ust_marker_mutex); |
867 | entry = get_ust_marker(channel, name); | |
68c1021b PMF |
868 | if (!entry) |
869 | goto end; | |
6cb88bc0 | 870 | //ust// if (entry->rcu_pending) |
0222e121 | 871 | //ust// rcu_cmm_barrier_sched(); |
b521931e MD |
872 | old = ust_marker_entry_remove_probe(entry, probe, probe_private); |
873 | pthread_mutex_unlock(&ust_marker_mutex); | |
68c1021b | 874 | |
b521931e | 875 | ust_marker_update_probes(); |
68c1021b | 876 | |
b521931e MD |
877 | pthread_mutex_lock(&ust_marker_mutex); |
878 | entry = get_ust_marker(channel, name); | |
68c1021b PMF |
879 | if (!entry) |
880 | goto end; | |
6cb88bc0 | 881 | //ust// if (entry->rcu_pending) |
0222e121 | 882 | //ust// rcu_cmm_barrier_sched(); |
68c1021b PMF |
883 | entry->oldptr = old; |
884 | entry->rcu_pending = 1; | |
885 | /* write rcu_pending before calling the RCU callback */ | |
0222e121 | 886 | cmm_smp_wmb(); |
6cb88bc0 PMF |
887 | //ust// call_rcu_sched(&entry->rcu, free_old_closure); |
888 | synchronize_rcu(); free_old_closure(&entry->rcu); | |
b521931e | 889 | remove_ust_marker(channel, name); /* Ignore busy error message */ |
68c1021b PMF |
890 | ret = 0; |
891 | end: | |
b521931e | 892 | pthread_mutex_unlock(&ust_marker_mutex); |
68c1021b PMF |
893 | return ret; |
894 | } | |
b521931e | 895 | //ust// EXPORT_SYMBOL_GPL(ust_marker_probe_unregister); |
68c1021b | 896 | |
b521931e MD |
897 | static struct ust_marker_entry * |
898 | get_ust_marker_from_private_data(ust_marker_probe_func *probe, void *probe_private) | |
68c1021b | 899 | { |
b521931e | 900 | struct ust_marker_entry *entry; |
68c1021b | 901 | unsigned int i; |
10c56168 DG |
902 | struct cds_hlist_head *head; |
903 | struct cds_hlist_node *node; | |
68c1021b | 904 | |
b521931e MD |
905 | for (i = 0; i < ust_marker_TABLE_SIZE; i++) { |
906 | head = &ust_marker_table[i]; | |
10c56168 | 907 | cds_hlist_for_each_entry(entry, node, head, hlist) { |
68c1021b PMF |
908 | if (!entry->ptype) { |
909 | if (entry->single.func == probe | |
910 | && entry->single.probe_private | |
911 | == probe_private) | |
912 | return entry; | |
913 | } else { | |
b521931e | 914 | struct ust_marker_probe_closure *closure; |
68c1021b PMF |
915 | closure = entry->multi; |
916 | for (i = 0; closure[i].func; i++) { | |
917 | if (closure[i].func == probe && | |
918 | closure[i].probe_private | |
919 | == probe_private) | |
920 | return entry; | |
921 | } | |
922 | } | |
923 | } | |
924 | } | |
925 | return NULL; | |
926 | } | |
927 | ||
928 | /** | |
b521931e | 929 | * ust_marker_probe_unregister_private_data - Disconnect a probe from a ust_marker |
68c1021b PMF |
930 | * @probe: probe function |
931 | * @probe_private: probe private data | |
932 | * | |
933 | * Unregister a probe by providing the registered private data. | |
b521931e | 934 | * Only removes the first ust_marker found in hash table. |
68c1021b PMF |
935 | * Return 0 on success or error value. |
936 | * We do not need to call a synchronize_sched to make sure the probes have | |
937 | * finished running before doing a module unload, because the module unload | |
938 | * itself uses stop_machine(), which insures that every preempt disabled section | |
939 | * have finished. | |
940 | */ | |
b521931e | 941 | int ust_marker_probe_unregister_private_data(ust_marker_probe_func *probe, |
68c1021b PMF |
942 | void *probe_private) |
943 | { | |
b521931e | 944 | struct ust_marker_entry *entry; |
68c1021b | 945 | int ret = 0; |
b521931e | 946 | struct ust_marker_probe_closure *old; |
909bc43f | 947 | char *channel = NULL, *name = NULL; |
68c1021b | 948 | |
b521931e MD |
949 | pthread_mutex_lock(&ust_marker_mutex); |
950 | entry = get_ust_marker_from_private_data(probe, probe_private); | |
68c1021b PMF |
951 | if (!entry) { |
952 | ret = -ENOENT; | |
953 | goto end; | |
954 | } | |
6cb88bc0 | 955 | //ust// if (entry->rcu_pending) |
0222e121 | 956 | //ust// rcu_cmm_barrier_sched(); |
b521931e | 957 | old = ust_marker_entry_remove_probe(entry, NULL, probe_private); |
909bc43f PMF |
958 | channel = strdup(entry->channel); |
959 | name = strdup(entry->name); | |
b521931e | 960 | pthread_mutex_unlock(&ust_marker_mutex); |
68c1021b | 961 | |
b521931e | 962 | ust_marker_update_probes(); |
68c1021b | 963 | |
b521931e MD |
964 | pthread_mutex_lock(&ust_marker_mutex); |
965 | entry = get_ust_marker(channel, name); | |
68c1021b PMF |
966 | if (!entry) |
967 | goto end; | |
6cb88bc0 | 968 | //ust// if (entry->rcu_pending) |
0222e121 | 969 | //ust// rcu_cmm_barrier_sched(); |
68c1021b PMF |
970 | entry->oldptr = old; |
971 | entry->rcu_pending = 1; | |
972 | /* write rcu_pending before calling the RCU callback */ | |
0222e121 | 973 | cmm_smp_wmb(); |
6cb88bc0 PMF |
974 | //ust// call_rcu_sched(&entry->rcu, free_old_closure); |
975 | synchronize_rcu(); free_old_closure(&entry->rcu); | |
68c1021b | 976 | /* Ignore busy error message */ |
b521931e | 977 | remove_ust_marker(channel, name); |
68c1021b | 978 | end: |
b521931e | 979 | pthread_mutex_unlock(&ust_marker_mutex); |
909bc43f PMF |
980 | free(channel); |
981 | free(name); | |
68c1021b PMF |
982 | return ret; |
983 | } | |
b521931e | 984 | //ust// EXPORT_SYMBOL_GPL(ust_marker_probe_unregister_private_data); |
68c1021b PMF |
985 | |
986 | /** | |
b521931e MD |
987 | * ust_marker_get_private_data - Get a ust_marker's probe private data |
988 | * @channel: ust_marker channel | |
989 | * @name: ust_marker name | |
68c1021b PMF |
990 | * @probe: probe to match |
991 | * @num: get the nth matching probe's private data | |
992 | * | |
993 | * Returns the nth private data pointer (starting from 0) matching, or an | |
994 | * ERR_PTR. | |
995 | * Returns the private data pointer, or an ERR_PTR. | |
996 | * The private data pointer should _only_ be dereferenced if the caller is the | |
997 | * owner of the data, or its content could vanish. This is mostly used to | |
998 | * confirm that a caller is the owner of a registered probe. | |
999 | */ | |
b521931e MD |
1000 | void *ust_marker_get_private_data(const char *channel, const char *name, |
1001 | ust_marker_probe_func *probe, int num) | |
68c1021b | 1002 | { |
10c56168 DG |
1003 | struct cds_hlist_head *head; |
1004 | struct cds_hlist_node *node; | |
b521931e | 1005 | struct ust_marker_entry *e; |
68c1021b PMF |
1006 | size_t channel_len = strlen(channel) + 1; |
1007 | size_t name_len = strlen(name) + 1; | |
1008 | int i; | |
1009 | u32 hash; | |
1010 | ||
1011 | hash = jhash(channel, channel_len-1, 0) ^ jhash(name, name_len-1, 0); | |
b521931e | 1012 | head = &ust_marker_table[hash & ((1 << ust_marker_HASH_BITS)-1)]; |
10c56168 | 1013 | cds_hlist_for_each_entry(e, node, head, hlist) { |
68c1021b PMF |
1014 | if (!strcmp(channel, e->channel) && !strcmp(name, e->name)) { |
1015 | if (!e->ptype) { | |
1016 | if (num == 0 && e->single.func == probe) | |
1017 | return e->single.probe_private; | |
1018 | } else { | |
b521931e | 1019 | struct ust_marker_probe_closure *closure; |
68c1021b PMF |
1020 | int match = 0; |
1021 | closure = e->multi; | |
1022 | for (i = 0; closure[i].func; i++) { | |
1023 | if (closure[i].func != probe) | |
1024 | continue; | |
1025 | if (match++ == num) | |
1026 | return closure[i].probe_private; | |
1027 | } | |
1028 | } | |
1029 | break; | |
1030 | } | |
1031 | } | |
1032 | return ERR_PTR(-ENOENT); | |
1033 | } | |
b521931e | 1034 | //ust// EXPORT_SYMBOL_GPL(ust_marker_get_private_data); |
68c1021b PMF |
1035 | |
1036 | /** | |
b521931e | 1037 | * ust_marker_compact_event_ids - Compact ust_marker event IDs and reassign channels |
68c1021b PMF |
1038 | * |
1039 | * Called when no channel users are active by the channel infrastructure. | |
b521931e | 1040 | * Called with lock_ust_marker() and channel mutex held. |
68c1021b | 1041 | */ |
b521931e | 1042 | //ust// void ust_marker_compact_event_ids(void) |
59b161cd | 1043 | //ust// { |
b521931e | 1044 | //ust// struct ust_marker_entry *entry; |
59b161cd PMF |
1045 | //ust// unsigned int i; |
1046 | //ust// struct hlist_head *head; | |
1047 | //ust// struct hlist_node *node; | |
1048 | //ust// int ret; | |
1049 | //ust// | |
b521931e MD |
1050 | //ust// for (i = 0; i < ust_marker_TABLE_SIZE; i++) { |
1051 | //ust// head = &ust_marker_table[i]; | |
59b161cd PMF |
1052 | //ust// hlist_for_each_entry(entry, node, head, hlist) { |
1053 | //ust// ret = ltt_channels_get_index_from_name(entry->channel); | |
1054 | //ust// WARN_ON(ret < 0); | |
1055 | //ust// entry->channel_id = ret; | |
1056 | //ust// ret = _ltt_channels_get_event_id(entry->channel, | |
1057 | //ust// entry->name); | |
1058 | //ust// WARN_ON(ret < 0); | |
1059 | //ust// entry->event_id = ret; | |
1060 | //ust// } | |
1061 | //ust// } | |
1062 | //ust// } | |
68c1021b | 1063 | |
9c67dc50 | 1064 | //ust//#ifdef CONFIG_MODULES |
68c1021b | 1065 | |
772030fe PMF |
1066 | /* |
1067 | * Returns 0 if current not found. | |
1068 | * Returns 1 if current found. | |
1069 | */ | |
b521931e | 1070 | int lib_get_iter_ust_marker(struct ust_marker_iter *iter) |
772030fe | 1071 | { |
b521931e | 1072 | struct ust_marker_lib *iter_lib; |
772030fe PMF |
1073 | int found = 0; |
1074 | ||
f7b16408 | 1075 | //ust// pthread_mutex_lock(&module_mutex); |
b521931e | 1076 | cds_list_for_each_entry(iter_lib, &ust_marker_libs, list) { |
772030fe PMF |
1077 | if (iter_lib < iter->lib) |
1078 | continue; | |
1079 | else if (iter_lib > iter->lib) | |
b521931e MD |
1080 | iter->ust_marker = NULL; |
1081 | found = ust_marker_get_iter_range(&iter->ust_marker, | |
1082 | iter_lib->ust_marker_start, | |
1083 | iter_lib->ust_marker_start + iter_lib->ust_marker_count); | |
772030fe PMF |
1084 | if (found) { |
1085 | iter->lib = iter_lib; | |
1086 | break; | |
1087 | } | |
1088 | } | |
f7b16408 | 1089 | //ust// pthread_mutex_unlock(&module_mutex); |
772030fe PMF |
1090 | return found; |
1091 | } | |
1092 | ||
68c1021b | 1093 | /** |
b521931e MD |
1094 | * ust_marker_get_iter_range - Get a next ust_marker iterator given a range. |
1095 | * @ust_marker: current ust_marker (in), next ust_marker (out) | |
68c1021b PMF |
1096 | * @begin: beginning of the range |
1097 | * @end: end of the range | |
1098 | * | |
b521931e MD |
1099 | * Returns whether a next ust_marker has been found (1) or not (0). |
1100 | * Will return the first ust_marker in the range if the input ust_marker is NULL. | |
68c1021b | 1101 | */ |
b521931e MD |
1102 | int ust_marker_get_iter_range(struct ust_marker * const **ust_marker, |
1103 | struct ust_marker * const *begin, | |
1104 | struct ust_marker * const *end) | |
68c1021b | 1105 | { |
b521931e MD |
1106 | if (!*ust_marker && begin != end) |
1107 | *ust_marker = begin; | |
1108 | while (*ust_marker >= begin && *ust_marker < end) { | |
1109 | if (!**ust_marker) | |
1110 | (*ust_marker)++; /* skip dummy */ | |
f08ebbe2 MD |
1111 | else |
1112 | return 1; | |
68c1021b | 1113 | } |
68c1021b PMF |
1114 | return 0; |
1115 | } | |
b521931e | 1116 | //ust// EXPORT_SYMBOL_GPL(ust_marker_get_iter_range); |
68c1021b | 1117 | |
b521931e | 1118 | static void ust_marker_get_iter(struct ust_marker_iter *iter) |
68c1021b PMF |
1119 | { |
1120 | int found = 0; | |
1121 | ||
b521931e | 1122 | found = lib_get_iter_ust_marker(iter); |
68c1021b | 1123 | if (!found) |
b521931e | 1124 | ust_marker_iter_reset(iter); |
68c1021b PMF |
1125 | } |
1126 | ||
b521931e | 1127 | void ust_marker_iter_start(struct ust_marker_iter *iter) |
68c1021b | 1128 | { |
b521931e | 1129 | ust_marker_get_iter(iter); |
68c1021b | 1130 | } |
b521931e | 1131 | //ust// EXPORT_SYMBOL_GPL(ust_marker_iter_start); |
68c1021b | 1132 | |
b521931e | 1133 | void ust_marker_iter_next(struct ust_marker_iter *iter) |
68c1021b | 1134 | { |
b521931e | 1135 | iter->ust_marker++; |
68c1021b | 1136 | /* |
b521931e MD |
1137 | * iter->ust_marker may be invalid because we blindly incremented it. |
1138 | * Make sure it is valid by marshalling on the ust_marker, getting the | |
1139 | * ust_marker from following modules if necessary. | |
68c1021b | 1140 | */ |
b521931e | 1141 | ust_marker_get_iter(iter); |
68c1021b | 1142 | } |
b521931e | 1143 | //ust// EXPORT_SYMBOL_GPL(ust_marker_iter_next); |
68c1021b | 1144 | |
b521931e | 1145 | void ust_marker_iter_stop(struct ust_marker_iter *iter) |
68c1021b PMF |
1146 | { |
1147 | } | |
b521931e | 1148 | //ust// EXPORT_SYMBOL_GPL(ust_marker_iter_stop); |
68c1021b | 1149 | |
b521931e | 1150 | void ust_marker_iter_reset(struct ust_marker_iter *iter) |
68c1021b | 1151 | { |
98963de4 | 1152 | iter->lib = NULL; |
b521931e | 1153 | iter->ust_marker = NULL; |
68c1021b | 1154 | } |
b521931e | 1155 | //ust// EXPORT_SYMBOL_GPL(ust_marker_iter_reset); |
68c1021b | 1156 | |
b521931e | 1157 | #ifdef CONFIG_UST_MARKER_USERSPACE |
68c1021b | 1158 | /* |
b521931e | 1159 | * must be called with current->user_ust_marker_mutex held |
68c1021b | 1160 | */ |
b521931e | 1161 | static void free_user_ust_marker(char __user *state, struct cds_hlist_head *head) |
68c1021b | 1162 | { |
b521931e | 1163 | struct user_ust_marker *umark; |
10c56168 | 1164 | struct cds_hlist_node *pos, *n; |
68c1021b | 1165 | |
10c56168 | 1166 | cds_hlist_for_each_entry_safe(umark, pos, n, head, hlist) { |
68c1021b | 1167 | if (umark->state == state) { |
10c56168 | 1168 | cds_hlist_del(&umark->hlist); |
909bc43f | 1169 | free(umark); |
68c1021b PMF |
1170 | } |
1171 | } | |
1172 | } | |
1173 | ||
c1f20530 PMF |
1174 | /* |
1175 | * Update current process. | |
1176 | * Note that we have to wait a whole scheduler period before we are sure that | |
b521931e | 1177 | * every running userspace threads have their ust_marker updated. |
c1f20530 PMF |
1178 | * (synchronize_sched() can be used to insure this). |
1179 | */ | |
b521931e | 1180 | //ust// void ust_marker_update_process(void) |
59b161cd | 1181 | //ust// { |
b521931e | 1182 | //ust// struct user_ust_marker *umark; |
c1f20530 | 1183 | //ust// struct hlist_node *pos; |
b521931e | 1184 | //ust// struct ust_marker_entry *entry; |
59b161cd | 1185 | //ust// |
b521931e MD |
1186 | //ust// pthread_mutex_lock(&ust_marker_mutex); |
1187 | //ust// pthread_mutex_lock(¤t->group_leader->user_ust_marker_mutex); | |
c1f20530 PMF |
1188 | //ust// if (strcmp(current->comm, "testprog") == 0) |
1189 | //ust// DBG("do update pending for testprog"); | |
1190 | //ust// hlist_for_each_entry(umark, pos, | |
b521931e MD |
1191 | //ust// ¤t->group_leader->user_ust_marker, hlist) { |
1192 | //ust// DBG("Updating ust_marker %s in %s", umark->name, current->comm); | |
1193 | //ust// entry = get_ust_marker("userspace", umark->name); | |
59b161cd PMF |
1194 | //ust// if (entry) { |
1195 | //ust// if (entry->format && | |
1196 | //ust// strcmp(entry->format, umark->format) != 0) { | |
c1f20530 | 1197 | //ust// WARN("error, wrong format in process %s", |
59b161cd | 1198 | //ust// current->comm); |
c1f20530 | 1199 | //ust// break; |
59b161cd | 1200 | //ust// } |
c1f20530 | 1201 | //ust// if (put_user(!!entry->refcount, umark->state)) { |
b521931e | 1202 | //ust// WARN("ust_marker in %s caused a fault", |
c1f20530 PMF |
1203 | //ust// current->comm); |
1204 | //ust// break; | |
59b161cd | 1205 | //ust// } |
59b161cd | 1206 | //ust// } else { |
59b161cd | 1207 | //ust// if (put_user(0, umark->state)) { |
b521931e | 1208 | //ust// WARN("ust_marker in %s caused a fault", current->comm); |
c1f20530 | 1209 | //ust// break; |
59b161cd PMF |
1210 | //ust// } |
1211 | //ust// } | |
59b161cd | 1212 | //ust// } |
b521931e MD |
1213 | //ust// clear_thread_flag(TIF_ust_marker_PENDING); |
1214 | //ust// pthread_mutex_unlock(¤t->group_leader->user_ust_marker_mutex); | |
1215 | //ust// pthread_mutex_unlock(&ust_marker_mutex); | |
59b161cd | 1216 | //ust// } |
68c1021b | 1217 | |
68c1021b PMF |
1218 | /* |
1219 | * Called at process exit and upon do_execve(). | |
1220 | * We assume that when the leader exits, no more references can be done to the | |
1221 | * leader structure by the other threads. | |
1222 | */ | |
b521931e | 1223 | void exit_user_ust_marker(struct task_struct *p) |
68c1021b | 1224 | { |
b521931e | 1225 | struct user_ust_marker *umark; |
10c56168 | 1226 | struct cds_hlist_node *pos, *n; |
68c1021b PMF |
1227 | |
1228 | if (thread_group_leader(p)) { | |
b521931e MD |
1229 | pthread_mutex_lock(&ust_marker_mutex); |
1230 | pthread_mutex_lock(&p->user_ust_marker_mutex); | |
1231 | cds_hlist_for_each_entry_safe(umark, pos, n, &p->user_ust_marker, | |
68c1021b | 1232 | hlist) |
909bc43f | 1233 | free(umark); |
b521931e MD |
1234 | INIT_HLIST_HEAD(&p->user_ust_marker); |
1235 | p->user_ust_marker_sequence++; | |
1236 | pthread_mutex_unlock(&p->user_ust_marker_mutex); | |
1237 | pthread_mutex_unlock(&ust_marker_mutex); | |
68c1021b PMF |
1238 | } |
1239 | } | |
1240 | ||
b521931e | 1241 | int is_ust_marker_enabled(const char *channel, const char *name) |
68c1021b | 1242 | { |
b521931e | 1243 | struct ust_marker_entry *entry; |
68c1021b | 1244 | |
b521931e MD |
1245 | pthread_mutex_lock(&ust_marker_mutex); |
1246 | entry = get_ust_marker(channel, name); | |
1247 | pthread_mutex_unlock(&ust_marker_mutex); | |
68c1021b PMF |
1248 | |
1249 | return entry && !!entry->refcount; | |
1250 | } | |
9c67dc50 | 1251 | //ust// #endif |
68c1021b | 1252 | |
b521931e | 1253 | int ust_marker_module_notify(struct notifier_block *self, |
68c1021b PMF |
1254 | unsigned long val, void *data) |
1255 | { | |
1256 | struct module *mod = data; | |
1257 | ||
1258 | switch (val) { | |
1259 | case MODULE_STATE_COMING: | |
b521931e MD |
1260 | ust_marker_update_probe_range(mod->ust_marker, |
1261 | mod->ust_marker + mod->num_ust_marker); | |
68c1021b PMF |
1262 | break; |
1263 | case MODULE_STATE_GOING: | |
b521931e MD |
1264 | ust_marker_update_probe_range(mod->ust_marker, |
1265 | mod->ust_marker + mod->num_ust_marker); | |
68c1021b PMF |
1266 | break; |
1267 | } | |
1268 | return 0; | |
1269 | } | |
1270 | ||
b521931e MD |
1271 | struct notifier_block ust_marker_module_nb = { |
1272 | .notifier_call = ust_marker_module_notify, | |
68c1021b PMF |
1273 | .priority = 0, |
1274 | }; | |
1275 | ||
b521931e | 1276 | //ust// static int init_ust_marker(void) |
59b161cd | 1277 | //ust// { |
b521931e | 1278 | //ust// return register_module_notifier(&ust_marker_module_nb); |
59b161cd | 1279 | //ust// } |
b521931e MD |
1280 | //ust// __initcall(init_ust_marker); |
1281 | /* TODO: call ust_marker_module_nb() when a library is linked at runtime (dlopen)? */ | |
68c1021b PMF |
1282 | |
1283 | #endif /* CONFIG_MODULES */ | |
1284 | ||
b521931e | 1285 | void ltt_dump_ust_marker_state(struct ust_trace *trace) |
9c67dc50 | 1286 | { |
b521931e | 1287 | struct ust_marker_entry *entry; |
9c67dc50 | 1288 | struct ltt_probe_private_data call_data; |
10c56168 DG |
1289 | struct cds_hlist_head *head; |
1290 | struct cds_hlist_node *node; | |
48454c95 | 1291 | unsigned int i; |
9c67dc50 | 1292 | |
b521931e | 1293 | pthread_mutex_lock(&ust_marker_mutex); |
9c67dc50 PMF |
1294 | call_data.trace = trace; |
1295 | call_data.serializer = NULL; | |
1296 | ||
b521931e MD |
1297 | for (i = 0; i < ust_marker_TABLE_SIZE; i++) { |
1298 | head = &ust_marker_table[i]; | |
10c56168 | 1299 | cds_hlist_for_each_entry(entry, node, head, hlist) { |
686debc3 | 1300 | __ust_marker(0, metadata, core_marker_id, |
9c67dc50 | 1301 | &call_data, |
48454c95 PMF |
1302 | "channel %s name %s event_id %hu " |
1303 | "int #1u%zu long #1u%zu pointer #1u%zu " | |
1304 | "size_t #1u%zu alignment #1u%u", | |
1305 | entry->channel, | |
1306 | entry->name, | |
1307 | entry->event_id, | |
1308 | sizeof(int), sizeof(long), | |
1309 | sizeof(void *), sizeof(size_t), | |
1310 | ltt_get_alignment()); | |
1311 | if (entry->format) | |
686debc3 | 1312 | __ust_marker(0, metadata, |
48454c95 PMF |
1313 | core_marker_format, |
1314 | &call_data, | |
1315 | "channel %s name %s format %s", | |
1316 | entry->channel, | |
1317 | entry->name, | |
1318 | entry->format); | |
1319 | } | |
9c67dc50 | 1320 | } |
b521931e | 1321 | pthread_mutex_unlock(&ust_marker_mutex); |
9c67dc50 | 1322 | } |
b521931e | 1323 | //ust// EXPORT_SYMBOL_GPL(ltt_dump_ust_marker_state); |
98963de4 | 1324 | |
b521931e | 1325 | static void (*new_ust_marker_cb)(struct ust_marker *) = NULL; |
20b37a31 | 1326 | |
b521931e | 1327 | void ust_marker_set_new_ust_marker_cb(void (*cb)(struct ust_marker *)) |
20b37a31 | 1328 | { |
b521931e | 1329 | new_ust_marker_cb = cb; |
20b37a31 PMF |
1330 | } |
1331 | ||
b521931e | 1332 | static void new_ust_marker(struct ust_marker * const *start, struct ust_marker * const *end) |
20b37a31 | 1333 | { |
b521931e MD |
1334 | if (new_ust_marker_cb) { |
1335 | struct ust_marker * const *m; | |
f08ebbe2 MD |
1336 | |
1337 | for(m = start; m < end; m++) { | |
1338 | if (*m) | |
b521931e | 1339 | new_ust_marker_cb(*m); |
20b37a31 PMF |
1340 | } |
1341 | } | |
1342 | } | |
1343 | ||
b521931e | 1344 | int ust_marker_register_lib(struct ust_marker * const *ust_marker_start, int ust_marker_count) |
98963de4 | 1345 | { |
b521931e | 1346 | struct ust_marker_lib *pl, *iter; |
98963de4 | 1347 | |
b521931e | 1348 | pl = (struct ust_marker_lib *) zmalloc(sizeof(struct ust_marker_lib)); |
98963de4 | 1349 | |
b521931e MD |
1350 | pl->ust_marker_start = ust_marker_start; |
1351 | pl->ust_marker_count = ust_marker_count; | |
98963de4 | 1352 | |
0b5207fa | 1353 | /* FIXME: maybe protect this with its own mutex? */ |
b521931e | 1354 | lock_ust_marker(); |
b467f7a7 MD |
1355 | |
1356 | /* | |
1357 | * We sort the libs by struct lib pointer address. | |
1358 | */ | |
b521931e | 1359 | cds_list_for_each_entry_reverse(iter, &ust_marker_libs, list) { |
b467f7a7 MD |
1360 | BUG_ON(iter == pl); /* Should never be in the list twice */ |
1361 | if (iter < pl) { | |
1362 | /* We belong to the location right after iter. */ | |
1363 | cds_list_add(&pl->list, &iter->list); | |
1364 | goto lib_added; | |
1365 | } | |
1366 | } | |
1367 | /* We should be added at the head of the list */ | |
b521931e | 1368 | cds_list_add(&pl->list, &ust_marker_libs); |
b467f7a7 | 1369 | lib_added: |
b521931e | 1370 | unlock_ust_marker(); |
98963de4 | 1371 | |
b521931e | 1372 | new_ust_marker(ust_marker_start, ust_marker_start + ust_marker_count); |
20b37a31 | 1373 | |
4db647c5 | 1374 | /* FIXME: update just the loaded lib */ |
b521931e | 1375 | lib_update_ust_marker(); |
4db647c5 | 1376 | |
b521931e | 1377 | DBG("just registered a ust_marker section from %p and having %d ust_marker (minus dummy ust_marker)", ust_marker_start, ust_marker_count); |
98963de4 PMF |
1378 | |
1379 | return 0; | |
1380 | } | |
c463904d | 1381 | |
b521931e | 1382 | int ust_marker_unregister_lib(struct ust_marker * const *ust_marker_start) |
0b5207fa | 1383 | { |
b521931e | 1384 | struct ust_marker_lib *lib; |
24b6668c | 1385 | |
b521931e | 1386 | /*FIXME: implement; but before implementing, ust_marker_register_lib must |
0b5207fa PMF |
1387 | have appropriate locking. */ |
1388 | ||
b521931e | 1389 | lock_ust_marker(); |
24b6668c PMF |
1390 | |
1391 | /* FIXME: we should probably take a mutex here on libs */ | |
f7b16408 | 1392 | //ust// pthread_mutex_lock(&module_mutex); |
b521931e MD |
1393 | cds_list_for_each_entry(lib, &ust_marker_libs, list) { |
1394 | if(lib->ust_marker_start == ust_marker_start) { | |
1395 | struct ust_marker_lib *lib2free = lib; | |
0222e121 | 1396 | cds_list_del(&lib->list); |
24b6668c PMF |
1397 | free(lib2free); |
1398 | break; | |
1399 | } | |
1400 | } | |
1401 | ||
b521931e | 1402 | unlock_ust_marker(); |
24b6668c | 1403 | |
0b5207fa PMF |
1404 | return 0; |
1405 | } | |
1406 | ||
4db647c5 PMF |
1407 | static int initialized = 0; |
1408 | ||
b521931e | 1409 | void __attribute__((constructor)) init_ust_marker(void) |
c463904d | 1410 | { |
900e307e | 1411 | if (!initialized) { |
b521931e MD |
1412 | ust_marker_register_lib(__start___ust_marker_ptrs, |
1413 | __stop___ust_marker_ptrs | |
1414 | - __start___ust_marker_ptrs); | |
4db647c5 PMF |
1415 | initialized = 1; |
1416 | } | |
c463904d | 1417 | } |
24b6668c | 1418 | |
b521931e | 1419 | void __attribute__((destructor)) destroy_ust_marker(void) |
24b6668c | 1420 | { |
b521931e | 1421 | ust_marker_unregister_lib(__start___ust_marker_ptrs); |
24b6668c | 1422 | } |