# Generating and viewing Tint code-coverage

Requirements:

* Host running Linux or macOS
* [vpython3](https://chromium.googlesource.com/chromium/tools/depot_tools/+/HEAD/vpython3)
  (usually provided by `depot_tools`)
* Clang toolchain

## Building Tint with coverage generation enabled

To generate coverage, you must build with Clang source-based coverage
instrumentation enabled.

### Using GN (Recommended)

Add the following flags to your `args.gn`:

```gn
use_clang_coverage = true
is_component_build = false
is_debug = false
dcheck_always_on = true
```

If you are building a fuzzer target, also include:

```gn
optimize_for_fuzzing = false
use_clang_modules = false
use_libfuzzer = true
```

### Using CMake

Add the additional `-DDAWN_EMIT_COVERAGE=1` CMake flag.

To use the `coverage.py` script's automated building features, you
must use the Ninja generator:

```bash
cmake -G Ninja -DDAWN_EMIT_COVERAGE=1 ..
```

If you use a different generator (like Make), you must build your
targets manually and use the `--no-compile` flag with the script.

## Generating Coverage Reports

Use the `tools/code_coverage/coverage.py` script to run your targets
and generate reports. (This is the Chromium maintained script that
replaces the previous Tint only script,
`tools/tint-generate-coverage`).

### 1. Generate LCOV for VSCode

To use the [VSCode Coverage
Gutters](https://marketplace.visualstudio.com/items?itemName=ryanluker.vscode-coverage-gutters)
extension, generate an LCOV tracefile:

```bash
vpython3 tools/code_coverage/coverage.py tint_unittests \
    -b out/gn-coverage \
    -o coverage_report \
    --format lcov \
    -c 'out/gn-coverage/tint_unittests'
```


The resulting file will be located at
`coverage_report/linux/coverage.lcov`.

### 2. Generate an HTML Report

To generate a browsable HTML report with line-by-line coverage:

```bash
vpython3 tools/code_coverage/coverage.py tint_unittests \
    --no-component-view \
    -b out/gn-coverage \
    -o coverage_report \
    --format html \
    -c 'out/gn-coverage/tint_unittests'
```

Open `coverage_report/linux/index.html` in your browser.

### 3. Advanced: Fuzzers with Dynamic Objects (Mesa/Vulkan)

If you are running a fuzzer that dynamically loads a Vulkan driver
(like Mesa's `lvp`), you need to explicitly include the driver object
and map its source paths:

```bash
vpython3 tools/code_coverage/coverage.py tint_ir_fuzzer \
    --no-component-view \
    -b out/gn-coverage \
    -o coverage_report \
    --format html \
    -a out/gn-coverage/libvulkan_lvp.so \
    --path-equivalence=/third_party/mesa,$(pwd)/third_party/mesa \
    --path-equivalence=$(pwd)/out/gn-coverage/src,$(pwd)/out/gn-coverage/gen/third_party/mesa/mesa_build_setup/src \
    -i '\/usr\/.*' \
    -c 'out/gn-coverage/tint_ir_fuzzer -max_total_time=30'
```

*   `-a`: Adds the dynamically loaded driver as object source to the
    coverage calculation.
*   `--path-equivalence`: Maps paths recorded in the binary (which may
    be absolute or relative to build artifacts) to your local
    checkout.
*   Multiple `--path-equivalence` flags can be provided to handle
    different source roots (e.g., standard sources vs. generated
    files).
*   `-i '\/usr\/.*` is used to not include system files that are
    outside the source tree.

## Troubleshooting

### 403 error when generating HTML reports
If you see something like this:
```
[2026-04-13 13:20:20,757 INFO] Generating code coverage report in html (this can take a while depending on size of target!).
Traceback (most recent call last):
  File "/home/<user>/workspace/dawn/tools/code_coverage/coverage.py", line 1271, in <module>
    sys.exit(Main())
             ^^^^^^
  File "/home/<user>/workspace/dawn/tools/code_coverage/coverage.py", line 1266, in Main
    _GenerateCoverageReport(args, binary_paths, profdata_file_path,
  File "/home/<user>/workspace/dawn/tools/code_coverage/coverage.py", line 968, in _GenerateCoverageReport
    component_mappings = json.load(urlopen(COMPONENT_MAPPING_URL))
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/<user>/.cache/vpython-root.119166/store/cpython+l5fnajrvijf7cvdkjqmbicg3i8/contents/lib/python3.11/urllib/request.py", line 216, in urlopen
    return opener.open(url, data, timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/<user>/.cache/vpython-root.119166/store/cpython+l5fnajrvijf7cvdkjqmbicg3i8/contents/lib/python3.11/urllib/request.py", line 525, in open
    response = meth(req, response)
               ^^^^^^^^^^^^^^^^^^^
  File "/home/<user>/.cache/vpython-root.119166/store/cpython+l5fnajrvijf7cvdkjqmbicg3i8/contents/lib/python3.11/urllib/request.py", line 634, in http_response
    response = self.parent.error(
               ^^^^^^^^^^^^^^^^^^
  File "/home/<user>/.cache/vpython-root.119166/store/cpython+l5fnajrvijf7cvdkjqmbicg3i8/contents/lib/python3.11/urllib/request.py", line 563, in error
    return self._call_chain(*args)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/<user>/.cache/vpython-root.119166/store/cpython+l5fnajrvijf7cvdkjqmbicg3i8/contents/lib/python3.11/urllib/request.py", line 496, in _call_chain
    result = func(*args)
             ^^^^^^^^^^^
  File "/home/<user>/.cache/vpython-root.119166/store/cpython+l5fnajrvijf7cvdkjqmbicg3i8/contents/lib/python3.11/urllib/request.py", line 643, in http_error_default
    raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 403: Forbidden

```

then the likely cause is missing `--no-component-view`  from the flags.

The component view functionality depends on the mapping of directories
in a Chromium checkout to components in the issue tracker, so
misbehaves when run in a Dawn checkout. Fixing this would add
complexity upstream for a view that doesn't provide any useful
information in the report, since it would just tell you all of the
code in the Dawn repo belongs to either the Dawn or Tint component.
