Tools: add support for GCOV code coverage tool and HTML reporting with LCOV.

first, "apt install gcovr lcov" as new prerequisites
then run:
./waf configure --enable-gcov
./waf build --lcov-report
and after much waiting review the report in HTML format with your preferred browser eg:
firefox reports/lcov-report/index.html
This commit is contained in:
Buzz 2019-02-26 15:25:16 +10:00 committed by Peter Barker
parent 6a5913ea7b
commit 31697a3b0c
3 changed files with 134 additions and 1 deletions

3
.gitignore vendored
View File

@ -11,6 +11,7 @@
/Tools/autotest/jsb_sim/rascal_test.xml
/Tools/autotest/jsbsim_fgout_0.xml
/Tools/autotest/jsbsim_start_0.xml
/Tools/autotest/rover-ch7_mission.txt
/tmp/*
*.bin
*.d
@ -109,4 +110,6 @@ parameters.edn
.idea/*
# CMake
cmake-build-*/
/reports/
/GCOV_*.log

View File

@ -11,7 +11,7 @@ PX4_PKGS="python-argparse openocd flex bison libncurses5-dev \
zip genromfs python-empy cmake cmake-data"
ARM_LINUX_PKGS="g++-arm-linux-gnueabihf pkg-config-arm-linux-gnueabihf"
# python-wxgtk packages are added to SITL_PKGS below
SITL_PKGS="libtool libxml2-dev libxslt1-dev python-dev python-pip python-setuptools python-matplotlib python-serial python-scipy python-opencv python-numpy python-pyparsing xterm"
SITL_PKGS="libtool libxml2-dev libxslt1-dev python-dev python-pip python-setuptools python-matplotlib python-serial python-scipy python-opencv python-numpy python-pyparsing xterm lcov gcovr"
ASSUME_YES=false
QUIET=false

130
wscript
View File

