11# Patch-Based Code Coverage (LLVM Git Integration)
22
33This tool provides patch-level code coverage for LLVM development workflows.
4- It helps developers and reviewers understand which lines introduced by a patch are covered by tests and which are not.
5-
6- It integrates with LLVM’s infrastructure (` llvm-lit ` , coverage instrumentation, and CI) and works both locally and in CI environments.
7-
4+ It helps developers and reviewers understand which lines introduced by a patch
5+ are covered by tests and which are not.
86
97## Overview
108
@@ -21,23 +19,14 @@ Given recent commits, the tool:
2119 * Covered lines introduced by the patch
2220 * Uncovered lines introduced by the patch
2321
24-
25- ## Key Features
26-
27- * Patch-aware coverage (only modified lines)
28- * Selective test execution (only affected tests)
29- * Supports:
30-
31- * ` llvm-lit ` tests
32- * LLVM unit tests
33- * Uses allowlist-based instrumentation for faster builds
34- * Avoids unnecessary rebuilds via patch hashing
35-
36-
3722## Setup
3823
3924### 1. Build LLVM (standard workflow)
4025
26+ If you have an existing LLVM build, point to it with ` -b ` . If not, skip this step —
27+ the tool will automatically configure and build LLVM in the specified build directory
28+ with a minimal configuration when first run.
29+
4130``` bash
4231git clone https://github.com/llvm/llvm-project.git
4332cd llvm-project
@@ -104,21 +93,69 @@ git patch-coverage -b build -n 3 opt llvm/test
10493| ------------------------------ | ----------------------------------------------------------- |
10594| ` -n, --num-commits ` | Number of recent commits to analyze (default: 1) |
10695| ` --projects ` | LLVM projects to enable (e.g. clang;mlir (default: None) |
107- | ` -i, --instrumented-build-dir ` | Use pre-built instrumented directory (Deafult : ` build_inst ` )|
96+ | ` -i, --instrumented-build-dir ` | Use pre-built instrumented directory (Dafault : ` build_inst ` )|
10897| ` -b, --build-dir ` | Path to LLVM build directory (default: ` build ` ) |
10998| ` <test-path> ` | One or more test suite paths (default: ` build/test ` ) |
11099
111100
112101
113102## How It Works
114-
115-
103+ ### 1. Patch Extraction
104+ The tool runs ` git diff HEAD~N HEAD ` to generate a patch from the last N commits.
105+ It parses the diff to extract:
106+ - ** Source files** — modified ` .cpp ` , ` .c ` , or ` .mm ` files outside test directories
107+ - ** Test files** — modified lit tests and unit tests within the patch
108+ - ** Modified lines** — the exact line numbers added or changed in each source file
109+
110+ ### 2. Allowlist Generation
111+ To avoid instrumenting the entire codebase, the tool generates an allowlist
112+ containing only the source files present in the patch:
113+ source:/path/to/modified/file.cpp=allow
114+ default: skip
115+
116+ This is passed to the compiler via ` -fprofile-list ` , so profiling overhead is
117+ scoped only to what changed.
118+
119+ ### 3. Instrumented Build
120+ The tool configures a second build directory (` build_inst ` ) with:
121+ - ` LLVM_BUILD_INSTRUMENTED_COVERAGE=ON ` — enables source-based coverage
122+ - ` LLVM_INDIVIDUAL_TEST_COVERAGE=ON ` — generates per-test ` .profraw ` files
123+ - The allowlist flags from step 2
124+
125+ Rebuilds are skipped if the patch hash (SHA-256 of the diff) matches the hash
126+ from the last successful build, stored in ` build_inst/.last_patch_hash ` .
127+
128+ ### 4. Test Execution
129+ Only tests modified in the patch are run, against the instrumented binary.
130+ Each test writes its coverage data to a dedicated ` .profraw ` file under
131+ ` build_inst/profiles/ ` , named by a hash of the test path to avoid collisions:
132+
133+ - ** Lit tests** — run via ` llvm-lit ` with ` LLVM_PROFILE_FILE ` set per test
134+ - ** Unit tests** — run directly as binaries with ` LLVM_PROFILE_FILE ` set
135+
136+ ### 5. Coverage Processing
137+ For each ` .profraw ` file collected during test execution:
138+ 1 . ` llvm-profdata merge ` converts it to a ` .profdata ` file
139+ 2 . ` llvm-cov show ` produces a line-level coverage report for each patched
140+ source file, using the instrumented binary as the symbol source
141+
142+ ### 6. Reporting
143+ The tool cross-references coverage results against the modified lines extracted
144+ in step 1. A line is considered:
145+ - ** Covered** — if it was executed by at least one test
146+ - ** Uncovered** — if it appears in the patch but was never executed
147+ - ** Effectively covered** — if it was uncovered in some tests but covered in
148+ others (union of all runs wins)
149+
150+ Results are printed to the terminal in diff style with color coding, and the
151+ full details are written to ` build_inst/patch_coverage.log ` .
116152
117153## Output
118154
119155### ` <build_inst>/patch_coverage.log `
120156
121157```
158+ ...
122159Modified File: llvm/lib/IR/Example.cpp
123160 Covered Line: 42 : foo();
124161 Uncovered Line: 45 : bar();
@@ -127,20 +164,22 @@ Modified File: llvm/lib/IR/Example.cpp
127164### Summary on Standard Output
128165
129166```
130- [code-coverage] Common uncovered lines for llvm/lib/IR/Example.cpp:
131- Line 45: bar();
167+ [code-coverage] Coverage report for llvm/lib/IR/Example.cpp:
168+ Line 42 : foo();
169+ Line 45 : bar();
170+
132171```
133172
134173## Troubleshooting
135174
136175### No coverage generated
137176
138- * Ensure instrumented build succeeded and tests run
139- * Check ` .profraw ` files in ` build_inst ` directory
177+ * Ensure instrumented build succeeded and tests run.
178+ * Check ` .profraw ` files in ` build_inst ` directory.
140179
141180### Rebuild skipped unexpectedly
142181
143182* Delete ` binary ` to force rebuild
144183
145184### For any other doubt
146- * Check ` <build_inst>/patch_coverage.log ` . It has full logging of tool
185+ * Check ` <build_inst>/patch_coverage.log ` . It has full logging of tool.
0 commit comments