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