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