Add loglevel enable/disable support
[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 *loglevel)
239 {
240 struct lttng_probe_desc *probe_desc;
241 int i;
242
243 cds_list_for_each_entry(probe_desc, &probe_list, head) {
244 for (i = 0; i < probe_desc->nr_events; i++) {
245 const struct tracepoint_loglevel_entry *ev_ll;
246
247 if (!(probe_desc->event_desc[i]->loglevel))
248 continue;
249 ev_ll = *probe_desc->event_desc[i]->loglevel;
250 if (!strcmp(ev_ll->identifier, loglevel->name)) {
251 struct ltt_event *ev;
252 int ret;
253
254 /* create event */
255 ret = ltt_event_create(loglevel->chan,
256 &loglevel->event_param, NULL,
257 &ev);
258 /*
259 * TODO: report error.
260 */
261 if (ret)
262 continue;
263 cds_list_add(&ev->loglevel_list,
264 &loglevel->events);
265 }
266
267 }
268 }
269 }
270
271 /*
272 * Add the loglevel to the loglevel hash table. Must be called with
273 * ust lock held.
274 */
275 struct loglevel_entry *add_loglevel(const char *name,
276 struct ltt_channel *chan,
277 struct lttng_ust_event *event_param)
278 {
279 struct cds_hlist_head *head;
280 struct cds_hlist_node *node;
281 struct loglevel_entry *e;
282 size_t name_len = strlen(name) + 1;
283 uint32_t hash = jhash(name, name_len-1, 0);
284
285 head = &loglevel_table[hash & (LOGLEVEL_TABLE_SIZE - 1)];
286 cds_hlist_for_each_entry(e, node, head, hlist) {
287 if (!strcmp(name, e->name)) {
288 DBG("loglevel %s busy", name);
289 return ERR_PTR(-EEXIST); /* Already there */
290 }
291 }
292 /*
293 * Using zmalloc here to allocate a variable length element. Could
294 * cause some memory fragmentation if overused.
295 */
296 e = zmalloc(sizeof(struct loglevel_entry) + name_len);
297 if (!e)
298 return ERR_PTR(-ENOMEM);
299 memcpy(&e->name[0], name, name_len);
300 e->chan = chan;
301 e->enabled = 1;
302 memcpy(&e->event_param, event_param, sizeof(e->event_param));
303 cds_hlist_add_head(&e->hlist, head);
304 CDS_INIT_LIST_HEAD(&e->events);
305 _probes_create_loglevel_events(e);
306 cds_list_add(&e->list, &chan->session->loglevels);
307 return e;
308 }
309
310 /*
311 * Remove the loglevel from the loglevel hash table. Must be called with
312 * ust_lock held. Only called at session teardown.
313 */
314 void _remove_loglevel(struct loglevel_entry *loglevel)
315 {
316 struct ltt_event *ev, *tmp;
317
318 /*
319 * Just remove the events owned (for enable/disable) by this
320 * loglevel from the list. The session teardown will take care
321 * of freeing the event memory.
322 */
323 cds_list_for_each_entry_safe(ev, tmp, &loglevel->events, list) {
324 cds_list_del(&ev->list);
325 }
326 cds_hlist_del(&loglevel->hlist);
327 cds_list_del(&loglevel->list);
328 free(loglevel);
329 }
330
331 int ltt_loglevel_enable(struct loglevel_entry *loglevel)
332 {
333 struct ltt_event *ev;
334 int ret;
335
336 if (loglevel->enabled)
337 return -EEXIST;
338 cds_list_for_each_entry(ev, &loglevel->events, list) {
339 ret = ltt_event_enable(ev);
340 if (ret) {
341 DBG("Error: enable error.\n");
342 return ret;
343 }
344 }
345 loglevel->enabled = 1;
346 return 0;
347 }
348
349 int ltt_loglevel_disable(struct loglevel_entry *loglevel)
350 {
351 struct ltt_event *ev;
352 int ret;
353
354 if (!loglevel->enabled)
355 return -EEXIST;
356 cds_list_for_each_entry(ev, &loglevel->events, list) {
357 ret = ltt_event_disable(ev);
358 if (ret) {
359 DBG("Error: disable error.\n");
360 return ret;
361 }
362 }
363 loglevel->enabled = 0;
364 return 0;
365 }
This page took 0.035704 seconds and 4 git commands to generate.