| # Generating Local Code Coverage for Fuzz Tests | |
| This document explains how to generate a local code coverage report for a | |
| FuzzTest integrated into a gtest suite. A local coverage report helps you | |
| visualize which code paths your fuzz test exercises so you can verify the logic | |
| before you upload a CL. | |
| To complete this process, use the | |
| [coverage.py script](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/testing/code_coverage.md#local-coverage-script) | |
| script to automate building with coverage instrumentation, running the test, and | |
| generating an HTML report. | |
| ## Update the toolchain | |
| The coverage script and build system require the Clang compiler and specific | |
| LLVM tools. | |
| In your terminal, navigate to the src directory and update the required tools: | |
| ```shell | |
| vpython3 tools/clang/scripts/update.py | |
| ``` | |
| ## Configure the build | |
| Create a dedicated build configuration for your coverage build. | |
| In your terminal, from the src directory, generate the configuration: | |
| ```shell | |
| # It's recommended to use a descriptive name like 'out/fuzz_coverage' | |
| # to keep it separate from your regular builds. | |
| gn gen out/fuzz_coverage --args=' | |
| use_clang_coverage=true | |
| is_component_build=false | |
| is_debug=false | |
| dcheck_always_on=true | |
| use_remoteexec=true' | |
| ``` | |
| ### Argument breakdown: | |
| * `use_clang_coverage=true`: Instructs the compiler to add coverage | |
| instrumentation. | |
| * `is_component_build=false`: (Recommended) Code coverage instrumentation | |
| works with both component and non-component builds, but setting this to true | |
| causes the tests to run significantly slower. | |
| * `is_debug=false`: (Recommended) Optimizes the build for coverage. | |
| * `dcheck_always_on=true`: (Recommended) Catches issues during test runs. | |
| * `use_remoteexec=true`: (Recommended) Compiles Chromium fast. | |
| ## Build the test target | |
| Compile the test suite that contains your FuzzTest. For example, if your fuzz | |
| test `MyFuzzer.MyTest` is located in `//foo/my_fuzzer_unittest.cc`, and this | |
| file is part of the `foo_unittests` target, you would build `foo_unittests`. | |
| In your terminal, compile the test target: | |
| ```shell | |
| # Replace foo_unittests with your actual test target | |
| autoninja -C out/fuzz_coverage foo_unittests | |
| ``` | |
| ## Run the coverage script | |
| Run the `coverage.py` script. Use the `--gtest_filter` argument to run only your | |
| specific fuzz test. Isolating the test produces a precise report on the coverage | |
| contributed by that test alone. | |
| In your terminal, run the script: | |
| ```shell | |
| # Customize the arguments below for your specific test. | |
| vpython3 tools/code_coverage/coverage.py \ | |
| foo_unittests \ | |
| -b out/fuzz_coverage \ | |
| -o out/my_fuzz_test_report \ | |
| -c 'out/fuzz_coverage/foo_unittests --gtest_filter=MyFuzzTestSuite.*' \ | |
| -f foo/ \ | |
| --no-component-view | |
| ``` | |
| ### Command breakdown: | |
| * `foo_unittests`: Specifies the test target you are analyzing. | |
| * `-b out/fuzz_coverage`: Specifies the build directory from Step 2. | |
| * `-o out/my_fuzz_test_report`: Specifies the output directory for the final | |
| HTML report. | |
| * `-c '...'`: Defines the command to execute the test binary. | |
| * `out/fuzz_coverage/foo_unittests`: Specifies the path to the test | |
| binary. | |
| * `--gtest_filter=MyFuzzTestSuite.*`: Isolates your fuzz test. Replace | |
| MyFuzzTestSuite with the name of your test suite. Using .* runs all | |
| tests within that suite. | |
| * `-f foo/`: (Recommended) Filters the report to only show files in the | |
| specified directory. | |
| * `--no-component-view`: Prevents the script from fetching a | |
| directory-to-component mapping from the network, which avoids a 403 | |
| Forbidden error. | |
| ## View the report | |
| The script generates a set of HTML files in your specified output directory. To | |
| organize the data, the script creates a sub-directory named after your target | |
| operating system (for example, `linux`, `mac`, or `win`). The index.html file is | |
| located inside this platform sub-directory. | |
| ### View on a local machine | |
| If you build the test on your local machine, open the | |
| `out/my_fuzz_test_report/<PLATFORM>/index.html` file in a browser. Replace | |
| `<PLATFORM>` with your target operating system | |
| ### View from a remote machine | |
| If you build the test on a remote machine or virtual machine, start an HTTP | |
| server to access the report from your local browser. | |
| In your remote terminal, navigate to the out directory: | |
| ```shell | |
| cd out/my_fuzz_test_report | |
| ``` | |
| Start an HTTP server: | |
| ```shell | |
| python3 -m http.server 8000 | |
| ``` | |
| On your local machine, open a browser and navigate to | |
| `<REMOTE_IP>:8000/<PLATFORM>/index.html`. Replace `<REMOTE_IP>` with your remote | |
| machine's IP address or hostname, and replace `<PLATFORM>` with your target | |
| operating system (for example, `linux`). | |
| When you open the report, navigate the directory view to your changed files. The | |
| report highlights lines covered by your fuzz test in green so you can verify the | |
| test's impact. |