1 // SPDX-License-Identifier: MIT
3 * Copyright 2022 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
16 #include <side/macros.h>
17 #include <side/endian.h>
20 * SIDE stands for "Software Instrumentation Dynamically Enabled"
22 * This is an instrumentation ABI for Linux user-space, which exposes an
23 * instrumentation type system and facilities allowing a kernel or
24 * user-space tracer to consume user-space instrumentation.
26 * The extensibility scheme for the SIDE ABI for event state is as
29 * * If the semantic of the "struct side_event_state_N" fields change,
30 * the SIDE_EVENT_STATE_ABI_VERSION should be increased. The
31 * "struct side_event_state_N" is not extensible and must have its
32 * ABI version increased whenever it is changed. Note that increasing
33 * the version of SIDE_EVENT_DESCRIPTION_ABI_VERSION is not necessary
34 * when changing the layout of "struct side_event_state_N".
37 #define SIDE_EVENT_STATE_ABI_VERSION 0
39 #include <side/abi/event-description.h>
40 #include <side/abi/type-argument.h>
41 #include <side/instrumentation-c-api.h>
49 SIDE_ERROR_EXITING
= 5,
53 * This structure is _not_ packed to allow atomic operations on its
54 * fields. Changes to this structure must bump the "Event state ABI
55 * version" and tracers _must_ learn how to deal with this ABI,
56 * otherwise they should reject the event.
59 struct side_event_state
{
60 uint32_t version
; /* Event state ABI version. */
63 struct side_event_state_0
{
64 struct side_event_state parent
; /* Required first field. */
65 uint32_t nr_callbacks
;
67 const struct side_callback
*callbacks
;
68 struct side_event_description
*desc
;
76 struct side_tracer_handle
;
77 struct side_statedump_request_handle
;
79 extern const char side_empty_callback
[];
81 void side_call(const struct side_event_state
*state
,
82 const struct side_arg_vec
*side_arg_vec
);
83 void side_call_variadic(const struct side_event_state
*state
,
84 const struct side_arg_vec
*side_arg_vec
,
85 const struct side_arg_dynamic_struct
*var_struct
);
87 struct side_events_register_handle
*side_events_register(struct side_event_description
**events
,
89 void side_events_unregister(struct side_events_register_handle
*handle
);
92 * Userspace tracer registration API. This allows userspace tracers to
93 * register event notification callbacks to be notified of the currently
94 * registered instrumentation, and to register their callbacks to
97 * Application statedump callbacks are allowed to invoke
98 * side event register/unregister(), but tracer callbacks are _not_
99 * allowed to invoke statedump request notification register/unregister.
100 * The latter could result in hangs across RCU grace period domains.
102 typedef void (*side_tracer_callback_func
)(const struct side_event_description
*desc
,
103 const struct side_arg_vec
*side_arg_vec
,
104 void *priv
, void *caller_addr
);
105 typedef void (*side_tracer_callback_variadic_func
)(const struct side_event_description
*desc
,
106 const struct side_arg_vec
*side_arg_vec
,
107 const struct side_arg_dynamic_struct
*var_struct
,
108 void *priv
, void *caller_addr
);
110 int side_tracer_request_key(uint64_t *key
);
112 int side_tracer_callback_register(struct side_event_description
*desc
,
113 side_tracer_callback_func call
,
114 void *priv
, uint64_t key
);
115 int side_tracer_callback_variadic_register(struct side_event_description
*desc
,
116 side_tracer_callback_variadic_func call_variadic
,
117 void *priv
, uint64_t key
);
118 int side_tracer_callback_unregister(struct side_event_description
*desc
,
119 side_tracer_callback_func call
,
120 void *priv
, uint64_t key
);
121 int side_tracer_callback_variadic_unregister(struct side_event_description
*desc
,
122 side_tracer_callback_variadic_func call_variadic
,
123 void *priv
, uint64_t key
);
125 enum side_tracer_notification
{
126 SIDE_TRACER_NOTIFICATION_INSERT_EVENTS
,
127 SIDE_TRACER_NOTIFICATION_REMOVE_EVENTS
,
130 /* Callback is invoked with side library internal lock held. */
131 struct side_tracer_handle
*side_tracer_event_notification_register(
132 void (*cb
)(enum side_tracer_notification notif
,
133 struct side_event_description
**events
, uint32_t nr_events
, void *priv
),
135 void side_tracer_event_notification_unregister(struct side_tracer_handle
*handle
);
138 * The side_statedump_call APIs should be used for application/library
140 * The statedump callback dumps application state to tracers by invoking
141 * side_statedump_call APIs.
142 * The statedump callback should not invoke libside statedump request
143 * notification register/unregister APIs.
145 void side_statedump_call(const struct side_event_state
*state
,
146 const struct side_arg_vec
*side_arg_vec
,
147 void *statedump_request_key
);
148 void side_statedump_call_variadic(const struct side_event_state
*state
,
149 const struct side_arg_vec
*side_arg_vec
,
150 const struct side_arg_dynamic_struct
*var_struct
,
151 void *statedump_request_key
);
154 * If side_statedump_request_notification_register is invoked from
155 * library constructors and side_statedump_request_notification_unregister
156 * from library destructors, make sure to:
157 * - invoke side_event_description_ptr_init before registration of the
159 * - invoke side_event_description_ptr_exit after unregistration of the
162 * In "polling" state dump mode, the application or library is responsible
163 * for periodically invoking side_statedump_run_pending_requests(). This
164 * mechanism is well-suited for single-threaded event-loop driven
165 * applications which do not wish to introduce multithreading nor
166 * locking-based synchronization of their state.
168 * In "agent thread" state dump mode, libside spawns a helper agent
169 * thread which is responsible for invoking the state dump callbacks
170 * when requested by the tracers. This mechanism is well-suited for
171 * instrumentation of multi-threaded applications which rely on
172 * locking to synchronize their data structures across threads, and
173 * for libraries which have no control on application event loops.
175 * Applications using fork/clone with locks held should not take those
176 * locks (or block on any resource that depend on these locks) within
177 * their statedump callbacks registered with the agent thread. This
178 * could result in deadlocks when pthread_atfork handler waits for
179 * agent thread quiescence.
181 * The statedump_request_key received by the statedump_cb is only
182 * valid until the statedump_cb returns.
184 enum side_statedump_mode
{
185 SIDE_STATEDUMP_MODE_POLLING
,
186 SIDE_STATEDUMP_MODE_AGENT_THREAD
,
189 struct side_statedump_request_handle
*
190 side_statedump_request_notification_register(
191 const char *state_name
,
192 void (*statedump_cb
)(void *statedump_request_key
),
193 enum side_statedump_mode mode
);
194 void side_statedump_request_notification_unregister(
195 struct side_statedump_request_handle
*handle
);
197 /* Returns true if the handle has pending statedump requests. */
198 bool side_statedump_poll_pending_requests(struct side_statedump_request_handle
*handle
);
199 int side_statedump_run_pending_requests(struct side_statedump_request_handle
*handle
);
202 * Request a state dump for tracer callbacks identified with "key".
204 int side_tracer_statedump_request(uint64_t key
);
206 * Cancel a statedump request.
208 int side_tracer_statedump_request_cancel(uint64_t key
);
211 * Explicit hooks to initialize/finalize the side instrumentation
212 * library. Those are also library constructor/destructor.
214 void side_init(void) __attribute__((constructor
));
215 void side_exit(void) __attribute__((destructor
));
218 * The following constructors/destructors perform automatic registration
219 * of the declared side events. Those may have to be called explicitly
220 * in a statically linked library.
224 * These weak symbols, the constructor, and destructor take care of
225 * registering only _one_ instance of the side instrumentation per
226 * shared-ojbect (or for the whole main program).
228 extern struct side_event_description
* __start_side_event_description_ptr
[]
229 __attribute__((weak
, visibility("hidden")));
230 extern struct side_event_description
* __stop_side_event_description_ptr
[]
231 __attribute__((weak
, visibility("hidden")));
232 int side_event_description_ptr_registered
233 __attribute__((weak
, visibility("hidden")));
234 struct side_events_register_handle
*side_events_handle
235 __attribute__((weak
, visibility("hidden")));
238 side_event_description_ptr_init(void)
239 __attribute__((no_instrument_function
))
240 __attribute__((constructor
));
242 side_event_description_ptr_init(void)
244 if (side_event_description_ptr_registered
++)
246 side_events_handle
= side_events_register(__start_side_event_description_ptr
,
247 __stop_side_event_description_ptr
- __start_side_event_description_ptr
);
251 side_event_description_ptr_exit(void)
252 __attribute__((no_instrument_function
))
253 __attribute__((destructor
));
255 side_event_description_ptr_exit(void)
257 if (--side_event_description_ptr_registered
)
259 side_events_unregister(side_events_handle
);
260 side_events_handle
= NULL
;
267 #endif /* _SIDE_TRACE_H */