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> |
ce5aef0b | 18 | |
8165c8da | 19 | #include "ltt-tracer-core.h" |
1f18504e MD |
20 | #include "jhash.h" |
21 | #include "error.h" | |
8165c8da MD |
22 | |
23 | /* | |
17dfb34b | 24 | * probe list is protected by ust_lock()/ust_unlock(). |
8165c8da | 25 | */ |
8d8a24c8 | 26 | static CDS_LIST_HEAD(probe_list); |
ce5aef0b | 27 | |
1f18504e MD |
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 | ||
df854e41 MD |
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 | ||
ce5aef0b MD |
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 | ||
8d8a24c8 | 54 | cds_list_for_each_entry(probe_desc, &probe_list, head) { |
ce5aef0b | 55 | for (i = 0; i < probe_desc->nr_events; i++) { |
df854e41 MD |
56 | if (!strcmp(probe_desc->event_desc[i]->name, name)) |
57 | return probe_desc->event_desc[i]; | |
ce5aef0b MD |
58 | } |
59 | } | |
60 | return NULL; | |
61 | } | |
62 | ||
63 | int ltt_probe_register(struct lttng_probe_desc *desc) | |
64 | { | |
df854e41 | 65 | struct lttng_probe_desc *iter; |
ce5aef0b MD |
66 | int ret = 0; |
67 | int i; | |
68 | ||
17dfb34b | 69 | ust_lock(); |
df854e41 MD |
70 | if (find_provider(desc->provider)) { |
71 | ret = -EEXIST; | |
72 | goto end; | |
73 | } | |
ce5aef0b MD |
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++) { | |
df854e41 | 79 | if (find_event(desc->event_desc[i]->name)) { |
ce5aef0b MD |
80 | ret = -EEXIST; |
81 | goto end; | |
82 | } | |
83 | } | |
df854e41 MD |
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 */ | |
8d8a24c8 | 98 | cds_list_add(&desc->head, &probe_list); |
df854e41 | 99 | desc_added: |
8165c8da MD |
100 | |
101 | /* | |
102 | * fix the events awaiting probe load. | |
103 | */ | |
104 | for (i = 0; i < desc->nr_events; i++) { | |
df854e41 | 105 | ret = pending_probe_fix_events(desc->event_desc[i]); |
8165c8da MD |
106 | assert(!ret); |
107 | } | |
ce5aef0b | 108 | end: |
17dfb34b | 109 | ust_unlock(); |
ce5aef0b MD |
110 | return ret; |
111 | } | |
ce5aef0b MD |
112 | |
113 | void ltt_probe_unregister(struct lttng_probe_desc *desc) | |
114 | { | |
17dfb34b | 115 | ust_lock(); |
8d8a24c8 | 116 | cds_list_del(&desc->head); |
17dfb34b | 117 | ust_unlock(); |
ce5aef0b | 118 | } |
ce5aef0b | 119 | |
8165c8da MD |
120 | /* |
121 | * called with UST lock held. | |
122 | */ | |
ce5aef0b MD |
123 | const struct lttng_event_desc *ltt_event_get(const char *name) |
124 | { | |
125 | const struct lttng_event_desc *event; | |
ce5aef0b | 126 | |
ce5aef0b | 127 | event = find_event(name); |
ce5aef0b MD |
128 | if (!event) |
129 | return NULL; | |
ce5aef0b MD |
130 | return event; |
131 | } | |
ce5aef0b MD |
132 | |
133 | void ltt_event_put(const struct lttng_event_desc *event) | |
134 | { | |
ce5aef0b | 135 | } |
c8fcf224 MD |
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 | } | |
1f18504e MD |
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 | |
574a6217 MD |
238 | void _probes_create_loglevel_events(struct loglevel_entry *entry, |
239 | struct session_loglevel *loglevel) | |
1f18504e MD |
240 | { |
241 | struct lttng_probe_desc *probe_desc; | |
242 | int i; | |
243 | ||
244 | cds_list_for_each_entry(probe_desc, &probe_list, head) { | |
245 | for (i = 0; i < probe_desc->nr_events; i++) { | |
246 | const struct tracepoint_loglevel_entry *ev_ll; | |
247 | ||
248 | if (!(probe_desc->event_desc[i]->loglevel)) | |
249 | continue; | |
250 | ev_ll = *probe_desc->event_desc[i]->loglevel; | |
574a6217 | 251 | if (!strcmp(ev_ll->identifier, entry->name)) { |
1f18504e MD |
252 | struct ltt_event *ev; |
253 | int ret; | |
254 | ||
255 | /* create event */ | |
256 | ret = ltt_event_create(loglevel->chan, | |
257 | &loglevel->event_param, NULL, | |
258 | &ev); | |
259 | /* | |
260 | * TODO: report error. | |
261 | */ | |
262 | if (ret) | |
263 | continue; | |
264 | cds_list_add(&ev->loglevel_list, | |
265 | &loglevel->events); | |
266 | } | |
1f18504e MD |
267 | } |
268 | } | |
269 | } | |
270 | ||
271 | /* | |
272 | * Add the loglevel to the loglevel hash table. Must be called with | |
273 | * ust lock held. | |
274 | */ | |
574a6217 | 275 | struct session_loglevel *add_loglevel(const char *name, |
1f18504e MD |
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; | |
574a6217 | 282 | struct session_loglevel *sl; |
1f18504e MD |
283 | size_t name_len = strlen(name) + 1; |
284 | uint32_t hash = jhash(name, name_len-1, 0); | |
574a6217 | 285 | int found = 0; |
1f18504e | 286 | |
574a6217 | 287 | /* loglevel entry */ |
1f18504e MD |
288 | head = &loglevel_table[hash & (LOGLEVEL_TABLE_SIZE - 1)]; |
289 | cds_hlist_for_each_entry(e, node, head, hlist) { | |
290 | if (!strcmp(name, e->name)) { | |
574a6217 MD |
291 | found = 1; |
292 | break; | |
293 | } | |
294 | } | |
295 | ||
296 | if (!found) { | |
297 | /* | |
298 | * Using zmalloc here to allocate a variable length element. Could | |
299 | * cause some memory fragmentation if overused. | |
300 | */ | |
301 | e = zmalloc(sizeof(struct loglevel_entry) + name_len); | |
302 | if (!e) | |
303 | return ERR_PTR(-ENOMEM); | |
304 | memcpy(&e->name[0], name, name_len); | |
305 | cds_hlist_add_head(&e->hlist, head); | |
d5201d38 | 306 | CDS_INIT_LIST_HEAD(&e->session_list); |
574a6217 MD |
307 | } |
308 | ||
309 | /* session loglevel */ | |
310 | cds_list_for_each_entry(sl, &e->session_list, session_list) { | |
311 | if (chan == sl->chan) { | |
312 | DBG("loglevel %s busy for this channel", name); | |
1f18504e MD |
313 | return ERR_PTR(-EEXIST); /* Already there */ |
314 | } | |
315 | } | |
574a6217 MD |
316 | sl = zmalloc(sizeof(struct session_loglevel)); |
317 | if (!sl) | |
1f18504e | 318 | return ERR_PTR(-ENOMEM); |
574a6217 MD |
319 | sl->chan = chan; |
320 | sl->enabled = 1; | |
321 | memcpy(&sl->event_param, event_param, sizeof(sl->event_param)); | |
d5201d38 | 322 | sl->event_param.instrumentation = LTTNG_UST_TRACEPOINT; |
574a6217 MD |
323 | CDS_INIT_LIST_HEAD(&sl->events); |
324 | cds_list_add(&sl->list, &chan->session->loglevels); | |
325 | cds_list_add(&sl->session_list, &e->session_list); | |
2981744d | 326 | sl->entry = e; |
574a6217 MD |
327 | _probes_create_loglevel_events(e, sl); |
328 | return sl; | |
1f18504e MD |
329 | } |
330 | ||
331 | /* | |
332 | * Remove the loglevel from the loglevel hash table. Must be called with | |
333 | * ust_lock held. Only called at session teardown. | |
334 | */ | |
574a6217 | 335 | void _remove_loglevel(struct session_loglevel *loglevel) |
1f18504e MD |
336 | { |
337 | struct ltt_event *ev, *tmp; | |
338 | ||
339 | /* | |
340 | * Just remove the events owned (for enable/disable) by this | |
341 | * loglevel from the list. The session teardown will take care | |
342 | * of freeing the event memory. | |
343 | */ | |
344 | cds_list_for_each_entry_safe(ev, tmp, &loglevel->events, list) { | |
345 | cds_list_del(&ev->list); | |
346 | } | |
574a6217 | 347 | cds_list_del(&loglevel->session_list); |
1f18504e | 348 | cds_list_del(&loglevel->list); |
574a6217 MD |
349 | if (cds_list_empty(&loglevel->entry->session_list)) { |
350 | cds_hlist_del(&loglevel->entry->hlist); | |
351 | free(loglevel->entry); | |
352 | } | |
1f18504e MD |
353 | free(loglevel); |
354 | } | |
355 | ||
574a6217 | 356 | int ltt_loglevel_enable(struct session_loglevel *loglevel) |
1f18504e MD |
357 | { |
358 | struct ltt_event *ev; | |
359 | int ret; | |
360 | ||
361 | if (loglevel->enabled) | |
362 | return -EEXIST; | |
363 | cds_list_for_each_entry(ev, &loglevel->events, list) { | |
364 | ret = ltt_event_enable(ev); | |
365 | if (ret) { | |
366 | DBG("Error: enable error.\n"); | |
367 | return ret; | |
368 | } | |
369 | } | |
370 | loglevel->enabled = 1; | |
371 | return 0; | |
372 | } | |
373 | ||
574a6217 | 374 | int ltt_loglevel_disable(struct session_loglevel *loglevel) |
1f18504e MD |
375 | { |
376 | struct ltt_event *ev; | |
377 | int ret; | |
378 | ||
379 | if (!loglevel->enabled) | |
380 | return -EEXIST; | |
381 | cds_list_for_each_entry(ev, &loglevel->events, list) { | |
382 | ret = ltt_event_disable(ev); | |
383 | if (ret) { | |
384 | DBG("Error: disable error.\n"); | |
385 | return ret; | |
386 | } | |
387 | } | |
388 | loglevel->enabled = 0; | |
389 | return 0; | |
390 | } |