Commit | Line | Data |
---|---|---|
d0b96690 | 1 | /* |
ab5be9fa | 2 | * Copyright (C) 2013 David Goulet <dgoulet@efficios.com> |
d0b96690 | 3 | * |
ab5be9fa | 4 | * SPDX-License-Identifier: GPL-2.0-only |
d0b96690 | 5 | * |
d0b96690 | 6 | */ |
890d8fe4 | 7 | |
6c1c0768 | 8 | #define _LGPL_SOURCE |
7972aab2 | 9 | |
c9e313bc | 10 | #include "ust-registry.hpp" |
c9e313bc SM |
11 | #include "lttng-sessiond.hpp" |
12 | #include "notification-thread-commands.hpp" | |
d7bfb9b0 JG |
13 | #include "ust-app.hpp" |
14 | #include "utils.hpp" | |
d0b96690 | 15 | |
d7bfb9b0 JG |
16 | #include <common/common.hpp> |
17 | #include <common/exception.hpp> | |
18 | #include <common/format.hpp> | |
19 | #include <common/hashtable/utils.hpp> | |
20 | #include <common/make-unique-wrapper.hpp> | |
21 | #include <lttng/lttng.h> | |
10b56aef | 22 | |
d7bfb9b0 | 23 | #include <inttypes.h> |
3b016e58 | 24 | |
d7bfb9b0 JG |
25 | namespace ls = lttng::sessiond; |
26 | namespace lst = lttng::sessiond::trace; | |
27 | namespace lsu = lttng::sessiond::ust; | |
10b56aef MD |
28 | |
29 | /* | |
30 | * Hash table match function for enumerations in the session. Match is | |
31 | * performed on enumeration name, and confirmed by comparing the enum | |
32 | * entries. | |
33 | */ | |
34 | static int ht_match_enum(struct cds_lfht_node *node, const void *_key) | |
35 | { | |
d7bfb9b0 JG |
36 | lsu::registry_enum *_enum; |
37 | const lsu::registry_enum *key; | |
10b56aef | 38 | |
a0377dfe FD |
39 | LTTNG_ASSERT(node); |
40 | LTTNG_ASSERT(_key); | |
10b56aef | 41 | |
d7bfb9b0 JG |
42 | DIAGNOSTIC_PUSH |
43 | DIAGNOSTIC_IGNORE_INVALID_OFFSETOF | |
44 | _enum = caa_container_of(node, lsu::registry_enum, | |
10b56aef | 45 | node.node); |
d7bfb9b0 | 46 | DIAGNOSTIC_POP |
10b56aef | 47 | |
d7bfb9b0 JG |
48 | LTTNG_ASSERT(_enum); |
49 | key = (lsu::registry_enum *) _key; | |
10b56aef | 50 | |
d7bfb9b0 | 51 | return *_enum == *key; |
10b56aef MD |
52 | } |
53 | ||
54 | /* | |
55 | * Hash table match function for enumerations in the session. Match is | |
56 | * performed by enumeration ID. | |
57 | */ | |
58 | static int ht_match_enum_id(struct cds_lfht_node *node, const void *_key) | |
59 | { | |
d7bfb9b0 JG |
60 | lsu::registry_enum *_enum; |
61 | const lsu::registry_enum *key = (lsu::registry_enum *) _key; | |
10b56aef | 62 | |
a0377dfe FD |
63 | LTTNG_ASSERT(node); |
64 | LTTNG_ASSERT(_key); | |
10b56aef | 65 | |
d7bfb9b0 JG |
66 | DIAGNOSTIC_PUSH |
67 | DIAGNOSTIC_IGNORE_INVALID_OFFSETOF | |
68 | _enum = caa_container_of(node, lsu::registry_enum, node.node); | |
69 | DIAGNOSTIC_POP | |
70 | ||
a0377dfe | 71 | LTTNG_ASSERT(_enum); |
10b56aef MD |
72 | |
73 | if (_enum->id != key->id) { | |
74 | goto no_match; | |
75 | } | |
76 | ||
77 | /* Match. */ | |
78 | return 1; | |
79 | ||
80 | no_match: | |
81 | return 0; | |
82 | } | |
83 | ||
84 | /* | |
85 | * Hash table hash function for enumerations in the session. The | |
86 | * enumeration name is used for hashing. | |
87 | */ | |
88 | static unsigned long ht_hash_enum(void *_key, unsigned long seed) | |
89 | { | |
d7bfb9b0 | 90 | lsu::registry_enum *key = (lsu::registry_enum *) _key; |
10b56aef | 91 | |
a0377dfe | 92 | LTTNG_ASSERT(key); |
d7bfb9b0 | 93 | return hash_key_str(key->name.c_str(), seed); |
d0b96690 DG |
94 | } |
95 | ||
96 | /* | |
97 | * Destroy event function call of the call RCU. | |
98 | */ | |
d7bfb9b0 | 99 | static void ust_registry_event_destroy_rcu(struct rcu_head *head) |
d0b96690 | 100 | { |
d7bfb9b0 JG |
101 | struct lttng_ht_node_u64 *node = caa_container_of(head, struct lttng_ht_node_u64, head); |
102 | DIAGNOSTIC_PUSH | |
103 | DIAGNOSTIC_IGNORE_INVALID_OFFSETOF | |
104 | lttng::sessiond::ust::registry_event *event = | |
105 | caa_container_of(node, lttng::sessiond::ust::registry_event, _node); | |
106 | DIAGNOSTIC_POP | |
107 | ||
108 | lttng::sessiond::ust::registry_event_destroy(event); | |
d0b96690 DG |
109 | } |
110 | ||
111 | /* | |
112 | * For a given event in a registry, delete the entry and destroy the event. | |
113 | * This MUST be called within a RCU read side lock section. | |
114 | */ | |
d7bfb9b0 JG |
115 | void ust_registry_channel_destroy_event(lsu::registry_channel *chan, |
116 | lttng::sessiond::ust::registry_event *event) | |
d0b96690 DG |
117 | { |
118 | int ret; | |
119 | struct lttng_ht_iter iter; | |
120 | ||
a0377dfe FD |
121 | LTTNG_ASSERT(chan); |
122 | LTTNG_ASSERT(event); | |
48b7cdc2 | 123 | ASSERT_RCU_READ_LOCKED(); |
d0b96690 DG |
124 | |
125 | /* Delete the node first. */ | |
d7bfb9b0 JG |
126 | iter.iter.node = &event->_node.node; |
127 | ret = lttng_ht_del(chan->_events, &iter); | |
a0377dfe | 128 | LTTNG_ASSERT(!ret); |
d0b96690 | 129 | |
d7bfb9b0 | 130 | call_rcu(&event->_node.head, ust_registry_event_destroy_rcu); |
d0b96690 DG |
131 | |
132 | return; | |
133 | } | |
134 | ||
d7bfb9b0 | 135 | static void destroy_enum(lsu::registry_enum *reg_enum) |
10b56aef MD |
136 | { |
137 | if (!reg_enum) { | |
138 | return; | |
139 | } | |
d7bfb9b0 JG |
140 | |
141 | delete reg_enum; | |
10b56aef MD |
142 | } |
143 | ||
144 | static void destroy_enum_rcu(struct rcu_head *head) | |
145 | { | |
d7bfb9b0 JG |
146 | DIAGNOSTIC_PUSH |
147 | DIAGNOSTIC_IGNORE_INVALID_OFFSETOF | |
148 | lsu::registry_enum *reg_enum = | |
149 | caa_container_of(head, lsu::registry_enum, rcu_head); | |
150 | DIAGNOSTIC_POP | |
10b56aef MD |
151 | |
152 | destroy_enum(reg_enum); | |
153 | } | |
154 | ||
155 | /* | |
156 | * Lookup enumeration by name and comparing enumeration entries. | |
157 | * Needs to be called from RCU read-side critical section. | |
158 | */ | |
d7bfb9b0 | 159 | static lsu::registry_enum *ust_registry_lookup_enum( |
aeeb48c6 | 160 | ust_registry_session *session, |
d7bfb9b0 | 161 | const lsu::registry_enum *reg_enum_lookup) |
10b56aef | 162 | { |
d7bfb9b0 | 163 | lsu::registry_enum *reg_enum = NULL; |
10b56aef MD |
164 | struct lttng_ht_node_str *node; |
165 | struct lttng_ht_iter iter; | |
166 | ||
48b7cdc2 FD |
167 | ASSERT_RCU_READ_LOCKED(); |
168 | ||
aeeb48c6 | 169 | cds_lfht_lookup(session->_enums->ht, |
a752d6fa FD |
170 | ht_hash_enum((void *) reg_enum_lookup, lttng_ht_seed), |
171 | ht_match_enum, reg_enum_lookup, &iter.iter); | |
10b56aef MD |
172 | node = lttng_ht_iter_get_node_str(&iter); |
173 | if (!node) { | |
174 | goto end; | |
175 | } | |
d7bfb9b0 JG |
176 | |
177 | DIAGNOSTIC_PUSH | |
178 | DIAGNOSTIC_IGNORE_INVALID_OFFSETOF | |
179 | reg_enum = caa_container_of(node, lsu::registry_enum, node); | |
180 | DIAGNOSTIC_POP | |
181 | ||
10b56aef MD |
182 | end: |
183 | return reg_enum; | |
184 | } | |
185 | ||
186 | /* | |
187 | * Lookup enumeration by enum ID. | |
10b56aef | 188 | */ |
d7bfb9b0 JG |
189 | lsu::registry_enum::const_rcu_protected_reference |
190 | ust_registry_lookup_enum_by_id(const ust_registry_session *session, | |
10b56aef MD |
191 | const char *enum_name, uint64_t enum_id) |
192 | { | |
d7bfb9b0 | 193 | lsu::registry_enum *reg_enum = NULL; |
10b56aef MD |
194 | struct lttng_ht_node_str *node; |
195 | struct lttng_ht_iter iter; | |
d7bfb9b0 JG |
196 | lttng::urcu::unique_read_lock rcu_lock; |
197 | /* | |
198 | * Hack: only the name is used for hashing; the rest of the attributes | |
199 | * can be fudged. | |
200 | */ | |
201 | lsu::registry_signed_enum reg_enum_lookup(enum_name, nullptr, 0); | |
10b56aef | 202 | |
48b7cdc2 FD |
203 | ASSERT_RCU_READ_LOCKED(); |
204 | ||
10b56aef | 205 | reg_enum_lookup.id = enum_id; |
aeeb48c6 | 206 | cds_lfht_lookup(session->_enums->ht, |
10b56aef MD |
207 | ht_hash_enum((void *) ®_enum_lookup, lttng_ht_seed), |
208 | ht_match_enum_id, ®_enum_lookup, &iter.iter); | |
209 | node = lttng_ht_iter_get_node_str(&iter); | |
210 | if (!node) { | |
d7bfb9b0 JG |
211 | LTTNG_THROW_PROTOCOL_ERROR(fmt::format( |
212 | "Unknown enumeration referenced by application event field: enum name = `{}`, enum id = {}", | |
213 | enum_name, enum_id)); | |
10b56aef | 214 | } |
d7bfb9b0 JG |
215 | |
216 | DIAGNOSTIC_PUSH | |
217 | DIAGNOSTIC_IGNORE_INVALID_OFFSETOF | |
218 | reg_enum = caa_container_of(node, lsu::registry_enum, node); | |
219 | DIAGNOSTIC_POP | |
220 | ||
221 | return lsu::registry_enum::const_rcu_protected_reference{*reg_enum, std::move(rcu_lock)}; | |
10b56aef MD |
222 | } |
223 | ||
224 | /* | |
d7bfb9b0 | 225 | * Create a lsu::registry_enum from the given parameters and add it to the |
10b56aef MD |
226 | * registry hash table, or find it if already there. |
227 | * | |
228 | * On success, return 0 else a negative value. | |
229 | * | |
230 | * Should be called with session registry mutex held. | |
231 | * | |
232 | * We receive ownership of entries. | |
233 | */ | |
aeeb48c6 | 234 | int ust_registry_create_or_find_enum(ust_registry_session *session, |
10b56aef | 235 | int session_objd, char *enum_name, |
d7bfb9b0 | 236 | struct lttng_ust_ctl_enum_entry *raw_entries, size_t nr_entries, |
10b56aef MD |
237 | uint64_t *enum_id) |
238 | { | |
239 | int ret = 0; | |
240 | struct cds_lfht_node *nodep; | |
d7bfb9b0 JG |
241 | lsu::registry_enum *reg_enum = NULL, *old_reg_enum; |
242 | auto entries = lttng::make_unique_wrapper<lttng_ust_ctl_enum_entry, lttng::free>(raw_entries); | |
10b56aef | 243 | |
a0377dfe FD |
244 | LTTNG_ASSERT(session); |
245 | LTTNG_ASSERT(enum_name); | |
10b56aef MD |
246 | |
247 | rcu_read_lock(); | |
248 | ||
249 | /* | |
250 | * This should not happen but since it comes from the UST tracer, an | |
251 | * external party, don't assert and simply validate values. | |
252 | */ | |
d7bfb9b0 JG |
253 | if (session_objd < 0 || nr_entries == 0 || |
254 | lttng_strnlen(enum_name, LTTNG_UST_ABI_SYM_NAME_LEN) == | |
255 | LTTNG_UST_ABI_SYM_NAME_LEN) { | |
10b56aef MD |
256 | ret = -EINVAL; |
257 | goto end; | |
258 | } | |
259 | ||
d7bfb9b0 JG |
260 | try { |
261 | if (entries->start.signedness) { | |
262 | reg_enum = new lsu::registry_signed_enum( | |
263 | enum_name, entries.get(), nr_entries); | |
264 | } else { | |
265 | reg_enum = new lsu::registry_unsigned_enum( | |
266 | enum_name, entries.get(), nr_entries); | |
267 | } | |
268 | } catch (const std::exception& ex) { | |
269 | ERR("Failed to create ust registry enumeration: %s", ex.what()); | |
10b56aef MD |
270 | ret = -ENOMEM; |
271 | goto end; | |
272 | } | |
10b56aef MD |
273 | |
274 | old_reg_enum = ust_registry_lookup_enum(session, reg_enum); | |
275 | if (old_reg_enum) { | |
276 | DBG("enum %s already in sess_objd: %u", enum_name, session_objd); | |
277 | /* Fall through. Use prior enum. */ | |
278 | destroy_enum(reg_enum); | |
279 | reg_enum = old_reg_enum; | |
280 | } else { | |
281 | DBG("UST registry creating enum: %s, sess_objd: %u", | |
282 | enum_name, session_objd); | |
aeeb48c6 | 283 | if (session->_next_enum_id == -1ULL) { |
10b56aef MD |
284 | ret = -EOVERFLOW; |
285 | destroy_enum(reg_enum); | |
286 | goto end; | |
287 | } | |
aeeb48c6 | 288 | reg_enum->id = session->_next_enum_id++; |
aeeb48c6 | 289 | nodep = cds_lfht_add_unique(session->_enums->ht, |
10b56aef MD |
290 | ht_hash_enum(reg_enum, lttng_ht_seed), |
291 | ht_match_enum_id, reg_enum, | |
292 | ®_enum->node.node); | |
a0377dfe | 293 | LTTNG_ASSERT(nodep == ®_enum->node.node); |
10b56aef MD |
294 | } |
295 | DBG("UST registry reply with enum %s with id %" PRIu64 " in sess_objd: %u", | |
296 | enum_name, reg_enum->id, session_objd); | |
297 | *enum_id = reg_enum->id; | |
298 | end: | |
10b56aef MD |
299 | rcu_read_unlock(); |
300 | return ret; | |
301 | } | |
302 | ||
303 | /* | |
304 | * For a given enumeration in a registry, delete the entry and destroy | |
305 | * the enumeration. | |
306 | * This MUST be called within a RCU read side lock section. | |
307 | */ | |
aeeb48c6 | 308 | void ust_registry_destroy_enum(ust_registry_session *reg_session, |
d7bfb9b0 | 309 | lsu::registry_enum *reg_enum) |
10b56aef MD |
310 | { |
311 | int ret; | |
312 | struct lttng_ht_iter iter; | |
313 | ||
a0377dfe FD |
314 | LTTNG_ASSERT(reg_session); |
315 | LTTNG_ASSERT(reg_enum); | |
48b7cdc2 | 316 | ASSERT_RCU_READ_LOCKED(); |
10b56aef MD |
317 | |
318 | /* Delete the node first. */ | |
319 | iter.iter.node = ®_enum->node.node; | |
aeeb48c6 | 320 | ret = lttng_ht_del(reg_session->_enums.get(), &iter); |
a0377dfe | 321 | LTTNG_ASSERT(!ret); |
10b56aef MD |
322 | call_rcu(®_enum->rcu_head, destroy_enum_rcu); |
323 | } | |
324 | ||
d7bfb9b0 | 325 | ust_registry_session *ust_registry_session_per_uid_create(const lttng::sessiond::trace::abi& abi, |
af6142cf | 326 | uint32_t major, |
d7ba1388 | 327 | uint32_t minor, |
3d071855 | 328 | const char *root_shm_path, |
d7ba1388 MD |
329 | const char *shm_path, |
330 | uid_t euid, | |
8de88061 JR |
331 | gid_t egid, |
332 | uint64_t tracing_id, | |
333 | uid_t tracing_uid) | |
d0b96690 | 334 | { |
aeeb48c6 | 335 | try { |
d7bfb9b0 | 336 | return new ust_registry_session_per_uid(abi, major, minor, root_shm_path, shm_path, |
aeeb48c6 | 337 | euid, egid, tracing_id, tracing_uid); |
d7bfb9b0 | 338 | } catch (const std::exception& ex) { |
aeeb48c6 JG |
339 | ERR("Failed to create per-uid registry session: %s", ex.what()); |
340 | return nullptr; | |
d7ba1388 | 341 | } |
aeeb48c6 | 342 | } |
8de88061 | 343 | |
aeeb48c6 | 344 | ust_registry_session *ust_registry_session_per_pid_create(struct ust_app *app, |
d7bfb9b0 | 345 | const lttng::sessiond::trace::abi& abi, |
aeeb48c6 JG |
346 | uint32_t major, |
347 | uint32_t minor, | |
348 | const char *root_shm_path, | |
349 | const char *shm_path, | |
350 | uid_t euid, | |
351 | gid_t egid, | |
352 | uint64_t tracing_id) | |
353 | { | |
354 | try { | |
d7bfb9b0 JG |
355 | return new ust_registry_session_per_pid(*app, abi, major, minor, root_shm_path, |
356 | shm_path, euid, egid, tracing_id); | |
357 | } catch (const std::exception& ex) { | |
aeeb48c6 JG |
358 | ERR("Failed to create per-pid registry session: %s", ex.what()); |
359 | return nullptr; | |
d0b96690 | 360 | } |
d0b96690 DG |
361 | } |
362 | ||
363 | /* | |
364 | * Destroy session registry. This does NOT free the given pointer since it | |
365 | * might get passed as a reference. The registry lock should NOT be acquired. | |
366 | */ | |
aeeb48c6 | 367 | void ust_registry_session_destroy(ust_registry_session *reg) |
d0b96690 | 368 | { |
aeeb48c6 | 369 | delete reg; |
d0b96690 | 370 | } |
d7bfb9b0 JG |
371 | |
372 | lsu::registry_enum::registry_enum( | |
373 | std::string in_name, enum lst::integer_type::signedness in_signedness) : | |
374 | name{std::move(in_name)}, signedness{in_signedness} | |
375 | { | |
376 | cds_lfht_node_init(&this->node.node); | |
377 | this->rcu_head = {}; | |
378 | } | |
379 | ||
380 | bool lsu::operator==(const lsu::registry_enum& lhs, const lsu::registry_enum& rhs) noexcept | |
381 | { | |
382 | if (lhs.signedness != rhs.signedness) { | |
383 | return false; | |
384 | } | |
385 | ||
386 | return lhs._is_equal(rhs); | |
387 | } |