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}