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> |
48740cab | 18 | #include <ctype.h> |
ce5aef0b | 19 | |
882a56d7 | 20 | #include "tracepoint-internal.h" |
8165c8da | 21 | #include "ltt-tracer-core.h" |
1f18504e MD |
22 | #include "jhash.h" |
23 | #include "error.h" | |
8165c8da MD |
24 | |
25 | /* | |
17dfb34b | 26 | * probe list is protected by ust_lock()/ust_unlock(). |
8165c8da | 27 | */ |
8d8a24c8 | 28 | static CDS_LIST_HEAD(probe_list); |
ce5aef0b | 29 | |
e6c12e3d MD |
30 | /* |
31 | * Wildcard list, containing the active wildcards. | |
32 | * Protected by ust lock. | |
33 | */ | |
34 | static CDS_LIST_HEAD(wildcard_list); | |
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++) { |
ff412fb5 MD |
56 | if (!strncmp(probe_desc->event_desc[i]->name, name, |
57 | LTTNG_UST_SYM_NAME_LEN - 1)) | |
df854e41 | 58 | return probe_desc->event_desc[i]; |
ce5aef0b MD |
59 | } |
60 | } | |
61 | return NULL; | |
62 | } | |
63 | ||
64 | int 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 | 100 | desc_added: |
e81a53af MD |
101 | DBG("just registered probe %s containing %u events", |
102 | desc->provider, desc->nr_events); | |
8165c8da MD |
103 | /* |
104 | * fix the events awaiting probe load. | |
105 | */ | |
106 | for (i = 0; i < desc->nr_events; i++) { | |
df854e41 | 107 | ret = pending_probe_fix_events(desc->event_desc[i]); |
8165c8da MD |
108 | assert(!ret); |
109 | } | |
ce5aef0b | 110 | end: |
17dfb34b | 111 | ust_unlock(); |
ce5aef0b MD |
112 | return ret; |
113 | } | |
ce5aef0b MD |
114 | |
115 | void ltt_probe_unregister(struct lttng_probe_desc *desc) | |
116 | { | |
17dfb34b | 117 | ust_lock(); |
8d8a24c8 | 118 | cds_list_del(&desc->head); |
e81a53af | 119 | DBG("just unregistered probe %s", desc->provider); |
17dfb34b | 120 | ust_unlock(); |
ce5aef0b | 121 | } |
ce5aef0b | 122 | |
8165c8da MD |
123 | /* |
124 | * called with UST lock held. | |
125 | */ | |
ce5aef0b MD |
126 | const struct lttng_event_desc *ltt_event_get(const char *name) |
127 | { | |
128 | const struct lttng_event_desc *event; | |
ce5aef0b | 129 | |
ce5aef0b | 130 | event = find_event(name); |
ce5aef0b MD |
131 | if (!event) |
132 | return NULL; | |
ce5aef0b MD |
133 | return event; |
134 | } | |
ce5aef0b MD |
135 | |
136 | void ltt_event_put(const struct lttng_event_desc *event) | |
137 | { | |
ce5aef0b | 138 | } |
c8fcf224 MD |
139 | |
140 | void ltt_probes_prune_event_list(struct lttng_ust_tracepoint_list *list) | |
141 | { | |
142 | struct tp_list_entry *list_entry, *tmp; | |
143 | ||
144 | cds_list_for_each_entry_safe(list_entry, tmp, &list->head, head) { | |
145 | cds_list_del(&list_entry->head); | |
146 | free(list_entry); | |
147 | } | |
148 | } | |
149 | ||
150 | /* | |
151 | * called with UST lock held. | |
152 | */ | |
153 | int ltt_probes_get_event_list(struct lttng_ust_tracepoint_list *list) | |
154 | { | |
155 | struct lttng_probe_desc *probe_desc; | |
156 | int i; | |
157 | ||
158 | CDS_INIT_LIST_HEAD(&list->head); | |
159 | cds_list_for_each_entry(probe_desc, &probe_list, head) { | |
160 | for (i = 0; i < probe_desc->nr_events; i++) { | |
161 | struct tp_list_entry *list_entry; | |
162 | ||
163 | list_entry = zmalloc(sizeof(*list_entry)); | |
164 | if (!list_entry) | |
165 | goto err_nomem; | |
166 | cds_list_add(&list_entry->head, &list->head); | |
167 | strncpy(list_entry->tp.name, | |
168 | probe_desc->event_desc[i]->name, | |
169 | LTTNG_UST_SYM_NAME_LEN); | |
170 | list_entry->tp.name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; | |
171 | if (!probe_desc->event_desc[i]->loglevel) { | |
882a56d7 | 172 | list_entry->tp.loglevel = TRACE_DEFAULT; |
c8fcf224 | 173 | } else { |
882a56d7 | 174 | list_entry->tp.loglevel = *(*probe_desc->event_desc[i]->loglevel); |
c8fcf224 MD |
175 | } |
176 | } | |
177 | } | |
178 | if (cds_list_empty(&list->head)) | |
179 | list->iter = NULL; | |
180 | else | |
181 | list->iter = | |
182 | cds_list_first_entry(&list->head, struct tp_list_entry, head); | |
183 | return 0; | |
184 | ||
185 | err_nomem: | |
186 | ltt_probes_prune_event_list(list); | |
187 | return -ENOMEM; | |
188 | } | |
189 | ||
190 | /* | |
191 | * Return current iteration position, advance internal iterator to next. | |
192 | * Return NULL if end of list. | |
193 | */ | |
194 | struct lttng_ust_tracepoint_iter * | |
195 | lttng_ust_tracepoint_list_get_iter_next(struct lttng_ust_tracepoint_list *list) | |
196 | { | |
197 | struct tp_list_entry *entry; | |
198 | ||
199 | if (!list->iter) | |
200 | return NULL; | |
201 | entry = list->iter; | |
202 | if (entry->head.next == &list->head) | |
203 | list->iter = NULL; | |
204 | else | |
205 | list->iter = cds_list_entry(entry->head.next, | |
206 | struct tp_list_entry, head); | |
207 | return &entry->tp; | |
208 | } | |
1f18504e | 209 | |
e6c12e3d MD |
210 | /* WILDCARDS */ |
211 | ||
212 | /* | |
213 | * Return wildcard for a given event name if the event name match the | |
214 | * one of the wildcards. | |
215 | * Must be called with ust lock held. | |
216 | * Returns NULL if not present. | |
217 | */ | |
218 | struct wildcard_entry *match_wildcard(const char *name) | |
219 | { | |
220 | struct wildcard_entry *e; | |
221 | ||
222 | cds_list_for_each_entry(e, &wildcard_list, list) { | |
223 | /* If only contain '*' */ | |
224 | if (strlen(e->name) == 1) | |
225 | return e; | |
226 | /* Compare excluding final '*' */ | |
227 | if (!strncmp(name, e->name, strlen(e->name) - 1)) | |
228 | return e; | |
229 | } | |
230 | return NULL; | |
231 | } | |
232 | ||
233 | /* | |
234 | * marshall all probes/all events and create those that fit the | |
235 | * wildcard. Add them to the events list as created. | |
236 | */ | |
237 | static | |
238 | void _probes_create_wildcard_events(struct wildcard_entry *entry, | |
239 | struct session_wildcard *wildcard) | |
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 lttng_event_desc *event_desc; | |
248 | int match = 0; | |
249 | ||
250 | event_desc = probe_desc->event_desc[i]; | |
251 | /* compare excluding final '*' */ | |
252 | assert(strlen(entry->name) > 0); | |
253 | if (strcmp(event_desc->name, "lttng_ust:metadata") | |
254 | && (strlen(entry->name) == 1 | |
255 | || !strncmp(event_desc->name, entry->name, | |
256 | strlen(entry->name) - 1))) { | |
51c5df09 MD |
257 | /* TODO: check if loglevel match */ |
258 | //if (event_desc->loglevel | |
882a56d7 | 259 | // && (*event_desc->loglevel) ...) |
e6c12e3d MD |
260 | match = 1; |
261 | } | |
262 | if (match) { | |
263 | struct ltt_event *ev; | |
264 | int ret; | |
265 | ||
266 | memcpy(&event_param, &wildcard->event_param, | |
267 | sizeof(event_param)); | |
268 | memcpy(event_param.name, | |
269 | event_desc->name, | |
270 | sizeof(event_param.name)); | |
271 | /* create event */ | |
272 | ret = ltt_event_create(wildcard->chan, | |
273 | &event_param, NULL, | |
274 | &ev); | |
275 | if (ret) { | |
276 | DBG("Error creating event"); | |
277 | continue; | |
278 | } | |
279 | cds_list_add(&ev->wildcard_list, | |
280 | &wildcard->events); | |
281 | } | |
282 | } | |
283 | } | |
284 | } | |
285 | ||
286 | /* | |
51c5df09 | 287 | * Add the wildcard to the wildcard list. Must be called with |
e6c12e3d MD |
288 | * ust lock held. |
289 | */ | |
290 | struct session_wildcard *add_wildcard(const char *name, | |
291 | struct ltt_channel *chan, | |
292 | struct lttng_ust_event *event_param) | |
293 | { | |
294 | struct wildcard_entry *e; | |
295 | struct session_wildcard *sw; | |
296 | size_t name_len = strlen(name) + 1; | |
297 | int found = 0; | |
298 | ||
51c5df09 | 299 | /* try to find global wildcard entry */ |
e6c12e3d | 300 | cds_list_for_each_entry(e, &wildcard_list, list) { |
ff412fb5 | 301 | if (!strncmp(name, e->name, LTTNG_UST_SYM_NAME_LEN - 1)) { |
e6c12e3d MD |
302 | found = 1; |
303 | break; | |
304 | } | |
305 | } | |
306 | ||
307 | if (!found) { | |
308 | /* | |
51c5df09 MD |
309 | * Create global wildcard entry if not found. Using |
310 | * zmalloc here to allocate a variable length element. | |
311 | * Could cause some memory fragmentation if overused. | |
e6c12e3d MD |
312 | */ |
313 | e = zmalloc(sizeof(struct wildcard_entry) + name_len); | |
314 | if (!e) | |
315 | return ERR_PTR(-ENOMEM); | |
316 | memcpy(&e->name[0], name, name_len); | |
317 | cds_list_add(&e->list, &wildcard_list); | |
318 | CDS_INIT_LIST_HEAD(&e->session_list); | |
319 | } | |
320 | ||
321 | /* session wildcard */ | |
322 | cds_list_for_each_entry(sw, &e->session_list, session_list) { | |
323 | if (chan == sw->chan) { | |
324 | DBG("wildcard %s busy for this channel", name); | |
325 | return ERR_PTR(-EEXIST); /* Already there */ | |
326 | } | |
327 | } | |
328 | sw = zmalloc(sizeof(struct session_wildcard)); | |
329 | if (!sw) | |
330 | return ERR_PTR(-ENOMEM); | |
331 | sw->chan = chan; | |
332 | sw->enabled = 1; | |
333 | memcpy(&sw->event_param, event_param, sizeof(sw->event_param)); | |
334 | sw->event_param.instrumentation = LTTNG_UST_TRACEPOINT; | |
335 | CDS_INIT_LIST_HEAD(&sw->events); | |
336 | cds_list_add(&sw->list, &chan->session->wildcards); | |
337 | cds_list_add(&sw->session_list, &e->session_list); | |
338 | sw->entry = e; | |
339 | _probes_create_wildcard_events(e, sw); | |
340 | return sw; | |
341 | } | |
342 | ||
343 | /* | |
51c5df09 | 344 | * Remove the wildcard from the wildcard list. Must be called with |
e6c12e3d MD |
345 | * ust_lock held. Only called at session teardown. |
346 | */ | |
347 | void _remove_wildcard(struct session_wildcard *wildcard) | |
348 | { | |
349 | struct ltt_event *ev, *tmp; | |
350 | ||
351 | /* | |
352 | * Just remove the events owned (for enable/disable) by this | |
353 | * wildcard from the list. The session teardown will take care | |
354 | * of freeing the event memory. | |
355 | */ | |
66221151 MD |
356 | cds_list_for_each_entry_safe(ev, tmp, &wildcard->events, |
357 | wildcard_list) { | |
358 | cds_list_del(&ev->wildcard_list); | |
e6c12e3d MD |
359 | } |
360 | cds_list_del(&wildcard->session_list); | |
361 | cds_list_del(&wildcard->list); | |
362 | if (cds_list_empty(&wildcard->entry->session_list)) { | |
363 | cds_list_del(&wildcard->entry->list); | |
364 | free(wildcard->entry); | |
365 | } | |
366 | free(wildcard); | |
367 | } | |
368 | ||
369 | int ltt_wildcard_enable(struct session_wildcard *wildcard) | |
370 | { | |
371 | struct ltt_event *ev; | |
372 | int ret; | |
373 | ||
374 | if (wildcard->enabled) | |
375 | return -EEXIST; | |
66221151 | 376 | cds_list_for_each_entry(ev, &wildcard->events, wildcard_list) { |
e6c12e3d MD |
377 | ret = ltt_event_enable(ev); |
378 | if (ret) { | |
379 | DBG("Error: enable error.\n"); | |
380 | return ret; | |
381 | } | |
382 | } | |
383 | wildcard->enabled = 1; | |
384 | return 0; | |
385 | } | |
386 | ||
387 | int ltt_wildcard_disable(struct session_wildcard *wildcard) | |
388 | { | |
389 | struct ltt_event *ev; | |
390 | int ret; | |
391 | ||
392 | if (!wildcard->enabled) | |
393 | return -EEXIST; | |
66221151 | 394 | cds_list_for_each_entry(ev, &wildcard->events, wildcard_list) { |
e6c12e3d MD |
395 | ret = ltt_event_disable(ev); |
396 | if (ret) { | |
397 | DBG("Error: disable error.\n"); | |
398 | return ret; | |
399 | } | |
400 | } | |
401 | wildcard->enabled = 0; | |
402 | return 0; | |
403 | } |