CI: Add ability to generate patch coverage reports
This commit is contained in:
parent
f09c30df35
commit
540e042221
36
Makefile.in
36
Makefile.in
|
|
@ -603,9 +603,16 @@ COVERAGE_DIR := obj_coverage
|
||||||
|
|
||||||
ifeq ($(CFG_WITH_DEV_GCOV),yes)
|
ifeq ($(CFG_WITH_DEV_GCOV),yes)
|
||||||
|
|
||||||
FASTCOV := nodist/fastcov.py
|
# Figure out base and head refs for coverage report
|
||||||
|
COVERAGE_REF_BASE := $(if $(COVERAGE_BASE),$(shell git rev-parse --short $(COVERAGE_BASE)))
|
||||||
|
COVERAGE_REF_HEAD := $(shell git rev-parse --short HEAD)
|
||||||
|
override undefine COVERAGE_BASE # Use the above variabels instead
|
||||||
|
COVERAGE_PATCH_FILE := $(COVERAGE_DIR)/$(if $(COVERAGE_REF_BASE),$(COVERAGE_REF_BASE).patch,.none.pach)
|
||||||
|
|
||||||
|
# 'fastcov' setup
|
||||||
|
FASTCOV := nodist/fastcov.py
|
||||||
FASTCOV_OPT := -j $(shell nproc)
|
FASTCOV_OPT := -j $(shell nproc)
|
||||||
|
FASTCOV_OPT += --lcov
|
||||||
FASTCOV_OPT += --process-gcno
|
FASTCOV_OPT += --process-gcno
|
||||||
FASTCOV_OPT += --branch-coverage
|
FASTCOV_OPT += --branch-coverage
|
||||||
FASTCOV_OPT += --dump-statistic
|
FASTCOV_OPT += --dump-statistic
|
||||||
|
|
@ -642,7 +649,9 @@ FASTCOV_OPT += BROKEN_BASE_RTN
|
||||||
FASTCOV_OPT += SELF_CHECK
|
FASTCOV_OPT += SELF_CHECK
|
||||||
FASTCOV_OPT += 'if (VL_UNCOVERABLE'
|
FASTCOV_OPT += 'if (VL_UNCOVERABLE'
|
||||||
FASTCOV_OPT += '} else if (VL_UNCOVERABLE'
|
FASTCOV_OPT += '} else if (VL_UNCOVERABLE'
|
||||||
|
FASTCOV_OPT += $(if $(COVERAGE_REF_BASE),--diff-filter $(COVERAGE_PATCH_FILE))
|
||||||
|
|
||||||
|
# 'genhtml' setup
|
||||||
GENHTML := genhtml
|
GENHTML := genhtml
|
||||||
GENHTML_OPT := -j $(shell nproc)
|
GENHTML_OPT := -j $(shell nproc)
|
||||||
GENHTML_OPT += --branch-coverage
|
GENHTML_OPT += --branch-coverage
|
||||||
|
|
@ -650,26 +659,43 @@ GENHTML_OPT += --demangle-cpp
|
||||||
GENHTML_OPT += --rc branch_coverage=1
|
GENHTML_OPT += --rc branch_coverage=1
|
||||||
GENHTML_OPT += --rc genhtml_hi_limit=100
|
GENHTML_OPT += --rc genhtml_hi_limit=100
|
||||||
GENHTML_OPT += --ignore-errors negative
|
GENHTML_OPT += --ignore-errors negative
|
||||||
GENHTML_OPT += --header-title "Verilator code coverage report"
|
ifeq ($(COVERAGE_REF_BASE),)
|
||||||
|
GENHTML_OPT += --header-title "Code coverage for Verilator $(shell git describe --dirty)"
|
||||||
|
else
|
||||||
|
GENHTML_OPT += --header-title "Patch coverage for Verilator $(COVERAGE_REF_BASE)..$(COVERAGE_REF_HEAD)$(if $(git status --porcelain),,-dirty)"
|
||||||
|
endif
|
||||||
|
GENHTML_OPT += --flat
|
||||||
|
GENHTML_OPT += --precision 2
|
||||||
|
GENHTML_OPT += --legend
|
||||||
|
GENHTML_OPT += --show-proportion
|
||||||
|
GENHTML_OPT += --filter brace,blank,range
|
||||||
|
|
||||||
# There are loads (~20k combined), but using this seems fine on modern hardware
|
# There are loads (~20k combined), but using this seems fine on modern hardware
|
||||||
GCNO_FILES = $(shell find . -name '*.gcno')
|
GCNO_FILES = $(shell find . -name '*.gcno')
|
||||||
GCDA_FILES = $(shell find . -name '*.gcda')
|
GCDA_FILES = $(shell find . -name '*.gcda')
|
||||||
|
|
||||||
|
# Patch file to filter coverage with - unused if doing full coverage
|
||||||
|
$(COVERAGE_PATCH_FILE):
|
||||||
|
@mkdir -p $(COVERAGE_DIR)
|
||||||
|
@touch $@
|
||||||
|
$(if $(COVERAGE_REF_BASE), git diff $(COVERAGE_REF_BASE) > $(COVERAGE_PATCH_FILE))
|
||||||
|
|
||||||
# Combine all .gcda coverage date files into lcov .info file
|
# Combine all .gcda coverage date files into lcov .info file
|
||||||
$(COVERAGE_DIR)/verilator.info: $(GCNO_FILES) $(GCDA_FILES)
|
$(COVERAGE_DIR)/verilator.info: $(COVERAGE_PATCH_FILE) $(GCNO_FILES) $(GCDA_FILES)
|
||||||
@echo "####################################################################"
|
@echo "####################################################################"
|
||||||
@echo "# fastcov: combining all .gcda files into lcov .info"
|
@echo "# fastcov: combining all .gcda files into lcov .info"
|
||||||
@echo "####################################################################"
|
@echo "####################################################################"
|
||||||
mkdir -p $(COVERAGE_DIR)
|
|
||||||
/usr/bin/time -f "That took %E" \
|
/usr/bin/time -f "That took %E" \
|
||||||
$(FASTCOV) $(FASTCOV_OPT) --lcov --output $@
|
$(FASTCOV) $(FASTCOV_OPT) --output $@
|
||||||
|
@# Uncommitted changes not tracked, force rebuild on next run if patch coverage
|
||||||
|
@$(if $(COVERAGE_REF_BASE),rm $(COVERAGE_PATCH_FILE))
|
||||||
|
|
||||||
# Build coverage report
|
# Build coverage report
|
||||||
$(COVERAGE_DIR)/report/index.html: $(COVERAGE_DIR)/verilator.info
|
$(COVERAGE_DIR)/report/index.html: $(COVERAGE_DIR)/verilator.info
|
||||||
@echo "####################################################################"
|
@echo "####################################################################"
|
||||||
@echo "# genhtml: Generating coverage report"
|
@echo "# genhtml: Generating coverage report"
|
||||||
@echo "####################################################################"
|
@echo "####################################################################"
|
||||||
|
@rm -rf $(COVERAGE_DIR)/reprot
|
||||||
/usr/bin/time -f "That took %E" \
|
/usr/bin/time -f "That took %E" \
|
||||||
$(GENHTML) $(GENHTML_OPT) --output-directory $(COVERAGE_DIR)/report $^
|
$(GENHTML) $(GENHTML_OPT) --output-directory $(COVERAGE_DIR)/report $^
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1695,6 +1695,15 @@ Be aware if changing a test, if that test no longer covers some item, the
|
||||||
report will still contain the old coverage. Use ``make coverage-zero`` and
|
report will still contain the old coverage. Use ``make coverage-zero`` and
|
||||||
rerun all tests if this is a concern.
|
rerun all tests if this is a concern.
|
||||||
|
|
||||||
|
It is also possible to generate a 'patch coverage' report, which will only
|
||||||
|
contain information about lines modified compared to a Git ref specified by the
|
||||||
|
``COVERAGE_BASE`` Make variable (including uncommitted changes). For example,
|
||||||
|
to see coverage of changes compared to upstream, use:
|
||||||
|
|
||||||
|
.. code:: shell
|
||||||
|
|
||||||
|
make coverage-view COVERAGE_BASE=origin/master
|
||||||
|
|
||||||
Fuzzing
|
Fuzzing
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -509,6 +509,13 @@ def containsMarker(markers, strBody):
|
||||||
def exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_branches_sw, exclude_line_marker, fallback_encodings, gcov_prefix, gcov_prefix_strip):
|
def exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_branches_sw, exclude_line_marker, fallback_encodings, gcov_prefix, gcov_prefix_strip):
|
||||||
source_to_open = processPrefix(source, gcov_prefix, gcov_prefix_strip)
|
source_to_open = processPrefix(source, gcov_prefix, gcov_prefix_strip)
|
||||||
|
|
||||||
|
# Before doing any work, check if this file even needs to be processed
|
||||||
|
if not exclude_branches_sw and not include_branches_sw:
|
||||||
|
# Ignore unencodable characters
|
||||||
|
with open(source_to_open, errors="ignore") as f:
|
||||||
|
if not containsMarker(exclude_line_marker + ["LCOV_EXCL"], f.read()):
|
||||||
|
return False
|
||||||
|
|
||||||
# If we've made it this far we have to check every line
|
# If we've made it this far we have to check every line
|
||||||
|
|
||||||
start_line = 0
|
start_line = 0
|
||||||
|
|
@ -526,10 +533,8 @@ def exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_bran
|
||||||
if del_exclude_br or del_include_br:
|
if del_exclude_br or del_include_br:
|
||||||
del fastcov_data["branches"][i]
|
del fastcov_data["branches"][i]
|
||||||
|
|
||||||
lineIsClosingBrace = line.strip() == "}"
|
|
||||||
|
|
||||||
# Skip to next line as soon as possible
|
# Skip to next line as soon as possible
|
||||||
if not containsMarker(exclude_line_marker + ["LCOV_EXCL"], line) and not lineIsClosingBrace:
|
if not containsMarker(exclude_line_marker + ["LCOV_EXCL"], line):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Build line to function dict so can quickly delete by line number
|
# Build line to function dict so can quickly delete by line number
|
||||||
|
|
@ -540,7 +545,7 @@ def exclProcessSource(fastcov_sources, source, exclude_branches_sw, include_bran
|
||||||
line_to_func[l] = set()
|
line_to_func[l] = set()
|
||||||
line_to_func[l].add(f)
|
line_to_func[l].add(f)
|
||||||
|
|
||||||
if lineIsClosingBrace or any(marker in line for marker in exclude_line_marker):
|
if any(marker in line for marker in exclude_line_marker):
|
||||||
for key in ["lines", "branches"]:
|
for key in ["lines", "branches"]:
|
||||||
if i in fastcov_data[key]:
|
if i in fastcov_data[key]:
|
||||||
del fastcov_data[key][i]
|
del fastcov_data[key][i]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue