sessiond: transition from lttng-ust to tracer agnostic API
[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 "utils.hpp"
15
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>
22
23 #include <inttypes.h>
24
25 namespace ls = lttng::sessiond;
26 namespace lst = lttng::sessiond::trace;
27 namespace lsu = lttng::sessiond::ust;
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 {
36 lsu::registry_enum *_enum;
37 const lsu::registry_enum *key;
38
39 LTTNG_ASSERT(node);
40 LTTNG_ASSERT(_key);
41
42 DIAGNOSTIC_PUSH
43 DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
44 _enum = caa_container_of(node, lsu::registry_enum,
45 node.node);
46 DIAGNOSTIC_POP
47
48 LTTNG_ASSERT(_enum);
49 key = (lsu::registry_enum *) _key;
50
51 return *_enum == *key;
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 {
60 lsu::registry_enum *_enum;
61 const lsu::registry_enum *key = (lsu::registry_enum *) _key;
62
63 LTTNG_ASSERT(node);
64 LTTNG_ASSERT(_key);
65
66 DIAGNOSTIC_PUSH
67 DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
68 _enum = caa_container_of(node, lsu::registry_enum, node.node);
69 DIAGNOSTIC_POP
70
71 LTTNG_ASSERT(_enum);
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 {
90 lsu::registry_enum *key = (lsu::registry_enum *) _key;
91
92 LTTNG_ASSERT(key);
93 return hash_key_str(key->name.c_str(), seed);
94 }
95
96 /*
97 * Destroy event function call of the call RCU.
98 */
99 static void ust_registry_event_destroy_rcu(struct rcu_head *head)
100 {
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);
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 */
115 void ust_registry_channel_destroy_event(lsu::registry_channel *chan,
116 lttng::sessiond::ust::registry_event *event)
117 {
118 int ret;
119 struct lttng_ht_iter iter;
120
121 LTTNG_ASSERT(chan);
122 LTTNG_ASSERT(event);
123 ASSERT_RCU_READ_LOCKED();
124
125 /* Delete the node first. */
126 iter.iter.node = &event->_node.node;
127 ret = lttng_ht_del(chan->_events, &iter);
128 LTTNG_ASSERT(!ret);
129
130 call_rcu(&event->_node.head, ust_registry_event_destroy_rcu);
131
132 return;
133 }
134
135 static void destroy_enum(lsu::registry_enum *reg_enum)
136 {
137 if (!reg_enum) {
138 return;
139 }
140
141 delete reg_enum;
142 }
143
144 static void destroy_enum_rcu(struct rcu_head *head)
145 {
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
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 */
159 static lsu::registry_enum *ust_registry_lookup_enum(
160 ust_registry_session *session,
161 const lsu::registry_enum *reg_enum_lookup)
162 {
163 lsu::registry_enum *reg_enum = NULL;
164 struct lttng_ht_node_str *node;
165 struct lttng_ht_iter iter;
166
167 ASSERT_RCU_READ_LOCKED();
168
169 cds_lfht_lookup(session->_enums->ht,
170 ht_hash_enum((void *) reg_enum_lookup, lttng_ht_seed),
171 ht_match_enum, reg_enum_lookup, &iter.iter);
172 node = lttng_ht_iter_get_node_str(&iter);
173 if (!node) {
174 goto end;
175 }
176
177 DIAGNOSTIC_PUSH
178 DIAGNOSTIC_IGNORE_INVALID_OFFSETOF
179 reg_enum = caa_container_of(node, lsu::registry_enum, node);
180 DIAGNOSTIC_POP
181
182 end:
183 return reg_enum;
184 }
185
186 /*
187 * Lookup enumeration by enum ID.
188 */
189 lsu::registry_enum::const_rcu_protected_reference
190 ust_registry_lookup_enum_by_id(const ust_registry_session *session,
191 const char *enum_name, uint64_t enum_id)
192 {
193 lsu::registry_enum *reg_enum = NULL;
194 struct lttng_ht_node_str *node;
195 struct lttng_ht_iter iter;
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);
202
203 ASSERT_RCU_READ_LOCKED();
204
205 reg_enum_lookup.id = enum_id;
206 cds_lfht_lookup(session->_enums->ht,
207 ht_hash_enum((void *) &reg_enum_lookup, lttng_ht_seed),
208 ht_match_enum_id, &reg_enum_lookup, &iter.iter);
209 node = lttng_ht_iter_get_node_str(&iter);
210 if (!node) {
211 LTTNG_THROW_PROTOCOL_ERROR(fmt::format(
212 "Unknown enumeration referenced by application event field: enum name = `{}`, enum id = {}",
213 enum_name, enum_id));
214 }
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)};
222 }
223
224 /*
225 * Create a lsu::registry_enum from the given parameters and add it to the
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 */
234 int ust_registry_create_or_find_enum(ust_registry_session *session,
235 int session_objd, char *enum_name,
236 struct lttng_ust_ctl_enum_entry *raw_entries, size_t nr_entries,
237 uint64_t *enum_id)
238 {
239 int ret = 0;
240 struct cds_lfht_node *nodep;
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);
243
244 LTTNG_ASSERT(session);
245 LTTNG_ASSERT(enum_name);
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 */
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) {
256 ret = -EINVAL;
257 goto end;
258 }
259
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());
270 ret = -ENOMEM;
271 goto end;
272 }
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);
283 if (session->_next_enum_id == -1ULL) {
284 ret = -EOVERFLOW;
285 destroy_enum(reg_enum);
286 goto end;
287 }
288 reg_enum->id = session->_next_enum_id++;
289 nodep = cds_lfht_add_unique(session->_enums->ht,
290 ht_hash_enum(reg_enum, lttng_ht_seed),
291 ht_match_enum_id, reg_enum,
292 &reg_enum->node.node);
293 LTTNG_ASSERT(nodep == &reg_enum->node.node);
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:
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 */
308 void ust_registry_destroy_enum(ust_registry_session *reg_session,
309 lsu::registry_enum *reg_enum)
310 {
311 int ret;
312 struct lttng_ht_iter iter;
313
314 LTTNG_ASSERT(reg_session);
315 LTTNG_ASSERT(reg_enum);
316 ASSERT_RCU_READ_LOCKED();
317
318 /* Delete the node first. */
319 iter.iter.node = &reg_enum->node.node;
320 ret = lttng_ht_del(reg_session->_enums.get(), &iter);
321 LTTNG_ASSERT(!ret);
322 call_rcu(&reg_enum->rcu_head, destroy_enum_rcu);
323 }
324
325 ust_registry_session *ust_registry_session_per_uid_create(const lttng::sessiond::trace::abi& abi,
326 uint32_t major,
327 uint32_t minor,
328 const char *root_shm_path,
329 const char *shm_path,
330 uid_t euid,
331 gid_t egid,
332 uint64_t tracing_id,
333 uid_t tracing_uid)
334 {
335 try {
336 return new ust_registry_session_per_uid(abi, major, minor, root_shm_path, shm_path,
337 euid, egid, tracing_id, tracing_uid);
338 } catch (const std::exception& ex) {
339 ERR("Failed to create per-uid registry session: %s", ex.what());
340 return nullptr;
341 }
342 }
343
344 ust_registry_session *ust_registry_session_per_pid_create(struct ust_app *app,
345 const lttng::sessiond::trace::abi& abi,
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 {
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) {
358 ERR("Failed to create per-pid registry session: %s", ex.what());
359 return nullptr;
360 }
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 */
367 void ust_registry_session_destroy(ust_registry_session *reg)
368 {
369 delete reg;
370 }
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 }
This page took 0.037149 seconds and 5 git commands to generate.