Update libside rfc
[libside.git] / src / side.c
CommitLineData
67337c4a
MD
1// SPDX-License-Identifier: MIT
2/*
3 * Copyright 2022 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 */
5
6#include <side/trace.h>
7#include <string.h>
b1bf768c 8#include <assert.h>
a125f217
MD
9#include <pthread.h>
10#include <unistd.h>
7e38dc2e 11#include <poll.h>
67337c4a 12
7e38dc2e 13#include "compiler.h"
67337c4a
MD
14#include "rcu.h"
15#include "list.h"
873bbf16 16#include "rculist.h"
67337c4a 17
871851e7 18/* Top 8 bits reserved for shared tracer use. */
67337c4a 19#if SIDE_BITS_PER_LONG == 64
871851e7
MD
20# define SIDE_EVENT_ENABLED_SHARED_MASK 0xFF00000000000000ULL
21# define SIDE_EVENT_ENABLED_SHARED_USER_EVENT_MASK 0x8000000000000000ULL
22# define SIDE_EVENT_ENABLED_SHARED_PTRACE_MASK 0x4000000000000000ULL
67337c4a 23
871851e7
MD
24/* Allow 2^56 private tracer references on an event. */
25# define SIDE_EVENT_ENABLED_PRIVATE_MASK 0x00FFFFFFFFFFFFFFULL
67337c4a 26#else
871851e7
MD
27# define SIDE_EVENT_ENABLED_SHARED_MASK 0xFF000000UL
28# define SIDE_EVENT_ENABLED_SHARED_USER_EVENT_MASK 0x80000000UL
29# define SIDE_EVENT_ENABLED_SHARED_PTRACE_MASK 0x40000000UL
67337c4a 30
871851e7
MD
31/* Allow 2^24 private tracer references on an event. */
32# define SIDE_EVENT_ENABLED_PRIVATE_MASK 0x00FFFFFFUL
67337c4a
MD
33#endif
34
bffe9ae3
MD
35#define SIDE_KEY_RESERVED_RANGE_END 0x8
36
37/* Key 0x0 is reserved to match all. */
38#define SIDE_KEY_MATCH_ALL 0x0
871851e7 39/* Key 0x1 is reserved for user event. */
bffe9ae3 40#define SIDE_KEY_USER_EVENT 0x1
871851e7 41/* Key 0x2 is reserved for ptrace. */
bffe9ae3 42#define SIDE_KEY_PTRACE 0x2
871851e7 43
7e38dc2e
MD
44#define SIDE_RETRY_BUSY_LOOP_ATTEMPTS 100
45#define SIDE_RETRY_DELAY_MS 1
46
67337c4a
MD
47struct side_events_register_handle {
48 struct side_list_node node;
49 struct side_event_description **events;
50 uint32_t nr_events;
51};
52
53struct side_tracer_handle {
54 struct side_list_node node;
55 void (*cb)(enum side_tracer_notification notif,
56 struct side_event_description **events, uint32_t nr_events, void *priv);
57 void *priv;
58};
59
bffe9ae3
MD
60struct side_statedump_notification {
61 struct side_list_node node;
62 uint64_t key;
63};
64
f0b01832 65struct side_statedump_request_handle {
bffe9ae3
MD
66 struct side_list_node node; /* Statedump request RCU list node. */
67 struct side_list_head notification_queue; /* Queue of struct side_statedump_notification */
3da13b2c 68 void (*cb)(void *statedump_request_key);
bffe9ae3
MD
69 char *name;
70 enum side_statedump_mode mode;
f0b01832
MD
71};
72
867b4725
MD
73struct side_callback {
74 union {
75 void (*call)(const struct side_event_description *desc,
76 const struct side_arg_vec *side_arg_vec,
5e523511 77 void *priv, void *caller_addr);
867b4725
MD
78 void (*call_variadic)(const struct side_event_description *desc,
79 const struct side_arg_vec *side_arg_vec,
80 const struct side_arg_dynamic_struct *var_struct,
5e523511 81 void *priv, void *caller_addr);
867b4725
MD
82 } u;
83 void *priv;
bffe9ae3 84 uint64_t key;
867b4725
MD
85};
86
a125f217
MD
87enum agent_thread_state {
88 AGENT_THREAD_STATE_BLOCKED = 0,
89 AGENT_THREAD_STATE_HANDLE_REQUEST = (1 << 0),
90 AGENT_THREAD_STATE_EXIT = (1 << 1),
7e38dc2e
MD
91 AGENT_THREAD_STATE_PAUSE = (1 << 2),
92 AGENT_THREAD_STATE_PAUSE_ACK = (1 << 3),
a125f217
MD
93};
94
95struct statedump_agent_thread {
96 long ref;
97 pthread_t id;
98 enum agent_thread_state state;
76dd11f9
MD
99 pthread_cond_t worker_cond;
100 pthread_cond_t waiter_cond;
a125f217
MD
101};
102
873bbf16 103static struct side_rcu_gp_state event_rcu_gp, statedump_rcu_gp;
67337c4a
MD
104
105/*
106 * Lazy initialization for early use within library constructors.
107 */
108static bool initialized;
109/*
110 * Do not register/unregister any more events after destructor.
111 */
112static bool finalized;
113
114/*
115 * Recursive mutex to allow tracer callbacks to use the side API.
116 */
873bbf16
MD
117static pthread_mutex_t side_event_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
118static pthread_mutex_t side_statedump_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
bffe9ae3 119static pthread_mutex_t side_key_lock = PTHREAD_MUTEX_INITIALIZER;
76dd11f9
MD
120/*
121 * The side_agent_thread_lock protects the life-time of the agent
122 * thread: reference counting, creation, join. It is not taken by
123 * the agent thread per se so it does not have a circular dependency
124 * with pthread join.
125 * The side_statedump_lock nests inside the side_agent_thread_lock.
126 */
127static pthread_mutex_t side_agent_thread_lock = PTHREAD_MUTEX_INITIALIZER;
bffe9ae3
MD
128
129/* Dynamic tracer key allocation. */
130static uint64_t side_key_next = SIDE_KEY_RESERVED_RANGE_END;
67337c4a 131
a125f217
MD
132static struct statedump_agent_thread statedump_agent_thread;
133
67337c4a
MD
134static DEFINE_SIDE_LIST_HEAD(side_events_list);
135static DEFINE_SIDE_LIST_HEAD(side_tracer_list);
bffe9ae3
MD
136
137/*
138 * The statedump request list is a RCU list to allow the agent thread to
139 * iterate over this list with a RCU read-side lock.
140 */
f0b01832 141static DEFINE_SIDE_LIST_HEAD(side_statedump_list);
67337c4a
MD
142
143/*
144 * The empty callback has a NULL function callback pointer, which stops
145 * iteration on the array of callbacks immediately.
146 */
867b4725 147const char side_empty_callback[sizeof(struct side_callback)];
67337c4a 148
bffe9ae3
MD
149side_static_event(side_statedump_begin, "side", "statedump_begin",
150 SIDE_LOGLEVEL_INFO, side_field_list(side_field_string("name")));
151side_static_event(side_statedump_end, "side", "statedump_end",
152 SIDE_LOGLEVEL_INFO, side_field_list(side_field_string("name")));
153
871851e7
MD
154/*
155 * side_ptrace_hook is a place holder for a debugger breakpoint.
156 * var_struct is NULL if not variadic.
157 */
158void side_ptrace_hook(const struct side_event_state *event_state __attribute__((unused)),
e7f33720 159 const struct side_arg_vec *side_arg_vec __attribute__((unused)),
5e523511
MD
160 const struct side_arg_dynamic_struct *var_struct __attribute__((unused)),
161 void *caller_addr __attribute__((unused)))
e7f33720 162 __attribute__((noinline));
871851e7 163void side_ptrace_hook(const struct side_event_state *event_state __attribute__((unused)),
e7f33720 164 const struct side_arg_vec *side_arg_vec __attribute__((unused)),
5e523511
MD
165 const struct side_arg_dynamic_struct *var_struct __attribute__((unused)),
166 void *caller_addr __attribute__((unused)))
871851e7
MD
167{
168}
169
5e523511 170static inline __attribute__((always_inline))
bffe9ae3 171void _side_call(const struct side_event_state *event_state, const struct side_arg_vec *side_arg_vec, uint64_t key)
67337c4a 172{
5e523511 173 void *caller_addr = __builtin_return_address(0);
67337c4a 174 struct side_rcu_read_state rcu_read_state;
b2a84b9f 175 const struct side_event_state_0 *es0;
67337c4a
MD
176 const struct side_callback *side_cb;
177 uintptr_t enabled;
178
179 if (side_unlikely(finalized))
180 return;
181 if (side_unlikely(!initialized))
182 side_init();
b2a84b9f
MD
183 if (side_unlikely(event_state->version != 0))
184 abort();
49aea3ef 185 es0 = side_container_of(event_state, const struct side_event_state_0, parent);
7269a8a3 186 assert(!(es0->desc->flags & SIDE_EVENT_FLAG_VARIADIC));
b2a84b9f 187 enabled = __atomic_load_n(&es0->enabled, __ATOMIC_RELAXED);
871851e7
MD
188 if (side_unlikely(enabled & SIDE_EVENT_ENABLED_SHARED_MASK)) {
189 if ((enabled & SIDE_EVENT_ENABLED_SHARED_USER_EVENT_MASK) &&
bffe9ae3 190 (key == SIDE_KEY_MATCH_ALL || key == SIDE_KEY_USER_EVENT)) {
871851e7
MD
191 // TODO: call kernel write.
192 }
193 if ((enabled & SIDE_EVENT_ENABLED_SHARED_PTRACE_MASK) &&
bffe9ae3 194 (key == SIDE_KEY_MATCH_ALL || key == SIDE_KEY_PTRACE))
5e523511 195 side_ptrace_hook(event_state, side_arg_vec, NULL, caller_addr);
67337c4a 196 }
873bbf16 197 side_rcu_read_begin(&event_rcu_gp, &rcu_read_state);
92c377f9 198 for (side_cb = side_rcu_dereference(es0->callbacks); side_cb->u.call != NULL; side_cb++) {
bffe9ae3 199 if (key != SIDE_KEY_MATCH_ALL && side_cb->key != SIDE_KEY_MATCH_ALL && side_cb->key != key)
92c377f9 200 continue;
5e523511 201 side_cb->u.call(es0->desc, side_arg_vec, side_cb->priv, caller_addr);
92c377f9 202 }
873bbf16 203 side_rcu_read_end(&event_rcu_gp, &rcu_read_state);
67337c4a
MD
204}
205
92c377f9
MD
206void side_call(const struct side_event_state *event_state, const struct side_arg_vec *side_arg_vec)
207{
bffe9ae3 208 _side_call(event_state, side_arg_vec, SIDE_KEY_MATCH_ALL);
92c377f9
MD
209}
210
3da13b2c
MD
211void side_statedump_call(const struct side_event_state *event_state,
212 const struct side_arg_vec *side_arg_vec,
213 void *statedump_request_key)
74be90b7 214{
3da13b2c 215 _side_call(event_state, side_arg_vec, *(const uint64_t *) statedump_request_key);
74be90b7
MD
216}
217
5e523511 218static inline __attribute__((always_inline))
74be90b7 219void _side_call_variadic(const struct side_event_state *event_state,
e7f33720
MD
220 const struct side_arg_vec *side_arg_vec,
221 const struct side_arg_dynamic_struct *var_struct,
222 uint64_t key)
67337c4a 223{
5e523511 224 void *caller_addr = __builtin_return_address(0);
67337c4a 225 struct side_rcu_read_state rcu_read_state;
b2a84b9f 226 const struct side_event_state_0 *es0;
67337c4a
MD
227 const struct side_callback *side_cb;
228 uintptr_t enabled;
229
230 if (side_unlikely(finalized))
231 return;
232 if (side_unlikely(!initialized))
233 side_init();
b2a84b9f
MD
234 if (side_unlikely(event_state->version != 0))
235 abort();
49aea3ef 236 es0 = side_container_of(event_state, const struct side_event_state_0, parent);
7269a8a3 237 assert(es0->desc->flags & SIDE_EVENT_FLAG_VARIADIC);
b2a84b9f 238 enabled = __atomic_load_n(&es0->enabled, __ATOMIC_RELAXED);
871851e7
MD
239 if (side_unlikely(enabled & SIDE_EVENT_ENABLED_SHARED_MASK)) {
240 if ((enabled & SIDE_EVENT_ENABLED_SHARED_USER_EVENT_MASK) &&
bffe9ae3 241 (key == SIDE_KEY_MATCH_ALL || key == SIDE_KEY_USER_EVENT)) {
871851e7
MD
242 // TODO: call kernel write.
243 }
244 if ((enabled & SIDE_EVENT_ENABLED_SHARED_PTRACE_MASK) &&
bffe9ae3 245 (key == SIDE_KEY_MATCH_ALL || key == SIDE_KEY_PTRACE))
5e523511 246 side_ptrace_hook(event_state, side_arg_vec, var_struct, caller_addr);
67337c4a 247 }
873bbf16 248 side_rcu_read_begin(&event_rcu_gp, &rcu_read_state);
92c377f9 249 for (side_cb = side_rcu_dereference(es0->callbacks); side_cb->u.call_variadic != NULL; side_cb++) {
bffe9ae3 250 if (key != SIDE_KEY_MATCH_ALL && side_cb->key != SIDE_KEY_MATCH_ALL && side_cb->key != key)
92c377f9 251 continue;
5e523511 252 side_cb->u.call_variadic(es0->desc, side_arg_vec, var_struct, side_cb->priv, caller_addr);
92c377f9 253 }
873bbf16 254 side_rcu_read_end(&event_rcu_gp, &rcu_read_state);
67337c4a
MD
255}
256
92c377f9 257void side_call_variadic(const struct side_event_state *event_state,
e7f33720
MD
258 const struct side_arg_vec *side_arg_vec,
259 const struct side_arg_dynamic_struct *var_struct)
92c377f9 260{
bffe9ae3 261 _side_call_variadic(event_state, side_arg_vec, var_struct, SIDE_KEY_MATCH_ALL);
74be90b7
MD
262}
263
f0b01832 264void side_statedump_call_variadic(const struct side_event_state *event_state,
3da13b2c
MD
265 const struct side_arg_vec *side_arg_vec,
266 const struct side_arg_dynamic_struct *var_struct,
267 void *statedump_request_key)
74be90b7 268{
3da13b2c 269 _side_call_variadic(event_state, side_arg_vec, var_struct, *(const uint64_t *) statedump_request_key);
92c377f9
MD
270}
271
67337c4a
MD
272static
273const struct side_callback *side_tracer_callback_lookup(
274 const struct side_event_description *desc,
bffe9ae3 275 void *call, void *priv, uint64_t key)
67337c4a 276{
0b9e59d6 277 struct side_event_state *event_state = side_ptr_get(desc->state);
b2a84b9f 278 const struct side_event_state_0 *es0;
67337c4a
MD
279 const struct side_callback *cb;
280
b2a84b9f
MD
281 if (side_unlikely(event_state->version != 0))
282 abort();
49aea3ef 283 es0 = side_container_of(event_state, const struct side_event_state_0, parent);
7269a8a3 284 for (cb = es0->callbacks; cb->u.call != NULL; cb++) {
92c377f9 285 if ((void *) cb->u.call == call && cb->priv == priv && cb->key == key)
67337c4a
MD
286 return cb;
287 }
288 return NULL;
289}
290
291static
292int _side_tracer_callback_register(struct side_event_description *desc,
bffe9ae3 293 void *call, void *priv, uint64_t key)
67337c4a 294{
0b9e59d6 295 struct side_event_state *event_state;
67337c4a 296 struct side_callback *old_cb, *new_cb;
b2a84b9f 297 struct side_event_state_0 *es0;
67337c4a
MD
298 int ret = SIDE_ERROR_OK;
299 uint32_t old_nr_cb;
300
301 if (!call)
302 return SIDE_ERROR_INVAL;
303 if (finalized)
304 return SIDE_ERROR_EXITING;
305 if (!initialized)
306 side_init();
873bbf16 307 pthread_mutex_lock(&side_event_lock);
0b9e59d6 308 event_state = side_ptr_get(desc->state);
b2a84b9f
MD
309 if (side_unlikely(event_state->version != 0))
310 abort();
49aea3ef 311 es0 = side_container_of(event_state, struct side_event_state_0, parent);
3cac1780 312 old_nr_cb = es0->nr_callbacks;
67337c4a
MD
313 if (old_nr_cb == UINT32_MAX) {
314 ret = SIDE_ERROR_INVAL;
315 goto unlock;
316 }
317 /* Reject duplicate (call, priv) tuples. */
92c377f9 318 if (side_tracer_callback_lookup(desc, call, priv, key)) {
67337c4a
MD
319 ret = SIDE_ERROR_EXIST;
320 goto unlock;
321 }
7269a8a3 322 old_cb = (struct side_callback *) es0->callbacks;
67337c4a
MD
323 /* old_nr_cb + 1 (new cb) + 1 (NULL) */
324 new_cb = (struct side_callback *) calloc(old_nr_cb + 2, sizeof(struct side_callback));
325 if (!new_cb) {
326 ret = SIDE_ERROR_NOMEM;
327 goto unlock;
328 }
329 memcpy(new_cb, old_cb, old_nr_cb);
330 if (desc->flags & SIDE_EVENT_FLAG_VARIADIC)
331 new_cb[old_nr_cb].u.call_variadic =
332 (side_tracer_callback_variadic_func) call;
333 else
334 new_cb[old_nr_cb].u.call =
335 (side_tracer_callback_func) call;
336 new_cb[old_nr_cb].priv = priv;
92c377f9 337 new_cb[old_nr_cb].key = key;
f60d8121 338 /* High order bits are already zeroed. */
7269a8a3 339 side_rcu_assign_pointer(es0->callbacks, new_cb);
873bbf16 340 side_rcu_wait_grace_period(&event_rcu_gp);
67337c4a
MD
341 if (old_nr_cb)
342 free(old_cb);
3cac1780 343 es0->nr_callbacks++;
67337c4a
MD
344 /* Increment concurrently with kernel setting the top bits. */
345 if (!old_nr_cb)
b2a84b9f 346 (void) __atomic_add_fetch(&es0->enabled, 1, __ATOMIC_RELAXED);
67337c4a 347unlock:
873bbf16 348 pthread_mutex_unlock(&side_event_lock);
67337c4a
MD
349 return ret;
350}
351
352int side_tracer_callback_register(struct side_event_description *desc,
353 side_tracer_callback_func call,
bffe9ae3 354 void *priv, uint64_t key)
67337c4a
MD
355{
356 if (desc->flags & SIDE_EVENT_FLAG_VARIADIC)
357 return SIDE_ERROR_INVAL;
92c377f9 358 return _side_tracer_callback_register(desc, (void *) call, priv, key);
67337c4a
MD
359}
360
361int side_tracer_callback_variadic_register(struct side_event_description *desc,
362 side_tracer_callback_variadic_func call_variadic,
bffe9ae3 363 void *priv, uint64_t key)
67337c4a
MD
364{
365 if (!(desc->flags & SIDE_EVENT_FLAG_VARIADIC))
366 return SIDE_ERROR_INVAL;
92c377f9 367 return _side_tracer_callback_register(desc, (void *) call_variadic, priv, key);
67337c4a
MD
368}
369
370static int _side_tracer_callback_unregister(struct side_event_description *desc,
bffe9ae3 371 void *call, void *priv, uint64_t key)
67337c4a 372{
0b9e59d6 373 struct side_event_state *event_state;
67337c4a
MD
374 struct side_callback *old_cb, *new_cb;
375 const struct side_callback *cb_pos;
b2a84b9f 376 struct side_event_state_0 *es0;
67337c4a
MD
377 uint32_t pos_idx;
378 int ret = SIDE_ERROR_OK;
379 uint32_t old_nr_cb;
380
381 if (!call)
382 return SIDE_ERROR_INVAL;
383 if (finalized)
384 return SIDE_ERROR_EXITING;
385 if (!initialized)
386 side_init();
873bbf16 387 pthread_mutex_lock(&side_event_lock);
0b9e59d6 388 event_state = side_ptr_get(desc->state);
b2a84b9f
MD
389 if (side_unlikely(event_state->version != 0))
390 abort();
49aea3ef 391 es0 = side_container_of(event_state, struct side_event_state_0, parent);
92c377f9 392 cb_pos = side_tracer_callback_lookup(desc, call, priv, key);
67337c4a
MD
393 if (!cb_pos) {
394 ret = SIDE_ERROR_NOENT;
395 goto unlock;
396 }
3cac1780 397 old_nr_cb = es0->nr_callbacks;
7269a8a3 398 old_cb = (struct side_callback *) es0->callbacks;
67337c4a
MD
399 if (old_nr_cb == 1) {
400 new_cb = (struct side_callback *) &side_empty_callback;
401 } else {
7269a8a3 402 pos_idx = cb_pos - es0->callbacks;
67337c4a
MD
403 /* Remove entry at pos_idx. */
404 /* old_nr_cb - 1 (removed cb) + 1 (NULL) */
405 new_cb = (struct side_callback *) calloc(old_nr_cb, sizeof(struct side_callback));
406 if (!new_cb) {
407 ret = SIDE_ERROR_NOMEM;
408 goto unlock;
409 }
410 memcpy(new_cb, old_cb, pos_idx);
411 memcpy(&new_cb[pos_idx], &old_cb[pos_idx + 1], old_nr_cb - pos_idx - 1);
412 }
f60d8121 413 /* High order bits are already zeroed. */
7269a8a3 414 side_rcu_assign_pointer(es0->callbacks, new_cb);
873bbf16 415 side_rcu_wait_grace_period(&event_rcu_gp);
67337c4a 416 free(old_cb);
3cac1780 417 es0->nr_callbacks--;
67337c4a
MD
418 /* Decrement concurrently with kernel setting the top bits. */
419 if (old_nr_cb == 1)
b2a84b9f 420 (void) __atomic_add_fetch(&es0->enabled, -1, __ATOMIC_RELAXED);
67337c4a 421unlock:
873bbf16 422 pthread_mutex_unlock(&side_event_lock);
67337c4a
MD
423 return ret;
424}
425
426int side_tracer_callback_unregister(struct side_event_description *desc,
427 side_tracer_callback_func call,
bffe9ae3 428 void *priv, uint64_t key)
67337c4a
MD
429{
430 if (desc->flags & SIDE_EVENT_FLAG_VARIADIC)
431 return SIDE_ERROR_INVAL;
92c377f9 432 return _side_tracer_callback_unregister(desc, (void *) call, priv, key);
67337c4a
MD
433}
434
435int side_tracer_callback_variadic_unregister(struct side_event_description *desc,
436 side_tracer_callback_variadic_func call_variadic,
bffe9ae3 437 void *priv, uint64_t key)
67337c4a
MD
438{
439 if (!(desc->flags & SIDE_EVENT_FLAG_VARIADIC))
440 return SIDE_ERROR_INVAL;
92c377f9 441 return _side_tracer_callback_unregister(desc, (void *) call_variadic, priv, key);
67337c4a
MD
442}
443
444struct side_events_register_handle *side_events_register(struct side_event_description **events, uint32_t nr_events)
445{
446 struct side_events_register_handle *events_handle = NULL;
447 struct side_tracer_handle *tracer_handle;
448
449 if (finalized)
450 return NULL;
451 if (!initialized)
452 side_init();
453 events_handle = (struct side_events_register_handle *)
454 calloc(1, sizeof(struct side_events_register_handle));
455 if (!events_handle)
456 return NULL;
457 events_handle->events = events;
458 events_handle->nr_events = nr_events;
459
873bbf16 460 pthread_mutex_lock(&side_event_lock);
67337c4a
MD
461 side_list_insert_node_tail(&side_events_list, &events_handle->node);
462 side_list_for_each_entry(tracer_handle, &side_tracer_list, node) {
463 tracer_handle->cb(SIDE_TRACER_NOTIFICATION_INSERT_EVENTS,
464 events, nr_events, tracer_handle->priv);
465 }
873bbf16 466 pthread_mutex_unlock(&side_event_lock);
67337c4a
MD
467 //TODO: call event batch register ioctl
468 return events_handle;
469}
470
471static
472void side_event_remove_callbacks(struct side_event_description *desc)
473{
0b9e59d6 474 struct side_event_state *event_state = side_ptr_get(desc->state);
b2a84b9f 475 struct side_event_state_0 *es0;
67337c4a 476 struct side_callback *old_cb;
3cac1780 477 uint32_t nr_cb;
67337c4a 478
b2a84b9f
MD
479 if (side_unlikely(event_state->version != 0))
480 abort();
49aea3ef 481 es0 = side_container_of(event_state, struct side_event_state_0, parent);
3cac1780
MD
482 nr_cb = es0->nr_callbacks;
483 if (!nr_cb)
484 return;
7269a8a3 485 old_cb = (struct side_callback *) es0->callbacks;
b2a84b9f 486 (void) __atomic_add_fetch(&es0->enabled, -1, __ATOMIC_RELAXED);
67337c4a
MD
487 /*
488 * Setting the state back to 0 cb and empty callbacks out of
489 * caution. This should not matter because instrumentation is
490 * unreachable.
491 */
3cac1780 492 es0->nr_callbacks = 0;
7269a8a3 493 side_rcu_assign_pointer(es0->callbacks, &side_empty_callback);
67337c4a
MD
494 /*
495 * No need to wait for grace period because instrumentation is
496 * unreachable.
497 */
498 free(old_cb);
499}
500
501/*
502 * Unregister event handle. At this point, all side events in that
503 * handle should be unreachable.
504 */
505void side_events_unregister(struct side_events_register_handle *events_handle)
506{
507 struct side_tracer_handle *tracer_handle;
508 uint32_t i;
509
510 if (!events_handle)
511 return;
512 if (finalized)
513 return;
514 if (!initialized)
515 side_init();
873bbf16 516 pthread_mutex_lock(&side_event_lock);
67337c4a
MD
517 side_list_remove_node(&events_handle->node);
518 side_list_for_each_entry(tracer_handle, &side_tracer_list, node) {
519 tracer_handle->cb(SIDE_TRACER_NOTIFICATION_REMOVE_EVENTS,
520 events_handle->events, events_handle->nr_events,
521 tracer_handle->priv);
522 }
523 for (i = 0; i < events_handle->nr_events; i++) {
524 struct side_event_description *event = events_handle->events[i];
525
526 /* Skip NULL pointers */
527 if (!event)
528 continue;
529 side_event_remove_callbacks(event);
530 }
873bbf16 531 pthread_mutex_unlock(&side_event_lock);
67337c4a
MD
532 //TODO: call event batch unregister ioctl
533 free(events_handle);
534}
535
536struct side_tracer_handle *side_tracer_event_notification_register(
537 void (*cb)(enum side_tracer_notification notif,
538 struct side_event_description **events, uint32_t nr_events, void *priv),
539 void *priv)
540{
541 struct side_tracer_handle *tracer_handle;
542 struct side_events_register_handle *events_handle;
543
544 if (finalized)
545 return NULL;
546 if (!initialized)
547 side_init();
548 tracer_handle = (struct side_tracer_handle *)
549 calloc(1, sizeof(struct side_tracer_handle));
550 if (!tracer_handle)
551 return NULL;
873bbf16 552 pthread_mutex_lock(&side_event_lock);
67337c4a
MD
553 tracer_handle->cb = cb;
554 tracer_handle->priv = priv;
555 side_list_insert_node_tail(&side_tracer_list, &tracer_handle->node);
556 side_list_for_each_entry(events_handle, &side_events_list, node) {
557 cb(SIDE_TRACER_NOTIFICATION_INSERT_EVENTS,
558 events_handle->events, events_handle->nr_events, priv);
559 }
873bbf16 560 pthread_mutex_unlock(&side_event_lock);
67337c4a
MD
561 return tracer_handle;
562}
563
564void side_tracer_event_notification_unregister(struct side_tracer_handle *tracer_handle)
565{
566 struct side_events_register_handle *events_handle;
567
568 if (finalized)
569 return;
570 if (!initialized)
571 side_init();
873bbf16 572 pthread_mutex_lock(&side_event_lock);
67337c4a
MD
573 side_list_for_each_entry(events_handle, &side_events_list, node) {
574 tracer_handle->cb(SIDE_TRACER_NOTIFICATION_REMOVE_EVENTS,
575 events_handle->events, events_handle->nr_events,
576 tracer_handle->priv);
577 }
578 side_list_remove_node(&tracer_handle->node);
873bbf16 579 pthread_mutex_unlock(&side_event_lock);
be787080 580 free(tracer_handle);
67337c4a
MD
581}
582
bffe9ae3
MD
583/* Called with side_statedump_lock held. */
584static
585void queue_statedump_pending(struct side_statedump_request_handle *handle, uint64_t key)
586{
587 struct side_statedump_notification *notif;
588
589 notif = (struct side_statedump_notification *) calloc(1, sizeof(struct side_statedump_notification));
590 if (!notif)
591 abort();
592 notif->key = key;
593 side_list_insert_node_tail(&handle->notification_queue, &notif->node);
76dd11f9 594 if (handle->mode == SIDE_STATEDUMP_MODE_AGENT_THREAD) {
a125f217 595 (void)__atomic_or_fetch(&statedump_agent_thread.state, AGENT_THREAD_STATE_HANDLE_REQUEST, __ATOMIC_SEQ_CST);
76dd11f9
MD
596 pthread_cond_broadcast(&statedump_agent_thread.worker_cond);
597 }
bffe9ae3
MD
598}
599
600/* Called with side_statedump_lock held. */
601static
602void unqueue_statedump_pending(struct side_statedump_request_handle *handle, uint64_t key)
603{
604 struct side_statedump_notification *notif, *tmp;
605
606 side_list_for_each_entry_safe(notif, tmp, &handle->notification_queue, node) {
607 if (key == SIDE_KEY_MATCH_ALL || key == notif->key) {
608 side_list_remove_node(&notif->node);
609 free(notif);
610 }
611 }
612}
613
a125f217
MD
614static
615void side_statedump_run(struct side_statedump_request_handle *handle,
616 struct side_statedump_notification *notif)
617{
3da13b2c 618 side_statedump_event_call(side_statedump_begin, &notif->key,
a125f217 619 side_arg_list(side_arg_string(handle->name)));
3da13b2c
MD
620 /* Invoke the state dump callback specifically for the tracer key. */
621 handle->cb(&notif->key);
622 side_statedump_event_call(side_statedump_end, &notif->key,
a125f217 623 side_arg_list(side_arg_string(handle->name)));
a125f217
MD
624}
625
626static
627void _side_statedump_run_pending_requests(struct side_statedump_request_handle *handle)
628{
629 struct side_statedump_notification *notif, *tmp;
630 DEFINE_SIDE_LIST_HEAD(tmp_head);
631
632 pthread_mutex_lock(&side_statedump_lock);
633 side_list_splice(&handle->notification_queue, &tmp_head);
634 side_list_head_init(&handle->notification_queue);
635 pthread_mutex_unlock(&side_statedump_lock);
636
637 /* We are now sole owner of the tmp_head list. */
638 side_list_for_each_entry(notif, &tmp_head, node)
639 side_statedump_run(handle, notif);
640 side_list_for_each_entry_safe(notif, tmp, &tmp_head, node)
641 free(notif);
76dd11f9
MD
642
643 if (handle->mode == SIDE_STATEDUMP_MODE_AGENT_THREAD) {
644 pthread_mutex_lock(&side_statedump_lock);
645 pthread_cond_broadcast(&statedump_agent_thread.waiter_cond);
646 pthread_mutex_unlock(&side_statedump_lock);
647 }
a125f217
MD
648}
649
650static
651void *statedump_agent_func(void *arg __attribute__((unused)))
652{
653 for (;;) {
654 struct side_statedump_request_handle *handle;
655 struct side_rcu_read_state rcu_read_state;
656 enum agent_thread_state state;
657
76dd11f9
MD
658 pthread_mutex_lock(&side_statedump_lock);
659 for (;;) {
660 state = __atomic_load_n(&statedump_agent_thread.state, __ATOMIC_SEQ_CST);
661 if (state == AGENT_THREAD_STATE_BLOCKED)
662 pthread_cond_wait(&statedump_agent_thread.worker_cond, &side_statedump_lock);
663 else
664 break;
a125f217 665 }
76dd11f9 666 pthread_mutex_unlock(&side_statedump_lock);
a125f217
MD
667 if (state & AGENT_THREAD_STATE_EXIT)
668 break;
7e38dc2e
MD
669 if (state & AGENT_THREAD_STATE_PAUSE) {
670 int attempt = 0;
671
672 (void)__atomic_or_fetch(&statedump_agent_thread.state, AGENT_THREAD_STATE_PAUSE_ACK, __ATOMIC_SEQ_CST);
673 for (;;) {
674 state = __atomic_load_n(&statedump_agent_thread.state, __ATOMIC_SEQ_CST);
675 if (!(state & AGENT_THREAD_STATE_PAUSE))
676 break;
677 if (attempt > SIDE_RETRY_BUSY_LOOP_ATTEMPTS) {
678 (void)poll(NULL, 0, SIDE_RETRY_DELAY_MS);
679 continue;
680 }
681 attempt++;
682 side_cpu_relax();
683 }
684 continue;
685 }
a125f217
MD
686 (void)__atomic_and_fetch(&statedump_agent_thread.state, ~AGENT_THREAD_STATE_HANDLE_REQUEST, __ATOMIC_SEQ_CST);
687 side_rcu_read_begin(&statedump_rcu_gp, &rcu_read_state);
688 side_list_for_each_entry_rcu(handle, &side_statedump_list, node)
689 _side_statedump_run_pending_requests(handle);
690 side_rcu_read_end(&statedump_rcu_gp, &rcu_read_state);
691 }
692 return NULL;
693}
694
7e38dc2e
MD
695static
696void statedump_agent_thread_init(void)
697{
698 pthread_cond_init(&statedump_agent_thread.worker_cond, NULL);
699 pthread_cond_init(&statedump_agent_thread.waiter_cond, NULL);
700 statedump_agent_thread.state = AGENT_THREAD_STATE_BLOCKED;
701}
702
76dd11f9 703/* Called with side_agent_thread_lock and side_statedump_lock held. */
a125f217
MD
704static
705void statedump_agent_thread_get(void)
706{
707 int ret;
708
709 if (statedump_agent_thread.ref++)
710 return;
7e38dc2e 711 statedump_agent_thread_init();
a125f217
MD
712 ret = pthread_create(&statedump_agent_thread.id, NULL,
713 statedump_agent_func, NULL);
714 if (ret) {
715 abort();
716 }
717}
718
76dd11f9
MD
719/*
720 * Called with side_agent_thread_lock and side_statedump_lock held.
721 * Returns true if join for agent thread is needed.
722 */
723static
724bool statedump_agent_thread_put(void)
725{
726 if (--statedump_agent_thread.ref)
727 return false;
728 (void)__atomic_or_fetch(&statedump_agent_thread.state, AGENT_THREAD_STATE_EXIT, __ATOMIC_SEQ_CST);
729 pthread_cond_broadcast(&statedump_agent_thread.worker_cond);
730 return true;
731}
732
7e38dc2e
MD
733static
734void statedump_agent_thread_fini(void)
735{
736 statedump_agent_thread.state = AGENT_THREAD_STATE_BLOCKED;
737 if (pthread_cond_destroy(&statedump_agent_thread.worker_cond))
738 abort();
739 if (pthread_cond_destroy(&statedump_agent_thread.waiter_cond))
740 abort();
741}
742
76dd11f9 743/* Called with side_agent_thread_lock held. */
a125f217 744static
76dd11f9 745void statedump_agent_thread_join(void)
a125f217
MD
746{
747 int ret;
748 void *retval;
749
a125f217
MD
750 ret = pthread_join(statedump_agent_thread.id, &retval);
751 if (ret) {
752 abort();
753 }
7e38dc2e 754 statedump_agent_thread_fini();
a125f217
MD
755}
756
bffe9ae3
MD
757struct side_statedump_request_handle *
758 side_statedump_request_notification_register(const char *state_name,
3da13b2c 759 void (*statedump_cb)(void *statedump_request_key),
bffe9ae3 760 enum side_statedump_mode mode)
f0b01832
MD
761{
762 struct side_statedump_request_handle *handle;
bffe9ae3 763 char *name;
f0b01832
MD
764
765 if (finalized)
766 return NULL;
767 if (!initialized)
768 side_init();
f0b01832
MD
769 handle = (struct side_statedump_request_handle *)
770 calloc(1, sizeof(struct side_statedump_request_handle));
771 if (!handle)
772 return NULL;
bffe9ae3
MD
773 name = strdup(state_name);
774 if (!name)
775 goto name_nomem;
f0b01832 776 handle->cb = statedump_cb;
bffe9ae3
MD
777 handle->name = name;
778 handle->mode = mode;
779 side_list_head_init(&handle->notification_queue);
873bbf16 780
76dd11f9
MD
781 if (mode == SIDE_STATEDUMP_MODE_AGENT_THREAD)
782 pthread_mutex_lock(&side_agent_thread_lock);
873bbf16 783 pthread_mutex_lock(&side_statedump_lock);
a125f217
MD
784 if (mode == SIDE_STATEDUMP_MODE_AGENT_THREAD)
785 statedump_agent_thread_get();
873bbf16 786 side_list_insert_node_tail_rcu(&side_statedump_list, &handle->node);
bffe9ae3
MD
787 /* Queue statedump pending for all tracers. */
788 queue_statedump_pending(handle, SIDE_KEY_MATCH_ALL);
873bbf16
MD
789 pthread_mutex_unlock(&side_statedump_lock);
790
a125f217 791 if (mode == SIDE_STATEDUMP_MODE_AGENT_THREAD) {
76dd11f9 792 pthread_mutex_unlock(&side_agent_thread_lock);
a125f217 793
76dd11f9
MD
794 pthread_mutex_lock(&side_statedump_lock);
795 while (!side_list_empty(&handle->notification_queue))
796 pthread_cond_wait(&statedump_agent_thread.waiter_cond, &side_statedump_lock);
797 pthread_mutex_unlock(&side_statedump_lock);
a125f217
MD
798 }
799
f0b01832 800 return handle;
bffe9ae3
MD
801
802name_nomem:
803 free(handle);
804 return NULL;
f0b01832
MD
805}
806
807void side_statedump_request_notification_unregister(struct side_statedump_request_handle *handle)
808{
76dd11f9
MD
809 bool join = false;
810
f0b01832
MD
811 if (finalized)
812 return;
813 if (!initialized)
814 side_init();
873bbf16 815
76dd11f9
MD
816 if (handle->mode == SIDE_STATEDUMP_MODE_AGENT_THREAD)
817 pthread_mutex_lock(&side_agent_thread_lock);
873bbf16 818 pthread_mutex_lock(&side_statedump_lock);
bffe9ae3 819 unqueue_statedump_pending(handle, SIDE_KEY_MATCH_ALL);
873bbf16 820 side_list_remove_node_rcu(&handle->node);
a125f217 821 if (handle->mode == SIDE_STATEDUMP_MODE_AGENT_THREAD)
76dd11f9 822 join = statedump_agent_thread_put();
873bbf16 823 pthread_mutex_unlock(&side_statedump_lock);
76dd11f9
MD
824 if (join)
825 statedump_agent_thread_join();
826 if (handle->mode == SIDE_STATEDUMP_MODE_AGENT_THREAD)
827 pthread_mutex_unlock(&side_agent_thread_lock);
873bbf16
MD
828
829 side_rcu_wait_grace_period(&statedump_rcu_gp);
bffe9ae3 830 free(handle->name);
f0b01832
MD
831 free(handle);
832}
833
bffe9ae3
MD
834/* Returns true if the handle has pending statedump requests. */
835bool side_statedump_poll_pending_requests(struct side_statedump_request_handle *handle)
f0b01832 836{
bffe9ae3
MD
837 bool ret;
838
839 if (handle->mode != SIDE_STATEDUMP_MODE_POLLING)
840 return false;
841 pthread_mutex_lock(&side_statedump_lock);
842 ret = !side_list_empty(&handle->notification_queue);
843 pthread_mutex_unlock(&side_statedump_lock);
844 return ret;
845}
f0b01832 846
bffe9ae3
MD
847/*
848 * Only polling mode state dump handles allow application to explicitly handle the
849 * pending requests.
850 */
851int side_statedump_run_pending_requests(struct side_statedump_request_handle *handle)
852{
853 if (handle->mode != SIDE_STATEDUMP_MODE_POLLING)
854 return SIDE_ERROR_INVAL;
855 _side_statedump_run_pending_requests(handle);
856 return SIDE_ERROR_OK;
857}
858
859/*
860 * Request a state dump for tracer callbacks identified with "key".
861 */
862int side_tracer_statedump_request(uint64_t key)
863{
864 struct side_statedump_request_handle *handle;
865
866 if (key == SIDE_KEY_MATCH_ALL)
867 return SIDE_ERROR_INVAL;
868 pthread_mutex_lock(&side_statedump_lock);
869 side_list_for_each_entry(handle, &side_statedump_list, node)
870 queue_statedump_pending(handle, key);
871 pthread_mutex_lock(&side_statedump_lock);
872 return SIDE_ERROR_OK;
873}
874
875/*
876 * Cancel a statedump request.
877 */
878int side_tracer_statedump_request_cancel(uint64_t key)
879{
880 struct side_statedump_request_handle *handle;
881
882 if (key == SIDE_KEY_MATCH_ALL)
883 return SIDE_ERROR_INVAL;
884 pthread_mutex_lock(&side_statedump_lock);
885 side_list_for_each_entry(handle, &side_statedump_list, node)
886 unqueue_statedump_pending(handle, key);
887 pthread_mutex_lock(&side_statedump_lock);
888 return SIDE_ERROR_OK;
889}
890
891/*
892 * Tracer keys are represented on 64-bit. Return SIDE_ERROR_NOMEM on
893 * overflow (which should never happen in practice).
894 */
895int side_tracer_request_key(uint64_t *key)
896{
897 int ret = SIDE_ERROR_OK;
898
899 pthread_mutex_lock(&side_key_lock);
900 if (side_key_next == 0) {
901 ret = SIDE_ERROR_NOMEM;
902 goto end;
903 }
904 *key = side_key_next++;
905end:
906 pthread_mutex_unlock(&side_key_lock);
907 return ret;
f0b01832
MD
908}
909
7e38dc2e
MD
910/*
911 * Use of pthread_atfork depends on glibc 2.24 to eliminate hangs when
912 * waiting for the agent thread if the agent thread calls malloc. This
913 * is corrected by GNU libc
914 * commit 8a727af925be63aa6ea0f5f90e16751fd541626b.
915 * Ref. https://bugzilla.redhat.com/show_bug.cgi?id=906468
916 */
917static
918void side_before_fork(void)
919{
920 int attempt = 0;
921
922 pthread_mutex_lock(&side_agent_thread_lock);
923 if (!statedump_agent_thread.ref)
924 return;
925 /* Pause agent thread. */
926 pthread_mutex_lock(&side_statedump_lock);
927 (void)__atomic_or_fetch(&statedump_agent_thread.state, AGENT_THREAD_STATE_PAUSE, __ATOMIC_SEQ_CST);
928 pthread_cond_broadcast(&statedump_agent_thread.worker_cond);
929 pthread_mutex_unlock(&side_statedump_lock);
930 /* Wait for agent thread acknowledge. */
931 while (!(__atomic_load_n(&statedump_agent_thread.state, __ATOMIC_SEQ_CST) & AGENT_THREAD_STATE_PAUSE_ACK)) {
932 if (attempt > SIDE_RETRY_BUSY_LOOP_ATTEMPTS) {
933 (void)poll(NULL, 0, SIDE_RETRY_DELAY_MS);
934 continue;
935 }
936 attempt++;
937 side_cpu_relax();
938 }
939}
940
941static
942void side_after_fork_parent(void)
943{
944 if (statedump_agent_thread.ref)
945 (void)__atomic_and_fetch(&statedump_agent_thread.state,
946 ~(AGENT_THREAD_STATE_PAUSE | AGENT_THREAD_STATE_PAUSE_ACK),
947 __ATOMIC_SEQ_CST);
948 pthread_mutex_unlock(&side_agent_thread_lock);
949}
950
951/*
952 * The agent thread does not exist in the child process after a fork.
953 * Re-initialize its data structures and create a new agent thread.
954 */
955static
956void side_after_fork_child(void)
957{
958 if (statedump_agent_thread.ref) {
959 int ret;
960
961 statedump_agent_thread_fini();
962 statedump_agent_thread_init();
963 ret = pthread_create(&statedump_agent_thread.id, NULL,
964 statedump_agent_func, NULL);
965 if (ret) {
966 abort();
967 }
968 }
969 pthread_mutex_unlock(&side_agent_thread_lock);
970}
971
67337c4a
MD
972void side_init(void)
973{
974 if (initialized)
975 return;
873bbf16
MD
976 side_rcu_gp_init(&event_rcu_gp);
977 side_rcu_gp_init(&statedump_rcu_gp);
7e38dc2e
MD
978 if (pthread_atfork(side_before_fork, side_after_fork_parent, side_after_fork_child))
979 abort();
67337c4a
MD
980 initialized = true;
981}
982
983/*
984 * side_exit() is executed from a library destructor. It can be called
985 * explicitly at application exit as well. Concurrent side API use is
986 * not expected at that point.
987 */
988void side_exit(void)
989{
990 struct side_events_register_handle *handle, *tmp;
991
992 if (finalized)
993 return;
994 side_list_for_each_entry_safe(handle, tmp, &side_events_list, node)
995 side_events_unregister(handle);
873bbf16
MD
996 side_rcu_gp_exit(&event_rcu_gp);
997 side_rcu_gp_exit(&statedump_rcu_gp);
67337c4a
MD
998 finalized = true;
999}
This page took 0.072761 seconds and 4 git commands to generate.