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