1================= 2RealtimeSanitizer 3================= 4 5.. contents:: 6 :local: 7 8Introduction 9============ 10RealtimeSanitizer (a.k.a. RTSan) is a real-time safety testing tool for C and C++ 11projects. RTSan can be used to detect real-time violations, i.e. calls to methods 12that are not safe for use in functions with deterministic run time requirements. 13RTSan considers any function marked with the ``[[clang::nonblocking]]`` attribute 14to be a real-time function. At run-time, if RTSan detects a call to ``malloc``, 15``free``, ``pthread_mutex_lock``, or anything else known to have a 16non-deterministic execution time in a function marked ``[[clang::nonblocking]]`` 17it raises an error. 18 19RTSan performs its analysis at run-time but shares the ``[[clang::nonblocking]]`` 20attribute with the :doc:`FunctionEffectAnalysis` system, which operates at 21compile-time to detect potential real-time safety violations. For comprehensive 22detection of real-time safety issues, it is recommended to use both systems together. 23 24The runtime slowdown introduced by RealtimeSanitizer is negligible. 25 26How to build 27============ 28 29Build LLVM/Clang with `CMake <https://llvm.org/docs/CMake.html>`_ and enable the 30``compiler-rt`` runtime. An example CMake configuration that will allow for the 31use/testing of RealtimeSanitizer: 32 33.. code-block:: console 34 35 $ cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang" -DLLVM_ENABLE_RUNTIMES="compiler-rt" <path to source>/llvm 36 37Usage 38===== 39 40There are two requirements: 41 421. The code must be compiled with the ``-fsanitize=realtime`` flag. 432. Functions that are subject to real-time constraints must be marked 44 with the ``[[clang::nonblocking]]`` attribute. 45 46Typically, these attributes should be added onto the functions that are entry 47points for threads with real-time priority. These threads are subject to a fixed 48callback time, such as audio callback threads or rendering loops in video game 49code. 50 51.. code-block:: console 52 53 % cat example_realtime_violation.cpp 54 #include <vector> 55 56 void violation() [[clang::nonblocking]]{ 57 std::vector<float> v; 58 v.resize(100); 59 } 60 61 int main() { 62 violation(); 63 return 0; 64 } 65 # Compile and link 66 % clang++ -fsanitize=realtime example_realtime_violation.cpp 67 68If a real-time safety violation is detected in a ``[[clang::nonblocking]]`` 69context, or any function invoked by that function, the program will exit with a 70non-zero exit code. 71 72.. code-block:: console 73 74 % clang++ -fsanitize=realtime example_realtime_violation.cpp 75 % ./a.out 76 ==76290==ERROR: RealtimeSanitizer: unsafe-library-call 77 Intercepted call to real-time unsafe function `malloc` in real-time context! 78 #0 0x000102a7b884 in malloc rtsan_interceptors.cpp:426 79 #1 0x00019c326bd0 in operator new(unsigned long)+0x1c (libc++abi.dylib:arm64+0x16bd0) 80 #2 0xa30d0001024f79a8 (<unknown module>) 81 #3 0x0001024f794c in std::__1::__libcpp_allocate[abi:ne200000](unsigned long, unsigned long)+0x44 82 #4 0x0001024f78c4 in std::__1::allocator<float>::allocate[abi:ne200000](unsigned long)+0x44 83 ... snip ... 84 #9 0x0001024f6868 in std::__1::vector<float, std::__1::allocator<float>>::resize(unsigned long)+0x48 85 #10 0x0001024f67b4 in violation()+0x24 86 #11 0x0001024f68f0 in main+0x18 (a.out:arm64+0x1000028f0) 87 #12 0x00019bfe3150 (<unknown module>) 88 #13 0xed5efffffffffffc (<unknown module>) 89 90 91Blocking functions 92------------------ 93 94Calls to system library functions such as ``malloc`` are automatically caught by 95RealtimeSanitizer. Real-time programmers may also write their own blocking 96(real-time unsafe) functions that they wish RealtimeSanitizer to be aware of. 97RealtimeSanitizer will raise an error at run time if any function attributed 98with ``[[clang::blocking]]`` is called in a ``[[clang::nonblocking]]`` context. 99 100.. code-block:: console 101 102 $ cat example_blocking_violation.cpp 103 #include <atomic> 104 #include <thread> 105 106 std::atomic<bool> has_permission{false}; 107 108 int wait_for_permission() [[clang::blocking]] { 109 while (has_permission.load() == false) 110 std::this_thread::yield(); 111 return 0; 112 } 113 114 int real_time_function() [[clang::nonblocking]] { 115 return wait_for_permission(); 116 } 117 118 int main() { 119 return real_time_function(); 120 } 121 122 $ clang++ -fsanitize=realtime example_blocking_violation.cpp && ./a.out 123 ==76131==ERROR: RealtimeSanitizer: blocking-call 124 Call to blocking function `wait_for_permission()` in real-time context! 125 #0 0x0001000c3db0 in wait_for_permission()+0x10 (a.out:arm64+0x100003db0) 126 #1 0x0001000c3e3c in real_time_function()+0x10 (a.out:arm64+0x100003e3c) 127 #2 0x0001000c3e68 in main+0x10 (a.out:arm64+0x100003e68) 128 #3 0x00019bfe3150 (<unknown module>) 129 #4 0x5a27fffffffffffc (<unknown module>) 130 131 132Run-time flags 133-------------- 134 135RealtimeSanitizer supports a number of run-time flags, which can be specified in the ``RTSAN_OPTIONS`` environment variable: 136 137.. code-block:: console 138 139 % RTSAN_OPTIONS=option_1=true:path_option_2="/some/file.txt" ./a.out 140 ... 141 142Or at compile-time by providing the symbol ``__rtsan_default_options``: 143 144.. code-block:: c 145 146 __attribute__((__visibility__("default"))) 147 extern "C" const char *__rtsan_default_options() { 148 return "symbolize=false:abort_on_error=0:log_to_syslog=0"; 149 } 150 151You can see all sanitizer options (some of which are unsupported) by using the ``help`` flag: 152 153.. code-block:: console 154 155 % RTSAN_OPTIONS=help=true ./a.out 156 157A **partial** list of flags RealtimeSanitizer respects: 158 159.. list-table:: Run-time Flags 160 :widths: 20 10 10 70 161 :header-rows: 1 162 163 * - Flag name 164 - Default value 165 - Type 166 - Short description 167 * - ``halt_on_error`` 168 - ``true`` 169 - boolean 170 - Exit after first reported error. 171 * - ``suppress_equal_stacks`` 172 - ``true`` 173 - boolean 174 - If true, suppress duplicate reports (i.e. only print each unique error once). Only particularly useful when ``halt_on_error=false``. 175 * - ``print_stats_on_exit`` 176 - ``false`` 177 - boolean 178 - Print stats on exit. Includes total and unique errors. 179 * - ``color`` 180 - ``"auto"`` 181 - string 182 - Colorize reports: (always|never|auto). 183 * - ``fast_unwind_on_fatal`` 184 - ``false`` 185 - boolean 186 - If available, use the fast frame-pointer-based unwinder on detected errors. If true, ensure the code under test has been compiled with frame pointers with ``-fno-omit-frame-pointers`` or similar. 187 * - ``abort_on_error`` 188 - OS dependent 189 - boolean 190 - If true, the tool calls ``abort()`` instead of ``_exit()`` after printing the error report. On some OSes (MacOS, for exmple) this is beneficial because a better stack trace is emitted on crash. 191 * - ``symbolize`` 192 - ``true`` 193 - boolean 194 - If set, use the symbolizer to turn virtual addresses to file/line locations. If false, can greatly speed up the error reporting. 195 * - ``suppressions`` 196 - ``""`` 197 - path 198 - If set to a valid suppressions file, will suppress issue reporting. See details in `Disabling and Suppressing`_. 199 * - ``verify_interceptors`` 200 - ``true`` 201 - boolean 202 - If true, verifies interceptors are working at initialization. The program will abort with error ``==ERROR: Interceptors are not working. This may be because RealtimeSanitizer is loaded too late (e.g. via dlopen)`` if an issue is detected. 203 204Some issues with flags can be debugged using the ``verbosity=$NUM`` flag: 205 206.. code-block:: console 207 208 % RTSAN_OPTIONS=verbosity=1:misspelled_flag=true ./a.out 209 WARNING: found 1 unrecognized flag(s): 210 misspelled_flag 211 ... 212 213Additional customization 214------------------------ 215 216In addition to ``__rtsan_default_options`` outlined above, you can provide definitions of other functions that affect how RTSan operates. 217 218To be notified on every error reported by RTsan, provide a definition of ``__sanitizer_report_error_summary``. 219 220.. code-block:: c 221 222 extern "C" void __sanitizer_report_error_summary(const char *error_summary) { 223 fprintf(stderr, "%s %s\n", "In custom handler! ", error_summary); 224 /* do other custom things */ 225 } 226 227The error summary will be of the form: 228 229.. code-block:: console 230 231 SUMMARY: RealtimeSanitizer: unsafe-library-call main.cpp:8 in process(std::__1::vector<int, std::__1::allocator<int>>&) 232 233To register a callback which will be invoked before a RTSan kills the process: 234 235.. code-block:: c 236 237 extern "C" void __sanitizer_set_death_callback(void (*callback)(void)); 238 239 void custom_on_die_callback() { 240 fprintf(stderr, "In custom handler!") 241 /* do other custom things */ 242 } 243 244 int main() 245 { 246 __sanitizer_set_death_callback(custom_on_die_callback); 247 ... 248 } 249 250.. _disabling-and-suppressing: 251 252Disabling and suppressing 253------------------------- 254 255There are multiple ways to disable error reporting when using RealtimeSanitizer. 256 257In general, ``ScopedDisabler`` should be preferred, as it is the most performant. 258 259.. list-table:: Suppression methods 260 :widths: 30 15 15 10 70 261 :header-rows: 1 262 263 * - Method 264 - Specified at? 265 - Scope 266 - Run-time cost 267 - Description 268 * - ``ScopedDisabler`` 269 - Compile-time 270 - Stack 271 - Very low 272 - Violations are ignored for the lifetime of the ``ScopedDisabler`` object. 273 * - ``function-name-matches`` suppression 274 - Run-time 275 - Single function 276 - Medium 277 - Suppresses intercepted and ``[[clang::blocking]]`` function calls by name. 278 * - ``call-stack-contains`` suppression 279 - Run-time 280 - Stack 281 - High 282 - Suppresses any stack trace contaning the specified pattern. 283 284 285``ScopedDisabler`` 286################## 287 288At compile time, RealtimeSanitizer may be disabled using ``__rtsan::ScopedDisabler``. RTSan ignores any errors originating within the ``ScopedDisabler`` instance variable scope. 289 290.. code-block:: c++ 291 292 #include <sanitizer/rtsan_interface.h> 293 294 void process(const std::vector<float>& buffer) [[clang::nonblocking]] { 295 { 296 __rtsan::ScopedDisabler d; 297 ... 298 } 299 } 300 301If RealtimeSanitizer is not enabled at compile time (i.e., the code is not compiled with the ``-fsanitize=realtime`` flag), the ``ScopedDisabler`` is compiled as a no-op. 302 303In C, you can use the ``__rtsan_disable()`` and ``rtsan_enable()`` functions to manually disable and re-enable RealtimeSanitizer checks. 304 305.. code-block:: c++ 306 307 #include <sanitizer/rtsan_interface.h> 308 309 int process(const float* buffer) [[clang::nonblocking]] 310 { 311 { 312 __rtsan_disable(); 313 314 ... 315 316 __rtsan_enable(); 317 } 318 } 319 320Each call to ``__rtsan_disable()`` must be paired with a subsequent call to ``__rtsan_enable()`` to restore normal sanitizer functionality. If a corresponding ``rtsan_enable()`` call is not made, the behavior is undefined. 321 322Suppression file 323################ 324 325At run-time, suppressions may be specified using a suppressions file passed in ``RTSAN_OPTIONS``. Run-time suppression may be useful if the source cannot be changed. 326 327.. code-block:: console 328 329 > cat suppressions.supp 330 call-stack-contains:MallocViolation 331 call-stack-contains:std::*vector 332 function-name-matches:free 333 function-name-matches:CustomMarkedBlocking* 334 > RTSAN_OPTIONS="suppressions=suppressions.supp" ./a.out 335 ... 336 337Suppressions specified in this file are one of two flavors. 338 339``function-name-matches`` suppresses reporting of any intercepted library call, or function marked ``[[clang::blocking]]`` by name. If, for instance, you know that ``malloc`` is real-time safe on your system, you can disable the check for it via ``function-name-matches:malloc``. 340 341``call-stack-contains`` suppresses reporting of errors in any stack that contains a string matching the pattern specified. For example, suppressing error reporting of any non-real-time-safe behavior in ``std::vector`` may be specified ``call-stack-contains:std::*vector``. You must include symbols in your build for this method to be effective, unsymbolicated stack traces cannot be matched. ``call-stack-contains`` has the highest run-time cost of any method of suppression. 342 343Patterns may be exact matches or are "regex-light" patterns, containing special characters such as ``^$*``. 344 345The number of potential errors suppressed via this method may be seen on exit when using the ``print_stats_on_exit`` flag. 346 347Compile-time sanitizer detection 348-------------------------------- 349 350Clang provides the pre-processor macro ``__has_feature`` which may be used to detect if RealtimeSanitizer is enabled at compile-time. 351 352.. code-block:: c++ 353 354 #if defined(__has_feature) && __has_feature(realtime_sanitizer) 355 ... 356 #endif 357