1================= 2SanitizerCoverage 3================= 4 5.. contents:: 6 :local: 7 8Introduction 9============ 10 11LLVM has a simple code coverage instrumentation built in (SanitizerCoverage). 12It inserts calls to user-defined functions on function-, basic-block-, and edge- levels. 13Default implementations of those callbacks are provided and implement 14simple coverage reporting and visualization, 15however if you need *just* coverage visualization you may want to use 16:doc:`SourceBasedCodeCoverage <SourceBasedCodeCoverage>` instead. 17 18Tracing PCs with guards 19======================= 20 21With ``-fsanitize-coverage=trace-pc-guard`` the compiler will insert the following code 22on every edge: 23 24.. code-block:: none 25 26 __sanitizer_cov_trace_pc_guard(&guard_variable) 27 28Every edge will have its own `guard_variable` (uint32_t). 29 30The compiler will also insert calls to a module constructor: 31 32.. code-block:: c++ 33 34 // The guards are [start, stop). 35 // This function will be called at least once per DSO and may be called 36 // more than once with the same values of start/stop. 37 __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop); 38 39With an additional ``...=trace-pc,indirect-calls`` flag 40``__sanitizer_cov_trace_pc_indirect(void *callee)`` will be inserted on every indirect call. 41 42The functions `__sanitizer_cov_trace_pc_*` should be defined by the user. 43 44Example: 45 46.. code-block:: c++ 47 48 // trace-pc-guard-cb.cc 49 #include <stdint.h> 50 #include <stdio.h> 51 #include <sanitizer/coverage_interface.h> 52 53 // This callback is inserted by the compiler as a module constructor 54 // into every DSO. 'start' and 'stop' correspond to the 55 // beginning and end of the section with the guards for the entire 56 // binary (executable or DSO). The callback will be called at least 57 // once per DSO and may be called multiple times with the same parameters. 58 extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, 59 uint32_t *stop) { 60 static uint64_t N; // Counter for the guards. 61 if (start == stop || *start) return; // Initialize only once. 62 printf("INIT: %p %p\n", start, stop); 63 for (uint32_t *x = start; x < stop; x++) 64 *x = ++N; // Guards should start from 1. 65 } 66 67 // This callback is inserted by the compiler on every edge in the 68 // control flow (some optimizations apply). 69 // Typically, the compiler will emit the code like this: 70 // if(*guard) 71 // __sanitizer_cov_trace_pc_guard(guard); 72 // But for large functions it will emit a simple call: 73 // __sanitizer_cov_trace_pc_guard(guard); 74 extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { 75 if (!*guard) return; // Duplicate the guard check. 76 // If you set *guard to 0 this code will not be called again for this edge. 77 // Now you can get the PC and do whatever you want: 78 // store it somewhere or symbolize it and print right away. 79 // The values of `*guard` are as you set them in 80 // __sanitizer_cov_trace_pc_guard_init and so you can make them consecutive 81 // and use them to dereference an array or a bit vector. 82 void *PC = __builtin_return_address(0); 83 char PcDescr[1024]; 84 // This function is a part of the sanitizer run-time. 85 // To use it, link with AddressSanitizer or other sanitizer. 86 __sanitizer_symbolize_pc(PC, "%p %F %L", PcDescr, sizeof(PcDescr)); 87 printf("guard: %p %x PC %s\n", guard, *guard, PcDescr); 88 } 89 90.. code-block:: c++ 91 92 // trace-pc-guard-example.cc 93 void foo() { } 94 int main(int argc, char **argv) { 95 if (argc > 1) foo(); 96 } 97 98.. code-block:: console 99 100 clang++ -g -fsanitize-coverage=trace-pc-guard trace-pc-guard-example.cc -c 101 clang++ trace-pc-guard-cb.cc trace-pc-guard-example.o -fsanitize=address 102 ASAN_OPTIONS=strip_path_prefix=`pwd`/ ./a.out 103 104.. code-block:: console 105 106 INIT: 0x71bcd0 0x71bce0 107 guard: 0x71bcd4 2 PC 0x4ecd5b in main trace-pc-guard-example.cc:2 108 guard: 0x71bcd8 3 PC 0x4ecd9e in main trace-pc-guard-example.cc:3:7 109 110.. code-block:: console 111 112 ASAN_OPTIONS=strip_path_prefix=`pwd`/ ./a.out with-foo 113 114 115.. code-block:: console 116 117 INIT: 0x71bcd0 0x71bce0 118 guard: 0x71bcd4 2 PC 0x4ecd5b in main trace-pc-guard-example.cc:3 119 guard: 0x71bcdc 4 PC 0x4ecdc7 in main trace-pc-guard-example.cc:4:17 120 guard: 0x71bcd0 1 PC 0x4ecd20 in foo() trace-pc-guard-example.cc:2:14 121 122Inline 8bit-counters 123==================== 124 125**Experimental, may change or disappear in future** 126 127With ``-fsanitize-coverage=inline-8bit-counters`` the compiler will insert 128inline counter increments on every edge. 129This is similar to ``-fsanitize-coverage=trace-pc-guard`` but instead of a 130callback the instrumentation simply increments a counter. 131 132Users need to implement a single function to capture the counters at startup. 133 134.. code-block:: c++ 135 136 extern "C" 137 void __sanitizer_cov_8bit_counters_init(char *start, char *end) { 138 // [start,end) is the array of 8-bit counters created for the current DSO. 139 // Capture this array in order to read/modify the counters. 140 } 141 142 143Inline bool-flag 144================ 145 146**Experimental, may change or disappear in future** 147 148With ``-fsanitize-coverage=inline-bool-flag`` the compiler will insert 149setting an inline boolean to true on every edge. 150This is similar to ``-fsanitize-coverage=inline-8bit-counter`` but instead of 151an increment of a counter, it just sets a boolean to true. 152 153Users need to implement a single function to capture the flags at startup. 154 155.. code-block:: c++ 156 157 extern "C" 158 void __sanitizer_cov_bool_flag_init(bool *start, bool *end) { 159 // [start,end) is the array of boolean flags created for the current DSO. 160 // Capture this array in order to read/modify the flags. 161 } 162 163 164PC-Table 165======== 166 167**Experimental, may change or disappear in future** 168 169**Note:** this instrumentation might be incompatible with dead code stripping 170(``-Wl,-gc-sections``) for linkers other than LLD, thus resulting in a 171significant binary size overhead. For more information, see 172`Bug 34636 <https://bugs.llvm.org/show_bug.cgi?id=34636>`_. 173 174With ``-fsanitize-coverage=pc-table`` the compiler will create a table of 175instrumented PCs. Requires either ``-fsanitize-coverage=inline-8bit-counters``, 176or ``-fsanitize-coverage=inline-bool-flag``, or ``-fsanitize-coverage=trace-pc-guard``. 177 178Users need to implement a single function to capture the PC table at startup: 179 180.. code-block:: c++ 181 182 extern "C" 183 void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg, 184 const uintptr_t *pcs_end) { 185 // [pcs_beg,pcs_end) is the array of ptr-sized integers representing 186 // pairs [PC,PCFlags] for every instrumented block in the current DSO. 187 // Capture this array in order to read the PCs and their Flags. 188 // The number of PCs and PCFlags for a given DSO is the same as the number 189 // of 8-bit counters (-fsanitize-coverage=inline-8bit-counters), or 190 // boolean flags (-fsanitize-coverage=inline=bool-flags), or trace_pc_guard 191 // callbacks (-fsanitize-coverage=trace-pc-guard). 192 // A PCFlags describes the basic block: 193 // * bit0: 1 if the block is the function entry block, 0 otherwise. 194 } 195 196 197Tracing PCs 198=========== 199 200With ``-fsanitize-coverage=trace-pc`` the compiler will insert 201``__sanitizer_cov_trace_pc()`` on every edge. 202With an additional ``...=trace-pc,indirect-calls`` flag 203``__sanitizer_cov_trace_pc_indirect(void *callee)`` will be inserted on every indirect call. 204These callbacks are not implemented in the Sanitizer run-time and should be defined 205by the user. 206This mechanism is used for fuzzing the Linux kernel 207(https://github.com/google/syzkaller). 208 209Instrumentation points 210====================== 211Sanitizer Coverage offers different levels of instrumentation. 212 213* ``edge`` (default): edges are instrumented (see below). 214* ``bb``: basic blocks are instrumented. 215* ``func``: only the entry block of every function will be instrumented. 216 217Use these flags together with ``trace-pc-guard`` or ``trace-pc``, 218like this: ``-fsanitize-coverage=func,trace-pc-guard``. 219 220When ``edge`` or ``bb`` is used, some of the edges/blocks may still be left 221uninstrumented (pruned) if such instrumentation is considered redundant. 222Use ``no-prune`` (e.g. ``-fsanitize-coverage=bb,no-prune,trace-pc-guard``) 223to disable pruning. This could be useful for better coverage visualization. 224 225 226Edge coverage 227------------- 228 229Consider this code: 230 231.. code-block:: c++ 232 233 void foo(int *a) { 234 if (a) 235 *a = 0; 236 } 237 238It contains 3 basic blocks, let's name them A, B, C: 239 240.. code-block:: none 241 242 A 243 |\ 244 | \ 245 | B 246 | / 247 |/ 248 C 249 250If blocks A, B, and C are all covered we know for certain that the edges A=>B 251and B=>C were executed, but we still don't know if the edge A=>C was executed. 252Such edges of control flow graph are called 253`critical <https://en.wikipedia.org/wiki/Control_flow_graph#Special_edges>`_. 254The edge-level coverage simply splits all critical edges by introducing new 255dummy blocks and then instruments those blocks: 256 257.. code-block:: none 258 259 A 260 |\ 261 | \ 262 D B 263 | / 264 |/ 265 C 266 267Tracing data flow 268================= 269 270Support for data-flow-guided fuzzing. 271With ``-fsanitize-coverage=trace-cmp`` the compiler will insert extra instrumentation 272around comparison instructions and switch statements. 273Similarly, with ``-fsanitize-coverage=trace-div`` the compiler will instrument 274integer division instructions (to capture the right argument of division) 275and with ``-fsanitize-coverage=trace-gep`` -- 276the `LLVM GEP instructions <https://llvm.org/docs/GetElementPtr.html>`_ 277(to capture array indices). 278 279Unless ``no-prune`` option is provided, some of the comparison instructions 280will not be instrumented. 281 282.. code-block:: c++ 283 284 // Called before a comparison instruction. 285 // Arg1 and Arg2 are arguments of the comparison. 286 void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2); 287 void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2); 288 void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2); 289 void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2); 290 291 // Called before a comparison instruction if exactly one of the arguments is constant. 292 // Arg1 and Arg2 are arguments of the comparison, Arg1 is a compile-time constant. 293 // These callbacks are emitted by -fsanitize-coverage=trace-cmp since 2017-08-11 294 void __sanitizer_cov_trace_const_cmp1(uint8_t Arg1, uint8_t Arg2); 295 void __sanitizer_cov_trace_const_cmp2(uint16_t Arg1, uint16_t Arg2); 296 void __sanitizer_cov_trace_const_cmp4(uint32_t Arg1, uint32_t Arg2); 297 void __sanitizer_cov_trace_const_cmp8(uint64_t Arg1, uint64_t Arg2); 298 299 // Called before a switch statement. 300 // Val is the switch operand. 301 // Cases[0] is the number of case constants. 302 // Cases[1] is the size of Val in bits. 303 // Cases[2:] are the case constants. 304 void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases); 305 306 // Called before a division statement. 307 // Val is the second argument of division. 308 void __sanitizer_cov_trace_div4(uint32_t Val); 309 void __sanitizer_cov_trace_div8(uint64_t Val); 310 311 // Called before a GetElemementPtr (GEP) instruction 312 // for every non-constant array index. 313 void __sanitizer_cov_trace_gep(uintptr_t Idx); 314 315Partially disabling instrumentation 316=================================== 317 318It is sometimes useful to tell SanitizerCoverage to instrument only a subset of the 319functions in your target. 320With ``-fsanitize-coverage-allowlist=allowlist.txt`` 321and ``-fsanitize-coverage-blocklist=blocklist.txt``, 322you can specify such a subset through the combination of a allowlist and a blocklist. 323 324SanitizerCoverage will only instrument functions that satisfy two conditions. 325First, the function should belong to a source file with a path that is both allowlisted 326and not blocklisted. 327Second, the function should have a mangled name that is both allowlisted and not blocklisted. 328 329The allowlist and blocklist format is similar to that of the sanitizer blocklist format. 330The default allowlist will match every source file and every function. 331The default blocklist will match no source file and no function. 332 333A common use case is to have the allowlist list folders or source files for which you want 334instrumentation and allow all function names, while the blocklist will opt out some specific 335files or functions that the allowlist loosely allowed. 336 337Here is an example allowlist: 338 339.. code-block:: none 340 341 # Enable instrumentation for a whole folder 342 src:bar/* 343 # Enable instrumentation for a specific source file 344 src:foo/a.cpp 345 # Enable instrumentation for all functions in those files 346 fun:* 347 348And an example blocklist: 349 350.. code-block:: none 351 352 # Disable instrumentation for a specific source file that the allowlist allowed 353 src:bar/b.cpp 354 # Disable instrumentation for a specific function that the allowlist allowed 355 fun:*myFunc* 356 357The use of ``*`` wildcards above is required because function names are matched after mangling. 358Without the wildcards, one would have to write the whole mangled name. 359 360Be careful that the paths of source files are matched exactly as they are provided on the clang 361command line. 362For example, the allowlist above would include file ``bar/b.cpp`` if the path was provided 363exactly like this, but would it would fail to include it with other ways to refer to the same 364file such as ``./bar/b.cpp``, or ``bar\b.cpp`` on Windows. 365So, please make sure to always double check that your lists are correctly applied. 366 367Default implementation 368====================== 369 370The sanitizer run-time (AddressSanitizer, MemorySanitizer, etc) provide a 371default implementations of some of the coverage callbacks. 372You may use this implementation to dump the coverage on disk at the process 373exit. 374 375Example: 376 377.. code-block:: console 378 379 % cat -n cov.cc 380 1 #include <stdio.h> 381 2 __attribute__((noinline)) 382 3 void foo() { printf("foo\n"); } 383 4 384 5 int main(int argc, char **argv) { 385 6 if (argc == 2) 386 7 foo(); 387 8 printf("main\n"); 388 9 } 389 % clang++ -g cov.cc -fsanitize=address -fsanitize-coverage=trace-pc-guard 390 % ASAN_OPTIONS=coverage=1 ./a.out; wc -c *.sancov 391 main 392 SanitizerCoverage: ./a.out.7312.sancov 2 PCs written 393 24 a.out.7312.sancov 394 % ASAN_OPTIONS=coverage=1 ./a.out foo ; wc -c *.sancov 395 foo 396 main 397 SanitizerCoverage: ./a.out.7316.sancov 3 PCs written 398 24 a.out.7312.sancov 399 32 a.out.7316.sancov 400 401Every time you run an executable instrumented with SanitizerCoverage 402one ``*.sancov`` file is created during the process shutdown. 403If the executable is dynamically linked against instrumented DSOs, 404one ``*.sancov`` file will be also created for every DSO. 405 406Sancov data format 407------------------ 408 409The format of ``*.sancov`` files is very simple: the first 8 bytes is the magic, 410one of ``0xC0BFFFFFFFFFFF64`` and ``0xC0BFFFFFFFFFFF32``. The last byte of the 411magic defines the size of the following offsets. The rest of the data is the 412offsets in the corresponding binary/DSO that were executed during the run. 413 414Sancov Tool 415----------- 416 417An simple ``sancov`` tool is provided to process coverage files. 418The tool is part of LLVM project and is currently supported only on Linux. 419It can handle symbolization tasks autonomously without any extra support 420from the environment. You need to pass .sancov files (named 421``<module_name>.<pid>.sancov`` and paths to all corresponding binary elf files. 422Sancov matches these files using module names and binaries file names. 423 424.. code-block:: console 425 426 USAGE: sancov [options] <action> (<binary file>|<.sancov file>)... 427 428 Action (required) 429 -print - Print coverage addresses 430 -covered-functions - Print all covered functions. 431 -not-covered-functions - Print all not covered functions. 432 -symbolize - Symbolizes the report. 433 434 Options 435 -blocklist=<string> - Blocklist file (sanitizer blocklist format). 436 -demangle - Print demangled function name. 437 -strip_path_prefix=<string> - Strip this prefix from file paths in reports 438 439 440Coverage Reports 441---------------- 442 443**Experimental** 444 445``.sancov`` files do not contain enough information to generate a source-level 446coverage report. The missing information is contained 447in debug info of the binary. Thus the ``.sancov`` has to be symbolized 448to produce a ``.symcov`` file first: 449 450.. code-block:: console 451 452 sancov -symbolize my_program.123.sancov my_program > my_program.123.symcov 453 454The ``.symcov`` file can be browsed overlaid over the source code by 455running ``tools/sancov/coverage-report-server.py`` script that will start 456an HTTP server. 457 458Output directory 459---------------- 460 461By default, .sancov files are created in the current working directory. 462This can be changed with ``ASAN_OPTIONS=coverage_dir=/path``: 463 464.. code-block:: console 465 466 % ASAN_OPTIONS="coverage=1:coverage_dir=/tmp/cov" ./a.out foo 467 % ls -l /tmp/cov/*sancov 468 -rw-r----- 1 kcc eng 4 Nov 27 12:21 a.out.22673.sancov 469 -rw-r----- 1 kcc eng 8 Nov 27 12:21 a.out.22679.sancov 470