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