//===-- xray_log_interface.h ----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file is a part of XRay, a function call tracing system. // // APIs for installing a new logging implementation. // //===----------------------------------------------------------------------===// /// /// XRay allows users to implement their own logging handlers and install them /// to replace the default runtime-controllable implementation that comes with /// compiler-rt/xray. The "flight data recorder" (FDR) mode implementation uses /// this API to install itself in an XRay-enabled binary. See /// compiler-rt/lib/xray_fdr_logging.{h,cc} for details of that implementation. /// /// The high-level usage pattern for these APIs look like the following: /// /// // We choose the mode which we'd like to install, and check whether this /// // has succeeded. Each mode will have their own set of flags they will /// // support, outside of the global XRay configuration options that are /// // defined in the XRAY_OPTIONS environment variable. /// auto select_status = __xray_log_select_mode("xray-fdr"); /// if (select_status != XRayLogRegisterStatus::XRAY_REGISTRATION_OK) { /// // This failed, we should not proceed with attempting to initialise /// // the currently selected mode. /// return; /// } /// /// // Once that's done, we can now attempt to configure the implementation. /// // To do this, we provide the string flags configuration for the mode. /// auto config_status = __xray_log_init_mode( /// "xray-fdr", "verbosity=1 some_flag=1 another_flag=2"); /// if (config_status != XRayLogInitStatus::XRAY_LOG_INITIALIZED) { /// // deal with the error here, if there is one. /// } /// /// // When the log implementation has had the chance to initialize, we can /// // now patch the instrumentation points. Note that we could have patched /// // the instrumentation points first, but there's no strict ordering to /// // these operations. /// auto patch_status = __xray_patch(); /// if (patch_status != XRayPatchingStatus::SUCCESS) { /// // deal with the error here, if it is an error. /// } /// /// // If we want to stop the implementation, we can then finalize it (before /// // optionally flushing the log). /// auto fin_status = __xray_log_finalize(); /// if (fin_status != XRayLogInitStatus::XRAY_LOG_FINALIZED) { /// // deal with the error here, if it is an error. /// } /// /// // We can optionally wait before flushing the log to give other threads a /// // chance to see that the implementation is already finalized. Also, at /// // this point we can optionally unpatch the instrumentation points to /// // reduce overheads at runtime. /// auto unpatch_status = __xray_unpatch(); /// if (unpatch_status != XRayPatchingStatus::SUCCESS) { /// // deal with the error here, if it is an error. /// } /// /// // If there are logs or data to be flushed somewhere, we can do so only /// // after we've finalized the log. Some implementations may not actually /// // have anything to log (it might keep the data in memory, or periodically /// // be logging the data anyway). /// auto flush_status = __xray_log_flushLog(); /// if (flush_status != XRayLogFlushStatus::XRAY_LOG_FLUSHED) { /// // deal with the error here, if it is an error. /// } /// /// // Alternatively, we can go through the buffers ourselves without /// // relying on the implementations' flushing semantics (if the /// // implementation supports exporting this data directly). /// auto MyBufferProcessor = +[](const char* mode, XRayBuffer buffer) { /// // Check the "mode" to see if it's something we know how to handle... /// // and/or do something with an XRayBuffer instance. /// }; /// auto process_status = __xray_log_process_buffers(MyBufferProcessor); /// if (process_status != XRayLogFlushStatus::XRAY_LOG_FLUSHED) { /// // deal with the error here, if it is an error. /// } /// /// NOTE: Before calling __xray_patch() again, consider re-initializing the /// implementation first. Some implementations might stay in an "off" state when /// they are finalized, while some might be in an invalid/unknown state. /// #ifndef XRAY_XRAY_LOG_INTERFACE_H #define XRAY_XRAY_LOG_INTERFACE_H #include "xray/xray_interface.h" #include <stddef.h> extern "C" { /// This enum defines the valid states in which the logging implementation can /// be at. enum XRayLogInitStatus { … }; /// This enum allows an implementation to signal log flushing operations via /// `__xray_log_flushLog()`, and the state of flushing the log. enum XRayLogFlushStatus { … }; /// This enum indicates the installation state of a logging implementation, when /// associating a mode to a particular logging implementation through /// `__xray_log_register_impl(...)` or through `__xray_log_select_mode(...`. enum XRayLogRegisterStatus { … }; /// A valid XRay logging implementation MUST provide all of the function /// pointers in XRayLogImpl when being installed through `__xray_set_log_impl`. /// To be precise, ALL the functions pointers MUST NOT be nullptr. struct XRayLogImpl { … }; /// DEPRECATED: Use the mode registration workflow instead with /// __xray_log_register_mode(...) and __xray_log_select_mode(...). See the /// documentation for those function. /// /// This function installs a new logging implementation that XRay will use. In /// case there are any nullptr members in Impl, XRay will *uninstall any /// existing implementations*. It does NOT patch the instrumentation points. /// /// NOTE: This function does NOT attempt to finalize the currently installed /// implementation. Use with caution. /// /// It is guaranteed safe to call this function in the following states: /// /// - When the implementation is UNINITIALIZED. /// - When the implementation is FINALIZED. /// - When there is no current implementation installed. /// /// It is logging implementation defined what happens when this function is /// called while in any other states. void __xray_set_log_impl(XRayLogImpl Impl); /// This function registers a logging implementation against a "mode" /// identifier. This allows multiple modes to be registered, and chosen at /// runtime using the same mode identifier through /// `__xray_log_select_mode(...)`. /// /// We treat the Mode identifier as a null-terminated byte string, as the /// identifier used when retrieving the log impl. /// /// Returns: /// - XRAY_REGISTRATION_OK on success. /// - XRAY_DUPLICATE_MODE when an implementation is already associated with /// the provided Mode; does not update the already-registered /// implementation. XRayLogRegisterStatus __xray_log_register_mode(const char *Mode, XRayLogImpl Impl); /// This function selects the implementation associated with Mode that has been /// registered through __xray_log_register_mode(...) and installs that /// implementation (as if through calling __xray_set_log_impl(...)). The same /// caveats apply to __xray_log_select_mode(...) as with /// __xray_log_set_log_impl(...). /// /// Returns: /// - XRAY_REGISTRATION_OK on success. /// - XRAY_MODE_NOT_FOUND if there is no implementation associated with Mode; /// does not update the currently installed implementation. XRayLogRegisterStatus __xray_log_select_mode(const char *Mode); /// Returns an identifier for the currently selected XRay mode chosen through /// the __xray_log_select_mode(...) function call. Returns nullptr if there is /// no currently installed mode. const char *__xray_log_get_current_mode(); /// This function removes the currently installed implementation. It will also /// uninstall any handlers that have been previously installed. It does NOT /// unpatch the instrumentation points. /// /// NOTE: This function does NOT attempt to finalize the currently installed /// implementation. Use with caution. /// /// It is guaranteed safe to call this function in the following states: /// /// - When the implementation is UNINITIALIZED. /// - When the implementation is FINALIZED. /// - When there is no current implementation installed. /// /// It is logging implementation defined what happens when this function is /// called while in any other states. void __xray_remove_log_impl(); /// DEPRECATED: Use __xray_log_init_mode() instead, and provide all the options /// in string form. /// Invokes the installed implementation initialization routine. See /// XRayLogInitStatus for what the return values mean. XRayLogInitStatus __xray_log_init(size_t BufferSize, size_t MaxBuffers, void *Args, size_t ArgsSize); /// Invokes the installed initialization routine, which *must* support the /// string based form. /// /// NOTE: When this API is used, we still invoke the installed initialization /// routine, but we will call it with the following convention to signal that we /// are using the string form: /// /// - BufferSize = 0 /// - MaxBuffers = 0 /// - ArgsSize = 0 /// - Args will be the pointer to the character buffer representing the /// configuration. /// /// FIXME: Updating the XRayLogImpl struct is an ABI breaking change. When we /// are ready to make a breaking change, we should clean this up appropriately. XRayLogInitStatus __xray_log_init_mode(const char *Mode, const char *Config); /// Like __xray_log_init_mode(...) this version allows for providing /// configurations that might have non-null-terminated strings. This will /// operate similarly to __xray_log_init_mode, with the exception that /// |ArgsSize| will be what |ConfigSize| is. XRayLogInitStatus __xray_log_init_mode_bin(const char *Mode, const char *Config, size_t ConfigSize); /// Invokes the installed implementation finalization routine. See /// XRayLogInitStatus for what the return values mean. XRayLogInitStatus __xray_log_finalize(); /// Invokes the install implementation log flushing routine. See /// XRayLogFlushStatus for what the return values mean. XRayLogFlushStatus __xray_log_flushLog(); /// An XRayBuffer represents a section of memory which can be treated by log /// processing functions as bytes stored in the logging implementation's /// buffers. struct XRayBuffer { … }; /// Registers an iterator function which takes an XRayBuffer argument, then /// returns another XRayBuffer function representing the next buffer. When the /// Iterator function returns an empty XRayBuffer (Data = nullptr, Size = 0), /// this signifies the end of the buffers. /// /// The first invocation of this Iterator function will always take an empty /// XRayBuffer (Data = nullptr, Size = 0). void __xray_log_set_buffer_iterator(XRayBuffer (*Iterator)(XRayBuffer)); /// Removes the currently registered buffer iterator function. void __xray_log_remove_buffer_iterator(); /// Invokes the provided handler to process data maintained by the logging /// handler. This API will be provided raw access to the data available in /// memory from the logging implementation. The callback function must: /// /// 1) Not modify the data, to avoid running into undefined behaviour. /// /// 2) Either know the data layout, or treat the data as raw bytes for later /// interpretation. /// /// This API is best used in place of the `__xray_log_flushLog()` implementation /// above to enable the caller to provide an alternative means of extracting the /// data from the XRay implementation. /// /// Implementations MUST then provide: /// /// 1) A function that will return an XRayBuffer. Functions that return an /// "empty" XRayBuffer signifies that there are no more buffers to be /// processed. This function should be registered through the /// `__xray_log_set_buffer_iterator(...)` function. /// /// 2) Its own means of converting data it holds in memory into an XRayBuffer /// structure. /// /// See XRayLogFlushStatus for what the return values mean. /// XRayLogFlushStatus __xray_log_process_buffers(void (*Processor)(const char *, XRayBuffer)); } // extern "C" #endif // XRAY_XRAY_LOG_INTERFACE_H