Commit | Line | Data |
---|---|---|
ce5aef0b MD |
1 | /* |
2 | * ltt-probes.c | |
3 | * | |
4 | * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | |
5 | * | |
6 | * Holds LTTng probes registry. | |
7 | * | |
8 | * Dual LGPL v2.1/GPL v2 license. | |
9 | */ | |
10 | ||
8d8a24c8 MD |
11 | #include <string.h> |
12 | #include <errno.h> | |
13 | #include <urcu/list.h> | |
1f18504e | 14 | #include <urcu/hlist.h> |
4318ae1b | 15 | #include <lttng/ust-events.h> |
a3bb4b27 | 16 | #include <assert.h> |
c8fcf224 | 17 | #include <helper.h> |
48740cab | 18 | #include <ctype.h> |
ce5aef0b | 19 | |
8165c8da | 20 | #include "ltt-tracer-core.h" |
1f18504e MD |
21 | #include "jhash.h" |
22 | #include "error.h" | |
8165c8da MD |
23 | |
24 | /* | |
17dfb34b | 25 | * probe list is protected by ust_lock()/ust_unlock(). |
8165c8da | 26 | */ |
8d8a24c8 | 27 | static CDS_LIST_HEAD(probe_list); |
ce5aef0b | 28 | |
1f18504e MD |
29 | /* |
30 | * Loglevel hash table, containing the active loglevels. | |
31 | * Protected by ust lock. | |
32 | */ | |
33 | #define LOGLEVEL_HASH_BITS 6 | |
34 | #define LOGLEVEL_TABLE_SIZE (1 << LOGLEVEL_HASH_BITS) | |
35 | static struct cds_hlist_head loglevel_table[LOGLEVEL_TABLE_SIZE]; | |
36 | ||
e6c12e3d MD |
37 | /* |
38 | * Wildcard list, containing the active wildcards. | |
39 | * Protected by ust lock. | |
40 | */ | |
41 | static CDS_LIST_HEAD(wildcard_list); | |
42 | ||
df854e41 MD |
43 | static |
44 | const struct lttng_probe_desc *find_provider(const char *provider) | |
45 | { | |
46 | struct lttng_probe_desc *iter; | |
47 | ||
48 | cds_list_for_each_entry(iter, &probe_list, head) { | |
49 | if (!strcmp(iter->provider, provider)) | |
50 | return iter; | |
51 | } | |
52 | return NULL; | |
53 | } | |
54 | ||
ce5aef0b MD |
55 | static |
56 | const struct lttng_event_desc *find_event(const char *name) | |
57 | { | |
58 | struct lttng_probe_desc *probe_desc; | |
59 | int i; | |
60 | ||
8d8a24c8 | 61 | cds_list_for_each_entry(probe_desc, &probe_list, head) { |
ce5aef0b | 62 | for (i = 0; i < probe_desc->nr_events; i++) { |
ff412fb5 MD |
63 | if (!strncmp(probe_desc->event_desc[i]->name, name, |
64 | LTTNG_UST_SYM_NAME_LEN - 1)) | |
df854e41 | 65 | return probe_desc->event_desc[i]; |
ce5aef0b MD |
66 | } |
67 | } | |
68 | return NULL; | |
69 | } | |
70 | ||
71 | int ltt_probe_register(struct lttng_probe_desc *desc) | |
72 | { | |
df854e41 | 73 | struct lttng_probe_desc *iter; |
ce5aef0b MD |
74 | int ret = 0; |
75 | int i; | |
76 | ||
17dfb34b | 77 | ust_lock(); |
df854e41 MD |
78 | if (find_provider(desc->provider)) { |
79 | ret = -EEXIST; | |
80 | goto end; | |
81 | } | |
ce5aef0b MD |
82 | /* |
83 | * TODO: This is O(N^2). Turn into a hash table when probe registration | |
84 | * overhead becomes an issue. | |
85 | */ | |
86 | for (i = 0; i < desc->nr_events; i++) { | |
df854e41 | 87 | if (find_event(desc->event_desc[i]->name)) { |
ce5aef0b MD |
88 | ret = -EEXIST; |
89 | goto end; | |
90 | } | |
91 | } | |
df854e41 MD |
92 | |
93 | /* | |
94 | * We sort the providers by struct lttng_probe_desc pointer | |
95 | * address. | |
96 | */ | |
97 | cds_list_for_each_entry_reverse(iter, &probe_list, head) { | |
98 | BUG_ON(iter == desc); /* Should never be in the list twice */ | |
99 | if (iter < desc) { | |
100 | /* We belong to the location right after iter. */ | |
101 | cds_list_add(&desc->head, &iter->head); | |
102 | goto desc_added; | |
103 | } | |
104 | } | |
105 | /* We should be added at the head of the list */ | |
8d8a24c8 | 106 | cds_list_add(&desc->head, &probe_list); |
df854e41 | 107 | desc_added: |
e81a53af MD |
108 | DBG("just registered probe %s containing %u events", |
109 | desc->provider, desc->nr_events); | |
8165c8da MD |
110 | /* |
111 | * fix the events awaiting probe load. | |
112 | */ | |
113 | for (i = 0; i < desc->nr_events; i++) { | |
df854e41 | 114 | ret = pending_probe_fix_events(desc->event_desc[i]); |
8165c8da MD |
115 | assert(!ret); |
116 | } | |
ce5aef0b | 117 | end: |
17dfb34b | 118 | ust_unlock(); |
ce5aef0b MD |
119 | return ret; |
120 | } | |
ce5aef0b MD |
121 | |
122 | void ltt_probe_unregister(struct lttng_probe_desc *desc) | |
123 | { | |
17dfb34b | 124 | ust_lock(); |
8d8a24c8 | 125 | cds_list_del(&desc->head); |
e81a53af | 126 | DBG("just unregistered probe %s", desc->provider); |
17dfb34b | 127 | ust_unlock(); |
ce5aef0b | 128 | } |
ce5aef0b | 129 | |
8165c8da MD |
130 | /* |
131 | * called with UST lock held. | |
132 | */ | |
ce5aef0b MD |
133 | const struct lttng_event_desc *ltt_event_get(const char *name) |
134 | { | |
135 | const struct lttng_event_desc *event; | |
ce5aef0b | 136 | |
ce5aef0b | 137 | event = find_event(name); |
ce5aef0b MD |
138 | if (!event) |
139 | return NULL; | |
ce5aef0b MD |
140 | return event; |
141 | } | |
ce5aef0b MD |
142 | |
143 | void ltt_event_put(const struct lttng_event_desc *event) | |
144 | { | |
ce5aef0b | 145 | } |
c8fcf224 MD |
146 | |
147 | void ltt_probes_prune_event_list(struct lttng_ust_tracepoint_list *list) | |
148 | { | |
149 | struct tp_list_entry *list_entry, *tmp; | |
150 | ||
151 | cds_list_for_each_entry_safe(list_entry, tmp, &list->head, head) { | |
152 | cds_list_del(&list_entry->head); | |
153 | free(list_entry); | |
154 | } | |
155 | } | |
156 | ||
157 | /* | |
158 | * called with UST lock held. | |
159 | */ | |
160 | int ltt_probes_get_event_list(struct lttng_ust_tracepoint_list *list) | |
161 | { | |
162 | struct lttng_probe_desc *probe_desc; | |
163 | int i; | |
164 | ||
165 | CDS_INIT_LIST_HEAD(&list->head); | |
166 | cds_list_for_each_entry(probe_desc, &probe_list, head) { | |
167 | for (i = 0; i < probe_desc->nr_events; i++) { | |
168 | struct tp_list_entry *list_entry; | |
169 | ||
170 | list_entry = zmalloc(sizeof(*list_entry)); | |
171 | if (!list_entry) | |
172 | goto err_nomem; | |
173 | cds_list_add(&list_entry->head, &list->head); | |
174 | strncpy(list_entry->tp.name, | |
175 | probe_desc->event_desc[i]->name, | |
176 | LTTNG_UST_SYM_NAME_LEN); | |
177 | list_entry->tp.name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; | |
178 | if (!probe_desc->event_desc[i]->loglevel) { | |
179 | list_entry->tp.loglevel[0] = '\0'; | |
180 | list_entry->tp.loglevel_value = 0; | |
181 | } else { | |
182 | strncpy(list_entry->tp.loglevel, | |
183 | (*probe_desc->event_desc[i]->loglevel)->identifier, | |
184 | LTTNG_UST_SYM_NAME_LEN); | |
185 | list_entry->tp.loglevel[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; | |
186 | list_entry->tp.loglevel_value = | |
187 | (*probe_desc->event_desc[i]->loglevel)->value; | |
188 | } | |
189 | } | |
190 | } | |
191 | if (cds_list_empty(&list->head)) | |
192 | list->iter = NULL; | |
193 | else | |
194 | list->iter = | |
195 | cds_list_first_entry(&list->head, struct tp_list_entry, head); | |
196 | return 0; | |
197 | ||
198 | err_nomem: | |
199 | ltt_probes_prune_event_list(list); | |
200 | return -ENOMEM; | |
201 | } | |
202 | ||
203 | /* | |
204 | * Return current iteration position, advance internal iterator to next. | |
205 | * Return NULL if end of list. | |
206 | */ | |
207 | struct lttng_ust_tracepoint_iter * | |
208 | lttng_ust_tracepoint_list_get_iter_next(struct lttng_ust_tracepoint_list *list) | |
209 | { | |
210 | struct tp_list_entry *entry; | |
211 | ||
212 | if (!list->iter) | |
213 | return NULL; | |
214 | entry = list->iter; | |
215 | if (entry->head.next == &list->head) | |
216 | list->iter = NULL; | |
217 | else | |
218 | list->iter = cds_list_entry(entry->head.next, | |
219 | struct tp_list_entry, head); | |
220 | return &entry->tp; | |
221 | } | |
1f18504e MD |
222 | |
223 | /* | |
224 | * Get loglevel if the loglevel is present in the loglevel hash table. | |
225 | * Must be called with ust lock held. | |
226 | * Returns NULL if not present. | |
227 | */ | |
228 | struct loglevel_entry *get_loglevel(const char *name) | |
229 | { | |
230 | struct cds_hlist_head *head; | |
231 | struct cds_hlist_node *node; | |
232 | struct loglevel_entry *e; | |
ff412fb5 MD |
233 | size_t name_len = strlen(name); |
234 | uint32_t hash; | |
1f18504e | 235 | |
ff412fb5 MD |
236 | if (name_len > LTTNG_UST_SYM_NAME_LEN - 1) { |
237 | WARN("Truncating loglevel name %s which exceeds size limits of %u chars", name, LTTNG_UST_SYM_NAME_LEN - 1); | |
238 | name_len = LTTNG_UST_SYM_NAME_LEN - 1; | |
239 | } | |
240 | hash = jhash(name, name_len, 0); | |
1f18504e MD |
241 | head = &loglevel_table[hash & (LOGLEVEL_TABLE_SIZE - 1)]; |
242 | cds_hlist_for_each_entry(e, node, head, hlist) { | |
ff412fb5 | 243 | if (!strncmp(name, e->name, LTTNG_UST_SYM_NAME_LEN - 1)) |
1f18504e MD |
244 | return e; |
245 | } | |
246 | return NULL; | |
247 | } | |
248 | ||
48740cab MD |
249 | struct loglevel_entry *get_loglevel_value(int64_t value) |
250 | { | |
251 | char name[LTTNG_UST_SYM_NAME_LEN]; | |
252 | int ret; | |
253 | ||
254 | ret = snprintf(name, LTTNG_UST_SYM_NAME_LEN, "%lld", (long long) value); | |
255 | if (ret < 0) | |
256 | return NULL; | |
257 | return get_loglevel(name); | |
258 | } | |
259 | ||
1f18504e MD |
260 | /* |
261 | * marshall all probes/all events and create those that fit the | |
262 | * loglevel. Add them to the events list as created. | |
263 | */ | |
264 | static | |
574a6217 MD |
265 | void _probes_create_loglevel_events(struct loglevel_entry *entry, |
266 | struct session_loglevel *loglevel) | |
1f18504e MD |
267 | { |
268 | struct lttng_probe_desc *probe_desc; | |
7ee3cc56 | 269 | struct lttng_ust_event event_param; |
1f18504e MD |
270 | int i; |
271 | ||
272 | cds_list_for_each_entry(probe_desc, &probe_list, head) { | |
273 | for (i = 0; i < probe_desc->nr_events; i++) { | |
274 | const struct tracepoint_loglevel_entry *ev_ll; | |
7ee3cc56 | 275 | const struct lttng_event_desc *event_desc; |
5e87eb4a | 276 | int match = 0; |
1f18504e | 277 | |
7ee3cc56 MD |
278 | event_desc = probe_desc->event_desc[i]; |
279 | if (!(event_desc->loglevel)) | |
1f18504e | 280 | continue; |
7ee3cc56 | 281 | ev_ll = *event_desc->loglevel; |
3431ca3e | 282 | if (isdigit(entry->name[0])) { |
48740cab MD |
283 | if (atoll(entry->name) == ev_ll->value) { |
284 | match = 1; | |
285 | } | |
ff412fb5 MD |
286 | } else if (!strncmp(ev_ll->identifier, entry->name, |
287 | LTTNG_UST_SYM_NAME_LEN - 1)) { | |
48740cab MD |
288 | match = 1; |
289 | } | |
290 | ||
291 | if (match) { | |
1f18504e MD |
292 | struct ltt_event *ev; |
293 | int ret; | |
294 | ||
7ee3cc56 MD |
295 | memcpy(&event_param, &loglevel->event_param, |
296 | sizeof(event_param)); | |
297 | memcpy(event_param.name, | |
298 | event_desc->name, | |
299 | sizeof(event_param.name)); | |
1f18504e MD |
300 | /* create event */ |
301 | ret = ltt_event_create(loglevel->chan, | |
7ee3cc56 | 302 | &event_param, NULL, |
1f18504e | 303 | &ev); |
7ee3cc56 MD |
304 | if (ret) { |
305 | DBG("Error creating event"); | |
1f18504e | 306 | continue; |
7ee3cc56 | 307 | } |
1f18504e MD |
308 | cds_list_add(&ev->loglevel_list, |
309 | &loglevel->events); | |
310 | } | |
1f18504e MD |
311 | } |
312 | } | |
313 | } | |
314 | ||
315 | /* | |
316 | * Add the loglevel to the loglevel hash table. Must be called with | |
317 | * ust lock held. | |
318 | */ | |
574a6217 | 319 | struct session_loglevel *add_loglevel(const char *name, |
1f18504e MD |
320 | struct ltt_channel *chan, |
321 | struct lttng_ust_event *event_param) | |
322 | { | |
323 | struct cds_hlist_head *head; | |
324 | struct cds_hlist_node *node; | |
325 | struct loglevel_entry *e; | |
574a6217 | 326 | struct session_loglevel *sl; |
574a6217 | 327 | int found = 0; |
ff412fb5 MD |
328 | size_t name_len = strlen(name); |
329 | uint32_t hash; | |
1f18504e | 330 | |
ff412fb5 MD |
331 | if (name_len > LTTNG_UST_SYM_NAME_LEN - 1) { |
332 | WARN("Truncating loglevel name %s which exceeds size limits of %u chars", name, LTTNG_UST_SYM_NAME_LEN - 1); | |
333 | name_len = LTTNG_UST_SYM_NAME_LEN - 1; | |
334 | } | |
335 | hash = jhash(name, name_len, 0); | |
574a6217 | 336 | /* loglevel entry */ |
1f18504e MD |
337 | head = &loglevel_table[hash & (LOGLEVEL_TABLE_SIZE - 1)]; |
338 | cds_hlist_for_each_entry(e, node, head, hlist) { | |
ff412fb5 | 339 | if (!strncmp(name, e->name, LTTNG_UST_SYM_NAME_LEN - 1)) { |
574a6217 MD |
340 | found = 1; |
341 | break; | |
342 | } | |
343 | } | |
344 | ||
345 | if (!found) { | |
346 | /* | |
347 | * Using zmalloc here to allocate a variable length element. Could | |
348 | * cause some memory fragmentation if overused. | |
349 | */ | |
ff412fb5 | 350 | e = zmalloc(sizeof(struct loglevel_entry) + name_len + 1); |
574a6217 MD |
351 | if (!e) |
352 | return ERR_PTR(-ENOMEM); | |
ff412fb5 MD |
353 | memcpy(&e->name[0], name, name_len + 1); |
354 | e->name[name_len] = '\0'; | |
574a6217 | 355 | cds_hlist_add_head(&e->hlist, head); |
d5201d38 | 356 | CDS_INIT_LIST_HEAD(&e->session_list); |
574a6217 MD |
357 | } |
358 | ||
359 | /* session loglevel */ | |
360 | cds_list_for_each_entry(sl, &e->session_list, session_list) { | |
361 | if (chan == sl->chan) { | |
362 | DBG("loglevel %s busy for this channel", name); | |
1f18504e MD |
363 | return ERR_PTR(-EEXIST); /* Already there */ |
364 | } | |
365 | } | |
574a6217 MD |
366 | sl = zmalloc(sizeof(struct session_loglevel)); |
367 | if (!sl) | |
1f18504e | 368 | return ERR_PTR(-ENOMEM); |
574a6217 MD |
369 | sl->chan = chan; |
370 | sl->enabled = 1; | |
371 | memcpy(&sl->event_param, event_param, sizeof(sl->event_param)); | |
d5201d38 | 372 | sl->event_param.instrumentation = LTTNG_UST_TRACEPOINT; |
574a6217 MD |
373 | CDS_INIT_LIST_HEAD(&sl->events); |
374 | cds_list_add(&sl->list, &chan->session->loglevels); | |
375 | cds_list_add(&sl->session_list, &e->session_list); | |
2981744d | 376 | sl->entry = e; |
574a6217 MD |
377 | _probes_create_loglevel_events(e, sl); |
378 | return sl; | |
1f18504e MD |
379 | } |
380 | ||
381 | /* | |
382 | * Remove the loglevel from the loglevel hash table. Must be called with | |
383 | * ust_lock held. Only called at session teardown. | |
384 | */ | |
574a6217 | 385 | void _remove_loglevel(struct session_loglevel *loglevel) |
1f18504e MD |
386 | { |
387 | struct ltt_event *ev, *tmp; | |
388 | ||
389 | /* | |
390 | * Just remove the events owned (for enable/disable) by this | |
391 | * loglevel from the list. The session teardown will take care | |
392 | * of freeing the event memory. | |
393 | */ | |
66221151 MD |
394 | cds_list_for_each_entry_safe(ev, tmp, &loglevel->events, |
395 | loglevel_list) { | |
396 | cds_list_del(&ev->loglevel_list); | |
1f18504e | 397 | } |
574a6217 | 398 | cds_list_del(&loglevel->session_list); |
1f18504e | 399 | cds_list_del(&loglevel->list); |
574a6217 MD |
400 | if (cds_list_empty(&loglevel->entry->session_list)) { |
401 | cds_hlist_del(&loglevel->entry->hlist); | |
402 | free(loglevel->entry); | |
403 | } | |
1f18504e MD |
404 | free(loglevel); |
405 | } | |
406 | ||
574a6217 | 407 | int ltt_loglevel_enable(struct session_loglevel *loglevel) |
1f18504e MD |
408 | { |
409 | struct ltt_event *ev; | |
410 | int ret; | |
411 | ||
412 | if (loglevel->enabled) | |
413 | return -EEXIST; | |
66221151 | 414 | cds_list_for_each_entry(ev, &loglevel->events, loglevel_list) { |
1f18504e MD |
415 | ret = ltt_event_enable(ev); |
416 | if (ret) { | |
417 | DBG("Error: enable error.\n"); | |
418 | return ret; | |
419 | } | |
420 | } | |
421 | loglevel->enabled = 1; | |
422 | return 0; | |
423 | } | |
424 | ||
574a6217 | 425 | int ltt_loglevel_disable(struct session_loglevel *loglevel) |
1f18504e MD |
426 | { |
427 | struct ltt_event *ev; | |
428 | int ret; | |
429 | ||
430 | if (!loglevel->enabled) | |
431 | return -EEXIST; | |
66221151 | 432 | cds_list_for_each_entry(ev, &loglevel->events, loglevel_list) { |
1f18504e MD |
433 | ret = ltt_event_disable(ev); |
434 | if (ret) { | |
435 | DBG("Error: disable error.\n"); | |
436 | return ret; | |
437 | } | |
438 | } | |
439 | loglevel->enabled = 0; | |
440 | return 0; | |
441 | } | |
e6c12e3d MD |
442 | |
443 | /* WILDCARDS */ | |
444 | ||
445 | /* | |
446 | * Return wildcard for a given event name if the event name match the | |
447 | * one of the wildcards. | |
448 | * Must be called with ust lock held. | |
449 | * Returns NULL if not present. | |
450 | */ | |
451 | struct wildcard_entry *match_wildcard(const char *name) | |
452 | { | |
453 | struct wildcard_entry *e; | |
454 | ||
455 | cds_list_for_each_entry(e, &wildcard_list, list) { | |
456 | /* If only contain '*' */ | |
457 | if (strlen(e->name) == 1) | |
458 | return e; | |
459 | /* Compare excluding final '*' */ | |
460 | if (!strncmp(name, e->name, strlen(e->name) - 1)) | |
461 | return e; | |
462 | } | |
463 | return NULL; | |
464 | } | |
465 | ||
466 | /* | |
467 | * marshall all probes/all events and create those that fit the | |
468 | * wildcard. Add them to the events list as created. | |
469 | */ | |
470 | static | |
471 | void _probes_create_wildcard_events(struct wildcard_entry *entry, | |
472 | struct session_wildcard *wildcard) | |
473 | { | |
474 | struct lttng_probe_desc *probe_desc; | |
475 | struct lttng_ust_event event_param; | |
476 | int i; | |
477 | ||
478 | cds_list_for_each_entry(probe_desc, &probe_list, head) { | |
479 | for (i = 0; i < probe_desc->nr_events; i++) { | |
480 | const struct lttng_event_desc *event_desc; | |
481 | int match = 0; | |
482 | ||
483 | event_desc = probe_desc->event_desc[i]; | |
484 | /* compare excluding final '*' */ | |
485 | assert(strlen(entry->name) > 0); | |
486 | if (strcmp(event_desc->name, "lttng_ust:metadata") | |
487 | && (strlen(entry->name) == 1 | |
488 | || !strncmp(event_desc->name, entry->name, | |
489 | strlen(entry->name) - 1))) { | |
490 | match = 1; | |
491 | } | |
492 | if (match) { | |
493 | struct ltt_event *ev; | |
494 | int ret; | |
495 | ||
496 | memcpy(&event_param, &wildcard->event_param, | |
497 | sizeof(event_param)); | |
498 | memcpy(event_param.name, | |
499 | event_desc->name, | |
500 | sizeof(event_param.name)); | |
501 | /* create event */ | |
502 | ret = ltt_event_create(wildcard->chan, | |
503 | &event_param, NULL, | |
504 | &ev); | |
505 | if (ret) { | |
506 | DBG("Error creating event"); | |
507 | continue; | |
508 | } | |
509 | cds_list_add(&ev->wildcard_list, | |
510 | &wildcard->events); | |
511 | } | |
512 | } | |
513 | } | |
514 | } | |
515 | ||
516 | /* | |
517 | * Add the wildcard to the wildcard hash table. Must be called with | |
518 | * ust lock held. | |
519 | */ | |
520 | struct session_wildcard *add_wildcard(const char *name, | |
521 | struct ltt_channel *chan, | |
522 | struct lttng_ust_event *event_param) | |
523 | { | |
524 | struct wildcard_entry *e; | |
525 | struct session_wildcard *sw; | |
526 | size_t name_len = strlen(name) + 1; | |
527 | int found = 0; | |
528 | ||
529 | /* wildcard entry */ | |
530 | cds_list_for_each_entry(e, &wildcard_list, list) { | |
ff412fb5 | 531 | if (!strncmp(name, e->name, LTTNG_UST_SYM_NAME_LEN - 1)) { |
e6c12e3d MD |
532 | found = 1; |
533 | break; | |
534 | } | |
535 | } | |
536 | ||
537 | if (!found) { | |
538 | /* | |
539 | * Using zmalloc here to allocate a variable length element. Could | |
540 | * cause some memory fragmentation if overused. | |
541 | */ | |
542 | e = zmalloc(sizeof(struct wildcard_entry) + name_len); | |
543 | if (!e) | |
544 | return ERR_PTR(-ENOMEM); | |
545 | memcpy(&e->name[0], name, name_len); | |
546 | cds_list_add(&e->list, &wildcard_list); | |
547 | CDS_INIT_LIST_HEAD(&e->session_list); | |
548 | } | |
549 | ||
550 | /* session wildcard */ | |
551 | cds_list_for_each_entry(sw, &e->session_list, session_list) { | |
552 | if (chan == sw->chan) { | |
553 | DBG("wildcard %s busy for this channel", name); | |
554 | return ERR_PTR(-EEXIST); /* Already there */ | |
555 | } | |
556 | } | |
557 | sw = zmalloc(sizeof(struct session_wildcard)); | |
558 | if (!sw) | |
559 | return ERR_PTR(-ENOMEM); | |
560 | sw->chan = chan; | |
561 | sw->enabled = 1; | |
562 | memcpy(&sw->event_param, event_param, sizeof(sw->event_param)); | |
563 | sw->event_param.instrumentation = LTTNG_UST_TRACEPOINT; | |
564 | CDS_INIT_LIST_HEAD(&sw->events); | |
565 | cds_list_add(&sw->list, &chan->session->wildcards); | |
566 | cds_list_add(&sw->session_list, &e->session_list); | |
567 | sw->entry = e; | |
568 | _probes_create_wildcard_events(e, sw); | |
569 | return sw; | |
570 | } | |
571 | ||
572 | /* | |
573 | * Remove the wildcard from the wildcard hash table. Must be called with | |
574 | * ust_lock held. Only called at session teardown. | |
575 | */ | |
576 | void _remove_wildcard(struct session_wildcard *wildcard) | |
577 | { | |
578 | struct ltt_event *ev, *tmp; | |
579 | ||
580 | /* | |
581 | * Just remove the events owned (for enable/disable) by this | |
582 | * wildcard from the list. The session teardown will take care | |
583 | * of freeing the event memory. | |
584 | */ | |
66221151 MD |
585 | cds_list_for_each_entry_safe(ev, tmp, &wildcard->events, |
586 | wildcard_list) { | |
587 | cds_list_del(&ev->wildcard_list); | |
e6c12e3d MD |
588 | } |
589 | cds_list_del(&wildcard->session_list); | |
590 | cds_list_del(&wildcard->list); | |
591 | if (cds_list_empty(&wildcard->entry->session_list)) { | |
592 | cds_list_del(&wildcard->entry->list); | |
593 | free(wildcard->entry); | |
594 | } | |
595 | free(wildcard); | |
596 | } | |
597 | ||
598 | int ltt_wildcard_enable(struct session_wildcard *wildcard) | |
599 | { | |
600 | struct ltt_event *ev; | |
601 | int ret; | |
602 | ||
603 | if (wildcard->enabled) | |
604 | return -EEXIST; | |
66221151 | 605 | cds_list_for_each_entry(ev, &wildcard->events, wildcard_list) { |
e6c12e3d MD |
606 | ret = ltt_event_enable(ev); |
607 | if (ret) { | |
608 | DBG("Error: enable error.\n"); | |
609 | return ret; | |
610 | } | |
611 | } | |
612 | wildcard->enabled = 1; | |
613 | return 0; | |
614 | } | |
615 | ||
616 | int ltt_wildcard_disable(struct session_wildcard *wildcard) | |
617 | { | |
618 | struct ltt_event *ev; | |
619 | int ret; | |
620 | ||
621 | if (!wildcard->enabled) | |
622 | return -EEXIST; | |
66221151 | 623 | cds_list_for_each_entry(ev, &wildcard->events, wildcard_list) { |
e6c12e3d MD |
624 | ret = ltt_event_disable(ev); |
625 | if (ret) { | |
626 | DBG("Error: disable error.\n"); | |
627 | return ret; | |
628 | } | |
629 | } | |
630 | wildcard->enabled = 0; | |
631 | return 0; | |
632 | } |