1 // SPDX-License-Identifier: MIT
3 * Copyright 2022 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
6 #include <side/trace.h>
18 /* Top 8 bits reserved for shared tracer use. */
19 #if SIDE_BITS_PER_LONG == 64
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
24 /* Allow 2^56 private tracer references on an event. */
25 # define SIDE_EVENT_ENABLED_PRIVATE_MASK 0x00FFFFFFFFFFFFFFULL
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
31 /* Allow 2^24 private tracer references on an event. */
32 # define SIDE_EVENT_ENABLED_PRIVATE_MASK 0x00FFFFFFUL
35 #define SIDE_KEY_RESERVED_RANGE_END 0x8
37 /* Key 0x0 is reserved to match all. */
38 #define SIDE_KEY_MATCH_ALL 0x0
39 /* Key 0x1 is reserved for user event. */
40 #define SIDE_KEY_USER_EVENT 0x1
41 /* Key 0x2 is reserved for ptrace. */
42 #define SIDE_KEY_PTRACE 0x2
44 #define SIDE_RETRY_BUSY_LOOP_ATTEMPTS 100
45 #define SIDE_RETRY_DELAY_MS 1
47 struct side_events_register_handle
{
48 struct side_list_node node
;
49 struct side_event_description
**events
;
53 struct 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
);
60 struct side_statedump_notification
{
61 struct side_list_node node
;
65 struct side_statedump_request_handle
{
66 struct side_list_node node
; /* Statedump request RCU list node. */
67 struct side_list_head notification_queue
; /* Queue of struct side_statedump_notification */
68 void (*cb
)(void *statedump_request_key
);
70 enum side_statedump_mode mode
;
73 struct side_callback
{
75 void (*call
)(const struct side_event_description
*desc
,
76 const struct side_arg_vec
*side_arg_vec
,
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
,
87 enum agent_thread_state
{
88 AGENT_THREAD_STATE_BLOCKED
= 0,
89 AGENT_THREAD_STATE_HANDLE_REQUEST
= (1 << 0),
90 AGENT_THREAD_STATE_EXIT
= (1 << 1),
91 AGENT_THREAD_STATE_PAUSE
= (1 << 2),
92 AGENT_THREAD_STATE_PAUSE_ACK
= (1 << 3),
95 struct statedump_agent_thread
{
98 enum agent_thread_state state
;
99 pthread_cond_t worker_cond
;
100 pthread_cond_t waiter_cond
;
103 static struct side_rcu_gp_state event_rcu_gp
, statedump_rcu_gp
;
106 * Lazy initialization for early use within library constructors.
108 static bool initialized
;
110 * Do not register/unregister any more events after destructor.
112 static bool finalized
;
115 * Recursive mutex to allow tracer callbacks to use the side API.
117 static pthread_mutex_t side_event_lock
= PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
;
118 static pthread_mutex_t side_statedump_lock
= PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
;
119 static pthread_mutex_t side_key_lock
= PTHREAD_MUTEX_INITIALIZER
;
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
125 * The side_statedump_lock nests inside the side_agent_thread_lock.
127 static pthread_mutex_t side_agent_thread_lock
= PTHREAD_MUTEX_INITIALIZER
;
129 /* Dynamic tracer key allocation. */
130 static uint64_t side_key_next
= SIDE_KEY_RESERVED_RANGE_END
;
132 static struct statedump_agent_thread statedump_agent_thread
;
134 static DEFINE_SIDE_LIST_HEAD(side_events_list
);
135 static DEFINE_SIDE_LIST_HEAD(side_tracer_list
);
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.
141 static DEFINE_SIDE_LIST_HEAD(side_statedump_list
);
144 * The empty callback has a NULL function callback pointer, which stops
145 * iteration on the array of callbacks immediately.
147 const char side_empty_callback
[sizeof(struct side_callback
)];
149 side_static_event(side_statedump_begin
, "side", "statedump_begin",
150 SIDE_LOGLEVEL_INFO
, side_field_list(side_field_string("name")));
151 side_static_event(side_statedump_end
, "side", "statedump_end",
152 SIDE_LOGLEVEL_INFO
, side_field_list(side_field_string("name")));
155 * side_ptrace_hook is a place holder for a debugger breakpoint.
156 * var_struct is NULL if not variadic.
158 void side_ptrace_hook(const struct side_event_state
*event_state
__attribute__((unused
)),
159 const struct side_arg_vec
*side_arg_vec
__attribute__((unused
)),
160 const struct side_arg_dynamic_struct
*var_struct
__attribute__((unused
)))
161 __attribute__((noinline
));
162 void side_ptrace_hook(const struct side_event_state
*event_state
__attribute__((unused
)),
163 const struct side_arg_vec
*side_arg_vec
__attribute__((unused
)),
164 const struct side_arg_dynamic_struct
*var_struct
__attribute__((unused
)))
169 void _side_call(const struct side_event_state
*event_state
, const struct side_arg_vec
*side_arg_vec
, uint64_t key
)
171 struct side_rcu_read_state rcu_read_state
;
172 const struct side_event_state_0
*es0
;
173 const struct side_callback
*side_cb
;
176 if (side_unlikely(finalized
))
178 if (side_unlikely(!initialized
))
180 if (side_unlikely(event_state
->version
!= 0))
182 es0
= side_container_of(event_state
, const struct side_event_state_0
, parent
);
183 assert(!(es0
->desc
->flags
& SIDE_EVENT_FLAG_VARIADIC
));
184 enabled
= __atomic_load_n(&es0
->enabled
, __ATOMIC_RELAXED
);
185 if (side_unlikely(enabled
& SIDE_EVENT_ENABLED_SHARED_MASK
)) {
186 if ((enabled
& SIDE_EVENT_ENABLED_SHARED_USER_EVENT_MASK
) &&
187 (key
== SIDE_KEY_MATCH_ALL
|| key
== SIDE_KEY_USER_EVENT
)) {
188 // TODO: call kernel write.
190 if ((enabled
& SIDE_EVENT_ENABLED_SHARED_PTRACE_MASK
) &&
191 (key
== SIDE_KEY_MATCH_ALL
|| key
== SIDE_KEY_PTRACE
))
192 side_ptrace_hook(event_state
, side_arg_vec
, NULL
);
194 side_rcu_read_begin(&event_rcu_gp
, &rcu_read_state
);
195 for (side_cb
= side_rcu_dereference(es0
->callbacks
); side_cb
->u
.call
!= NULL
; side_cb
++) {
196 if (key
!= SIDE_KEY_MATCH_ALL
&& side_cb
->key
!= SIDE_KEY_MATCH_ALL
&& side_cb
->key
!= key
)
198 side_cb
->u
.call(es0
->desc
, side_arg_vec
, side_cb
->priv
);
200 side_rcu_read_end(&event_rcu_gp
, &rcu_read_state
);
203 void side_call(const struct side_event_state
*event_state
, const struct side_arg_vec
*side_arg_vec
)
205 _side_call(event_state
, side_arg_vec
, SIDE_KEY_MATCH_ALL
);
208 void side_statedump_call(const struct side_event_state
*event_state
,
209 const struct side_arg_vec
*side_arg_vec
,
210 void *statedump_request_key
)
212 _side_call(event_state
, side_arg_vec
, *(const uint64_t *) statedump_request_key
);
216 void _side_call_variadic(const struct side_event_state
*event_state
,
217 const struct side_arg_vec
*side_arg_vec
,
218 const struct side_arg_dynamic_struct
*var_struct
,
221 struct side_rcu_read_state rcu_read_state
;
222 const struct side_event_state_0
*es0
;
223 const struct side_callback
*side_cb
;
226 if (side_unlikely(finalized
))
228 if (side_unlikely(!initialized
))
230 if (side_unlikely(event_state
->version
!= 0))
232 es0
= side_container_of(event_state
, const struct side_event_state_0
, parent
);
233 assert(es0
->desc
->flags
& SIDE_EVENT_FLAG_VARIADIC
);
234 enabled
= __atomic_load_n(&es0
->enabled
, __ATOMIC_RELAXED
);
235 if (side_unlikely(enabled
& SIDE_EVENT_ENABLED_SHARED_MASK
)) {
236 if ((enabled
& SIDE_EVENT_ENABLED_SHARED_USER_EVENT_MASK
) &&
237 (key
== SIDE_KEY_MATCH_ALL
|| key
== SIDE_KEY_USER_EVENT
)) {
238 // TODO: call kernel write.
240 if ((enabled
& SIDE_EVENT_ENABLED_SHARED_PTRACE_MASK
) &&
241 (key
== SIDE_KEY_MATCH_ALL
|| key
== SIDE_KEY_PTRACE
))
242 side_ptrace_hook(event_state
, side_arg_vec
, var_struct
);
244 side_rcu_read_begin(&event_rcu_gp
, &rcu_read_state
);
245 for (side_cb
= side_rcu_dereference(es0
->callbacks
); side_cb
->u
.call_variadic
!= NULL
; side_cb
++) {
246 if (key
!= SIDE_KEY_MATCH_ALL
&& side_cb
->key
!= SIDE_KEY_MATCH_ALL
&& side_cb
->key
!= key
)
248 side_cb
->u
.call_variadic(es0
->desc
, side_arg_vec
, var_struct
, side_cb
->priv
);
250 side_rcu_read_end(&event_rcu_gp
, &rcu_read_state
);
253 void side_call_variadic(const struct side_event_state
*event_state
,
254 const struct side_arg_vec
*side_arg_vec
,
255 const struct side_arg_dynamic_struct
*var_struct
)
257 _side_call_variadic(event_state
, side_arg_vec
, var_struct
, SIDE_KEY_MATCH_ALL
);
260 void side_statedump_call_variadic(const struct side_event_state
*event_state
,
261 const struct side_arg_vec
*side_arg_vec
,
262 const struct side_arg_dynamic_struct
*var_struct
,
263 void *statedump_request_key
)
265 _side_call_variadic(event_state
, side_arg_vec
, var_struct
, *(const uint64_t *) statedump_request_key
);
269 const struct side_callback
*side_tracer_callback_lookup(
270 const struct side_event_description
*desc
,
271 void *call
, void *priv
, uint64_t key
)
273 struct side_event_state
*event_state
= side_ptr_get(desc
->state
);
274 const struct side_event_state_0
*es0
;
275 const struct side_callback
*cb
;
277 if (side_unlikely(event_state
->version
!= 0))
279 es0
= side_container_of(event_state
, const struct side_event_state_0
, parent
);
280 for (cb
= es0
->callbacks
; cb
->u
.call
!= NULL
; cb
++) {
281 if ((void *) cb
->u
.call
== call
&& cb
->priv
== priv
&& cb
->key
== key
)
288 int _side_tracer_callback_register(struct side_event_description
*desc
,
289 void *call
, void *priv
, uint64_t key
)
291 struct side_event_state
*event_state
;
292 struct side_callback
*old_cb
, *new_cb
;
293 struct side_event_state_0
*es0
;
294 int ret
= SIDE_ERROR_OK
;
298 return SIDE_ERROR_INVAL
;
300 return SIDE_ERROR_EXITING
;
303 pthread_mutex_lock(&side_event_lock
);
304 event_state
= side_ptr_get(desc
->state
);
305 if (side_unlikely(event_state
->version
!= 0))
307 es0
= side_container_of(event_state
, struct side_event_state_0
, parent
);
308 old_nr_cb
= es0
->nr_callbacks
;
309 if (old_nr_cb
== UINT32_MAX
) {
310 ret
= SIDE_ERROR_INVAL
;
313 /* Reject duplicate (call, priv) tuples. */
314 if (side_tracer_callback_lookup(desc
, call
, priv
, key
)) {
315 ret
= SIDE_ERROR_EXIST
;
318 old_cb
= (struct side_callback
*) es0
->callbacks
;
319 /* old_nr_cb + 1 (new cb) + 1 (NULL) */
320 new_cb
= (struct side_callback
*) calloc(old_nr_cb
+ 2, sizeof(struct side_callback
));
322 ret
= SIDE_ERROR_NOMEM
;
325 memcpy(new_cb
, old_cb
, old_nr_cb
);
326 if (desc
->flags
& SIDE_EVENT_FLAG_VARIADIC
)
327 new_cb
[old_nr_cb
].u
.call_variadic
=
328 (side_tracer_callback_variadic_func
) call
;
330 new_cb
[old_nr_cb
].u
.call
=
331 (side_tracer_callback_func
) call
;
332 new_cb
[old_nr_cb
].priv
= priv
;
333 new_cb
[old_nr_cb
].key
= key
;
334 /* High order bits are already zeroed. */
335 side_rcu_assign_pointer(es0
->callbacks
, new_cb
);
336 side_rcu_wait_grace_period(&event_rcu_gp
);
340 /* Increment concurrently with kernel setting the top bits. */
342 (void) __atomic_add_fetch(&es0
->enabled
, 1, __ATOMIC_RELAXED
);
344 pthread_mutex_unlock(&side_event_lock
);
348 int side_tracer_callback_register(struct side_event_description
*desc
,
349 side_tracer_callback_func call
,
350 void *priv
, uint64_t key
)
352 if (desc
->flags
& SIDE_EVENT_FLAG_VARIADIC
)
353 return SIDE_ERROR_INVAL
;
354 return _side_tracer_callback_register(desc
, (void *) call
, priv
, key
);
357 int side_tracer_callback_variadic_register(struct side_event_description
*desc
,
358 side_tracer_callback_variadic_func call_variadic
,
359 void *priv
, uint64_t key
)
361 if (!(desc
->flags
& SIDE_EVENT_FLAG_VARIADIC
))
362 return SIDE_ERROR_INVAL
;
363 return _side_tracer_callback_register(desc
, (void *) call_variadic
, priv
, key
);
366 static int _side_tracer_callback_unregister(struct side_event_description
*desc
,
367 void *call
, void *priv
, uint64_t key
)
369 struct side_event_state
*event_state
;
370 struct side_callback
*old_cb
, *new_cb
;
371 const struct side_callback
*cb_pos
;
372 struct side_event_state_0
*es0
;
374 int ret
= SIDE_ERROR_OK
;
378 return SIDE_ERROR_INVAL
;
380 return SIDE_ERROR_EXITING
;
383 pthread_mutex_lock(&side_event_lock
);
384 event_state
= side_ptr_get(desc
->state
);
385 if (side_unlikely(event_state
->version
!= 0))
387 es0
= side_container_of(event_state
, struct side_event_state_0
, parent
);
388 cb_pos
= side_tracer_callback_lookup(desc
, call
, priv
, key
);
390 ret
= SIDE_ERROR_NOENT
;
393 old_nr_cb
= es0
->nr_callbacks
;
394 old_cb
= (struct side_callback
*) es0
->callbacks
;
395 if (old_nr_cb
== 1) {
396 new_cb
= (struct side_callback
*) &side_empty_callback
;
398 pos_idx
= cb_pos
- es0
->callbacks
;
399 /* Remove entry at pos_idx. */
400 /* old_nr_cb - 1 (removed cb) + 1 (NULL) */
401 new_cb
= (struct side_callback
*) calloc(old_nr_cb
, sizeof(struct side_callback
));
403 ret
= SIDE_ERROR_NOMEM
;
406 memcpy(new_cb
, old_cb
, pos_idx
);
407 memcpy(&new_cb
[pos_idx
], &old_cb
[pos_idx
+ 1], old_nr_cb
- pos_idx
- 1);
409 /* High order bits are already zeroed. */
410 side_rcu_assign_pointer(es0
->callbacks
, new_cb
);
411 side_rcu_wait_grace_period(&event_rcu_gp
);
414 /* Decrement concurrently with kernel setting the top bits. */
416 (void) __atomic_add_fetch(&es0
->enabled
, -1, __ATOMIC_RELAXED
);
418 pthread_mutex_unlock(&side_event_lock
);
422 int side_tracer_callback_unregister(struct side_event_description
*desc
,
423 side_tracer_callback_func call
,
424 void *priv
, uint64_t key
)
426 if (desc
->flags
& SIDE_EVENT_FLAG_VARIADIC
)
427 return SIDE_ERROR_INVAL
;
428 return _side_tracer_callback_unregister(desc
, (void *) call
, priv
, key
);
431 int side_tracer_callback_variadic_unregister(struct side_event_description
*desc
,
432 side_tracer_callback_variadic_func call_variadic
,
433 void *priv
, uint64_t key
)
435 if (!(desc
->flags
& SIDE_EVENT_FLAG_VARIADIC
))
436 return SIDE_ERROR_INVAL
;
437 return _side_tracer_callback_unregister(desc
, (void *) call_variadic
, priv
, key
);
440 struct side_events_register_handle
*side_events_register(struct side_event_description
**events
, uint32_t nr_events
)
442 struct side_events_register_handle
*events_handle
= NULL
;
443 struct side_tracer_handle
*tracer_handle
;
449 events_handle
= (struct side_events_register_handle
*)
450 calloc(1, sizeof(struct side_events_register_handle
));
453 events_handle
->events
= events
;
454 events_handle
->nr_events
= nr_events
;
456 pthread_mutex_lock(&side_event_lock
);
457 side_list_insert_node_tail(&side_events_list
, &events_handle
->node
);
458 side_list_for_each_entry(tracer_handle
, &side_tracer_list
, node
) {
459 tracer_handle
->cb(SIDE_TRACER_NOTIFICATION_INSERT_EVENTS
,
460 events
, nr_events
, tracer_handle
->priv
);
462 pthread_mutex_unlock(&side_event_lock
);
463 //TODO: call event batch register ioctl
464 return events_handle
;
468 void side_event_remove_callbacks(struct side_event_description
*desc
)
470 struct side_event_state
*event_state
= side_ptr_get(desc
->state
);
471 struct side_event_state_0
*es0
;
472 struct side_callback
*old_cb
;
475 if (side_unlikely(event_state
->version
!= 0))
477 es0
= side_container_of(event_state
, struct side_event_state_0
, parent
);
478 nr_cb
= es0
->nr_callbacks
;
481 old_cb
= (struct side_callback
*) es0
->callbacks
;
482 (void) __atomic_add_fetch(&es0
->enabled
, -1, __ATOMIC_RELAXED
);
484 * Setting the state back to 0 cb and empty callbacks out of
485 * caution. This should not matter because instrumentation is
488 es0
->nr_callbacks
= 0;
489 side_rcu_assign_pointer(es0
->callbacks
, &side_empty_callback
);
491 * No need to wait for grace period because instrumentation is
498 * Unregister event handle. At this point, all side events in that
499 * handle should be unreachable.
501 void side_events_unregister(struct side_events_register_handle
*events_handle
)
503 struct side_tracer_handle
*tracer_handle
;
512 pthread_mutex_lock(&side_event_lock
);
513 side_list_remove_node(&events_handle
->node
);
514 side_list_for_each_entry(tracer_handle
, &side_tracer_list
, node
) {
515 tracer_handle
->cb(SIDE_TRACER_NOTIFICATION_REMOVE_EVENTS
,
516 events_handle
->events
, events_handle
->nr_events
,
517 tracer_handle
->priv
);
519 for (i
= 0; i
< events_handle
->nr_events
; i
++) {
520 struct side_event_description
*event
= events_handle
->events
[i
];
522 /* Skip NULL pointers */
525 side_event_remove_callbacks(event
);
527 pthread_mutex_unlock(&side_event_lock
);
528 //TODO: call event batch unregister ioctl
532 struct side_tracer_handle
*side_tracer_event_notification_register(
533 void (*cb
)(enum side_tracer_notification notif
,
534 struct side_event_description
**events
, uint32_t nr_events
, void *priv
),
537 struct side_tracer_handle
*tracer_handle
;
538 struct side_events_register_handle
*events_handle
;
544 tracer_handle
= (struct side_tracer_handle
*)
545 calloc(1, sizeof(struct side_tracer_handle
));
548 pthread_mutex_lock(&side_event_lock
);
549 tracer_handle
->cb
= cb
;
550 tracer_handle
->priv
= priv
;
551 side_list_insert_node_tail(&side_tracer_list
, &tracer_handle
->node
);
552 side_list_for_each_entry(events_handle
, &side_events_list
, node
) {
553 cb(SIDE_TRACER_NOTIFICATION_INSERT_EVENTS
,
554 events_handle
->events
, events_handle
->nr_events
, priv
);
556 pthread_mutex_unlock(&side_event_lock
);
557 return tracer_handle
;
560 void side_tracer_event_notification_unregister(struct side_tracer_handle
*tracer_handle
)
562 struct side_events_register_handle
*events_handle
;
568 pthread_mutex_lock(&side_event_lock
);
569 side_list_for_each_entry(events_handle
, &side_events_list
, node
) {
570 tracer_handle
->cb(SIDE_TRACER_NOTIFICATION_REMOVE_EVENTS
,
571 events_handle
->events
, events_handle
->nr_events
,
572 tracer_handle
->priv
);
574 side_list_remove_node(&tracer_handle
->node
);
575 pthread_mutex_unlock(&side_event_lock
);
579 /* Called with side_statedump_lock held. */
581 void queue_statedump_pending(struct side_statedump_request_handle
*handle
, uint64_t key
)
583 struct side_statedump_notification
*notif
;
585 notif
= (struct side_statedump_notification
*) calloc(1, sizeof(struct side_statedump_notification
));
589 side_list_insert_node_tail(&handle
->notification_queue
, ¬if
->node
);
590 if (handle
->mode
== SIDE_STATEDUMP_MODE_AGENT_THREAD
) {
591 (void)__atomic_or_fetch(&statedump_agent_thread
.state
, AGENT_THREAD_STATE_HANDLE_REQUEST
, __ATOMIC_SEQ_CST
);
592 pthread_cond_broadcast(&statedump_agent_thread
.worker_cond
);
596 /* Called with side_statedump_lock held. */
598 void unqueue_statedump_pending(struct side_statedump_request_handle
*handle
, uint64_t key
)
600 struct side_statedump_notification
*notif
, *tmp
;
602 side_list_for_each_entry_safe(notif
, tmp
, &handle
->notification_queue
, node
) {
603 if (key
== SIDE_KEY_MATCH_ALL
|| key
== notif
->key
) {
604 side_list_remove_node(¬if
->node
);
611 void side_statedump_run(struct side_statedump_request_handle
*handle
,
612 struct side_statedump_notification
*notif
)
614 side_statedump_event_call(side_statedump_begin
, ¬if
->key
,
615 side_arg_list(side_arg_string(handle
->name
)));
616 /* Invoke the state dump callback specifically for the tracer key. */
617 handle
->cb(¬if
->key
);
618 side_statedump_event_call(side_statedump_end
, ¬if
->key
,
619 side_arg_list(side_arg_string(handle
->name
)));
623 void _side_statedump_run_pending_requests(struct side_statedump_request_handle
*handle
)
625 struct side_statedump_notification
*notif
, *tmp
;
626 DEFINE_SIDE_LIST_HEAD(tmp_head
);
628 pthread_mutex_lock(&side_statedump_lock
);
629 side_list_splice(&handle
->notification_queue
, &tmp_head
);
630 side_list_head_init(&handle
->notification_queue
);
631 pthread_mutex_unlock(&side_statedump_lock
);
633 /* We are now sole owner of the tmp_head list. */
634 side_list_for_each_entry(notif
, &tmp_head
, node
)
635 side_statedump_run(handle
, notif
);
636 side_list_for_each_entry_safe(notif
, tmp
, &tmp_head
, node
)
639 if (handle
->mode
== SIDE_STATEDUMP_MODE_AGENT_THREAD
) {
640 pthread_mutex_lock(&side_statedump_lock
);
641 pthread_cond_broadcast(&statedump_agent_thread
.waiter_cond
);
642 pthread_mutex_unlock(&side_statedump_lock
);
647 void *statedump_agent_func(void *arg
__attribute__((unused
)))
650 struct side_statedump_request_handle
*handle
;
651 struct side_rcu_read_state rcu_read_state
;
652 enum agent_thread_state state
;
654 pthread_mutex_lock(&side_statedump_lock
);
656 state
= __atomic_load_n(&statedump_agent_thread
.state
, __ATOMIC_SEQ_CST
);
657 if (state
== AGENT_THREAD_STATE_BLOCKED
)
658 pthread_cond_wait(&statedump_agent_thread
.worker_cond
, &side_statedump_lock
);
662 pthread_mutex_unlock(&side_statedump_lock
);
663 if (state
& AGENT_THREAD_STATE_EXIT
)
665 if (state
& AGENT_THREAD_STATE_PAUSE
) {
668 (void)__atomic_or_fetch(&statedump_agent_thread
.state
, AGENT_THREAD_STATE_PAUSE_ACK
, __ATOMIC_SEQ_CST
);
670 state
= __atomic_load_n(&statedump_agent_thread
.state
, __ATOMIC_SEQ_CST
);
671 if (!(state
& AGENT_THREAD_STATE_PAUSE
))
673 if (attempt
> SIDE_RETRY_BUSY_LOOP_ATTEMPTS
) {
674 (void)poll(NULL
, 0, SIDE_RETRY_DELAY_MS
);
682 (void)__atomic_and_fetch(&statedump_agent_thread
.state
, ~AGENT_THREAD_STATE_HANDLE_REQUEST
, __ATOMIC_SEQ_CST
);
683 side_rcu_read_begin(&statedump_rcu_gp
, &rcu_read_state
);
684 side_list_for_each_entry_rcu(handle
, &side_statedump_list
, node
)
685 _side_statedump_run_pending_requests(handle
);
686 side_rcu_read_end(&statedump_rcu_gp
, &rcu_read_state
);
692 void statedump_agent_thread_init(void)
694 pthread_cond_init(&statedump_agent_thread
.worker_cond
, NULL
);
695 pthread_cond_init(&statedump_agent_thread
.waiter_cond
, NULL
);
696 statedump_agent_thread
.state
= AGENT_THREAD_STATE_BLOCKED
;
699 /* Called with side_agent_thread_lock and side_statedump_lock held. */
701 void statedump_agent_thread_get(void)
705 if (statedump_agent_thread
.ref
++)
707 statedump_agent_thread_init();
708 ret
= pthread_create(&statedump_agent_thread
.id
, NULL
,
709 statedump_agent_func
, NULL
);
716 * Called with side_agent_thread_lock and side_statedump_lock held.
717 * Returns true if join for agent thread is needed.
720 bool statedump_agent_thread_put(void)
722 if (--statedump_agent_thread
.ref
)
724 (void)__atomic_or_fetch(&statedump_agent_thread
.state
, AGENT_THREAD_STATE_EXIT
, __ATOMIC_SEQ_CST
);
725 pthread_cond_broadcast(&statedump_agent_thread
.worker_cond
);
730 void statedump_agent_thread_fini(void)
732 statedump_agent_thread
.state
= AGENT_THREAD_STATE_BLOCKED
;
733 if (pthread_cond_destroy(&statedump_agent_thread
.worker_cond
))
735 if (pthread_cond_destroy(&statedump_agent_thread
.waiter_cond
))
739 /* Called with side_agent_thread_lock held. */
741 void statedump_agent_thread_join(void)
746 ret
= pthread_join(statedump_agent_thread
.id
, &retval
);
750 statedump_agent_thread_fini();
753 struct side_statedump_request_handle
*
754 side_statedump_request_notification_register(const char *state_name
,
755 void (*statedump_cb
)(void *statedump_request_key
),
756 enum side_statedump_mode mode
)
758 struct side_statedump_request_handle
*handle
;
765 handle
= (struct side_statedump_request_handle
*)
766 calloc(1, sizeof(struct side_statedump_request_handle
));
769 name
= strdup(state_name
);
772 handle
->cb
= statedump_cb
;
775 side_list_head_init(&handle
->notification_queue
);
777 if (mode
== SIDE_STATEDUMP_MODE_AGENT_THREAD
)
778 pthread_mutex_lock(&side_agent_thread_lock
);
779 pthread_mutex_lock(&side_statedump_lock
);
780 if (mode
== SIDE_STATEDUMP_MODE_AGENT_THREAD
)
781 statedump_agent_thread_get();
782 side_list_insert_node_tail_rcu(&side_statedump_list
, &handle
->node
);
783 /* Queue statedump pending for all tracers. */
784 queue_statedump_pending(handle
, SIDE_KEY_MATCH_ALL
);
785 pthread_mutex_unlock(&side_statedump_lock
);
787 if (mode
== SIDE_STATEDUMP_MODE_AGENT_THREAD
) {
788 pthread_mutex_unlock(&side_agent_thread_lock
);
790 pthread_mutex_lock(&side_statedump_lock
);
791 while (!side_list_empty(&handle
->notification_queue
))
792 pthread_cond_wait(&statedump_agent_thread
.waiter_cond
, &side_statedump_lock
);
793 pthread_mutex_unlock(&side_statedump_lock
);
803 void side_statedump_request_notification_unregister(struct side_statedump_request_handle
*handle
)
812 if (handle
->mode
== SIDE_STATEDUMP_MODE_AGENT_THREAD
)
813 pthread_mutex_lock(&side_agent_thread_lock
);
814 pthread_mutex_lock(&side_statedump_lock
);
815 unqueue_statedump_pending(handle
, SIDE_KEY_MATCH_ALL
);
816 side_list_remove_node_rcu(&handle
->node
);
817 if (handle
->mode
== SIDE_STATEDUMP_MODE_AGENT_THREAD
)
818 join
= statedump_agent_thread_put();
819 pthread_mutex_unlock(&side_statedump_lock
);
821 statedump_agent_thread_join();
822 if (handle
->mode
== SIDE_STATEDUMP_MODE_AGENT_THREAD
)
823 pthread_mutex_unlock(&side_agent_thread_lock
);
825 side_rcu_wait_grace_period(&statedump_rcu_gp
);
830 /* Returns true if the handle has pending statedump requests. */
831 bool side_statedump_poll_pending_requests(struct side_statedump_request_handle
*handle
)
835 if (handle
->mode
!= SIDE_STATEDUMP_MODE_POLLING
)
837 pthread_mutex_lock(&side_statedump_lock
);
838 ret
= !side_list_empty(&handle
->notification_queue
);
839 pthread_mutex_unlock(&side_statedump_lock
);
844 * Only polling mode state dump handles allow application to explicitly handle the
847 int side_statedump_run_pending_requests(struct side_statedump_request_handle
*handle
)
849 if (handle
->mode
!= SIDE_STATEDUMP_MODE_POLLING
)
850 return SIDE_ERROR_INVAL
;
851 _side_statedump_run_pending_requests(handle
);
852 return SIDE_ERROR_OK
;
856 * Request a state dump for tracer callbacks identified with "key".
858 int side_tracer_statedump_request(uint64_t key
)
860 struct side_statedump_request_handle
*handle
;
862 if (key
== SIDE_KEY_MATCH_ALL
)
863 return SIDE_ERROR_INVAL
;
864 pthread_mutex_lock(&side_statedump_lock
);
865 side_list_for_each_entry(handle
, &side_statedump_list
, node
)
866 queue_statedump_pending(handle
, key
);
867 pthread_mutex_lock(&side_statedump_lock
);
868 return SIDE_ERROR_OK
;
872 * Cancel a statedump request.
874 int side_tracer_statedump_request_cancel(uint64_t key
)
876 struct side_statedump_request_handle
*handle
;
878 if (key
== SIDE_KEY_MATCH_ALL
)
879 return SIDE_ERROR_INVAL
;
880 pthread_mutex_lock(&side_statedump_lock
);
881 side_list_for_each_entry(handle
, &side_statedump_list
, node
)
882 unqueue_statedump_pending(handle
, key
);
883 pthread_mutex_lock(&side_statedump_lock
);
884 return SIDE_ERROR_OK
;
888 * Tracer keys are represented on 64-bit. Return SIDE_ERROR_NOMEM on
889 * overflow (which should never happen in practice).
891 int side_tracer_request_key(uint64_t *key
)
893 int ret
= SIDE_ERROR_OK
;
895 pthread_mutex_lock(&side_key_lock
);
896 if (side_key_next
== 0) {
897 ret
= SIDE_ERROR_NOMEM
;
900 *key
= side_key_next
++;
902 pthread_mutex_unlock(&side_key_lock
);
907 * Use of pthread_atfork depends on glibc 2.24 to eliminate hangs when
908 * waiting for the agent thread if the agent thread calls malloc. This
909 * is corrected by GNU libc
910 * commit 8a727af925be63aa6ea0f5f90e16751fd541626b.
911 * Ref. https://bugzilla.redhat.com/show_bug.cgi?id=906468
914 void side_before_fork(void)
918 pthread_mutex_lock(&side_agent_thread_lock
);
919 if (!statedump_agent_thread
.ref
)
921 /* Pause agent thread. */
922 pthread_mutex_lock(&side_statedump_lock
);
923 (void)__atomic_or_fetch(&statedump_agent_thread
.state
, AGENT_THREAD_STATE_PAUSE
, __ATOMIC_SEQ_CST
);
924 pthread_cond_broadcast(&statedump_agent_thread
.worker_cond
);
925 pthread_mutex_unlock(&side_statedump_lock
);
926 /* Wait for agent thread acknowledge. */
927 while (!(__atomic_load_n(&statedump_agent_thread
.state
, __ATOMIC_SEQ_CST
) & AGENT_THREAD_STATE_PAUSE_ACK
)) {
928 if (attempt
> SIDE_RETRY_BUSY_LOOP_ATTEMPTS
) {
929 (void)poll(NULL
, 0, SIDE_RETRY_DELAY_MS
);
938 void side_after_fork_parent(void)
940 if (statedump_agent_thread
.ref
)
941 (void)__atomic_and_fetch(&statedump_agent_thread
.state
,
942 ~(AGENT_THREAD_STATE_PAUSE
| AGENT_THREAD_STATE_PAUSE_ACK
),
944 pthread_mutex_unlock(&side_agent_thread_lock
);
948 * The agent thread does not exist in the child process after a fork.
949 * Re-initialize its data structures and create a new agent thread.
952 void side_after_fork_child(void)
954 if (statedump_agent_thread
.ref
) {
957 statedump_agent_thread_fini();
958 statedump_agent_thread_init();
959 ret
= pthread_create(&statedump_agent_thread
.id
, NULL
,
960 statedump_agent_func
, NULL
);
965 pthread_mutex_unlock(&side_agent_thread_lock
);
972 side_rcu_gp_init(&event_rcu_gp
);
973 side_rcu_gp_init(&statedump_rcu_gp
);
974 if (pthread_atfork(side_before_fork
, side_after_fork_parent
, side_after_fork_child
))
980 * side_exit() is executed from a library destructor. It can be called
981 * explicitly at application exit as well. Concurrent side API use is
982 * not expected at that point.
986 struct side_events_register_handle
*handle
, *tmp
;
990 side_list_for_each_entry_safe(handle
, tmp
, &side_events_list
, node
)
991 side_events_unregister(handle
);
992 side_rcu_gp_exit(&event_rcu_gp
);
993 side_rcu_gp_exit(&statedump_rcu_gp
);