ce76e403f3bb361882be569c7a60619f56ebbd4b
[lttng-tools.git] / src / bin / lttng-sessiond / ust-registry.cpp
1 /*
2 * Copyright (C) 2013 David Goulet <dgoulet@efficios.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
8 #define _LGPL_SOURCE
9
10 #include "ust-registry.hpp"
11 #include "lttng-sessiond.hpp"
12 #include "notification-thread-commands.hpp"
13 #include "ust-app.hpp"
14 #include "ust-registry-session-pid.hpp"
15 #include "ust-registry-session-uid.hpp"
16 #include "utils.hpp"
17
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>
24
25 #include <inttypes.h>
26
27 namespace ls = lttng::sessiond;
28 namespace lst = lttng::sessiond::trace;
29 namespace lsu = lttng::sessiond::ust;
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 {
38 lsu::registry_enum *_enum;
39 const lsu::registry_enum *key;
40
41 LTTNG_ASSERT(node);
42 LTTNG_ASSERT(_key);
43
44 DIAGNOSTIC_PUSH
45 DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
46 _enum = caa_container_of(node, lsu::registry_enum,
47 node.node);
48 DIAGNOSTIC_POP
49
50 LTTNG_ASSERT(_enum);
51 key = (lsu::registry_enum *) _key;
52
53 return *_enum == *key;
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 {
62 lsu::registry_enum *_enum;
63 const lsu::registry_enum *key = (lsu::registry_enum *) _key;
64
65 LTTNG_ASSERT(node);
66 LTTNG_ASSERT(_key);
67
68 DIAGNOSTIC_PUSH
69 DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
70 _enum = caa_container_of(node, lsu::registry_enum, node.node);
71 DIAGNOSTIC_POP
72
73 LTTNG_ASSERT(_enum);
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 {
92 lsu::registry_enum *key = (lsu::registry_enum *) _key;
93
94 LTTNG_ASSERT(key);
95 return hash_key_str(key->name.c_str(), seed);
96 }
97
98 /*
99 * Destroy event function call of the call RCU.
100 */
101 static void ust_registry_event_destroy_rcu(struct rcu_head *head)
102 {
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);
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 */
117 void ust_registry_channel_destroy_event(lsu::registry_channel *chan,
118 lttng::sessiond::ust::registry_event *event)
119 {
120 int ret;
121 struct lttng_ht_iter iter;
122
123 LTTNG_ASSERT(chan);
124 LTTNG_ASSERT(event);
125 ASSERT_RCU_READ_LOCKED();
126
127 /* Delete the node first. */
128 iter.iter.node = &event->_node.node;
129 ret = lttng_ht_del(chan->_events, &iter);
130 LTTNG_ASSERT(!ret);
131
132 call_rcu(&event->_node.head, ust_registry_event_destroy_rcu);
133
134 return;
135 }
136
137 static void destroy_enum(lsu::registry_enum *reg_enum)
138 {
139 if (!reg_enum) {
140 return;
141 }
142
143 delete reg_enum;
144 }
145
146 static void destroy_enum_rcu(struct rcu_head *head)
147 {
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
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 */
161 static lsu::registry_enum *ust_registry_lookup_enum(
162 lsu::registry_session *session,
163 const lsu::registry_enum *reg_enum_lookup)
164 {
165 lsu::registry_enum *reg_enum = NULL;
166 struct lttng_ht_node_str *node;
167 struct lttng_ht_iter iter;
168
169 ASSERT_RCU_READ_LOCKED();
170
171 cds_lfht_lookup(session->_enums->ht,
172 ht_hash_enum((void *) reg_enum_lookup, lttng_ht_seed),
173 ht_match_enum, reg_enum_lookup, &iter.iter);
174 node = lttng_ht_iter_get_node_str(&iter);
175 if (!node) {
176 goto end;
177 }
178
179 DIAGNOSTIC_PUSH
180 DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
181 reg_enum = caa_container_of(node, lsu::registry_enum, node);
182 DIAGNOSTIC_POP
183
184 end:
185 return reg_enum;
186 }
187
188 /*
189 * Lookup enumeration by enum ID.
190 */
191 lsu::registry_enum::const_rcu_protected_reference
192 ust_registry_lookup_enum_by_id(const lsu::registry_session *session,
193 const char *enum_name, uint64_t enum_id)
194 {
195 lsu::registry_enum *reg_enum = NULL;
196 struct lttng_ht_node_str *node;
197 struct lttng_ht_iter iter;
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);
204
205 ASSERT_RCU_READ_LOCKED();
206
207 reg_enum_lookup.id = enum_id;
208 cds_lfht_lookup(session->_enums->ht,
209 ht_hash_enum((void *) &reg_enum_lookup, lttng_ht_seed),
210 ht_match_enum_id, &reg_enum_lookup, &iter.iter);
211 node = lttng_ht_iter_get_node_str(&iter);
212 if (!node) {
213 LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
214 "Unknown enumeration referenced by application event field: enum name = `{}`, enum id = {}",
215 enum_name, enum_id));
216 }
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)};
224 }
225
226 /*
227 * Create a lsu::registry_enum from the given parameters and add it to the
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 */
236 int ust_registry_create_or_find_enum(lsu::registry_session *session,
237 int session_objd, char *enum_name,
238 struct lttng_ust_ctl_enum_entry *raw_entries, size_t nr_entries,
239 uint64_t *enum_id)
240 {
241 int ret = 0;
242 struct cds_lfht_node *nodep;
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);
245
246 LTTNG_ASSERT(session);
247 LTTNG_ASSERT(enum_name);
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 */
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) {
258 ret = -EINVAL;
259 goto end;
260 }
261
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());
272 ret = -ENOMEM;
273 goto end;
274 }
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);
285 if (session->_next_enum_id == -1ULL) {
286 ret = -EOVERFLOW;
287 destroy_enum(reg_enum);
288 goto end;
289 }
290 reg_enum->id = session->_next_enum_id++;
291 nodep = cds_lfht_add_unique(session->_enums->ht,
292 ht_hash_enum(reg_enum, lttng_ht_seed),
293 ht_match_enum_id, reg_enum,
294 &reg_enum->node.node);
295 LTTNG_ASSERT(nodep == &reg_enum->node.node);
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:
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 */
310 void ust_registry_destroy_enum(lsu::registry_session *reg_session,
311 lsu::registry_enum *reg_enum)
312 {
313 int ret;
314 struct lttng_ht_iter iter;
315
316 LTTNG_ASSERT(reg_session);
317 LTTNG_ASSERT(reg_enum);
318 ASSERT_RCU_READ_LOCKED();
319
320 /* Delete the node first. */
321 iter.iter.node = &reg_enum->node.node;
322 ret = lttng_ht_del(reg_session->_enums.get(), &iter);
323 LTTNG_ASSERT(!ret);
324 call_rcu(&reg_enum->rcu_head, destroy_enum_rcu);
325 }
326
327 lsu::registry_session *ust_registry_session_per_uid_create(const lttng::sessiond::trace::abi& abi,
328 uint32_t major,
329 uint32_t minor,
330 const char *root_shm_path,
331 const char *shm_path,
332 uid_t euid,
333 gid_t egid,
334 uint64_t tracing_id,
335 uid_t tracing_uid)
336 {
337 try {
338 return new lsu::registry_session_per_uid(abi, major, minor, root_shm_path, shm_path,
339 euid, egid, tracing_id, tracing_uid);
340 } catch (const std::exception& ex) {
341 ERR("Failed to create per-uid registry session: %s", ex.what());
342 return nullptr;
343 }
344 }
345
346 lsu::registry_session *ust_registry_session_per_pid_create(struct ust_app *app,
347 const lttng::sessiond::trace::abi& abi,
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 {
357 return new lsu::registry_session_per_pid(*app, abi, major, minor, root_shm_path,
358 shm_path, euid, egid, tracing_id);
359 } catch (const std::exception& ex) {
360 ERR("Failed to create per-pid registry session: %s", ex.what());
361 return nullptr;
362 }
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 */
369 void ust_registry_session_destroy(lsu::registry_session *reg)
370 {
371 delete reg;
372 }
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 }
This page took 0.036395 seconds and 3 git commands to generate.