loglevel enable fix: enable event names, not loglevel
[lttng-ust.git] / liblttng-ust / ltt-probes.c
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
11 #include <string.h>
12 #include <errno.h>
13 #include <urcu/list.h>
14 #include <urcu/hlist.h>
15 #include <lttng/ust-events.h>
16 #include <assert.h>
17 #include <helper.h>
18
19 #include "ltt-tracer-core.h"
20 #include "jhash.h"
21 #include "error.h"
22
23 /*
24 * probe list is protected by ust_lock()/ust_unlock().
25 */
26 static CDS_LIST_HEAD(probe_list);
27
28 /*
29 * Loglevel hash table, containing the active loglevels.
30 * Protected by ust lock.
31 */
32 #define LOGLEVEL_HASH_BITS 6
33 #define LOGLEVEL_TABLE_SIZE (1 << LOGLEVEL_HASH_BITS)
34 static struct cds_hlist_head loglevel_table[LOGLEVEL_TABLE_SIZE];
35
36 static
37 const struct lttng_probe_desc *find_provider(const char *provider)
38 {
39 struct lttng_probe_desc *iter;
40
41 cds_list_for_each_entry(iter, &probe_list, head) {
42 if (!strcmp(iter->provider, provider))
43 return iter;
44 }
45 return NULL;
46 }
47
48 static
49 const struct lttng_event_desc *find_event(const char *name)
50 {
51 struct lttng_probe_desc *probe_desc;
52 int i;
53
54 cds_list_for_each_entry(probe_desc, &probe_list, head) {
55 for (i = 0; i < probe_desc->nr_events; i++) {
56 if (!strcmp(probe_desc->event_desc[i]->name, name))
57 return probe_desc->event_desc[i];
58 }
59 }
60 return NULL;
61 }
62
63 int ltt_probe_register(struct lttng_probe_desc *desc)
64 {
65 struct lttng_probe_desc *iter;
66 int ret = 0;
67 int i;
68
69 ust_lock();
70 if (find_provider(desc->provider)) {
71 ret = -EEXIST;
72 goto end;
73 }
74 /*
75 * TODO: This is O(N^2). Turn into a hash table when probe registration
76 * overhead becomes an issue.
77 */
78 for (i = 0; i < desc->nr_events; i++) {
79 if (find_event(desc->event_desc[i]->name)) {
80 ret = -EEXIST;
81 goto end;
82 }
83 }
84
85 /*
86 * We sort the providers by struct lttng_probe_desc pointer
87 * address.
88 */
89 cds_list_for_each_entry_reverse(iter, &probe_list, head) {
90 BUG_ON(iter == desc); /* Should never be in the list twice */
91 if (iter < desc) {
92 /* We belong to the location right after iter. */
93 cds_list_add(&desc->head, &iter->head);
94 goto desc_added;
95 }
96 }
97 /* We should be added at the head of the list */
98 cds_list_add(&desc->head, &probe_list);
99 desc_added:
100
101 /*
102 * fix the events awaiting probe load.
103 */
104 for (i = 0; i < desc->nr_events; i++) {
105 ret = pending_probe_fix_events(desc->event_desc[i]);
106 assert(!ret);
107 }
108 end:
109 ust_unlock();
110 return ret;
111 }
112
113 void ltt_probe_unregister(struct lttng_probe_desc *desc)
114 {
115 ust_lock();
116 cds_list_del(&desc->head);
117 ust_unlock();
118 }
119
120 /*
121 * called with UST lock held.
122 */
123 const struct lttng_event_desc *ltt_event_get(const char *name)
124 {
125 const struct lttng_event_desc *event;
126
127 event = find_event(name);
128 if (!event)
129 return NULL;
130 return event;
131 }
132
133 void ltt_event_put(const struct lttng_event_desc *event)
134 {
135 }
136
137 void ltt_probes_prune_event_list(struct lttng_ust_tracepoint_list *list)
138 {
139 struct tp_list_entry *list_entry, *tmp;
140
141 cds_list_for_each_entry_safe(list_entry, tmp, &list->head, head) {
142 cds_list_del(&list_entry->head);
143 free(list_entry);
144 }
145 }
146
147 /*
148 * called with UST lock held.
149 */
150 int ltt_probes_get_event_list(struct lttng_ust_tracepoint_list *list)
151 {
152 struct lttng_probe_desc *probe_desc;
153 int i;
154
155 CDS_INIT_LIST_HEAD(&list->head);
156 cds_list_for_each_entry(probe_desc, &probe_list, head) {
157 for (i = 0; i < probe_desc->nr_events; i++) {
158 struct tp_list_entry *list_entry;
159
160 list_entry = zmalloc(sizeof(*list_entry));
161 if (!list_entry)
162 goto err_nomem;
163 cds_list_add(&list_entry->head, &list->head);
164 strncpy(list_entry->tp.name,
165 probe_desc->event_desc[i]->name,
166 LTTNG_UST_SYM_NAME_LEN);
167 list_entry->tp.name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0';
168 if (!probe_desc->event_desc[i]->loglevel) {
169 list_entry->tp.loglevel[0] = '\0';
170 list_entry->tp.loglevel_value = 0;
171 } else {
172 strncpy(list_entry->tp.loglevel,
173 (*probe_desc->event_desc[i]->loglevel)->identifier,
174 LTTNG_UST_SYM_NAME_LEN);
175 list_entry->tp.loglevel[LTTNG_UST_SYM_NAME_LEN - 1] = '\0';
176 list_entry->tp.loglevel_value =
177 (*probe_desc->event_desc[i]->loglevel)->value;
178 }
179 }
180 }
181 if (cds_list_empty(&list->head))
182 list->iter = NULL;
183 else
184 list->iter =
185 cds_list_first_entry(&list->head, struct tp_list_entry, head);
186 return 0;
187
188 err_nomem:
189 ltt_probes_prune_event_list(list);
190 return -ENOMEM;
191 }
192
193 /*
194 * Return current iteration position, advance internal iterator to next.
195 * Return NULL if end of list.
196 */
197 struct lttng_ust_tracepoint_iter *
198 lttng_ust_tracepoint_list_get_iter_next(struct lttng_ust_tracepoint_list *list)
199 {
200 struct tp_list_entry *entry;
201
202 if (!list->iter)
203 return NULL;
204 entry = list->iter;
205 if (entry->head.next == &list->head)
206 list->iter = NULL;
207 else
208 list->iter = cds_list_entry(entry->head.next,
209 struct tp_list_entry, head);
210 return &entry->tp;
211 }
212
213 /*
214 * Get loglevel if the loglevel is present in the loglevel hash table.
215 * Must be called with ust lock held.
216 * Returns NULL if not present.
217 */
218 struct loglevel_entry *get_loglevel(const char *name)
219 {
220 struct cds_hlist_head *head;
221 struct cds_hlist_node *node;
222 struct loglevel_entry *e;
223 uint32_t hash = jhash(name, strlen(name), 0);
224
225 head = &loglevel_table[hash & (LOGLEVEL_TABLE_SIZE - 1)];
226 cds_hlist_for_each_entry(e, node, head, hlist) {
227 if (!strcmp(name, e->name))
228 return e;
229 }
230 return NULL;
231 }
232
233 /*
234 * marshall all probes/all events and create those that fit the
235 * loglevel. Add them to the events list as created.
236 */
237 static
238 void _probes_create_loglevel_events(struct loglevel_entry *entry,
239 struct session_loglevel *loglevel)
240 {
241 struct lttng_probe_desc *probe_desc;
242 struct lttng_ust_event event_param;
243 int i;
244
245 cds_list_for_each_entry(probe_desc, &probe_list, head) {
246 for (i = 0; i < probe_desc->nr_events; i++) {
247 const struct tracepoint_loglevel_entry *ev_ll;
248 const struct lttng_event_desc *event_desc;
249
250 event_desc = probe_desc->event_desc[i];
251 if (!(event_desc->loglevel))
252 continue;
253 ev_ll = *event_desc->loglevel;
254 if (!strcmp(ev_ll->identifier, entry->name)) {
255 struct ltt_event *ev;
256 int ret;
257
258 memcpy(&event_param, &loglevel->event_param,
259 sizeof(event_param));
260 memcpy(event_param.name,
261 event_desc->name,
262 sizeof(event_param.name));
263 /* create event */
264 ret = ltt_event_create(loglevel->chan,
265 &event_param, NULL,
266 &ev);
267 if (ret) {
268 DBG("Error creating event");
269 continue;
270 }
271 cds_list_add(&ev->loglevel_list,
272 &loglevel->events);
273 }
274 }
275 }
276 }
277
278 /*
279 * Add the loglevel to the loglevel hash table. Must be called with
280 * ust lock held.
281 */
282 struct session_loglevel *add_loglevel(const char *name,
283 struct ltt_channel *chan,
284 struct lttng_ust_event *event_param)
285 {
286 struct cds_hlist_head *head;
287 struct cds_hlist_node *node;
288 struct loglevel_entry *e;
289 struct session_loglevel *sl;
290 size_t name_len = strlen(name) + 1;
291 uint32_t hash = jhash(name, name_len-1, 0);
292 int found = 0;
293
294 /* loglevel entry */
295 head = &loglevel_table[hash & (LOGLEVEL_TABLE_SIZE - 1)];
296 cds_hlist_for_each_entry(e, node, head, hlist) {
297 if (!strcmp(name, e->name)) {
298 found = 1;
299 break;
300 }
301 }
302
303 if (!found) {
304 /*
305 * Using zmalloc here to allocate a variable length element. Could
306 * cause some memory fragmentation if overused.
307 */
308 e = zmalloc(sizeof(struct loglevel_entry) + name_len);
309 if (!e)
310 return ERR_PTR(-ENOMEM);
311 memcpy(&e->name[0], name, name_len);
312 cds_hlist_add_head(&e->hlist, head);
313 CDS_INIT_LIST_HEAD(&e->session_list);
314 }
315
316 /* session loglevel */
317 cds_list_for_each_entry(sl, &e->session_list, session_list) {
318 if (chan == sl->chan) {
319 DBG("loglevel %s busy for this channel", name);
320 return ERR_PTR(-EEXIST); /* Already there */
321 }
322 }
323 sl = zmalloc(sizeof(struct session_loglevel));
324 if (!sl)
325 return ERR_PTR(-ENOMEM);
326 sl->chan = chan;
327 sl->enabled = 1;
328 memcpy(&sl->event_param, event_param, sizeof(sl->event_param));
329 sl->event_param.instrumentation = LTTNG_UST_TRACEPOINT;
330 CDS_INIT_LIST_HEAD(&sl->events);
331 cds_list_add(&sl->list, &chan->session->loglevels);
332 cds_list_add(&sl->session_list, &e->session_list);
333 sl->entry = e;
334 _probes_create_loglevel_events(e, sl);
335 return sl;
336 }
337
338 /*
339 * Remove the loglevel from the loglevel hash table. Must be called with
340 * ust_lock held. Only called at session teardown.
341 */
342 void _remove_loglevel(struct session_loglevel *loglevel)
343 {
344 struct ltt_event *ev, *tmp;
345
346 /*
347 * Just remove the events owned (for enable/disable) by this
348 * loglevel from the list. The session teardown will take care
349 * of freeing the event memory.
350 */
351 cds_list_for_each_entry_safe(ev, tmp, &loglevel->events, list) {
352 cds_list_del(&ev->list);
353 }
354 cds_list_del(&loglevel->session_list);
355 cds_list_del(&loglevel->list);
356 if (cds_list_empty(&loglevel->entry->session_list)) {
357 cds_hlist_del(&loglevel->entry->hlist);
358 free(loglevel->entry);
359 }
360 free(loglevel);
361 }
362
363 int ltt_loglevel_enable(struct session_loglevel *loglevel)
364 {
365 struct ltt_event *ev;
366 int ret;
367
368 if (loglevel->enabled)
369 return -EEXIST;
370 cds_list_for_each_entry(ev, &loglevel->events, list) {
371 ret = ltt_event_enable(ev);
372 if (ret) {
373 DBG("Error: enable error.\n");
374 return ret;
375 }
376 }
377 loglevel->enabled = 1;
378 return 0;
379 }
380
381 int ltt_loglevel_disable(struct session_loglevel *loglevel)
382 {
383 struct ltt_event *ev;
384 int ret;
385
386 if (!loglevel->enabled)
387 return -EEXIST;
388 cds_list_for_each_entry(ev, &loglevel->events, list) {
389 ret = ltt_event_disable(ev);
390 if (ret) {
391 DBG("Error: disable error.\n");
392 return ret;
393 }
394 }
395 loglevel->enabled = 0;
396 return 0;
397 }
This page took 0.043587 seconds and 5 git commands to generate.