@ -4,7 +4,9 @@
from __future__ import print_function
import os.path
import os
import sys
import subprocess
sys.path.insert(0, 'Tools/ardupilotwaf/')
import ardupilotwaf
@ -193,6 +195,110 @@ configuration in order to save typing.
default=False,
help='Force a static build')
g.add_option("--enable-gcov",
help=("Enable gcov code coverage analysis."
" WARNING: this option only has effect "
"with the configure command. "
"You should also add --lcov-report to your build command."),
action="store_true", default=False,
dest="enable_gcov")
g.add_option("--lcov-report",
help=("Generates a lcov code coverage report "
"(use this option at build time, not in configure)"),
action="store_true", default=False,
dest="lcov_report")
# EXECUTE enough code via the autotest tool to see coverage results afterwards, but don't build/rebuild anything
# our aim here is to try to execute as many code path/s as we have available to us, and we'll afterward report
# on the percentage of code executed and not executed etc.
def run_coverage_tests(bld):
FNULL = open(os.devnull, 'w')
#tests = ['fly.ArduPlane']
#tests = ['fly.ArduCopter','fly.ArduPlane']
tests = ['fly.ArduCopter','fly.ArduPlane', 'fly.QuadPlane', 'drive.APMrover2', 'dive.ArduSub']
for test in tests:
print("LCOV/GCOV -> "+test+" started.... this will take quite some time...")
testcmd = '( ./Tools/autotest/autotest.py --debug --no-configure '+test+' ) '
print("Coverage Tests Executing:"+testcmd+" > ./GCOV_"+test+".log")
FLOG = open("./GCOV_"+test+".log", 'w')
if subprocess.Popen(testcmd, shell=True , stdout=FLOG, stderr=FNULL).wait():
print("LCOV/GCOV -> "+test+" see ./GCOV_"+test+".log for log of activity)")
raise SystemExit(1)
print("LCOV/GCOV -> "+test+" succeeded")
FLOG.close()
#TODO add any other execution path/s we can to maximise the actually used code, can we run other tests or things?
# eg run.unit_tests, run.examples , test.AntennaTracker or other things?
def lcov_report(bld):
"""
Generates the coverage report
:param bld: temporal options context
:type bld: wscript.tmp
"""
env = bld.env
REPORTS = "reports"
if not env.GCOV_ENABLED:
raise WafError("project not configured for code coverage;"
" reconfigure with --enable-gcov")
run_coverage_tests(bld)
lcov_report_dir = os.path.join(REPORTS, "lcov-report")
try:
if not os.path.exists(REPORTS):
os.mkdir(REPORTS)
create_dir_command = "rm -rf " + lcov_report_dir
create_dir_command += " && mkdir " + lcov_report_dir
print (create_dir_command );
if subprocess.Popen(create_dir_command, shell=True).wait():
raise SystemExit(1)
info_file = os.path.join(lcov_report_dir, "lcov.info")
FLOG = open("GCOV_lcov.log", 'w')
FNULL = open(os.devnull, 'w')
lcov_command =\
"lcov --no-external --capture --directory . -o " + info_file
lcov_command +=\
" && lcov --remove " + info_file + " \".waf*\" -o " + info_file
print("LCOV/GCOV executing lcov -> ")
print ("\t"+lcov_command + " > ./GCOV_lcov.log")
if subprocess.Popen(lcov_command, shell=True, stdout=FLOG, stderr=FNULL).wait():
raise SystemExit(1)
FLOG.close()
FLOG = open("GCOV_genhtml.log", 'w')
genhtml_command = "genhtml " + info_file
genhtml_command += " -o " + lcov_report_dir
print("LCOV/GCOV building html report -> ")
print ("\t"+genhtml_command + " > ./GCOV_genhtml.log")
if subprocess.Popen(genhtml_command, shell=True, stdout=FLOG, stderr=FNULL).wait():
raise SystemExit(1)
FLOG.close()
except:
print (\
"LCOV/GCOV -> Problems running coverage. Try manually" );
finally:
print (\
"LCOV/GCOV -> Coverage successful. Open " + lcov_report_dir +\
"/index.html" );
def _collect_autoconfig_files(cfg):
for m in sys.modules.values():
paths = []
@ -212,10 +318,16 @@ def _collect_autoconfig_files(cfg):
cfg.files.append(p)
def configure(cfg):
# we need to enable debug mode when building for gconv, and force it to sitl
if cfg.options.enable_gcov:
cfg.options.debug = True
cfg.options.board = 'sitl'
if cfg.options.board is None:
cfg.options.board = 'sitl'
cfg.env.BOARD = cfg.options.board
cfg.env.DEBUG = cfg.options.debug
cfg.env.AUTOCONFIG = cfg.options.autoconfig
_set_build_context_variant(cfg.env.BOARD)
@ -234,6 +346,21 @@ def configure(cfg):
cfg.msg('Autoconfiguration', 'enabled' if cfg.options.autoconfig else 'disabled')
#Sets the lcov flag if is configurated
if cfg.options.enable_gcov:
cfg.start_msg("GCOV code coverage analysis")
cfg.env.GCOV_ENABLED = True
cfg.env.append_value('CCFLAGS', '-fprofile-arcs')
cfg.env.append_value('CCFLAGS', '-ftest-coverage')
cfg.env.append_value('CXXFLAGS', '-fprofile-arcs')
cfg.env.append_value('CXXFLAGS', '-ftest-coverage')
cfg.env.append_value('LINKFLAGS', '-lgcov')
cfg.env.append_value('LINKFLAGS', '-coverage')
cfg.end_msg('yes' , color='RED')
else:
cfg.start_msg("GCOV code coverage analysis")
cfg.end_msg('no' , color='GREEN')
if cfg.options.static:
cfg.msg('Using static linking', 'yes', color='YELLOW')
cfg.env.STATIC_LINKING = True
@ -472,6 +599,9 @@ def _build_post_funs(bld):
if bld.env.SUBMODULE_UPDATE:
bld.git_submodule_post_fun()
if bld.env.GCOV_ENABLED:
bld.add_post_fun(lcov_report)
def _load_pre_build(bld):
'''allow for a pre_build() function in build modules'''
brd = bld.get_board()