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