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