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