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