| Pirama Arumuga Nainar | 2b34678 | 2022-01-25 13:08:39 -0800 | [diff] [blame] | 1 | ## Native Code Coverage for Android |
| 2 | |
| 3 | ## Scope |
| 4 | |
| 5 | These instructions are for Android developers to collect and inspect code |
| 6 | coverage for C++ and Rust code on the Android platform. |
| 7 | |
| 8 | ## Building with Native Code Coverage Instrumentation |
| 9 | |
| 10 | Identify the paths where native code-coverage instrumentation should be enabled |
| 11 | and set up the following environment variables. |
| 12 | |
| 13 | ``` |
| 14 | export CLANG_COVERAGE=true |
| 15 | export NATIVE_COVERAGE_PATHS="<paths-to-instrument-for-coverage>" |
| 16 | ``` |
| 17 | |
| 18 | `NATIVE_COVERAGE_PATHS` should be a list of paths. Any Android.bp module defined |
| 19 | under these paths is instrumented for code-coverage. E.g: |
| 20 | |
| 21 | ``` |
| 22 | export NATIVE_COVERAGE_PATHS="external/libcxx system/core/adb" |
| 23 | ``` |
| 24 | |
| 25 | ### Additional Notes |
| 26 | |
| 27 | - Native Code coverage is not supported for host modules or `Android.mk` |
| 28 | modules. |
| 29 | - `NATIVE_COVERAGE_PATHS="*"` enables coverage instrumentation for all paths. |
| 30 | - Set `native_coverage: false` blueprint property to always disable code |
| 31 | coverage instrumentation for a module. This is useful if this module has |
| 32 | issues when building or running with coverage. |
| 33 | - `NATIVE_COVERAGE_EXCLUDE_PATHS` can be set to exclude subdirs under |
| 34 | `NATIVE_COVERAGE_PATHS` from coverage instrumentation. E.g. |
| 35 | `NATIVE_COVERAGE_PATHS=frameworks/native |
| 36 | NATIVE_COVERAGE_PATHS=frameworks/native/vulkan` will instrument all native |
| 37 | code under `frameworks/native` except`frameworks/native/vulkan`. |
| 38 | |
| 39 | ## Running Tests |
| 40 | |
| 41 | ### Collecting Profiles |
| 42 | |
| 43 | When an instrumented program is run, the profiles are stored to the path and |
| 44 | name specified in the `LLVM_PROFILE_FILE` environment variable. On Android |
| 45 | coverage builds it is set to `/data/misc/trace/clang-%p-%20m.profraw`. |
| 46 | |
| 47 | * `%`p is replaced by the pid of the process |
| 48 | * `%m` by the hash of the library/binary |
| 49 | * The `20` in`%20m` creates a pool of 20 profraw files and "online" profile |
| 50 | merging is used to merge coverage to profiles onto this pool. |
| 51 | |
| 52 | Reference:`LLVM_PROFILE_FILE` can include additional specifiers as described |
| 53 | [here](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#running-the-instrumented-program). |
| 54 | |
| 55 | For this and following steps, use the `acov-llvm.py` script: |
| 56 | `$ANDROID_BUILD_TOP/development/scripts/acov-llvm.py`. |
| 57 | |
| 58 | There may be profiles in `/data/misc/trace` collected before the test is run. |
| 59 | Clear this data before running the test. |
| 60 | |
| 61 | ``` |
| 62 | # Clear any coverage that's already written to /data/misc/trace |
| 63 | # and reset coverage for all daemons. |
| 64 | <host>$ acov-llvm.py clean-device |
| 65 | |
| 66 | # Run the test. The exact command depends on the nature of the test. |
| 67 | <device>$ /data/local/tmp/$MY_TEST |
| 68 | ``` |
| 69 | |
| 70 | For tests that exercise a daemon/service running in another process, write out |
| 71 | the coverage for those processes as well. |
| 72 | |
| 73 | ``` |
| 74 | # Flush coverage of all daemons/processes running on the device. |
| 75 | <host>$ acov-llvm.py flush |
| 76 | |
| 77 | # Flush coverage for a particular daemon, say adbd. |
| 78 | <host>$ acov-llvm.py flush adbd |
| 79 | ``` |
| 80 | |
| 81 | ## Viewing Coverage Data (acov-llvm.py) |
| 82 | |
| 83 | To post-process and view coverage information we use the `acov-llvm.py report` |
| 84 | command. It invokes two LLVM utilities `llvm-profdata` and `llvm-cov`. An |
| 85 | advanced user can manually invoke these utilities for fine-grained control. This |
| 86 | is discussed [below](#viewing-coverage-data-manual). |
| 87 | |
| 88 | To generate coverage report need the following parameters. These are dependent |
| 89 | on the test/module: |
| 90 | |
| 91 | 1. One or more binaries and shared libraries from which coverage was collected. |
| 92 | E.g.: |
| 93 | |
| 94 | 1. ART mainline module contains a few libraries such as `libart.so`, |
| 95 | `libart-compiler.so`. |
| 96 | 2. Bionic tests exercise code in `libc.so` and `libm.so`. |
| 97 | |
| 98 | We need the *unstripped* copies of these binaries. Source information |
| 99 | included in the debuginfo is used to process the coverage data. |
| 100 | |
| 101 | 2. One or more source directories under `$ANDROID_BUILD_TOP` for which coverage |
| 102 | needs to be reported. |
| 103 | |
| 104 | Invoke the report subcommand of acov-llvm.py to produce a html coverage summary: |
| 105 | |
| 106 | ``` |
| 107 | $ acov-llvm.py report \ |
| 108 | -s <one-or-more-source-paths-in-$ANDROID_BUILD_TOP \ |
| 109 | -b <one-or-more-(unstripped)-binaries-in-$OUT> |
| 110 | ``` |
| 111 | |
| 112 | E.g.: |
| 113 | |
| 114 | ``` |
| 115 | $ acov-llvm.py report \ |
| 116 | -s bionic \ |
| 117 | -b \ |
| 118 | $OUT/symbols/apex/com.android.runtime/lib/bionic/libc.so \ |
| 119 | $OUT/symbols/apex/com.android.runtime/lib/bionic/libm.so |
| 120 | ``` |
| 121 | |
| 122 | The script will produce a report in a temporary directory under |
| 123 | `$ANDROID_BUILD_TOP`. It'll produce a log as below: |
| 124 | |
| 125 | ``` |
| 126 | generating coverage report in covreport-xxxxxx |
| 127 | ``` |
| 128 | |
| 129 | A html report would be generated under `covreport-xxxxxx/html`. |
| 130 | |
| 131 | ## Viewing Coverage Data (manual) |
| 132 | |
| 133 | `acov-llvm.py report` does a few operations under the hood which we can also |
| 134 | manually invoke for flexibility. |
| 135 | |
| 136 | ### Post-processing Coverage Files |
| 137 | |
| 138 | Fetch coverage files from the device and post-process them to a `.profdata` file |
| 139 | as follows: |
| 140 | |
| 141 | ``` |
| 142 | # Fetch the coverage data from the device. |
| 143 | <host>$ cd coverage_data |
| 144 | <host>$ adb pull /data/misc/trace/ $TRACE_DIR_HOST |
| 145 | |
| 146 | # Convert from .profraw format to the .profdata format. |
| 147 | <host>$ llvm-profdata merge --output=$MY_TEST.profdata \ |
| 148 | $TRACE_DIR_HOST/clang-*.profraw |
| 149 | ``` |
| 150 | |
| 151 | For added specificity, restrict the above command to just the <PID>s of the |
| 152 | daemon or test processes of interest. |
| 153 | |
| 154 | ``` |
| 155 | <host>$ llvm-profdata merge --output=$MY_TEST.profdata \ |
| 156 | $MY_TEST.profraw \ |
| 157 | trace/clang-<pid1>.profraw trace/clang-<pid2>.profraw ... |
| 158 | ``` |
| 159 | |
| 160 | ### Generating Coverage report |
| 161 | |
| 162 | Documentation on Clang source-instrumentation-based coverage is available |
| 163 | [here](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html#creating-coverage-reports). |
| 164 | The `llvm-cov` utility is used to show coverage from a `.profdata` file. The |
| 165 | documentation for commonly used `llvm-cov` command-line arguments is available |
| 166 | [here](https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-report). (Try |
| 167 | `llvm-cov show --help` for a complete list). |
| 168 | |
| 169 | #### `show` subcommand |
| 170 | |
| 171 | The `show` command displays the function and line coverage for each source file |
| 172 | in the binary. |
| 173 | |
| 174 | ``` |
| 175 | <host>$ llvm-cov show \ |
| 176 | --show-region-summary=false |
| 177 | --format=html --output-dir=coverage-html \ |
| 178 | --instr-profile=$MY_TEST.profdata \ |
| 179 | $MY_BIN \ |
| 180 | ``` |
| 181 | |
| 182 | * In the above command, `$MY_BIN` should be the unstripped binary (i.e. with |
| 183 | debuginfo) since `llvm-cov` reads some debuginfo to process the coverage |
| 184 | data. |
| 185 | |
| 186 | E.g.: |
| 187 | |
| 188 | ~~~ |
| 189 | ``` |
| 190 | <host>$ llvm-cov report \ |
| 191 | --instr-profile=adbd.profdata \ |
| 192 | $LOCATION_OF_UNSTRIPPED_ADBD/adbd \ |
| 193 | --show-region-summary=false |
| 194 | ``` |
| 195 | ~~~ |
| 196 | |
| 197 | * The `-ignore-filename-regex=<regex>` option can be used to ignore files that |
| 198 | are not of interest. E.g: `-ignore-filename-regex="external/*"` |
| 199 | |
| 200 | * Use the `--object=<BIN>` argument to specify additional binaries and shared |
| 201 | libraries whose coverage is included in this profdata. See the earlier |
| 202 | [section](#viewing-coverage-data-acov-llvm-py) for examples where more than |
| 203 | one binary may need to be used. |
| 204 | |
| 205 | E.g., the following command is used for `bionic-unit-tests`, which tests |
| 206 | both `libc.so` and `libm.so`: |
| 207 | |
| 208 | ~~~ |
| 209 | ``` |
| 210 | <host>$ llvm-cov report \ |
| 211 | --instr-profile=bionic.profdata \ |
| 212 | $OUT/.../libc.so \ |
| 213 | --object=$OUT/.../libm.so |
| 214 | ``` |
| 215 | ~~~ |
| 216 | |
| 217 | * `llvm-cov` also takes positional SOURCES argument to consider/display only |
| 218 | particular paths of interest. E.g: |
| 219 | |
| 220 | ~~~ |
| 221 | ``` |
| 222 | <host>$ llvm-cov report \ |
| 223 | --instr-profile=adbd.profdata \ |
| 224 | $LOCATION_OF_ADBD/adbd \ |
| 225 | --show-region-summary=false \ |
| 226 | /proc/self/cwd/system/core/adb |
| 227 | ``` |
| 228 | ~~~ |
| 229 | |
| 230 | Note that the paths for the sources need to be prepended with |
| 231 | '`/proc/self/cwd/`'. This is because Android C/C++ compilations run with |
| 232 | `PWD=/proc/self/cwd` and consequently the source names are recorded with that |
| 233 | prefix. Alternatively, the |
| 234 | [`--path-equivalence`](https://llvm.org/docs/CommandGuide/llvm-cov.html#cmdoption-llvm-cov-show-path-equivalence) |
| 235 | option to `llvm-cov` can be used. |
| 236 | |
| 237 | #### `report` subcommand |
| 238 | |
| 239 | The [`report`](https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-report) |
| 240 | subcommand summarizes the percentage of covered lines to the console. It takes |
| 241 | options similar to the `show` subcommand. |