Commit | Line | Data |
---|---|---|
ebdb334b JR |
1 | /* |
2 | * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com> | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0-only | |
5 | * | |
6 | */ | |
7 | ||
8 | ||
9 | #include "lttng/domain.h" | |
10 | #include <common/kernel-ctl/kernel-ctl.h> | |
11 | #include <lttng/map/map.h> | |
12 | #include <lttng/map/map-internal.h> | |
13 | ||
14 | #include "lttng-sessiond.h" | |
15 | #include "lttng-ust-error.h" | |
16 | #include "notification-thread-commands.h" | |
17 | #include "trace-kernel.h" | |
18 | #include "trace-ust.h" | |
19 | ||
20 | #include "map.h" | |
21 | ||
22 | enum lttng_error_code map_kernel_add(struct ltt_kernel_session *ksession, | |
23 | struct lttng_map *map) | |
24 | { | |
25 | enum lttng_error_code ret; | |
26 | struct ltt_kernel_map *kmap; | |
27 | enum lttng_map_status map_status; | |
28 | const char *map_name; | |
29 | ||
30 | assert(lttng_map_get_domain(map) == LTTNG_DOMAIN_KERNEL); | |
31 | ||
32 | map_status = lttng_map_get_name(map, &map_name); | |
33 | if (map_status != LTTNG_MAP_STATUS_OK) { | |
34 | ERR("Can't get map name"); | |
35 | ret = -1; | |
36 | goto error; | |
37 | } | |
38 | ||
39 | kmap = trace_kernel_get_map_by_name(map_name, ksession); | |
40 | if (kmap) { | |
41 | DBG("Kernel map named \"%s\" already present", map_name); | |
42 | ret = -1; | |
43 | goto error; | |
44 | } | |
45 | ||
46 | kmap = trace_kernel_create_map(map); | |
47 | assert(kmap); | |
48 | ||
49 | ret = kernctl_create_session_counter(ksession->fd, | |
50 | &kmap->counter_conf); | |
51 | if (ret < 0) { | |
52 | PERROR("ioctl kernel create session counter"); | |
53 | goto error; | |
54 | } | |
55 | ||
56 | kmap->fd = ret; | |
57 | ||
58 | /* Prevent fd duplication after execlp() */ | |
59 | ret = fcntl(kmap->fd, F_SETFD, FD_CLOEXEC); | |
60 | if (ret < 0) { | |
61 | PERROR("fcntl session counter fd"); | |
62 | goto error; | |
63 | } | |
64 | ||
65 | kmap->map = map; | |
66 | lttng_map_get(map); | |
67 | cds_list_add(&kmap->list, &ksession->map_list.head); | |
68 | ksession->map_count++; | |
69 | ||
70 | DBG("Kernel session counter created (fd: %d)", kmap->fd); | |
71 | ||
72 | ret = kernctl_enable(kmap->fd); | |
73 | if (ret < 0) { | |
74 | PERROR("Enable kernel map"); | |
75 | } | |
76 | ||
77 | ret = LTTNG_OK; | |
78 | error: | |
79 | return ret; | |
80 | } | |
81 | ||
82 | enum lttng_error_code map_kernel_enable(struct ltt_kernel_session *ksess, | |
83 | struct ltt_kernel_map *kmap) | |
84 | { | |
85 | enum lttng_error_code ret = LTTNG_OK; | |
86 | const char *map_name; | |
87 | enum lttng_map_status map_status; | |
88 | ||
89 | ||
90 | assert(ksess); | |
91 | assert(kmap); | |
92 | ||
93 | map_status = lttng_map_get_name(kmap->map, &map_name); | |
94 | if (map_status != LTTNG_MAP_STATUS_OK) { | |
95 | ERR("Error getting kernel map name"); | |
96 | ret = -1; | |
97 | goto end; | |
98 | } | |
99 | ||
100 | /* If already enabled, everything is OK */ | |
101 | if (kmap->enabled) { | |
102 | DBG3("Map %s already enabled. Skipping", map_name); | |
103 | ret = LTTNG_ERR_UST_MAP_EXIST; | |
104 | goto end; | |
105 | } else { | |
106 | kmap->enabled = 1; | |
107 | lttng_map_set_is_enabled(kmap->map, true); | |
108 | DBG2("Map %s enabled successfully", map_name); | |
109 | } | |
110 | ||
111 | DBG2("Map %s being enabled in kernel domain", map_name); | |
112 | ||
113 | /* | |
114 | * Enable map for UST global domain on all applications. Ignore return | |
115 | * value here since whatever error we got, it means that the map was | |
116 | * not created on one or many registered applications and we can not report | |
117 | * this to the user yet. However, at this stage, the map was | |
118 | * successfully created on the session daemon side so the enable-map | |
119 | * command is a success. | |
120 | */ | |
121 | ||
122 | ret = kernctl_enable(kmap->fd); | |
123 | if (ret < 0) { | |
124 | PERROR("Enable kernel map"); | |
125 | } | |
126 | ||
127 | ret = LTTNG_OK; | |
128 | end: | |
129 | return ret; | |
130 | } | |
131 | ||
132 | enum lttng_error_code map_kernel_disable(struct ltt_kernel_session *usess, | |
133 | struct ltt_kernel_map *kmap) | |
134 | { | |
135 | enum lttng_error_code ret = LTTNG_OK; | |
136 | enum lttng_map_status map_status; | |
137 | const char *map_name = NULL; | |
138 | ||
139 | assert(usess); | |
140 | assert(kmap); | |
141 | ||
142 | map_status = lttng_map_get_name(kmap->map, &map_name); | |
143 | if (map_status != LTTNG_MAP_STATUS_OK) { | |
144 | ERR("Error getting kernel map name"); | |
145 | ret = -1; | |
146 | goto end; | |
147 | } | |
148 | ||
149 | /* Already disabled */ | |
150 | if (kmap->enabled == 0) { | |
151 | DBG2("Map kernel %s already disabled", map_name); | |
152 | ret = LTTNG_ERR_KERNEL_MAP_EXIST; | |
153 | goto end; | |
154 | } | |
155 | ||
156 | kmap->enabled = 0; | |
157 | lttng_map_set_is_enabled(kmap->map, false); | |
158 | ||
159 | DBG2("Map %s being disabled in kernel global domain", map_name); | |
160 | ||
161 | /* Disable map for global domain */ | |
162 | ret = kernctl_disable(kmap->fd); | |
163 | if (ret < 0) { | |
164 | ret = LTTNG_ERR_KERNEL_MAP_DISABLE_FAIL; | |
165 | goto error; | |
166 | } | |
167 | ||
168 | ||
169 | DBG2("Map %s disabled successfully", map_name); | |
170 | ||
171 | return LTTNG_OK; | |
172 | ||
173 | end: | |
174 | error: | |
175 | return ret; | |
176 | } | |
177 | ||
178 | int map_ust_add(struct ltt_ust_session *usession, struct lttng_map *map) | |
179 | { | |
180 | int ret = 0; | |
181 | struct ltt_ust_map *umap; | |
182 | enum lttng_map_status map_status; | |
183 | const char *map_name; | |
184 | enum lttng_buffer_type buffer_type; | |
185 | ||
186 | assert(lttng_map_get_domain(map) == LTTNG_DOMAIN_UST); | |
187 | ||
188 | map_status = lttng_map_get_name(map, &map_name); | |
189 | if (map_status != LTTNG_MAP_STATUS_OK) { | |
190 | ERR("Can't get map name"); | |
191 | ret = -1; | |
192 | goto end; | |
193 | } | |
194 | ||
195 | umap = trace_ust_find_map_by_name(usession->domain_global.maps, | |
196 | map_name); | |
197 | if (umap) { | |
198 | DBG("UST map named \"%s\" already present", map_name); | |
199 | ret = -1; | |
200 | goto end; | |
201 | } | |
202 | ||
203 | buffer_type = lttng_map_get_buffer_type(map); | |
204 | ||
205 | umap = trace_ust_create_map(map); | |
206 | assert(umap); | |
207 | ||
208 | umap->enabled = 1; | |
209 | umap->id = trace_ust_get_next_chan_id(usession); | |
210 | umap->map = map; | |
211 | lttng_map_get(map); | |
212 | ||
213 | lttng_map_set_is_enabled(umap->map, true); | |
214 | ||
215 | DBG2("Map %s is being created for UST with buffer type %d and id %" PRIu64, | |
216 | umap->name, buffer_type, umap->id); | |
217 | ||
218 | /* Flag session buffer type. */ | |
219 | if (!usession->buffer_type_changed) { | |
220 | usession->buffer_type = buffer_type; | |
221 | usession->buffer_type_changed = 1; | |
222 | } else if (usession->buffer_type != buffer_type) { | |
223 | /* Buffer type was already set. Refuse to create channel. */ | |
224 | ret = LTTNG_ERR_BUFFER_TYPE_MISMATCH; | |
225 | goto error_free_map; | |
226 | } | |
227 | ||
228 | rcu_read_lock(); | |
229 | ||
230 | /* Adding the map to the map hash table. */ | |
231 | lttng_ht_add_unique_str(usession->domain_global.maps, &umap->node); | |
232 | ||
233 | rcu_read_unlock(); | |
234 | ||
235 | DBG2("Map %s created successfully", umap->name); | |
236 | ||
237 | ret = 0; | |
238 | goto end; | |
239 | ||
240 | error_free_map: | |
241 | trace_ust_destroy_map(umap); | |
242 | end: | |
243 | return ret; | |
244 | } | |
245 | ||
246 | /* | |
247 | * Enable UST map for session and domain. | |
248 | */ | |
249 | int map_ust_enable(struct ltt_ust_session *usess, | |
250 | struct ltt_ust_map *umap) | |
251 | { | |
252 | int ret = LTTNG_OK; | |
253 | ||
254 | assert(usess); | |
255 | assert(umap); | |
256 | ||
257 | /* If already enabled, everything is OK */ | |
258 | if (umap->enabled) { | |
259 | DBG3("Map %s already enabled. Skipping", umap->name); | |
260 | ret = LTTNG_ERR_UST_MAP_EXIST; | |
261 | goto end; | |
262 | } else { | |
263 | umap->enabled = 1; | |
264 | lttng_map_set_is_enabled(umap->map, true); | |
265 | DBG2("Map %s enabled successfully", umap->name); | |
266 | } | |
267 | ||
268 | if (!usess->active) { | |
269 | /* | |
270 | * The map will be activated against the apps | |
271 | * when the session is started as part of the | |
272 | * application map "synchronize" operation. | |
273 | */ | |
274 | goto end; | |
275 | } | |
276 | ||
277 | DBG2("Map %s being enabled in UST domain", umap->name); | |
278 | ||
279 | /* | |
280 | * Enable map for UST global domain on all applications. Ignore return | |
281 | * value here since whatever error we got, it means that the map was | |
282 | * not created on one or many registered applications and we can not report | |
283 | * this to the user yet. However, at this stage, the map was | |
284 | * successfully created on the session daemon side so the enable-map | |
285 | * command is a success. | |
286 | */ | |
287 | (void) ust_app_enable_map_glb(usess, umap); | |
288 | ||
289 | ||
290 | end: | |
291 | return ret; | |
292 | } | |
293 | ||
294 | int map_ust_disable(struct ltt_ust_session *usess, | |
295 | struct ltt_ust_map *umap) | |
296 | { | |
297 | int ret = LTTNG_OK; | |
298 | ||
299 | assert(usess); | |
300 | assert(umap); | |
301 | ||
302 | /* Already disabled */ | |
303 | if (umap->enabled == 0) { | |
304 | DBG2("Map UST %s already disabled", umap->name); | |
305 | ret = LTTNG_ERR_UST_MAP_EXIST; | |
306 | goto end; | |
307 | } | |
308 | ||
309 | umap->enabled = 0; | |
310 | lttng_map_set_is_enabled(umap->map, false); | |
311 | ||
312 | /* | |
313 | * If session is inactive we don't notify the tracer right away. We | |
314 | * wait for the next synchronization. | |
315 | */ | |
316 | if (!usess->active) { | |
317 | goto end; | |
318 | } | |
319 | ||
320 | DBG2("Map %s being disabled in UST global domain", umap->name); | |
321 | ||
322 | /* Disable map for global domain */ | |
323 | ret = ust_app_disable_map_glb(usess, umap); | |
324 | if (ret < 0 && ret != -LTTNG_UST_ERR_EXIST) { | |
325 | ret = LTTNG_ERR_UST_MAP_DISABLE_FAIL; | |
326 | goto error; | |
327 | } | |
328 | ||
329 | ||
330 | DBG2("Map %s disabled successfully", umap->name); | |
331 | ||
332 | return LTTNG_OK; | |
333 | ||
334 | end: | |
335 | error: | |
336 | return ret; | |
337 | } | |
338 | ||
339 | void map_add_or_increment_map_values(struct lttng_ht *map_values, const char *key, | |
340 | int64_t value, bool has_underflowed, bool has_overflowed) | |
341 | { | |
342 | struct map_kv_ht_entry *kv_entry; | |
343 | struct lttng_ht_node_str *node; | |
344 | struct lttng_ht_iter ht_iter; | |
345 | ||
346 | lttng_ht_lookup(map_values, (void *) key, &ht_iter); | |
347 | node = lttng_ht_iter_get_node_str(&ht_iter); | |
348 | if (node == NULL) { | |
349 | /* | |
350 | * If the key is absent, the key value mapping. | |
351 | */ | |
352 | kv_entry = zmalloc(sizeof(*kv_entry)); | |
353 | if (!kv_entry) { | |
354 | abort(); | |
355 | } | |
356 | ||
357 | kv_entry->key = strdup(key); | |
358 | kv_entry->value = value; | |
359 | kv_entry->has_underflowed = has_underflowed; | |
360 | kv_entry->has_overflowed = has_overflowed; | |
361 | ||
362 | lttng_ht_node_init_str(&kv_entry->node, (char *) kv_entry->key); | |
363 | lttng_ht_add_unique_str(map_values, &kv_entry->node); | |
364 | ||
365 | } else { | |
366 | /* | |
367 | * If the key is already present, increment the current value with the | |
368 | * new value. | |
369 | */ | |
370 | kv_entry = caa_container_of(node, typeof(*kv_entry), node); | |
371 | kv_entry->value += value; | |
372 | kv_entry->has_underflowed |= has_underflowed; | |
373 | kv_entry->has_overflowed |= has_overflowed; | |
374 | } | |
375 | } | |
376 | ||
377 | int map_new_content_section(struct lttng_map_content *map_content, | |
378 | enum lttng_map_key_value_pair_list_type list_type, | |
379 | bool summed_all_cpus, unsigned int identifier, | |
380 | int cpu, struct lttng_ht *values) | |
381 | { | |
382 | int ret; | |
383 | struct lttng_map_key_value_pair_list *kv_pair_list; | |
384 | enum lttng_map_status map_status; | |
385 | struct map_kv_ht_entry *kv_entry; | |
386 | struct lttng_ht_iter key_iter; | |
387 | ||
388 | kv_pair_list = lttng_map_key_value_pair_list_create(list_type, | |
389 | summed_all_cpus); | |
390 | switch (list_type) { | |
391 | case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID: | |
392 | case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID: | |
393 | map_status = lttng_map_key_value_pair_list_set_identifier( | |
394 | kv_pair_list, identifier); | |
395 | assert(map_status == LTTNG_MAP_STATUS_OK); | |
396 | break; | |
397 | default: | |
398 | break; | |
399 | } | |
400 | ||
401 | if (!summed_all_cpus) { | |
402 | map_status = lttng_map_key_value_pair_list_set_cpu(kv_pair_list, | |
403 | cpu); | |
404 | } | |
405 | ||
406 | cds_lfht_for_each_entry(values->ht, &key_iter.iter, kv_entry, node.node) { | |
407 | struct lttng_ht_iter entry_iter; | |
408 | ||
409 | struct lttng_map_key_value_pair *pair = | |
410 | lttng_map_key_value_pair_create(kv_entry->key, | |
411 | kv_entry->value); | |
412 | if (kv_entry->has_overflowed) { | |
413 | lttng_map_key_value_pair_set_has_overflowed(pair); | |
414 | } | |
415 | ||
416 | if (kv_entry->has_underflowed) { | |
417 | lttng_map_key_value_pair_set_has_underflowed(pair); | |
418 | } | |
419 | ||
420 | map_status = lttng_map_key_value_pair_list_append_key_value( | |
421 | kv_pair_list, pair); | |
422 | ||
423 | entry_iter.iter.node = &kv_entry->node.node; | |
424 | lttng_ht_del(values, &entry_iter); | |
425 | ||
426 | free(kv_entry->key); | |
427 | free(kv_entry); | |
428 | } | |
429 | ||
430 | map_status = lttng_map_content_append_key_value_list(map_content, | |
431 | kv_pair_list); | |
432 | if (map_status != LTTNG_MAP_STATUS_OK) { | |
433 | lttng_map_key_value_pair_list_destroy(kv_pair_list); | |
434 | ret = -1; | |
435 | ERR("Error appending key-value pair list to map content object"); | |
436 | goto end; | |
437 | } | |
438 | ret = 0; | |
439 | end: | |
440 | return ret; | |
441 | } | |
442 |