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