c12639a74441830fb2718d25229f90c7b90f6911
[libside.git] / include / side / trace.h
1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright 2022 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 */
5
6 #ifndef _SIDE_TRACE_H
7 #define _SIDE_TRACE_H
8
9 #include <stdint.h>
10 #include <inttypes.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <math.h>
14 #include <stdbool.h>
15 #include <stddef.h>
16 #include <side/macros.h>
17 #include <side/endian.h>
18
19 /*
20 * SIDE stands for "Software Instrumentation Dynamically Enabled"
21 *
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.
25 *
26 * The extensibility scheme for the SIDE ABI for event state is as
27 * follows:
28 *
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".
35 */
36
37 #define SIDE_EVENT_STATE_ABI_VERSION 0
38
39 #include <side/abi/event-description.h>
40 #include <side/abi/type-argument.h>
41 #include <side/instrumentation-c-api.h>
42
43 enum side_error {
44 SIDE_ERROR_OK = 0,
45 SIDE_ERROR_INVAL = 1,
46 SIDE_ERROR_EXIST = 2,
47 SIDE_ERROR_NOMEM = 3,
48 SIDE_ERROR_NOENT = 4,
49 SIDE_ERROR_EXITING = 5,
50 };
51
52 /*
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.
57 */
58
59 struct side_event_state {
60 uint32_t version; /* Event state ABI version. */
61 };
62
63 struct side_event_state_0 {
64 struct side_event_state parent; /* Required first field. */
65 uint32_t nr_callbacks;
66 uintptr_t enabled;
67 const struct side_callback *callbacks;
68 struct side_event_description *desc;
69 };
70
71 #ifdef __cplusplus
72 extern "C" {
73 #endif
74
75 struct side_callback;
76 struct side_tracer_handle;
77 struct side_statedump_request_handle;
78
79 extern const char side_empty_callback[];
80
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);
86
87 struct side_events_register_handle *side_events_register(struct side_event_description **events,
88 uint32_t nr_events);
89 void side_events_unregister(struct side_events_register_handle *handle);
90
91 /*
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
95 * specific events.
96 *
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.
101 */
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);
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);
109
110 int side_tracer_callback_register(struct side_event_description *desc,
111 side_tracer_callback_func call,
112 void *priv, void *key);
113 int side_tracer_callback_variadic_register(struct side_event_description *desc,
114 side_tracer_callback_variadic_func call_variadic,
115 void *priv, void *key);
116 int side_tracer_callback_unregister(struct side_event_description *desc,
117 side_tracer_callback_func call,
118 void *priv, void *key);
119 int side_tracer_callback_variadic_unregister(struct side_event_description *desc,
120 side_tracer_callback_variadic_func call_variadic,
121 void *priv, void *key);
122
123 enum side_tracer_notification {
124 SIDE_TRACER_NOTIFICATION_INSERT_EVENTS,
125 SIDE_TRACER_NOTIFICATION_REMOVE_EVENTS,
126 };
127
128 /* Callback is invoked with side library internal lock held. */
129 struct side_tracer_handle *side_tracer_event_notification_register(
130 void (*cb)(enum side_tracer_notification notif,
131 struct side_event_description **events, uint32_t nr_events, void *priv),
132 void *priv);
133 void side_tracer_event_notification_unregister(struct side_tracer_handle *handle);
134
135 /*
136 * The side_statedump_call APIs should be used for application/library
137 * state dump.
138 * The statedump callback dumps application state to tracers by invoking
139 * side_statedump_call APIs.
140 * The statedump callback should not invoke libside statedump request
141 * notification register/unregister APIs.
142 */
143 void side_statedump_call(const struct side_event_state *state,
144 const struct side_arg_vec *side_arg_vec);
145 void side_statedump_call_variadic(const struct side_event_state *state,
146 const struct side_arg_vec *side_arg_vec,
147 const struct side_arg_dynamic_struct *var_struct);
148
149 /*
150 * If side_statedump_request_notification_register is invoked from
151 * library constructors and side_statedump_request_notification_unregister
152 * from library destructors, make sure to:
153 * - invoke side_event_description_ptr_init before registration of the
154 * callback,
155 * - invoke side_event_description_ptr_exit after unregistration of the
156 * callback.
157 *
158 * In "polling" state dump mode, the application or library is responsible
159 * for periodically invoking side_statedump_run_pending_requests(). This
160 * mechanism is well-suited for single-threaded event-loop driven
161 * applications which do not wish to introduce multithreading nor
162 * locking-based synchronization of their state.
163 *
164 * In "agent thread" state dump mode, libside spawns a helper agent
165 * thread which is responsible for invoking the state dump callbacks
166 * when requested by the tracers. This mechanism is well-suited for
167 * instrumentation of multi-threaded applications which rely on
168 * locking to synchronize their data structures across threads, and
169 * for libraries which have no control on application event loops.
170 */
171 enum side_statedump_mode {
172 SIDE_STATEDUMP_MODE_POLLING,
173 SIDE_STATEDUMP_MODE_AGENT_THREAD,
174 };
175
176 struct side_statedump_request_handle *
177 side_statedump_request_notification_register(
178 const char *state_name,
179 void (*statedump_cb)(void),
180 enum side_statedump_mode mode);
181 void side_statedump_request_notification_unregister(
182 struct side_statedump_request_handle *handle);
183
184 /* Returns true if the handle has pending statedump requests. */
185 bool side_statedump_poll_pending_requests(struct side_statedump_request_handle *handle);
186 void side_statedump_run_pending_requests(struct side_statedump_request_handle *handle);
187
188 /*
189 * Request a state dump for tracer callbacks identified with "key".
190 * Calls the completion callback when the statedump request is fulfilled.
191 */
192 void side_tracer_statedump_request(void *key, void (*completion)(void *priv), void *priv);
193 /*
194 * Cancel a statedump request.
195 * Returns true if the request is cancelled before completion.
196 * The completion callback is not invoked when a statedump request is
197 * cancelled.
198 */
199 bool side_tracer_statedump_request_cancel(void *key);
200
201 /*
202 * Explicit hooks to initialize/finalize the side instrumentation
203 * library. Those are also library constructor/destructor.
204 */
205 void side_init(void) __attribute__((constructor));
206 void side_exit(void) __attribute__((destructor));
207
208 /*
209 * The following constructors/destructors perform automatic registration
210 * of the declared side events. Those may have to be called explicitly
211 * in a statically linked library.
212 */
213
214 /*
215 * These weak symbols, the constructor, and destructor take care of
216 * registering only _one_ instance of the side instrumentation per
217 * shared-ojbect (or for the whole main program).
218 */
219 extern struct side_event_description * __start_side_event_description_ptr[]
220 __attribute__((weak, visibility("hidden")));
221 extern struct side_event_description * __stop_side_event_description_ptr[]
222 __attribute__((weak, visibility("hidden")));
223 int side_event_description_ptr_registered
224 __attribute__((weak, visibility("hidden")));
225 struct side_events_register_handle *side_events_handle
226 __attribute__((weak, visibility("hidden")));
227
228 static void
229 side_event_description_ptr_init(void)
230 __attribute__((no_instrument_function))
231 __attribute__((constructor));
232 static void
233 side_event_description_ptr_init(void)
234 {
235 if (side_event_description_ptr_registered++)
236 return;
237 side_events_handle = side_events_register(__start_side_event_description_ptr,
238 __stop_side_event_description_ptr - __start_side_event_description_ptr);
239 }
240
241 static void
242 side_event_description_ptr_exit(void)
243 __attribute__((no_instrument_function))
244 __attribute__((destructor));
245 static void
246 side_event_description_ptr_exit(void)
247 {
248 if (--side_event_description_ptr_registered)
249 return;
250 side_events_unregister(side_events_handle);
251 side_events_handle = NULL;
252 }
253
254 #ifdef __cplusplus
255 }
256 #endif
257
258 #endif /* _SIDE_TRACE_H */
This page took 0.033941 seconds and 3 git commands to generate.