1 | //===-- xray_log_interface.h ----------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file is a part of XRay, a function call tracing system. |
10 | // |
11 | // APIs for installing a new logging implementation. |
12 | // |
13 | //===----------------------------------------------------------------------===// |
14 | /// |
15 | /// XRay allows users to implement their own logging handlers and install them |
16 | /// to replace the default runtime-controllable implementation that comes with |
17 | /// compiler-rt/xray. The "flight data recorder" (FDR) mode implementation uses |
18 | /// this API to install itself in an XRay-enabled binary. See |
19 | /// compiler-rt/lib/xray_fdr_logging.{h,cc} for details of that implementation. |
20 | /// |
21 | /// The high-level usage pattern for these APIs look like the following: |
22 | /// |
23 | /// // We choose the mode which we'd like to install, and check whether this |
24 | /// // has succeeded. Each mode will have their own set of flags they will |
25 | /// // support, outside of the global XRay configuration options that are |
26 | /// // defined in the XRAY_OPTIONS environment variable. |
27 | /// auto select_status = __xray_log_select_mode("xray-fdr"); |
28 | /// if (select_status != XRayLogRegisterStatus::XRAY_REGISTRATION_OK) { |
29 | /// // This failed, we should not proceed with attempting to initialise |
30 | /// // the currently selected mode. |
31 | /// return; |
32 | /// } |
33 | /// |
34 | /// // Once that's done, we can now attempt to configure the implementation. |
35 | /// // To do this, we provide the string flags configuration for the mode. |
36 | /// auto config_status = __xray_log_init_mode( |
37 | /// "xray-fdr", "verbosity=1 some_flag=1 another_flag=2"); |
38 | /// if (config_status != XRayLogInitStatus::XRAY_LOG_INITIALIZED) { |
39 | /// // deal with the error here, if there is one. |
40 | /// } |
41 | /// |
42 | /// // When the log implementation has had the chance to initialize, we can |
43 | /// // now patch the instrumentation points. Note that we could have patched |
44 | /// // the instrumentation points first, but there's no strict ordering to |
45 | /// // these operations. |
46 | /// auto patch_status = __xray_patch(); |
47 | /// if (patch_status != XRayPatchingStatus::SUCCESS) { |
48 | /// // deal with the error here, if it is an error. |
49 | /// } |
50 | /// |
51 | /// // If we want to stop the implementation, we can then finalize it (before |
52 | /// // optionally flushing the log). |
53 | /// auto fin_status = __xray_log_finalize(); |
54 | /// if (fin_status != XRayLogInitStatus::XRAY_LOG_FINALIZED) { |
55 | /// // deal with the error here, if it is an error. |
56 | /// } |
57 | /// |
58 | /// // We can optionally wait before flushing the log to give other threads a |
59 | /// // chance to see that the implementation is already finalized. Also, at |
60 | /// // this point we can optionally unpatch the instrumentation points to |
61 | /// // reduce overheads at runtime. |
62 | /// auto unpatch_status = __xray_unpatch(); |
63 | /// if (unpatch_status != XRayPatchingStatus::SUCCESS) { |
64 | /// // deal with the error here, if it is an error. |
65 | /// } |
66 | /// |
67 | /// // If there are logs or data to be flushed somewhere, we can do so only |
68 | /// // after we've finalized the log. Some implementations may not actually |
69 | /// // have anything to log (it might keep the data in memory, or periodically |
70 | /// // be logging the data anyway). |
71 | /// auto flush_status = __xray_log_flushLog(); |
72 | /// if (flush_status != XRayLogFlushStatus::XRAY_LOG_FLUSHED) { |
73 | /// // deal with the error here, if it is an error. |
74 | /// } |
75 | /// |
76 | /// // Alternatively, we can go through the buffers ourselves without |
77 | /// // relying on the implementations' flushing semantics (if the |
78 | /// // implementation supports exporting this data directly). |
79 | /// auto MyBufferProcessor = +[](const char* mode, XRayBuffer buffer) { |
80 | /// // Check the "mode" to see if it's something we know how to handle... |
81 | /// // and/or do something with an XRayBuffer instance. |
82 | /// }; |
83 | /// auto process_status = __xray_log_process_buffers(MyBufferProcessor); |
84 | /// if (process_status != XRayLogFlushStatus::XRAY_LOG_FLUSHED) { |
85 | /// // deal with the error here, if it is an error. |
86 | /// } |
87 | /// |
88 | /// NOTE: Before calling __xray_patch() again, consider re-initializing the |
89 | /// implementation first. Some implementations might stay in an "off" state when |
90 | /// they are finalized, while some might be in an invalid/unknown state. |
91 | /// |
92 | #ifndef XRAY_XRAY_LOG_INTERFACE_H |
93 | #define XRAY_XRAY_LOG_INTERFACE_H |
94 | |
95 | #include "xray/xray_interface.h" |
96 | #include <stddef.h> |
97 | |
98 | extern "C" { |
99 | |
100 | /// This enum defines the valid states in which the logging implementation can |
101 | /// be at. |
102 | enum XRayLogInitStatus { |
103 | /// The default state is uninitialized, and in case there were errors in the |
104 | /// initialization, the implementation MUST return XRAY_LOG_UNINITIALIZED. |
105 | XRAY_LOG_UNINITIALIZED = 0, |
106 | |
107 | /// Some implementations support multi-stage init (or asynchronous init), and |
108 | /// may return XRAY_LOG_INITIALIZING to signal callers of the API that |
109 | /// there's an ongoing initialization routine running. This allows |
110 | /// implementations to support concurrent threads attempting to initialize, |
111 | /// while only signalling success in one. |
112 | XRAY_LOG_INITIALIZING = 1, |
113 | |
114 | /// When an implementation is done initializing, it MUST return |
115 | /// XRAY_LOG_INITIALIZED. When users call `__xray_patch()`, they are |
116 | /// guaranteed that the implementation installed with |
117 | /// `__xray_set_log_impl(...)` has been initialized. |
118 | XRAY_LOG_INITIALIZED = 2, |
119 | |
120 | /// Some implementations might support multi-stage finalization (or |
121 | /// asynchronous finalization), and may return XRAY_LOG_FINALIZING to signal |
122 | /// callers of the API that there's an ongoing finalization routine running. |
123 | /// This allows implementations to support concurrent threads attempting to |
124 | /// finalize, while only signalling success/completion in one. |
125 | XRAY_LOG_FINALIZING = 3, |
126 | |
127 | /// When an implementation is done finalizing, it MUST return |
128 | /// XRAY_LOG_FINALIZED. It is up to the implementation to determine what the |
129 | /// semantics of a finalized implementation is. Some implementations might |
130 | /// allow re-initialization once the log is finalized, while some might always |
131 | /// be on (and that finalization is a no-op). |
132 | XRAY_LOG_FINALIZED = 4, |
133 | }; |
134 | |
135 | /// This enum allows an implementation to signal log flushing operations via |
136 | /// `__xray_log_flushLog()`, and the state of flushing the log. |
137 | enum XRayLogFlushStatus { |
138 | XRAY_LOG_NOT_FLUSHING = 0, |
139 | XRAY_LOG_FLUSHING = 1, |
140 | XRAY_LOG_FLUSHED = 2, |
141 | }; |
142 | |
143 | /// This enum indicates the installation state of a logging implementation, when |
144 | /// associating a mode to a particular logging implementation through |
145 | /// `__xray_log_register_impl(...)` or through `__xray_log_select_mode(...`. |
146 | enum XRayLogRegisterStatus { |
147 | XRAY_REGISTRATION_OK = 0, |
148 | XRAY_DUPLICATE_MODE = 1, |
149 | XRAY_MODE_NOT_FOUND = 2, |
150 | XRAY_INCOMPLETE_IMPL = 3, |
151 | }; |
152 | |
153 | /// A valid XRay logging implementation MUST provide all of the function |
154 | /// pointers in XRayLogImpl when being installed through `__xray_set_log_impl`. |
155 | /// To be precise, ALL the functions pointers MUST NOT be nullptr. |
156 | struct XRayLogImpl { |
157 | /// The log initialization routine provided by the implementation, always |
158 | /// provided with the following parameters: |
159 | /// |
160 | /// - buffer size (unused) |
161 | /// - maximum number of buffers (unused) |
162 | /// - a pointer to an argument struct that the implementation MUST handle |
163 | /// - the size of the argument struct |
164 | /// |
165 | /// See XRayLogInitStatus for details on what the implementation MUST return |
166 | /// when called. |
167 | /// |
168 | /// If the implementation needs to install handlers aside from the 0-argument |
169 | /// function call handler, it MUST do so in this initialization handler. |
170 | /// |
171 | /// See xray_interface.h for available handler installation routines. |
172 | XRayLogInitStatus (*log_init)(size_t, size_t, void *, size_t); |
173 | |
174 | /// The log finalization routine provided by the implementation. |
175 | /// |
176 | /// See XRayLogInitStatus for details on what the implementation MUST return |
177 | /// when called. |
178 | XRayLogInitStatus (*log_finalize)(); |
179 | |
180 | /// The 0-argument function call handler. XRay logging implementations MUST |
181 | /// always have a handler for function entry and exit events. In case the |
182 | /// implementation wants to support arg1 (or other future extensions to XRay |
183 | /// logging) those MUST be installed by the installed 'log_init' handler. |
184 | /// |
185 | /// Because we didn't want to change the ABI of this struct, the arg1 handler |
186 | /// may be silently overwritten during initialization as well. |
187 | void (*handle_arg0)(int32_t, XRayEntryType); |
188 | |
189 | /// The log implementation provided routine for when __xray_log_flushLog() is |
190 | /// called. |
191 | /// |
192 | /// See XRayLogFlushStatus for details on what the implementation MUST return |
193 | /// when called. |
194 | XRayLogFlushStatus (*flush_log)(); |
195 | }; |
196 | |
197 | /// DEPRECATED: Use the mode registration workflow instead with |
198 | /// __xray_log_register_mode(...) and __xray_log_select_mode(...). See the |
199 | /// documentation for those function. |
200 | /// |
201 | /// This function installs a new logging implementation that XRay will use. In |
202 | /// case there are any nullptr members in Impl, XRay will *uninstall any |
203 | /// existing implementations*. It does NOT patch the instrumentation points. |
204 | /// |
205 | /// NOTE: This function does NOT attempt to finalize the currently installed |
206 | /// implementation. Use with caution. |
207 | /// |
208 | /// It is guaranteed safe to call this function in the following states: |
209 | /// |
210 | /// - When the implementation is UNINITIALIZED. |
211 | /// - When the implementation is FINALIZED. |
212 | /// - When there is no current implementation installed. |
213 | /// |
214 | /// It is logging implementation defined what happens when this function is |
215 | /// called while in any other states. |
216 | void __xray_set_log_impl(XRayLogImpl Impl); |
217 | |
218 | /// This function registers a logging implementation against a "mode" |
219 | /// identifier. This allows multiple modes to be registered, and chosen at |
220 | /// runtime using the same mode identifier through |
221 | /// `__xray_log_select_mode(...)`. |
222 | /// |
223 | /// We treat the Mode identifier as a null-terminated byte string, as the |
224 | /// identifier used when retrieving the log impl. |
225 | /// |
226 | /// Returns: |
227 | /// - XRAY_REGISTRATION_OK on success. |
228 | /// - XRAY_DUPLICATE_MODE when an implementation is already associated with |
229 | /// the provided Mode; does not update the already-registered |
230 | /// implementation. |
231 | XRayLogRegisterStatus __xray_log_register_mode(const char *Mode, |
232 | XRayLogImpl Impl); |
233 | |
234 | /// This function selects the implementation associated with Mode that has been |
235 | /// registered through __xray_log_register_mode(...) and installs that |
236 | /// implementation (as if through calling __xray_set_log_impl(...)). The same |
237 | /// caveats apply to __xray_log_select_mode(...) as with |
238 | /// __xray_log_set_log_impl(...). |
239 | /// |
240 | /// Returns: |
241 | /// - XRAY_REGISTRATION_OK on success. |
242 | /// - XRAY_MODE_NOT_FOUND if there is no implementation associated with Mode; |
243 | /// does not update the currently installed implementation. |
244 | XRayLogRegisterStatus __xray_log_select_mode(const char *Mode); |
245 | |
246 | /// Returns an identifier for the currently selected XRay mode chosen through |
247 | /// the __xray_log_select_mode(...) function call. Returns nullptr if there is |
248 | /// no currently installed mode. |
249 | const char *__xray_log_get_current_mode(); |
250 | |
251 | /// This function removes the currently installed implementation. It will also |
252 | /// uninstall any handlers that have been previously installed. It does NOT |
253 | /// unpatch the instrumentation points. |
254 | /// |
255 | /// NOTE: This function does NOT attempt to finalize the currently installed |
256 | /// implementation. Use with caution. |
257 | /// |
258 | /// It is guaranteed safe to call this function in the following states: |
259 | /// |
260 | /// - When the implementation is UNINITIALIZED. |
261 | /// - When the implementation is FINALIZED. |
262 | /// - When there is no current implementation installed. |
263 | /// |
264 | /// It is logging implementation defined what happens when this function is |
265 | /// called while in any other states. |
266 | void __xray_remove_log_impl(); |
267 | |
268 | /// DEPRECATED: Use __xray_log_init_mode() instead, and provide all the options |
269 | /// in string form. |
270 | /// Invokes the installed implementation initialization routine. See |
271 | /// XRayLogInitStatus for what the return values mean. |
272 | XRayLogInitStatus __xray_log_init(size_t BufferSize, size_t MaxBuffers, |
273 | void *Args, size_t ArgsSize); |
274 | |
275 | /// Invokes the installed initialization routine, which *must* support the |
276 | /// string based form. |
277 | /// |
278 | /// NOTE: When this API is used, we still invoke the installed initialization |
279 | /// routine, but we will call it with the following convention to signal that we |
280 | /// are using the string form: |
281 | /// |
282 | /// - BufferSize = 0 |
283 | /// - MaxBuffers = 0 |
284 | /// - ArgsSize = 0 |
285 | /// - Args will be the pointer to the character buffer representing the |
286 | /// configuration. |
287 | /// |
288 | /// FIXME: Updating the XRayLogImpl struct is an ABI breaking change. When we |
289 | /// are ready to make a breaking change, we should clean this up appropriately. |
290 | XRayLogInitStatus __xray_log_init_mode(const char *Mode, const char *Config); |
291 | |
292 | /// Like __xray_log_init_mode(...) this version allows for providing |
293 | /// configurations that might have non-null-terminated strings. This will |
294 | /// operate similarly to __xray_log_init_mode, with the exception that |
295 | /// |ArgsSize| will be what |ConfigSize| is. |
296 | XRayLogInitStatus __xray_log_init_mode_bin(const char *Mode, const char *Config, |
297 | size_t ConfigSize); |
298 | |
299 | /// Invokes the installed implementation finalization routine. See |
300 | /// XRayLogInitStatus for what the return values mean. |
301 | XRayLogInitStatus __xray_log_finalize(); |
302 | |
303 | /// Invokes the install implementation log flushing routine. See |
304 | /// XRayLogFlushStatus for what the return values mean. |
305 | XRayLogFlushStatus __xray_log_flushLog(); |
306 | |
307 | /// An XRayBuffer represents a section of memory which can be treated by log |
308 | /// processing functions as bytes stored in the logging implementation's |
309 | /// buffers. |
310 | struct XRayBuffer { |
311 | const void *Data; |
312 | size_t Size; |
313 | }; |
314 | |
315 | /// Registers an iterator function which takes an XRayBuffer argument, then |
316 | /// returns another XRayBuffer function representing the next buffer. When the |
317 | /// Iterator function returns an empty XRayBuffer (Data = nullptr, Size = 0), |
318 | /// this signifies the end of the buffers. |
319 | /// |
320 | /// The first invocation of this Iterator function will always take an empty |
321 | /// XRayBuffer (Data = nullptr, Size = 0). |
322 | void __xray_log_set_buffer_iterator(XRayBuffer (*Iterator)(XRayBuffer)); |
323 | |
324 | /// Removes the currently registered buffer iterator function. |
325 | void __xray_log_remove_buffer_iterator(); |
326 | |
327 | /// Invokes the provided handler to process data maintained by the logging |
328 | /// handler. This API will be provided raw access to the data available in |
329 | /// memory from the logging implementation. The callback function must: |
330 | /// |
331 | /// 1) Not modify the data, to avoid running into undefined behaviour. |
332 | /// |
333 | /// 2) Either know the data layout, or treat the data as raw bytes for later |
334 | /// interpretation. |
335 | /// |
336 | /// This API is best used in place of the `__xray_log_flushLog()` implementation |
337 | /// above to enable the caller to provide an alternative means of extracting the |
338 | /// data from the XRay implementation. |
339 | /// |
340 | /// Implementations MUST then provide: |
341 | /// |
342 | /// 1) A function that will return an XRayBuffer. Functions that return an |
343 | /// "empty" XRayBuffer signifies that there are no more buffers to be |
344 | /// processed. This function should be registered through the |
345 | /// `__xray_log_set_buffer_iterator(...)` function. |
346 | /// |
347 | /// 2) Its own means of converting data it holds in memory into an XRayBuffer |
348 | /// structure. |
349 | /// |
350 | /// See XRayLogFlushStatus for what the return values mean. |
351 | /// |
352 | XRayLogFlushStatus __xray_log_process_buffers(void (*Processor)(const char *, |
353 | XRayBuffer)); |
354 | |
355 | } // extern "C" |
356 | |
357 | #endif // XRAY_XRAY_LOG_INTERFACE_H |
358 | |