Yuke Liao | d3b4627 | 2018-03-14 18:25:14 | [diff] [blame] | 1 | # Code coverage in Chromium |
| 2 | |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 3 | Table of contents: |
| 4 | - [Coverage Script](#coverage-script) |
| 5 | - [Workflow](#workflow) |
| 6 | * [Step 0 Download Tooling](#step-0-download-tooling) |
| 7 | * [Step 1 Build](#step-1-build) |
| 8 | * [Step 2 Create Raw Profiles](#step-2-create-raw-profiles) |
| 9 | * [Step 3 Create Indexed Profile](#step-3-create-indexed-profile) |
| 10 | * [Step 4 Create Coverage Reports](#step-4-create-coverage-reports) |
Max Moroz | d73e45f | 2018-04-24 18:32:47 | [diff] [blame] | 11 | - [Contacts](#contacts) |
| 12 | - [FAQ](#faq) |
Yuke Liao | d3b4627 | 2018-03-14 18:25:14 | [diff] [blame] | 13 | |
Abhishek Arya | af9811f2 | 2018-05-11 22:17:48 | [diff] [blame^] | 14 | Chromium uses Clang source-based code coverage. This [documentation] explains |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 15 | how to use Clang’s source-based coverage features in general. |
Yuke Liao | d3b4627 | 2018-03-14 18:25:14 | [diff] [blame] | 16 | |
Abhishek Arya | af9811f2 | 2018-05-11 22:17:48 | [diff] [blame^] | 17 | In this document, we first introduce a code coverage script that can be used to |
| 18 | generate code coverage reports for Chromium code in one command, and then |
| 19 | describe the code coverage reports generation workflow. |
Yuke Liao | d3b4627 | 2018-03-14 18:25:14 | [diff] [blame] | 20 | |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 21 | ## Coverage Script |
| 22 | The [coverage script] automates the process described below and provides a |
| 23 | one-stop service to generate code coverage reports in just one command. |
Yuke Liao | d3b4627 | 2018-03-14 18:25:14 | [diff] [blame] | 24 | |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 25 | This script is currently supported on Linux, Mac, iOS and ChromeOS platforms. |
| 26 | |
| 27 | Here is an example usage: |
| 28 | |
Yuke Liao | d3b4627 | 2018-03-14 18:25:14 | [diff] [blame] | 29 | ``` |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 30 | $ gn gen out/coverage \ |
| 31 | --args='use_clang_coverage=true is_component_build=false' |
| 32 | $ python tools/code_coverage/coverage.py \ |
| 33 | crypto_unittests url_unittests \ |
| 34 | -b out/coverage -o out/report \ |
| 35 | -c 'out/coverage/crypto_unittests' \ |
| 36 | -c 'out/coverage/url_unittests --gtest_filter=URLParser.PathURL' \ |
| 37 | -f url/ -f crypto/ |
| 38 | ``` |
| 39 | The command above builds `crypto_unittests` and `url_unittests` targets and then |
Abhishek Arya | af9811f2 | 2018-05-11 22:17:48 | [diff] [blame^] | 40 | runs them individually with their commands and arguments specified by the `-c` flag. |
| 41 | For `url_unittests`, it only runs the test `URLParser.PathURL`. The coverage report |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 42 | is filtered to include only files and sub-directories under `url/` and `crypto/` |
| 43 | directories. |
| 44 | |
Abhishek Arya | af9811f2 | 2018-05-11 22:17:48 | [diff] [blame^] | 45 | Aside from automating the process, this script provides visualization features to |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 46 | view code coverage breakdown by directories and by components, for example: |
| 47 | |
Abhishek Arya | af9811f2 | 2018-05-11 22:17:48 | [diff] [blame^] | 48 | ### Directory View |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 49 | |
| 50 | ![code coverage report directory view] |
| 51 | |
Abhishek Arya | af9811f2 | 2018-05-11 22:17:48 | [diff] [blame^] | 52 | ### Component View |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 53 | |
| 54 | ![code coverage report component view] |
| 55 | |
Abhishek Arya | af9811f2 | 2018-05-11 22:17:48 | [diff] [blame^] | 56 | ### Source View |
| 57 | |
| 58 | When you click on a particular source file in one of the views above, you can check |
| 59 | per-line coverage information such as |
| 60 | |
| 61 | - Uncovered / Covered line fragments, lines and code blocks. This information can be |
| 62 | useful to identify areas of code that lack test coverage. |
| 63 | - Per-line hit counts indicating how many times this line was hit by all tested targets. |
| 64 | This information can be useful to determine hot spots in your code. |
| 65 | |
| 66 | ![code coverage source view] |
| 67 | |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 68 | ## Workflow |
| 69 | This section presents the workflow of generating code coverage reports using two |
| 70 | unit test targets in Chromium repo as an example: `crypto_unittests` and |
| 71 | `url_unittests`, and the following diagram shows a step-by-step overview of the |
| 72 | process. |
| 73 | |
| 74 |  |
| 75 | |
| 76 | ### Step 0 Download Tooling |
| 77 | Generating code coverage reports requires llvm-profdata and llvm-cov tools. |
| 78 | Currently, these two tools are not part of Chromium’s Clang bundle, |
| 79 | [coverage script] downloads and updates them automatically, you can also |
| 80 | download the tools manually ([link]). |
| 81 | |
| 82 | ### Step 1 Build |
| 83 | In Chromium, to compile code with coverage enabled, one needs to add |
| 84 | `use_clang_coverage=true` and `is_component_build=false` GN flags to the args.gn |
| 85 | file in the build output directory. Under the hood, they ensure |
| 86 | `-fprofile-instr-generate` and `-fcoverage-mapping` flags are passed to the |
| 87 | compiler. |
| 88 | |
| 89 | ``` |
| 90 | $ gn gen out/coverage \ |
| 91 | --args='use_clang_coverage=true is_component_build=false' |
| 92 | $ gclient runhooks |
| 93 | $ ninja -C out/coverage crypto_unittests url_unittests |
Yuke Liao | d3b4627 | 2018-03-14 18:25:14 | [diff] [blame] | 94 | ``` |
| 95 | |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 96 | ### Step 2 Create Raw Profiles |
Abhishek Arya | af9811f2 | 2018-05-11 22:17:48 | [diff] [blame^] | 97 | The next step is to run the instrumented binaries. When the program exits, it |
| 98 | writes a raw profile for each process. Because Chromium runs tests in |
| 99 | multiple processes, the number of processes spawned can be as many as a few |
| 100 | hundred, resulting in the generation of a few hundred gigabytes’ raw |
| 101 | profiles. To limit the number of raw profiles, `%Nm` pattern in |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 102 | `LLVM_PROFILE_FILE` environment variable is used to run tests in multi-process |
| 103 | mode, where `N` is the number of raw profiles. With `N = 4`, the total size of |
| 104 | the raw profiles are limited to a few gigabytes. |
| 105 | |
| 106 | ``` |
| 107 | $ export LLVM_PROFILE_FILE=”out/report/crypto_unittests.%4m.profraw” |
| 108 | $ ./out/coverage/crypto_unittests |
| 109 | $ ls out/report/ |
| 110 | crypto_unittests.3657994905831792357_0.profraw |
| 111 | ... |
| 112 | crypto_unittests.3657994905831792357_3.profraw |
| 113 | ``` |
| 114 | |
| 115 | ### Step 3 Create Indexed Profile |
| 116 | Raw profiles must be indexed before generating code coverage reports, and this |
| 117 | is done using the `merge` command of `llvm-profdata` tool, which merges multiple |
Abhishek Arya | af9811f2 | 2018-05-11 22:17:48 | [diff] [blame^] | 118 | raw profiles (.profraw) and indexes them to create a single profile (.profdata). |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 119 | |
| 120 | At this point, all the raw profiles can be thrown away because their information |
Abhishek Arya | af9811f2 | 2018-05-11 22:17:48 | [diff] [blame^] | 121 | is already contained in the indexed profile. |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 122 | |
| 123 | ``` |
| 124 | $ llvm-profdata merge -o out/report/coverage.profdata \ |
| 125 | out/report/crypto_unittests.3657994905831792357_0.profraw |
| 126 | ... |
| 127 | out/report/crypto_unittests.3657994905831792357_3.profraw |
| 128 | out/report/url_unittests.714228855822523802_0.profraw |
| 129 | ... |
| 130 | out/report/url_unittests.714228855822523802_3.profraw |
| 131 | $ ls out/report/coverage.profdata |
| 132 | out/report/coverage.profdata |
| 133 | ``` |
| 134 | |
| 135 | ### Step 4 Create Coverage Reports |
| 136 | Finally, `llvm-cov` is used to render code coverage reports. There are different |
Abhishek Arya | af9811f2 | 2018-05-11 22:17:48 | [diff] [blame^] | 137 | report generation modes, and all of them require the following as input: |
| 138 | - Indexed profile |
| 139 | - All built target binaries |
| 140 | - All exercised source files. |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 141 | |
Abhishek Arya | af9811f2 | 2018-05-11 22:17:48 | [diff] [blame^] | 142 | For example, the following command can be used to generate per-file line-by-line |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 143 | code coverage report: |
| 144 | |
| 145 | ``` |
| 146 | $ llvm-cov show -output-dir=out/report -format=html \ |
| 147 | -instr-profile=out/report/coverage.profdata \ |
| 148 | -object=out/coverage/url_unittests \ |
| 149 | out/coverage/crypto_unittests |
| 150 | ``` |
| 151 | |
| 152 | For more information on how to use llvm-cov, please refer to the [guide]. |
Yuke Liao | d3b4627 | 2018-03-14 18:25:14 | [diff] [blame] | 153 | |
Max Moroz | d73e45f | 2018-04-24 18:32:47 | [diff] [blame] | 154 | ## Contacts |
| 155 | |
| 156 | ### Reporting problems |
Yuke Liao | d3b4627 | 2018-03-14 18:25:14 | [diff] [blame] | 157 | For any breakage report and feature requests, please [file a bug]. |
| 158 | |
Max Moroz | d73e45f | 2018-04-24 18:32:47 | [diff] [blame] | 159 | ### Mailing list |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 160 | For questions and general discussions, please join [chrome-code-coverage group]. |
| 161 | |
Max Moroz | d73e45f | 2018-04-24 18:32:47 | [diff] [blame] | 162 | ## FAQ |
| 163 | |
| 164 | ### Can I use `is_component_build=true` for code coverage build? |
| 165 | |
| 166 | Yes, code coverage instrumentation works with both component and non-component |
| 167 | builds. Component build is usually faster to compile, but can be up to several |
| 168 | times slower to run with code coverage instrumentation. For more information, |
Max Moroz | c5e364a | 2018-04-25 23:19:49 | [diff] [blame] | 169 | see [crbug.com/831939]. |
| 170 | |
| 171 | ### I am getting some warnings while using the script, is that fine? |
| 172 | |
Abhishek Arya | af9811f2 | 2018-05-11 22:17:48 | [diff] [blame^] | 173 | Usually this is not a critical issue, but in general we tend not to have any |
Max Moroz | c5e364a | 2018-04-25 23:19:49 | [diff] [blame] | 174 | warnings. Please check the list of [known issues], and if there is a similar |
| 175 | bug, leave a comment with the command you run, the output you get, and Chromium |
| 176 | revision you use. Otherwise, please [file a new issue] providing the same |
| 177 | information. |
| 178 | |
| 179 | ### How do crashes affect code coverage? |
| 180 | |
| 181 | If a crash of any type occurs (Segmentation Fault, CHECK failure, ASan error), |
| 182 | the crashing process will not dump coverage information necessary to generate |
| 183 | code coverage report. For single-process applications (e.g. fuzz targets), that |
| 184 | means no coverage will be reported at all. For multi-process applications, the |
Abhishek Arya | af9811f2 | 2018-05-11 22:17:48 | [diff] [blame^] | 185 | report will be incomplete. It is important to fix the crash first. If this is |
| 186 | happening only in the coverage instrumented build, please [file a bug]. |
Max Moroz | d73e45f | 2018-04-24 18:32:47 | [diff] [blame] | 187 | |
Max Moroz | 63cd04d | 2018-05-02 16:40:23 | [diff] [blame] | 188 | ### Is it possible to obtain code coverage from a full Chromium build? |
| 189 | |
| 190 | Yes, with some important caveats. It is possible to build `chrome` target with |
| 191 | code coverage instrumentation enabled. However, there are some inconveniences |
| 192 | involved: |
| 193 | |
| 194 | * Linking may take a while |
| 195 | * The binary is huge (~4GB) |
| 196 | * The browser "works", but is noticeably slow and laggy |
| 197 | * The sandbox needs to be disabled (`--no-sandbox`) |
| 198 | * Coverage can be incomplete for child processes |
| 199 | |
| 200 | For more information, please see [crbug.com/834781]. |
| 201 | |
Max Moroz | d73e45f | 2018-04-24 18:32:47 | [diff] [blame] | 202 | |
Max Moroz | c5e364a | 2018-04-25 23:19:49 | [diff] [blame] | 203 | [chrome-code-coverage group]: https://groups.google.com/a/google.com/forum/#!forum/chrome-code-coverage |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 204 | [coverage script]: https://cs.chromium.org/chromium/src/tools/code_coverage/coverage.py |
| 205 | [code coverage report directory view]: images/code_coverage_directory_view.png |
| 206 | [code coverage report component view]: images/code_coverage_component_view.png |
Abhishek Arya | af9811f2 | 2018-05-11 22:17:48 | [diff] [blame^] | 207 | [code coverage source view]: images/code_coverage_source_view.png |
Max Moroz | c5e364a | 2018-04-25 23:19:49 | [diff] [blame] | 208 | [crbug.com/831939]: https://crbug.com/831939 |
Max Moroz | 63cd04d | 2018-05-02 16:40:23 | [diff] [blame] | 209 | [crbug.com/834781]: https://crbug.com/834781 |
Max Moroz | c5e364a | 2018-04-25 23:19:49 | [diff] [blame] | 210 | [documentation]: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html |
Yuke Liao | 1ffc8cb6 | 2018-04-06 19:09:07 | [diff] [blame] | 211 | [file a bug]: https://bugs.chromium.org/p/chromium/issues/entry?components=Tools%3ECodeCoverage |
Max Moroz | c5e364a | 2018-04-25 23:19:49 | [diff] [blame] | 212 | [file a new issue]: https://bugs.chromium.org/p/chromium/issues/entry?components=Tools%3ECodeCoverage |
| 213 | [guide]: http://llvm.org/docs/CommandGuide/llvm-cov.html |
| 214 | [known issues]: https://bugs.chromium.org/p/chromium/issues/list?q=component:Tools%3ECodeCoverage |
| 215 | [link]: https://storage.googleapis.com/chromium-browser-clang-staging/ |