diff --git a/.gitignore b/.gitignore index a23729142c..7172cf0274 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/Tools/scripts/install-prereqs-ubuntu.sh b/Tools/scripts/install-prereqs-ubuntu.sh index 447470a24a..7510e06895 100755 --- a/Tools/scripts/install-prereqs-ubuntu.sh +++ b/Tools/scripts/install-prereqs-ubuntu.sh @@ -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 diff --git a/wscript b/wscript index a01f21c1c4..a0fe1001e1 100644 --- a/wscript +++ b/wscript @@ -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()